`
peryt
  • 浏览: 52454 次
  • 来自: ...
最近访客 更多访客>>
社区版块
存档分类
最新评论
  • waiting: 既然都指定了dataType为'script'那就不必特别在b ...
    jQuery

11.3 manipulating microposts.

 
阅读更多

1. since all micropost actions will be done in users page, so we only need :create and :destroy actions.

 

so the routes will be:

 

  resources :microposts, :only => [:create, :destroy]

 

2. we will test access control to the microposts controller first:

 

describe MicropostsController do
  render_views

  describe "access control" do

    it "should deny access to 'create'" do
      post :create
      response.should redirect_to(signin_path)
    end

    it "should deny access to 'destroy'" do
      delete :destroy, :id => 1
      response.should redirect_to(signin_path)
    end
  end
end

 

3. next we will try to create mircropost.

here is the test:

 

describe MicropostsController do
  .
  .
  . 
  describe "POST 'create'" do

    before(:each) do
      @user = test_sign_in(Factory(:user))
    end

    describe "failure" do

      before(:each) do
        @attr = { :content => "" }
      end

      it "should not create a micropost" do
        lambda do
          post :create, :micropost => @attr
        end.should_not change(Micropost, :count)
      end

      it "should render the home page" do
        post :create, :micropost => @attr
        response.should render_template('pages/home')
      end
    end

    describe "success" do

      before(:each) do
        @attr = { :content => "Lorem ipsum" }
      end

      it "should create a micropost" do
        lambda do
          post :create, :micropost => @attr
        end.should change(Micropost, :count).by(1)
      end

      it "should redirect to the home page" do
        post :create, :micropost => @attr
        response.should redirect_to(root_path)
      end

      it "should have a flash message" do
        post :create, :micropost => @attr
        flash[:success].should =~ /micropost created/i
      end
    end
  end
end

 

then we will write the create action.

 

  def create
    @micropost =  current_user.microposts.build(params[:micropost])
    if @micropost.save
      flash[:success] = "Micropost created!"
      redirect_to root_path
    else
      render 'pages/home'
    end
  end

 4. now it is time to make a form for creating new micropost:

 

<% if signed_in? %>
  <table class="front" summary="For signed-in users">
    <tr>
      <td class="main">
        <h1 class="micropost">What's up?</h1>
        <%= render 'shared/micropost_form' %>
      </td>
      <td class="sidebar round">
        <%= render 'shared/user_info' %>
      </td>
    </tr>
  </table>
<% else %>
  <h1>Sample App</h1>

  <p>
    This is the home page for the
    <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
    sample application.
  </p>

  <%= link_to "Sign up now!", signup_path, :class => "signup_button round" %>
<% end %>

 and the partials:

 

<%= form_for @micropost do |f| %>
  <%= render 'shared/error_messages', :object => f.object %>
  <div class="field">
    <%= f.text_area :content %>
  </div>
  <div class="actions">
    <%= f.submit "Submit" %>
  </div>
<% end %>

 <div class="user_info">

  <a href="<%= user_path(current_user) %>">
    <%= gravatar_for current_user, :size => 30 %>
    <span class="user_name">
      <%= current_user.name %>
    </span>
    <span class="microposts">
      <%= pluralize(current_user.microposts.count, "micropost") %>
    </span>
  </a>
</div>

 

5. add a feed of this user's micropost:

this feed include all this user's micropost as an array:

here is the test codes:

 

    describe "status feed" do
      it "should have a feed" do
        @user.should respond_to :feed
      end
      it "should include the user's microposts" do
        @user.feed.include?(@mp1).should be_true
        @user.feed.include?(@mp2).should be_true
      end
      it "should not include a diff user's micropost" do
        mp3 = Factory(:micropost, :user => Factory(:user, :email => Factory.next(:email)))
        @user.feed.include?(mp3).shoud be_false
      end

 

6. for the feed method:

def feed
  Mircropost.where("user_id = ?", id)
end

  note:

the question mark is use to escape the id to prevent SQL injection.

 

7. in the home controller we need to prepare the pagination of the feed:

 

 def home
    @title = "Home"
    if signed_in?
      @micropost = Micropost.new
      @feed_items = current_user.feed.paginate(:page => params[:page])
    end
  end

 

 

8. then it is turn for the _feed partial:

 

<% unless @feed_items.empty? %>
  <table class="microposts" summary="User microposts">
    <%= render :partial => 'shared/feed_item', :collection => @feed_items %>
  </table>
  <%= will_paginate @feed_items %>
<% end %>

 

 you can see that we are not omitting the :partial param in this rendering of partial.

 

because we have another param of :collection, so the :partial can not be omitted.

 

9. now it is the _feed_item.html.erb partial:

 

 

<tr>
  <td class="gravatar">
    <%= link_to gravatar_for(feed_item.user), feed_item.user %>
  </td>
  <td class="micropost">
    <span class="user">
      <%= link_to feed_item.user.name, feed_item.user %>
    </span>
    <span class="content"><%= feed_item.content %></span>
    <span class="timestamp">
      Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
    </span>
  </td>
  <% if current_user?(feed_item.user) %>
  <td>
    <%= link_to "delete", feed_item, :method => :delete,
                                     :confirm => "You sure?",
                                     :title => feed_item.content %>
  </td>
  <% end %>
</tr>

 

 

10. destroying micropost:

we need to add delete link to _mircropost.html.erb partial:

 

<% if current_user?(micropost.user) %>
		<td>
			<%= link_to 'Delete', micropost, :method => :delete, :confirm => "you sure?", :title => micropost.content %>
		</td>
	<% end %>

 

 

look at the below code, it is amazing:

 

user = micropost.user rescue User.find(micropost.user_id)

 

 in this line of code, it try to get micropost.user, if get an exception, it will instead get from User.find(micropost.user_id)

 

11. then let's try to add test to the destroy action:

 

describe MicropostsController do
  .
  .
  .
  describe "DELETE 'destroy'" do

    describe "for an unauthorized user" do

      before(:each) do
        @user = Factory(:user)
        wrong_user = Factory(:user, :email => Factory.next(:email))
        test_sign_in(wrong_user)
        @micropost = Factory(:micropost, :user => @user)
      end

      it "should deny access" do
        delete :destroy, :id => @micropost
        response.should redirect_to(root_path)
      end
    end

    describe "for an authorized user" do

      before(:each) do
        @user = test_sign_in(Factory(:user))
        @micropost = Factory(:micropost, :user => @user)
      end

      it "should destroy the micropost" do
        lambda do 
          delete :destroy, :id => @micropost
        end.should change(Micropost, :count).by(-1)
      end
    end
  end
end

 

 12. let's try to implement the destroy action:

 

before_filter :authorized_user, :only => :destroy

 class MicropostsController < ApplicationController

 

  before_filter :authenticate, :only => [:create, :destroy]
  before_filter :authorized_user, :only => :destroy
  .
  .
  .
  def destroy
    @micropost.destroy
    redirect_back_or root_path
  end

  private

    def authorized_user
      @micropost = current_user.microposts.find_by_id(params[:id])
      redirect_to root_path if @micropost.nil?
    end
end

 

note, we use find_by_id instead of find, the later one will throw an exception if not found.

if you are comfortable with the exception, you can also write this code:

 

def authorized_user
  @micropost = current_user.microposts.find(params[:id])
rescue
  redirect_to root_path
end
 

 

13. ok, we are done, now we will do some integration test for micropost.

 

 

describe "Microposts" do

  before(:each) do
    user = Factory(:user)
    visit signin_path
    fill_in :email,    :with => user.email
    fill_in :password, :with => user.password
    click_button
  end

  describe "creation" do

    describe "failure" do

      it "should not make a new micropost" do
        lambda do
          visit root_path
          fill_in :micropost_content, :with => ""
          click_button
          response.should render_template('pages/home')
          response.should have_selector("div#error_explanation")
        end.should_not change(Micropost, :count)
      end
    end

    describe "success" do

      it "should make a new micropost" do
        content = "Lorem ipsum dolor sit amet"
        lambda do
          visit root_path
          fill_in :micropost_content, :with => content
          click_button
          response.should have_selector("span.content", :content => content)
        end.should change(Micropost, :count).by(1)
      end
    end
  end
end
 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics