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
发表评论
-
12.3.3 scaling issue of the status feed
2011-10-30 17:54 801the problem of the implementati ... -
12.3 the status feed
2011-10-30 15:34 8491. we need to get all the micro ... -
12.2 a working follow button with Ajax
2011-10-29 18:10 9021. in the last chapter, in the ... -
12.2 a web interface for following and followers.
2011-10-28 22:14 8681.before we do the UI, we need ... -
12. following user, 12.1 relationship model
2011-10-18 14:29 7361. we need to use a relationshi ... -
11.2 show microposts.
2011-10-17 12:01 6941. add test to test the new use ... -
11.1 user micropost -- a micropost model.
2011-10-17 10:43 10941. we will first generate a mic ... -
10.4 destroying users.
2011-10-16 15:47 724in this chapter, we will add de ... -
10.3 showing users list
2011-10-15 20:41 762in this chapter, we will do use ... -
10.2 protect pages.
2011-10-15 15:11 645again, we will start from TD ... -
10.1 updating users.
2011-10-14 18:30 6971. git checkout -b updating-use ... -
9.4 sign out
2011-10-13 15:21 724whew!!!, last chapter is a long ... -
9.3 sign in success.
2011-10-12 15:39 7351. we will first finish the cre ... -
9.1 about flash.now[:error] vs flash[:error]
2011-10-12 15:37 714There’s a subtle difference ... -
9.2 sign in failure
2011-10-12 12:19 652start from TDD!!! 1. requir ... -
9.1 sessions
2011-10-12 10:00 640a session is a semi-permanent c ... -
what test framework should you use?
2011-10-11 16:56 0for integration test, i have no ... -
what test framework should you use?
2011-10-11 16:56 0<p>for integration test, ... -
8.4 rspec integration tests
2011-10-11 16:53 707in integration test, you can te ... -
8.3 sign up success
2011-10-11 14:39 772Chapter 8.3 this part, we will ...
相关推荐
matlab作业1题目 Introduction to MATLAB This homework is designed to teach you to think in terms of matrices and vectors because this is how MATLAB organizes data. You will find that complicated ...
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,被广泛用于Web服务和应用程序之间的数据传输。在C语言环境中,处理JSON数据通常需要借助专门的库,本压缩包提供的就是一个C语言编写的用于编码、...
Bringing the power of SQL to Flink, this book will then explore the Table API for querying and manipulating data. In the latter half of the book, readers will get to learn the remaining ecosystem of ...
1 You Write Software; You have Bugs . . . . . . . . ....2 A Systematic Approach to Debugging .... ....2.2 Making the Most of Your Opportunities ....2.3 13 Golden Rules ....2.3.1 Understand the Requirements ....
Table of Contents Summary of gdb . . . . . . . . ....Free software ....Free Software Needs Free Documentation ....Contributors to gdb....1 A Sample gdb Session ....2 Getting In and Out of gdb ....2.1 Invoking gdb ....
Table of Contents Summary of GDB . . . . . . . . ....Free software ....Free Software Needs Free Documentation ....Contributors to GDB ....1 A Sample GDB Session ....2 Getting In and Out of GDB ....Invoking GDB ....
这是标准的官方库,请大家仔细参详! Libraries 目录 Libraries provide extra ... working with hardware or manipulating data. To use a library in a sketch, select it from Sketch > Import Library.
2 Representing and Manipulating Information 21 2.1 Information Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.1.1 HexadecimalNotation . . . . . . . . . . . ...
Conditional Branch Logger is a plugin which gives control and logging capabilities for conditional ... Numerous options are available for fine tuning the logging ranges and manipulating breakpoints.
Table of Contents Section 1 Introduction to SystemVerilog ...................................................................................................... 1 Section 2 Literal Values................
Not Using Commons Logging ................................................................... 12 Using SLF4J ..............................................................................................
data: It contains all the data accessing and manipulating components. ui: View classes along with their corresponding Presenters. utils: Utility classes. Key points Using base abstract classes to ...
Image processing algorithms based on the mammalian visual cortex are powerful tools for extraction information and manipulating images. This book reviews the neural theory and translates them into ...
data: It contains all the data accessing and manipulating components. ui: View classes along with their corresponding ViewModel. utils: Utility classes. Using Jetpack Architecture Components ...
Not Using Commons Logging ................................................................... 12 Using SLF4J ..............................................................................................
Enable participation by manipulating data. Share data by implementing powerful search utilities. Syndicate web content created using RSS and REST. Discover advanced features available...
《SAS编程II:使用DATA步操作数据》是SAS学习者进阶的重要教程,主要聚焦于如何利用SAS的DATA步来高效处理和转换数据。在这个教程中,你将深入理解SAS编程的核心概念,特别是DATA步在数据管理中的应用。...
Section 11.3. Constructor Overloading and Destructors 496 Section 11.4. Default Arguments and Object Assignment 504 Section 11.5. General Function Overloading and Function Templates 511 Chapter...