Topic: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

The first 10 steps tipically used for every Rails Application.
The steps 10-15 make your Blog functional.
If you choose to use AJAX follow additional steps from 16-20.

1.- rails blog -d mysql

2.- cd blog

3.- Edit your config/database.yml

4.- rake db:create:all

5.- Lets use the generators !!!

ruby script\generate scaffold Post title:string body:text
ruby script\generate scaffold Comment body:text post:references

6.- rake db:migrate

7.- In config/routes.rb:   map.root :controller => "posts"

8.- Delete public/index.html

9.- Edit the post & comments model.

[app/models/post.rb]
class Post < ActiveRecord::Base
  has_many :comments
end

[app/models/comment.rb]
class Comment < ActiveRecord::Base
  belongs_to :post
end


10.- ruby script\server
http://127.0.0.1:3000/posts

Create a couple Posts
Helo Rails 2.0.2
My Rails 2.0.2 Blog

All this is pretty standar procedure creating Rails Applications.

11.- Create a Partial file called [ _show_comments.html.erb ] in [ app/view/posts/ ]
You can copy the file from [ app/view/comments/index.html.erb ]

cp app/view/comments/index.html.erb app/view/posts/_show_comments.html.erb

11b.- Edit the file [ app/view/posts/_show_comments.html.erb ] and modify the line:
OLD <% for comment in @comments %> to  NEW <% for comment in @post.comments %>
This will use the asociation between Post/Comments

11c.- Delete the last line:
<%= link_to 'New comment', new_comment_path %>
We will insert another partial to do this in the same page.

12.- Lets include the partial in the show post file to show the comments for that particular Post !!!
Edit [ app/view/posts/show.html.erb ] add to the end of the file:

<%= render :partial => "show_comments" %>

Browse to the show Post and you should be able to see the list of comments for each Post.
NOTE: Unless you use the console at this time there is no comments in our Blog. as you complete the remaining steps you will be able to do it.

13.- Create a Partial file called [ _new_comment.html.erb ] in [ app/view/posts/ ]
You can copy the file from [ app/view/comments/new.html.erb ]

cp app/view/comments/new.html.erb app/view/posts/_new_comment.html.erb

13b.- Edit the file: [app/view/posts/_new_comment.html.erb]
Change line:
<% form_for(@comment) do |f| %>
to:
<% form_for :comment, @comment, :url => comments_path do |f| %>

13c.- Change our reference to the post_id using the @post.id value smile :

  <p>
    <b>Post</b><br />
    <%= f.text_field :post %>
  </p>

to:
  <p>
    <%= f.hidden_field "post_id", :value => @post.id %>
  </p>


13d.- Delete the last line:
<%= link_to 'Back', comments_path %>
Is not necesary since we always use the Show/Post page.

14.- Lets include the partial in the show post file to show the form to enter comments for that particular Post !!!
Edit [ app/view/posts/show.html.erb ] add to the end of the file:

<%= render :partial => "show_comments" %>

15.- At this point the Comments form is fully functional, but lets modify the redirect to back to the same page.

Edit the file [app/contorllers/comments_controller.rb]
In the create method change:
        #format.html { redirect_to(@comment) }
        format.html { redirect_to(:controller => 'posts', :action => 'show', :id => @comment.post_id ) }
This will automatically redirect to the specific Post when we add our comment !!!.

That's all folks !!!

Feel free to modify your views, stylesheets & layouts to fit the look & feel of your blog.

Modify the _show_comments.html.erb partial to show the title of the post.
<%=h comment.post %> to <%=h comment.post.title %>

The _show_comments partial can show the number of Comments if you change the first line:
<h1>Listing comments (<%= @post.comments.size %>)</h1

This Tutorial is not ready to deploy in a production server is just for learning purposes. please look into:
a) RoR 2.0.2 :: Very simple authentication from scratch in 15 steps
http://railsforum.com/viewtopic.php?id=15130
b) HowTo - Advanced MD5 database authentication from scratch in 20 steps.
http://railsforum.com/viewtopic.php?id=17512
c) Perhaps check into some of the rails plugins to make it secure !!!.

Keep in mind the comments URL still accessible, needs to be secured too.

* AJAX functionality is an option
The recipe should add few more steps like:

16.- Lets add some AJAX
Edit the file [ app/views/layouts/posts.html.erb ] add to the <head> section:
  <%= javascript_include_tag :defaults %>

17.- Lets update the _new_comment.html.erb partial replacing form_for with remote_form_for:
<% remote_form_for :comment, @comment, :url => comments_path do |f| %>

18.- When the submit button send the request is going to expect JavaScript code to be returned. So we need to update the respond_to block to

respond appropriately [app/controller/comments_controller.rb]
format.js # renders create.js.rjs

19.- Create the [app/views/comments/create.js.rjs]

page[:show_comments].replace_html :partial => 'posts/show_comments', 
                             :locals  => {:post    => @post}

20.- Create the <div> with id="show_comments" in the show posts page:
<div id="show_comments">
<%= render :partial => "show_comments" %>
</div>

Once you save your changes you should be able to add comments and the portion of the page with the div="show_comments" should be refreshed. I

I might be missing some little detail. The new comment post succeed but does not update the page. I'm trying to debug with Firebug.
Any suggestion is welcome !!!.  But you got the idea smile

This is the error returned from Firebug and the same reported in the development.log sad
########################################
<p>
  Showing <i>posts/_show_comments.html.erb</i> where line <b>#9</b> raised:
  <pre><code>You have a nil object when you didn't expect it!
The error occurred while evaluating nil.comments</code></pre>
</p>
<p>Extracted source (around line <b>#9</b>):
<pre><code>6:     &lt;th&gt;Post&lt;/th&gt;
7:   &lt;/tr&gt;
8:
9: &lt;% for comment in @post.comments %&gt;
10:   &lt;tr&gt;
11:     &lt;td&gt;&lt;%=h comment.body %&gt;&lt;/td&gt;
12:     &lt;td&gt;&lt;%=h comment.post.title %&gt;&lt;/td&gt;
</code></pre></p>
########################################

Dinooz.
Brains R like Books only work when they R Open.

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

Here is the update for steps 13b and 15 which can be acomplished in a much simpler way using the conventions of Rails.

Step 13b Another way to acomplish the same result with changes in the Post Controller

If you decide to leave the default:
[app/view/posts/_new_comment.html.erb]

...
<% form_for(@comment) do |f| %>
...

Declare the @comment in the [ app/controllers/posts_controller.rb ] in the show method:
 def show
    @post = Post.find(params[:id])
    @comment = Comment.new

That's all now will work just fine !!!.

Step: 15 Another way to acomplish the same result with changes in the Post Controller
In order to keep the redirection with similar sintax to the default created code, lets add the value of the @post in the create acction of the Post controller:

  def create
    @comment  = Comment.new(params[:comment])
    @post = Post.find(@comment.post_id)

NOTE: We carry the variable @comment.post_id from the hidden value in the show Post form.

Now the redirect can be similar to the previously code using the variable @post !!!.

        #format.html { redirect_to(@comment) }
        format.html { redirect_to(@post) }

As soon as got the fix for the last step of the recipe with AJAX will post it smile.

Dinooz

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

Hey there, newb here.

Followed all the steps up till Ajax stuff but cant get it to work i cant create a new comment.
Error;

Post expected, got String


C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations/association_proxy.rb:150:in `raise_on_type_mismatch'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations/belongs_to_association.rb:22:in `replace'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations.rb:1009:in `post='
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:2117:in `send'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:2117:in `attributes='
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:2116:in `each'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:2116:in `attributes='
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:1926:in `initialize'
app/controllers/comments_controller.rb:43:in `new'
app/controllers/comments_controller.rb:43:in `create'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:1158:in `send'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:1158:in `perform_action_without_filters'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/filters.rb:697:in `call_filters'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/filters.rb:689:in `perform_action_without_benchmark'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/1.8/benchmark.rb:293:in `measure'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/rescue.rb:199:in `perform_action_without_caching'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/caching.rb:678:in `perform_action'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract/query_cache.rb:33:in `cache'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/query_cache.rb:8:in `cache'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/caching.rb:677:in `perform_action'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:524:in `send'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:524:in `process_without_filters'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/filters.rb:685:in `process_without_session_management_support'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/session_management.rb:123:in `process'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:388:in `process'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/dispatcher.rb:171:in `handle_request'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/dispatcher.rb:115:in `dispatch'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/dispatcher.rb:126:in `dispatch_cgi'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/dispatcher.rb:9:in `dispatch'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel/rails.rb:78:in `process'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel/rails.rb:76:in `synchronize'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel/rails.rb:76:in `process'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:618:in `process_client'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:617:in `each'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:617:in `process_client'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:736:in `run'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:736:in `initialize'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:736:in `new'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:736:in `run'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:720:in `initialize'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:720:in `new'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel.rb:720:in `run'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel/configurator.rb:271:in `run'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel/configurator.rb:270:in `each'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel/configurator.rb:270:in `run'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/bin/mongrel_rails:127:in `run'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/lib/mongrel/command.rb:211:in `run'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/mongrel-1.0.1-mswin32/bin/mongrel_rails:243
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:489:in `load'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:489:in `load'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:489:in `load'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/rails-2.0.2/lib/commands/servers/mongrel.rb:64
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:496:in `require'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:496:in `require'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/gems/1.8/gems/rails-2.0.2/lib/commands/server.rb:39
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
C:/InstantRails-1.7-win/InstantRails/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
script/server:3

the hidden field does not seem to be functioning. Some comments maybe would be useful.
Thanks in Advance

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

Thanks for the tutorial, I do however encountered a problem.
It seems comment form is not showing up. When I click 'show' from main page, I get the following

Title: Test

Body: test

Edit | Back
Listing comments

Body    Post

It looks good except there is no form to submit a comment.

I am probably doing something stupid, but please help!

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

hi thanks a lot for the tutorial..
I went through the first 15 steps and all went along nicely..

This was the first thing i tried after installing rails..

But now i need to understand whats happening in more detail, which tut would be best to start...
also i want to change the CSS & am trying to use Blueprint CSS..
http://code.google.com/p/blueprintcss/
But am getting no where ???

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

mr.flow3r wrote:

Thanks for the tutorial, I do however encountered a problem.
It seems comment form is not showing up. When I click 'show' from main page, I get the following

Title: Test

Body: test

Edit | Back
Listing comments

Body    Post

It looks good except there is no form to submit a comment.

I am probably doing something stupid, but please help!

There was a typo, in step 14 it says to edit the file app/view/posts/show.html.erb
and it says to add this line of code at the bottom

<%= render :partial => "show_comments" %>

when it should be

<%= render :partial => "new_comment" %>

should work after that.

thanks for the tutorial dinooz keep up the good work. If you want to fix that typo may help future readers.

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

It works very well.
Thanks a million time.
It's reeeeeeeally helpful.

Last edited by danxer (2008-10-23 23:31:14)

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

Wow, this looks like a great tutorial. Just want to check before I go through it - this is for rails 2.0, right?

Re: HowTo Create a Rails Blog in 15 steps without nested resources + AJAX

Solution to the problem concerning the AJAX functionality, steps 16-19, add an instance of the current post in the CommentsController create def

def create
   @comment = Comment.new(params[:comment])
   #additional line for @post
   @post = Post.find(@comment.post_id)
   #.... rest of the code
end

Now updating partial _show_comments.rhtml show work wink