diff --git a/CHANGELOG b/CHANGELOG index 4735aca..e6033e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +* API change: save_resource deprecated + + So save_resource is now deprecated, just use resource.save + ActiveRecords can now be asked if they are saved? + * rspec compat: Added new rake task to test that an RC controller passes the default rspec_scaffold controller specs. diff --git a/README.rdoc b/README.rdoc index 3912149..e35fa01 100644 --- a/README.rdoc +++ b/README.rdoc @@ -14,6 +14,7 @@ resources_controller works with rails 2.x and edge. * The SPECDOC lists the specifications * Coverage is 100% (C0), and the spec suite is quite comprehensive +* Rspec's generated rspec_scaffold controller specs are tested against a simple rc controller (see rake spec:generate) RSpec is used for testing, so the tests are in spec/ rather than test/ Do rake --tasks for more details. diff --git a/Rakefile b/Rakefile index 4a65036..8fce7b6 100644 --- a/Rakefile +++ b/Rakefile @@ -60,7 +60,7 @@ Rake::RDocTask.new(:doc) do |t| t.title = "#{plugin_name}" t.template = ENV['RDOC_TEMPLATE'] t.options = ['--line-numbers', '--inline-source', '--all'] - t.rdoc_files.include('README', 'SPECDOC', 'MIT-LICENSE', 'CHANGELOG') + t.rdoc_files.include('README.rdoc', 'SPECDOC', 'MIT-LICENSE', 'CHANGELOG') t.rdoc_files.include('lib/**/*.rb') end diff --git a/SPECDOC b/SPECDOC index 0c0bf97..2378792 100644 --- a/SPECDOC +++ b/SPECDOC @@ -256,6 +256,53 @@ Requesting /forums/3/posts/3/comments/1 using DELETE - should call destroy on the found comment - should redirect to the comments list +CommentsController without stubs responding to GET index +- should expose all comments as @comments + +CommentsController without stubs responding to GET index with mime type of xml +- should render all comments as xml + +CommentsController without stubs responding to GET show +- should expose the requested comment as @comment + +CommentsController without stubs responding to GET show with mime type of xml +- should render the requested comment as xml + +CommentsController without stubs responding to GET new +- should expose a new comment as @comment + +CommentsController without stubs responding to GET edit +- should expose the requested comment as @comment + +CommentsController without stubs responding to POST create with valid params +- should create a comment +- should expose the newly created comment as @comment +- should be resource_saved? +- should redirect to the created comment + +CommentsController without stubs responding to POST create with invalid params +- should not create a comment +- should expose a newly created but unsaved comment as @comment +- should not be resource_saved? +- should re-render the 'new' template + +CommentsController without stubs responding to PUT udpate with valid params +- should update the requested comment +- should not contain errors on comment +- should be resource_saved? +- should expose the requested comment as @comment +- should redirect to the comment + +CommentsController without stubs responding to PUT udpate with invalid params +- should fail to update the requested comment +- should not be resource_saved? +- should expose the requested comment as @comment +- should re-render the 'edit' template + +CommentsController without stubs responding to DELETE destroy +- should delete the requested comment +- should redirect to the comments list + Routing shortcuts for ForumPosts (forums/2/posts/1) should map - resources_path to /forums/2/posts - resource_path to /forums/2/posts/1 @@ -575,6 +622,25 @@ Requesting /forums/2/owner using DELETE - should set the flash notice - should redirect to forums/2 +CommentsController#resource_saved Comment.new() +- should not be resource saved + +CommentsController#resource_saved Comment.new().save +- should not be resource saved + +CommentsController#resource_saved Comment.new().save then update_attributes() +- should be resource saved + +CommentsController#resource_saved Comment.find() +- should be resource saved +- .save should be saved + +CommentsController#resource_saved Comment.find() then update_attributes() +- should not be resource saved + +CommentsController#resource_saved Comment.find() then update_attributes() +- should be resource saved + Routing shortcuts for Tags should map - resources_path to /tags - resource_path to /tags/2 @@ -768,6 +834,18 @@ UsersController handling PUT /users/dave UsersController handling DELETE /users/dave - should be unknown action +(re: saved?) Comment.new() +- should not be validation attempted +- should not be saved + +(re: saved?) Comment.new().save +- should be validation attempted +- should not be saved + +(re: saved?) Comment.new().save then update_attributes() +- should be validation attempted +- should be saved + ActionView with resources_controller Helper - should forward #resource_name to controller - should forward #resources_name to controller @@ -792,12 +870,6 @@ Helper#form_for_resource (when resource is existing record) Helper#remote_form_for_resource (when resource is existing record) - should call remote_form_for with update form options -ResourcesController.load_enclosing_resources_filter_exists? when :find_filter defined -- should call :find_filter with :load_enclosing_resources - -ResourcesController.load_enclosing_resources_filter_exists? when :find_filter not defined -- should call :filter_chain - #load_enclosing_resources for resources_controller_for :tags (when route_enclosing_names is [['users', false]]) - should call load_wildcard once - should call Specification.new('user', :singleton => false, :as => nil) @@ -839,6 +911,12 @@ ResourcesController.load_enclosing_resources_filter_exists? when :find_filter no - should call load_enclosing_resource_from_specification with user spec, then load_wildcard once with 'taggable' - should call Specification.new with ('comment', :singleton => false, :as => 'taggable') +ResourcesController.load_enclosing_resources_filter_exists? when :find_filter defined +- should call :find_filter with :load_enclosing_resources + +ResourcesController.load_enclosing_resources_filter_exists? when :find_filter not defined +- should call :filter_chain + ResourcesController (in general) - nested_in :foo, :polymorphic => true, :class => User should raise argument error (no options or block with polymorphic) - resources_controller_for :forums, :in => [:user, '*', '*', :comment] should raise argument error (no multiple wildcards in a row) @@ -849,6 +927,9 @@ ResourcesController#enclosing_resource_name A controller's resource_service - may be explicitly set with #resource_service= +deprecated methods +- #save_resource should send resource.save + #route_enclosing_names TagsController for named_route: - :tags should be [] - :new_tag should be [] @@ -870,6 +951,6 @@ A controller's resource_service #route_enclosing_names Admin::Superduper::ForumsController for named_route: - :admin_superduper_forums should be [] -Finished in 5.097926 seconds +Finished in 8.293483 seconds -593 examples, 0 failures +632 examples, 0 failures diff --git a/garlic_example.rb b/garlic_example.rb index 3d5bcb3..8d82a29 100644 --- a/garlic_example.rb +++ b/garlic_example.rb @@ -23,7 +23,7 @@ target '2.0-stable', :branch => 'origin/2-0-stable' target '2.1-stable', :branch => 'origin/2-1-stable' target '2.0.3', :tag => 'v2.0.3' - target '2.1.0', :tag => 'v2.1.0' + target '2.1.1', :tag => 'v2.1.1' all_targets do prepare do @@ -36,8 +36,7 @@ run do cd "vendor/plugins/resources_controller" do - sh "rake spec:rcov:verify" - sh "rake spec:generate" + sh "rake spec:rcov:verify && rake spec:generate" end end end diff --git a/init.rb b/init.rb index 43947a1..a3aa07f 100644 --- a/init.rb +++ b/init.rb @@ -1,3 +1,5 @@ require 'ardes/resources_controller' +ActionController::Base.extend Ardes::ResourcesController -ActionController::Base.extend Ardes::ResourcesController \ No newline at end of file +require 'ardes/active_record/saved' +ActiveRecord::Base.send :include, Ardes::ActiveRecord::Saved \ No newline at end of file diff --git a/lib/ardes/active_record/saved.rb b/lib/ardes/active_record/saved.rb new file mode 100644 index 0000000..7353024 --- /dev/null +++ b/lib/ardes/active_record/saved.rb @@ -0,0 +1,15 @@ +module Ardes + module ActiveRecord + module Saved + # returns true if this record is not new, and has no errors + def saved? + !new_record? && (@errors.nil? || errors.empty?) + end + + # returns true if this instance has had validation (maybe via save) attempted + def validation_attempted? + !@errors.nil? + end + end + end +end \ No newline at end of file diff --git a/lib/ardes/resources_controller.rb b/lib/ardes/resources_controller.rb index 3b955d5..ffc0c46 100644 --- a/lib/ardes/resources_controller.rb +++ b/lib/ardes/resources_controller.rb @@ -665,18 +665,19 @@ def enclosing_collection_resources @enclosing_collection_resources ||= [] end - # Returns self.resource.save and caches the result for future calls. - # This is useful when you want to know outside of an action whether the resource was saved. - # - # Pass true to ignore the cached value - def resource_saved?(reload = false) - save_resource if reload || @resource_saved.nil? - @resource_saved + # Has the resource been saved successfully? + # If the record has not had validation attempted, it is saved. + # Returns true if the record is not new, and there are no errors + def resource_saved? + resource.save unless resource.validation_attempted? + resource.saved? end + # DEPRECATED: just use resource.save def save_resource - @resource_saved = resource.save + resource.save end + deprecate :save_resource => 'Use resource.save' private # returns the route that was used to invoke this controller and current action. The path is found first from params[:resource_path] diff --git a/lib/ardes/resources_controller/actions.rb b/lib/ardes/resources_controller/actions.rb index 24364d3..a9729e8 100644 --- a/lib/ardes/resources_controller/actions.rb +++ b/lib/ardes/resources_controller/actions.rb @@ -93,9 +93,9 @@ def edit # POST /events.xml def create self.resource = new_resource - + respond_to do |format| - if resource_saved? + if resource.save format.html do flash[:notice] = "#{resource_name.humanize} was successfully created." redirect_to resource_url @@ -114,10 +114,9 @@ def create # PUT /events/1.xml def update self.resource = find_resource - resource.attributes = params[resource_name] - + respond_to do |format| - if resource_saved? + if resource.update_attributes(params[resource_name]) format.html do flash[:notice] = "#{resource_name.humanize} was successfully updated." redirect_to resource_url diff --git a/spec/app.rb b/spec/app.rb index d1b7ccf..97951c9 100644 --- a/spec/app.rb +++ b/spec/app.rb @@ -156,12 +156,13 @@ class Post < ActiveRecord::Base end class Comment < ActiveRecord::Base + validates_presence_of :user, :post + belongs_to :user belongs_to :post has_many :tags, :as => :taggable end - ############## # Controllers ############## diff --git a/spec/controllers/addresses_controller_spec.rb b/spec/controllers/addresses_controller_spec.rb index ba2e9af..f45b1cf 100644 --- a/spec/controllers/addresses_controller_spec.rb +++ b/spec/controllers/addresses_controller_spec.rb @@ -309,7 +309,7 @@ def do_update end it "should update the found address" do - @address.should_receive(:attributes=) + @address.should_receive(:update_attributes).and_return(true) do_update end diff --git a/spec/controllers/admin_forums_controller_spec.rb b/spec/controllers/admin_forums_controller_spec.rb index d550a8f..708c639 100644 --- a/spec/controllers/admin_forums_controller_spec.rb +++ b/spec/controllers/admin_forums_controller_spec.rb @@ -522,7 +522,7 @@ def do_update end it "should update the found forum" do - @mock_forum.should_receive(:attributes=) + @mock_forum.should_receive(:update_attributes).and_return(true) do_update assigns(:forum).should == @mock_forum end @@ -558,7 +558,7 @@ def do_update end it "should update the found forum" do - @mock_forum.should_receive(:attributes=) + @mock_forum.should_receive(:update_attributes).and_return(true) do_update assigns(:forum).should == @mock_forum end @@ -579,7 +579,7 @@ def do_update end it "should render edit.rjs, on unsuccessful save" do - @mock_forum.stub!(:save).and_return(false) + @mock_forum.stub!(:update_attributes).and_return(false) do_update response.should render_template('edit') end diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 28f8dce..562a8c8 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -77,7 +77,7 @@ def setup_mocks before(:each) do @forum = Forum.create @post = Post.create :forum_id => @forum.id - @comment = Comment.create :post_id => @post.id + @comment = Comment.create :post_id => @post.id, :user => User.create @other_post = Post.create :forum_id => @forum.id @other_comment = Comment.create :post_id => @other_post.id @@ -342,7 +342,7 @@ def do_update end it "should update the found comment" do - @comment.should_receive(:attributes=) + @comment.should_receive(:update_attributes).and_return(true) do_update end diff --git a/spec/controllers/comments_controller_with_models_spec.rb b/spec/controllers/comments_controller_with_models_spec.rb new file mode 100644 index 0000000..122a2e6 --- /dev/null +++ b/spec/controllers/comments_controller_with_models_spec.rb @@ -0,0 +1,203 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper')) +require File.expand_path(File.join(File.dirname(__FILE__), '../app')) + +describe CommentsController, "without stubs" do + before do + @user = User.create! + @forum = Forum.create! + @post = Post.create! :forum => @forum + @comment = Comment.create! :user => @user, :post => @post + end + + describe "responding to GET index" do + def do_get + get :index, :forum_id => @forum.id, :post_id => @post.id + end + + it "should expose all comments as @comments" do + do_get + assigns[:comments].should == [@comment] + end + + describe "with mime type of xml" do + it "should render all comments as xml" do + request.env["HTTP_ACCEPT"] = "application/xml" + do_get + response.body.should == [@comment].to_xml + end + end + end + + describe "responding to GET show" do + def do_get + get :show, :id => @comment.id, :forum_id => @forum.id, :post_id => @post.id + end + + it "should expose the requested comment as @comment" do + do_get + assigns[:comment].should == @comment + end + + describe "with mime type of xml" do + it "should render the requested comment as xml" do + request.env["HTTP_ACCEPT"] = "application/xml" + do_get + response.body.should == @comment.to_xml + end + end + end + + describe "responding to GET new" do + def do_get + get :new, :forum_id => @forum.id, :post_id => @post.id + end + + it "should expose a new comment as @comment" do + do_get + assigns[:comment].should be_new_record + assigns[:comment].post.should == @post + end + end + + describe "responding to GET edit" do + def do_get + get :edit, :id => @comment.id, :forum_id => @forum.id, :post_id => @post.id + end + + it "should expose the requested comment as @comment" do + do_get + assigns[:comment].should == @comment + end + end + + describe "responding to POST create" do + describe "with valid params" do + def do_post + post :create, :forum_id => @forum.id, :post_id => @post.id, :comment => {:user_id => @user.id} + end + + it "should create a comment" do + lambda { do_post }.should change(Comment, :count).by(1) + end + + it "should expose the newly created comment as @comment" do + do_post + assigns(:comment).should == Comment.find(:first, :order => 'id DESC') + end + + it "should be resource_saved?" do + do_post + @controller.should be_resource_saved + end + + it "should redirect to the created comment" do + do_post + response.should redirect_to(forum_post_comment_url(@forum, @post, Comment.find(:first, :order => 'id DESC'))) + end + end + + describe "with invalid params" do + def do_post + post :create, :forum_id => @forum.id, :post_id => @post.id, :comment => {:user_id => ''} + end + + it "should not create a comment" do + lambda { do_post }.should_not change(Comment, :count) + end + + it "should expose a newly created but unsaved comment as @comment" do + do_post + assigns(:comment).should be_new_record + assigns(:comment).post.should == @post + end + + it "should not be resource_saved?" do + do_post + @controller.should_not be_resource_saved + end + + it "should re-render the 'new' template" do + do_post + response.should render_template('new') + end + end + end + + describe "responding to PUT udpate" do + describe "with valid params" do + before do + @new_user = User.create! + end + + def do_put + put :update, :id => @comment.id, :forum_id => @forum.id, :post_id => @post.id, :comment => {:user_id => @new_user.id} + end + + it "should update the requested comment" do + do_put + Comment.find(@comment.id).user_id.should == @new_user.id + end + + it "should not contain errors on comment" do + do_put + @comment.errors.should be_empty + end + + it "should be resource_saved?" do + do_put + @controller.should be_resource_saved + end + + it "should expose the requested comment as @comment" do + do_put + assigns[:comment].should == @comment + end + + it "should redirect to the comment" do + do_put + response.should redirect_to(forum_post_comment_url(@forum, @post, @comment)) + end + end + + describe "with invalid params" do + def do_put + put :update, :id => @comment.id, :forum_id => @forum.id, :post_id => @post.id, :comment => {:user_id => ''} + end + + it "should fail to update the requested comment" do + do_put + Comment.find(@comment.id).user_id.should == @user.id + end + + it "should not be resource_saved?" do + do_put + @controller.should_not be_resource_saved + end + + it "should expose the requested comment as @comment" do + do_put + assigns[:comment].should == @comment + end + + it "should re-render the 'edit' template" do + do_put + response.should render_template('edit') + end + end + end + + describe "responding to DELETE destroy" do + def do_delete + delete :destroy, :id => @comment.id, :forum_id => @forum.id, :post_id => @post.id + end + + it "should delete the requested comment" do + lambda { do_delete }.should change(Comment, :count).by(-1) + end + + it "should redirect to the comments list" do + do_delete + response.should redirect_to(forum_post_comments_url(@forum, @post)) + end + end +end diff --git a/spec/controllers/forum_posts_controller_spec.rb b/spec/controllers/forum_posts_controller_spec.rb index 3410f2d..a89e98a 100644 --- a/spec/controllers/forum_posts_controller_spec.rb +++ b/spec/controllers/forum_posts_controller_spec.rb @@ -395,7 +395,7 @@ def do_update end it "should update the found post" do - @post.should_receive(:attributes=) + @post.should_receive(:update_attributes) do_update end diff --git a/spec/controllers/forums_controller_spec.rb b/spec/controllers/forums_controller_spec.rb index 06f3690..2e37e1c 100644 --- a/spec/controllers/forums_controller_spec.rb +++ b/spec/controllers/forums_controller_spec.rb @@ -599,7 +599,7 @@ def do_update end it "should update the found forum" do - @mock_forum.should_receive(:attributes=) + @mock_forum.should_receive(:update_attributes) do_update assigns(:forum).should == @mock_forum end @@ -635,7 +635,7 @@ def do_update end it "should update the found forum" do - @mock_forum.should_receive(:attributes=) + @mock_forum.should_receive(:update_attributes) do_update assigns(:forum).should == @mock_forum end @@ -656,7 +656,7 @@ def do_update end it "should render edit.rjs, on unsuccessful save" do - @mock_forum.stub!(:save).and_return(false) + @mock_forum.stub!(:update_attributes).and_return(false) do_update response.should render_template('edit') end diff --git a/spec/controllers/infos_controller_spec.rb b/spec/controllers/infos_controller_spec.rb index 75e0cf8..22d9cfb 100644 --- a/spec/controllers/infos_controller_spec.rb +++ b/spec/controllers/infos_controller_spec.rb @@ -52,10 +52,9 @@ def setup_mocks end it "PUT /account/info should be successful" do - @info.stub!(:attributes=) - @info.stub!(:save) + @info.stub!(:update_attributes).and_return(true) put :update - response.should be_success + response.should be_redirect end it "GET /account/info/new should raise UnknownAction" do diff --git a/spec/controllers/owners_controller_spec.rb b/spec/controllers/owners_controller_spec.rb index d177d2c..be0b9ca 100644 --- a/spec/controllers/owners_controller_spec.rb +++ b/spec/controllers/owners_controller_spec.rb @@ -220,8 +220,7 @@ def do_post before(:each) do setup_mocks - @owner.stub!(:save).and_return(true) - @owner.stub!(:attributes=) + @owner.stub!(:update_attributes).and_return(true) end def do_update @@ -239,7 +238,7 @@ def do_update end it "should update the owner" do - @owner.should_receive(:attributes=).with('name' => 'Fred') + @owner.should_receive(:update_attributes).with('name' => 'Fred') do_update end diff --git a/spec/controllers/resource_saved_spec.rb b/spec/controllers/resource_saved_spec.rb new file mode 100644 index 0000000..86e01e3 --- /dev/null +++ b/spec/controllers/resource_saved_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper')) +require File.expand_path(File.join(File.dirname(__FILE__), '../app')) + +describe CommentsController, "#resource_saved" do + describe "Comment.new()" do + before { @controller.resource = Comment.new } + + it { @controller.should_not be_resource_saved } + + describe ".save" do + before { @controller.resource.save } + + it { @controller.should_not be_resource_saved } + + describe "then update_attributes()" do + before { @controller.resource.update_attributes :user => User.create!, :post => Post.create! } + + it { @controller.should be_resource_saved } + end + end + end + + describe "Comment.find()" do + before do + Comment.create! :user => User.create!, :post => Post.create! + @controller.resource = Comment.find(:first) + end + + it { @controller.should be_resource_saved } + + it ".save should be saved" do + @controller.resource.save + @controller.should be_resource_saved + end + + describe "then update_attributes()" do + before { @controller.resource.update_attributes :user => nil } + + it { @controller.should_not be_resource_saved } + end + + describe "then update_attributes()" do + before { @controller.resource.update_attributes :user => User.create! } + + it { @controller.should be_resource_saved } + end + end +end \ No newline at end of file diff --git a/spec/controllers/tags_controller_via_forum_post_comment_spec.rb b/spec/controllers/tags_controller_via_forum_post_comment_spec.rb index b05a140..bd68fe5 100644 --- a/spec/controllers/tags_controller_via_forum_post_comment_spec.rb +++ b/spec/controllers/tags_controller_via_forum_post_comment_spec.rb @@ -71,7 +71,7 @@ def setup_mocks before(:each) do @forum = Forum.create @post = Post.create :forum_id => @forum.id - @comment = Comment.create :post_id => @post.id + @comment = Comment.create :post_id => @post.id, :user => User.create! @tag = Tag.create :taggable_id => @comment.id, :taggable_type => 'Comment' @other_comment = Comment.create :post_id => @forum.id @other_tag = Tag.create :taggable_id => @other_comment.id, :taggable_type => 'Comment' diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 728d1a1..7a0f2b7 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -235,14 +235,12 @@ def do_get end def put_with_successful_update - @user.should_receive(:attributes=).once.ordered - @user.should_receive(:save).once.ordered.and_return(true) + @user.should_receive(:update_attributes).and_return(true) put :update, :id => "dave" end def put_with_failed_update - @user.should_receive(:attributes=).once.ordered - @user.should_receive(:save).once.ordered.and_return(false) + @user.should_receive(:update_attributes).and_return(false) put :update, :id => "dave" end diff --git a/spec/models/comment_saved_spec.rb b/spec/models/comment_saved_spec.rb new file mode 100644 index 0000000..b9e3eec --- /dev/null +++ b/spec/models/comment_saved_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper')) +require File.expand_path(File.join(File.dirname(__FILE__), '../app')) + +describe "(re: saved?) Comment" do + describe ".new()" do + before { @comment = Comment.new } + + it { @comment.should_not be_validation_attempted } + it { @comment.should_not be_saved } + + describe ".save" do + before { @comment.save } + + it { @comment.should be_validation_attempted } + it { @comment.should_not be_saved } + + describe "then update_attributes()" do + before { @comment.update_attributes :user => User.create!, :post => Post.create! } + + it { @comment.should be_validation_attempted } + it { @comment.should be_saved } + end + end + end +end \ No newline at end of file diff --git a/spec/specs/resources_controller_spec.rb b/spec/specs/resources_controller_spec.rb index ba7b4d3..b2f0444 100644 --- a/spec/specs/resources_controller_spec.rb +++ b/spec/specs/resources_controller_spec.rb @@ -40,4 +40,18 @@ @controller.resource_service = 'foo' @controller.resource_service.should == 'foo' end +end + +describe "deprecated methods" do + before do + @controller = ForumsController.new + @controller.resource = Forum.new + end + + it "#save_resource should send resource.save" do + ActiveSupport::Deprecation.silence do + @controller.resource.should_receive :save + @controller.save_resource + end + end end \ No newline at end of file