The nesting in /config/routes.rb has already been taken care of in the first tutorial:
map.resources :topics, :has_many => :replies
The first thing we are going to do is to create partials for /app/views/replies/new.html.erb and /app/views/replies/edit.html.erb. Create the file /app/views/replies/_reply.html.erb
#/app/views/replies/_reply.html.erb
<% form_for([@topic, @reply]) do |f| %>
<p>
<b>Subject</b><br />
<%= f.text_field :subject %>
</p>
<p>
<b>Body</b><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit button_name %>
</p>
<% end %>
And now change edit and new to:
#/app/views/replies/edit.html.erb
<h1>Editing reply</h1>
<%= error_messages_for :reply %>
<%= render :partial => @reply, :locals => { :button_name => "Submit" } %>
<%= link_to 'Show', [@topic, @reply] %> |
<%= link_to 'Back', replies_path %>
#/app/views/replies/new.html.erb
<h1>New reply</h1>
<%= error_messages_for :reply %>
<%= render :partial => @reply, :locals => { :button_name => "Reply" } %>
<%= link_to 'Back', replies_path %>
Same stuff we did last time...
Now lets go to /app/controllers/replies_controller.rb
Add this at the top, to make sure a topic gets loaded everytime the replies controller is used:
before_filter :load_topic
def load_topic
@topic = Topic.find(params[:topic_id])
end
inside the new function change the line:
@reply = Reply.new
to this:
@reply = @topic.replies.build
Change the edit function:
#old edit function
def edit
@reply = Reply.find(params[:id])
end
#new edit function
def edit
@reply = @topic.replies.find(params[:id])
end
Change the create function:
#old create
def create
@reply = Reply.new(params[:reply])
respond_to do |format|
if @reply.save
flash[:notice] = 'Reply was successfully created.'
format.html { redirect_to(@reply) }
format.xml { render :xml => @reply, :status => :created, :location => @reply }
else
format.html { render :action => "new" }
format.xml { render :xml => @reply.errors, :status => :unprocessable_entity }
end
end
end
#new create
def create
@reply = @topic.replies.build(params[:reply])
respond_to do |format|
if @reply.save
flash[:notice] = 'Reply was successfully created.'
format.html { redirect_to([Forum.find(@topic.forum_id), @topic]) }
format.xml { render :xml => @reply, :status => :created, :location => @reply }
else
format.html { render :action => "new" }
format.xml { render :xml => @reply.errors, :status => :unprocessable_entity }
end
end
end
Notice the change in redirect_to...after a reply to a topic is submitted, I want to send the user back to the forum the topic was in, to get the forum, I had to use the find function and pass it the forum_id of the topic and the topic.
In the update and destroy functions:
#old update and destroy
def update
@reply = Reply.find(params[:id])
respond_to do |format|
if @reply.update_attributes(params[:reply])
flash[:notice] = 'Reply was successfully updated.'
format.html { redirect_to(@reply) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @reply.errors, :status => :unprocessable_entity }
end
end
end
def destroy
@reply = Reply.find(params[:id])
@reply.destroy
respond_to do |format|
format.html { redirect_to(replies_url) }
format.xml { head :ok }
end
end
#new update and destroy
def update
@reply = @topic.replies.find(params[:id])
respond_to do |format|
if @reply.update_attributes(params[:reply])
flash[:notice] = 'Reply was successfully updated.'
format.html { redirect_to(@topic) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @reply.errors, :status => :unprocessable_entity }
end
end
end
def destroy
@reply = @topic.replies.find(params[:id])
@reply.destroy
respond_to do |format|
format.html { redirect_to(@topic) }
format.xml { head :ok }
end
end
Now that we have all of that done...lets head over to /app/views/topics/show.html.erb so we can display our replies and the form for a new reply.
In addition to showing replies and a form for a new one, I changed the formatting a little, so here are the contents on show.html.erb
<p>
<h3>Forum: - <%=h @forum.name %></h3>
</p>
<p>
<b><%=h @topic.subject %></b>
</p>
<p>
<%=h @topic.body %>
</p>
<% unless @topic.replies.empty? %>
<% @topic.replies.each do |reply| %>
<p>
<b><%= reply.subject %></b>
</p>
<p>
<%=h reply.body %>
</p>
<% end %>
<% end %>
<h2>Reply</h2>
<%= render :partial => @reply = Reply.new, :locals => { :button_name => 'Reply' } %>
<%= link_to 'Edit', edit_forum_topic_path(@forum, @topic) %> |
<%= link_to 'Back', topics_path %>
Since this is the same stuff we were doing in the first tutorial, I won't bother explaining any of it...you should be able to tell what is going on. Feel free to leave a comment if you do have any questions.
Now, fire up the web server and head to localhost:3000/forums/1/topics/1 (assuming you have a topic already created).
And create a couple of replies:
And thats it! (10 points go to anyone who catches the video game reference in reply #2 in the screenshot).
In this tutorial, we nested replies inside of a topic, and set the views/controllers accordingly. Next time, we will take a look at authentication using the RESTful authentication plug-in and acts_as_state_machine plugin.
Feel free to leave comments, and link me from your site. See you next time!
16 comments:
Thank you for this great tutorial.
I'am looking forward to the last part.
Marek
Flamestrike! Ultima online references in the screenshot.
chouinard += 10
Thank you for making RoR seem less like Harry Potter & more like Transformers. Currently I am having problems with line 29 in topics/show.html.erb should there be [square brackets] around the params in edit_topic_path(@forum, @topic)?
Regards,
Bill
(ahhhhhh, just answered my own question)
I just found your tutorial and it's excellent. Also the video game reference would the the words of power from Ultima Online
lol "kal vas flam" !
Nice to see some UO fans out there!
Great tute, I'd love to see some comments on Ruby syntax, I can't find a tutorial where learning Rails has some dashes of Ruby thrown in...
Part 2 is nice reinforcement for what was learned in part 1, and some of the differences provided insight into the workings of MCV.
The only thing I did a bit differently was to add
validates_presence_of :subject, :body
to the app\models reply.rb and topic.rb files.
you blog is very helpfull!thanks.
the topics/show.html.erb
last line is better replace by this:
= link_to 'Back', forum_path
When I click the "Reply" button,
I got this error:
ArgumentError in RepliesController#create
wrong number of arguments (2 for 1)
RAILS_ROOT: D:/railsProjects/myforum
Application Trace | Framework Trace | Full Trace
app/controllers/replies_controller.rb:54:in `[]'
app/controllers/replies_controller.rb:54:in `create'
app/controllers/replies_controller.rb:51:in `create'
Pls give me some suggestion.
Thanks.
When click a topic,then get the following error:
NoMethodError in Topics#show
Showing topics/show.html.erb where line #15 raised:
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.subject
Extracted source (around line #15):
12:
13: unless @topic.replies.empty?
14:
15: @reply.subject
16:
18: @reply.body
RAILS_ROOT: D:/railsProjects/myforum
What's the problem here?
Could anyone help me?
Thanks.
It works for me now.
Find the problem.
I'm getting a problem after a reply:
undefined method `replies' for # < Topic:0xb7458348 >
RAILS_ROOT: /home/friedman/public_html/studentsfirst
Application Trace | Framework Trace | Full Trace
/usr/lib/ruby/gems/1.8/gems/activerecord-2.1.2/lib/active_record/attribute_methods.rb:256:in `method_missing'
app/controllers/replies_controller.rb:51:in `create'
Could you be of any help??
Thanks,
Jacob
Never mind! Fixed it. Was a typo in a model. Figures...
Again, thank you for your time to make such a great tutorial. Your efforts are nothing less than noble.
nice tutor, keep it up..
Goodness, there's a great deal of useful info in this post!
Post a Comment