├── .gitignore
├── .idea
├── .gitignore
├── inspectionProfiles
│ └── Project_Default.xml
├── make_resourceful.iml
├── misc.xml
├── modules.xml
└── vcs.xml
├── DEFAULTS
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.rdoc
├── Rakefile
├── TODO
├── VERSION
├── lib
├── make_resourceful.rb
└── resourceful
│ ├── base.rb
│ ├── builder.rb
│ ├── default
│ ├── accessors.rb
│ ├── actions.rb
│ ├── callbacks.rb
│ ├── responses.rb
│ └── urls.rb
│ ├── generators
│ └── resourceful_scaffold
│ │ ├── resourceful_scaffold_generator.rb
│ │ └── templates
│ │ ├── controller.rb
│ │ ├── fixtures.yml
│ │ ├── functional_test.rb
│ │ ├── helper.rb
│ │ ├── migration.rb
│ │ ├── model.rb
│ │ ├── unit_test.rb
│ │ ├── view__form.haml
│ │ ├── view_edit.haml
│ │ ├── view_index.haml
│ │ ├── view_new.haml
│ │ ├── view_partial.haml
│ │ └── view_show.haml
│ ├── maker.rb
│ ├── response.rb
│ └── serialize.rb
├── make_resourceful.gemspec
└── spec
├── accessors_spec.rb
├── actions_spec.rb
├── base_spec.rb
├── builder_spec.rb
├── callbacks_spec.rb
├── integration_spec.rb
├── maker_spec.rb
├── response_spec.rb
├── responses_spec.rb
├── rspec-rails
├── LICENSE
├── redirect_to.rb
└── render_template.rb
├── serialize_spec.rb
├── spec_helper.rb
├── support
├── helper_methods.rb
└── integration_helpers.rb
├── urls_spec.rb
└── views
└── things
├── create.rjs
├── destroy.rjs
├── edit.html.erb
├── edit.rjs
├── index.html.erb
├── index.rjs
├── new.html.erb
├── new.rjs
├── show.html.erb
├── show.rjs
└── update.rjs
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | tags
3 | testapp
4 | Gemfile.lock
5 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/make_resourceful.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/DEFAULTS:
--------------------------------------------------------------------------------
1 | There's a rough equivalence between a basic @make_resourceful@ call
2 | and a hand-made controller.
3 | This:
4 |
5 | class PostsController < ApplicationController
6 | make_resourceful { actions :all }
7 | end
8 |
9 | Creates a controller that works more or less like the one that follows.
10 | Note that the real code generated by make_resourceful
11 | is more extensible in various ways.
12 | Thus whenever possible, there are comments in the following controller
13 | indicating how to customize various bits of the controller.
14 |
15 | class PostsController < ApplicationController
16 | def index
17 | # Override #current_objects to change this
18 | @posts = Post.all
19 |
20 | # Use before :index to add something here
21 |
22 | # Use response_for :index to change this
23 | respond_to { |f| f.html; f.js }
24 | end
25 |
26 | def show
27 | # Override #current_object to change this
28 | @post = Post.find(params[:id])
29 |
30 | # Use before :show to add something here
31 |
32 | # Use response_for :show to change this
33 | respond_to { |f| f.html; f.js }
34 | end
35 |
36 | def create
37 | # Override #build_object to change this
38 | @post = Post.new(params[:post])
39 |
40 | # Use before :create to add something here
41 |
42 | if @post.save
43 | # Use after :create to add something here
44 |
45 | # Use response_for :create to change this
46 | respond_to do |f|
47 | f.html do
48 | flash[:notice] = "Create successful!"
49 | redirect_to post_path(@post)
50 | end
51 | f.js
52 | end
53 | else
54 | # Use after :create_fails to add something here
55 |
56 | # Use response_for :create_fails to change this
57 | respond_to do |f|
58 | format.html
59 | flash[:error] = "There was a problem!"
60 | render :action => :new, :status => 422
61 | end
62 | format.js
63 | end
64 | end
65 | end
66 |
67 | def update
68 | # Override #current_object to change this
69 | @post = Post.find(params[:id])
70 |
71 | # Use before :update to do something here
72 |
73 | if @post.update_attributes params[:post]
74 | # Use after :update to add something here
75 |
76 | # Use response_for :update to change this
77 | respond_to do |f|
78 | f.html do
79 | flash[:notice] = "Save successful!"
80 | redirect_to post_path(@post)
81 | end
82 | f.js
83 | end
84 | else
85 | # Use after :update_fails to add something here
86 |
87 | # Use response_for :update_fails to change this
88 | respond_to do |f|
89 | format.html
90 | flash[:error] = "There was a problem saving!"
91 | render :action => :edit, :status => 422
92 | end
93 | format.js
94 | end
95 | end
96 | end
97 |
98 | def new
99 | # Override #build_object to change this
100 | @post = Post.new(params[:post])
101 |
102 | # Use before :new to add something here
103 |
104 | # Use response_for :new to change this
105 | respond_to { |f| f.html; f.js }
106 | end
107 |
108 | def edit
109 | # Override #current_object to change this
110 | @post = Post.find(params[:id])
111 |
112 | # Use before :edit to add something here
113 |
114 | # Use response_for :edit to change this
115 | respond_to { |f| f.html; f.js }
116 | end
117 |
118 | def destroy
119 | # Override #current_object to change this
120 | @post = Post.find(params[:id])
121 |
122 | # Use before :destroy to do something here
123 |
124 | if @post.destroy
125 | # Use after :destroy to add something here
126 |
127 | # Use response_for :destroy to change this
128 | respond_to do |f|
129 | f.html do
130 | flash[:notice] = "Record deleted!"
131 | redirect_to posts_path(@post)
132 | end
133 | f.js
134 | end
135 | else
136 | # Use after :destroy_fails to add something here
137 |
138 | # Use response_for :destroy_fails to change this
139 | respond_to do |f|
140 | format.html
141 | flash[:error] = "There was a problem deleting!"
142 | render :back
143 | end
144 | format.js
145 | end
146 | end
147 | end
148 | end
149 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec
4 |
5 | gem 'mocha'
6 | gem 'rspec-rails'
7 | gem 'activemodel'
8 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | make_resourceful (2.0.0)
5 |
6 | GEM
7 | remote: https://rubygems.org/
8 | specs:
9 | actionpack (7.1.3.4)
10 | actionview (= 7.1.3.4)
11 | activesupport (= 7.1.3.4)
12 | nokogiri (>= 1.8.5)
13 | racc
14 | rack (>= 2.2.4)
15 | rack-session (>= 1.0.1)
16 | rack-test (>= 0.6.3)
17 | rails-dom-testing (~> 2.2)
18 | rails-html-sanitizer (~> 1.6)
19 | actionview (7.1.3.4)
20 | activesupport (= 7.1.3.4)
21 | builder (~> 3.1)
22 | erubi (~> 1.11)
23 | rails-dom-testing (~> 2.2)
24 | rails-html-sanitizer (~> 1.6)
25 | activemodel (7.1.3.4)
26 | activesupport (= 7.1.3.4)
27 | activesupport (7.1.3.4)
28 | base64
29 | bigdecimal
30 | concurrent-ruby (~> 1.0, >= 1.0.2)
31 | connection_pool (>= 2.2.5)
32 | drb
33 | i18n (>= 1.6, < 2)
34 | minitest (>= 5.1)
35 | mutex_m
36 | tzinfo (~> 2.0)
37 | base64 (0.2.0)
38 | bigdecimal (3.1.8)
39 | builder (3.3.0)
40 | concurrent-ruby (1.3.3)
41 | connection_pool (2.4.1)
42 | crass (1.0.6)
43 | diff-lcs (1.5.1)
44 | drb (2.2.1)
45 | erubi (1.13.0)
46 | i18n (1.14.5)
47 | concurrent-ruby (~> 1.0)
48 | io-console (0.7.2)
49 | irb (1.13.2)
50 | rdoc (>= 4.0.0)
51 | reline (>= 0.4.2)
52 | loofah (2.22.0)
53 | crass (~> 1.0.2)
54 | nokogiri (>= 1.12.0)
55 | mini_portile2 (2.8.7)
56 | minitest (5.24.0)
57 | mocha (2.4.0)
58 | ruby2_keywords (>= 0.0.5)
59 | mutex_m (0.2.0)
60 | nokogiri (1.16.6)
61 | mini_portile2 (~> 2.8.2)
62 | racc (~> 1.4)
63 | psych (5.1.2)
64 | stringio
65 | racc (1.8.0)
66 | rack (3.1.4)
67 | rack-session (2.0.0)
68 | rack (>= 3.0.0)
69 | rack-test (2.1.0)
70 | rack (>= 1.3)
71 | rackup (2.1.0)
72 | rack (>= 3)
73 | webrick (~> 1.8)
74 | rails-dom-testing (2.2.0)
75 | activesupport (>= 5.0.0)
76 | minitest
77 | nokogiri (>= 1.6)
78 | rails-html-sanitizer (1.6.0)
79 | loofah (~> 2.21)
80 | nokogiri (~> 1.14)
81 | railties (7.1.3.4)
82 | actionpack (= 7.1.3.4)
83 | activesupport (= 7.1.3.4)
84 | irb
85 | rackup (>= 1.0.0)
86 | rake (>= 12.2)
87 | thor (~> 1.0, >= 1.2.2)
88 | zeitwerk (~> 2.6)
89 | rake (13.2.1)
90 | rdoc (6.7.0)
91 | psych (>= 4.0.0)
92 | reline (0.5.9)
93 | io-console (~> 0.5)
94 | rspec-core (3.13.0)
95 | rspec-support (~> 3.13.0)
96 | rspec-expectations (3.13.1)
97 | diff-lcs (>= 1.2.0, < 2.0)
98 | rspec-support (~> 3.13.0)
99 | rspec-mocks (3.13.1)
100 | diff-lcs (>= 1.2.0, < 2.0)
101 | rspec-support (~> 3.13.0)
102 | rspec-rails (6.1.3)
103 | actionpack (>= 6.1)
104 | activesupport (>= 6.1)
105 | railties (>= 6.1)
106 | rspec-core (~> 3.13)
107 | rspec-expectations (~> 3.13)
108 | rspec-mocks (~> 3.13)
109 | rspec-support (~> 3.13)
110 | rspec-support (3.13.1)
111 | ruby2_keywords (0.0.5)
112 | stringio (3.1.1)
113 | thor (1.3.1)
114 | tzinfo (2.0.6)
115 | concurrent-ruby (~> 1.0)
116 | webrick (1.8.1)
117 | zeitwerk (2.6.16)
118 |
119 | PLATFORMS
120 | ruby
121 |
122 | DEPENDENCIES
123 | activemodel
124 | make_resourceful!
125 | mocha
126 | rspec-rails
127 |
128 | BUNDLED WITH
129 | 2.5.9
130 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2007-2010 Hampton Catlin
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | = make_resourceful
2 | ===== Take back control of your Controllers. Make them awesome. Make them sleek. Make them resourceful.
3 |
4 | REST is a fine pattern for designing controllers,
5 | but it can be pretty repetitive.
6 | Who wants to write out the same actions and copy the same model lookup logic
7 | all over their application?
8 |
9 | make_resourceful handles all that for you.
10 | It sets up all your RESTful actions and responses with next to no code.
11 | Everything has full, sensible default functionality.
12 |
13 | Of course, no controller _only_ uses the defaults.
14 | So make_resourceful can be massively customized,
15 | while still keeping your controllers trim and readable.
16 |
17 | == Get it!
18 |
19 | Rails 3.0+
20 |
21 | gem "make_resourceful" #ZOMG, SO EASY
22 |
23 | Git
24 |
25 | $ git clone git://github.com/hcatlin/make_resourceful.git
26 |
27 | == Use it!
28 |
29 | If you want to try make_resourceful on one of your current controllers,
30 | just replace the mess of repetition with this:
31 |
32 | class FooController < ApplicationController
33 | make_resourceful do
34 | actions :all
35 | end
36 | end
37 |
38 | Those three lines will replace the entire default controller
39 | that comes out of the scaffold_resource generator.
40 |
41 | === Really?
42 |
43 | Yes.
44 |
45 | === Can I do nested resources?
46 |
47 | make_resourceful do
48 | actions :all
49 | belongs_to :post
50 | end
51 |
52 | === What if I want to use fancy permalinks?
53 |
54 | def current_object
55 | @current_object ||= current_model.find_by_permalink(params[:id])
56 | end
57 |
58 | === What about paging?
59 |
60 | def current_objects
61 | @current_objects ||= current_model.find(:all,
62 | :order => "created_at DESC", :page => {:current => params[:page], :size => 10 } )
63 | end
64 |
65 | === What if I want to do something in the middle of an action?
66 |
67 | before :show, :index do
68 | @page_title = "Awesome!"
69 | end
70 |
71 | after :create_fails do
72 | @page_title = "Not So Awesome!"
73 | end
74 |
75 | === What about all of my awesome respond_to blocks for my XML APIs and RJS responses?
76 |
77 | response_for :show do |format|
78 | format.html
79 | format.js
80 | format.xml
81 | end
82 |
83 | response_for :update_fails do |format|
84 | format.html { render :action => 'edit' }
85 | format.json { render :json => false.to_json, :status => 422 }
86 | end
87 |
88 | === So I guess I have to write responses for all my actions?
89 |
90 | Nope! make_resourceful makes them do the right thing by default.
91 | You only need to customize them if you want to do something special.
92 |
93 | === Seriously?!
94 |
95 | Yes!
96 |
97 | == Grok it!
98 |
99 | === +make_resourceful+ the Method
100 |
101 | The +make_resourceful+ block is where most of the action happens.
102 | Here you specify which actions you want to auto-generate,
103 | what code you want to run for given callbacks,
104 | and so forth.
105 |
106 | You also use the block to declare various bits of information about your controller.
107 | For instance, if the controller is nested, you'd call +belongs_to+.
108 | If you wanted to expose your models as some sort of text format,
109 | you'd call +publish+.
110 |
111 | Check out the documentation of Resourceful::Builder
112 | for more information on the methods you can call here.
113 |
114 | === Helper Methods
115 |
116 | make_resourceful provides lots of useful methods
117 | that can be used in your callbacks and in your views.
118 | They range from accessing the records you're looking up
119 | to easily generating URLs for a record
120 | to getting information about the action itself.
121 |
122 | Two of the most useful methods are +current_object+ and +current_objects+
123 | (note the subtle plurality difference).
124 | +current_objects+ only works for +index+,
125 | and returns all the records in the current model.
126 | +current_object+ works for all actions other than +index+,
127 | and returns the record that's currently being dealt with.
128 |
129 | The full documentation of the helper methods
130 | is in Resourceful::Default::Accessors and Resourceful::Default::URLs.
131 |
132 | === Nested Resources
133 |
134 | make_resourceful supports easy management of nested resources.
135 | This is set up with the Resourceful::Builder#belongs_to declaration.
136 | Pass in the name of the parent model,
137 |
138 | belongs_to :user
139 |
140 | and everything will be taken care of.
141 | When +index+ is run for GET /users/12/albums,
142 | parent_object
143 | will get User.find(params[:user_id]),
144 | and current_objects
145 | will get parent_object.albums.
146 | When +create+ is run for POST /users/12/albums,
147 | the newly created Album will automatically belong to the user
148 | with id 12.
149 |
150 | The normal non-scoped actions still work, too.
151 | GET /albums/15 runs just fine.
152 | make_resourceful knows that since there's no params[:user_id],
153 | you just want to deal with the album.
154 |
155 | You can even have a single resource nested under several different resources.
156 | Just pass multiple parent names to the Resourceful::Builder#belongs_to, like
157 |
158 | belongs_to :user, :artist
159 |
160 | Then /users/15/albums and /artists/7/albums will both work.
161 |
162 | This does, however, mean that make_resourceful only supports one level of nesting.
163 | There's no automatic handling of /users/15/collections/437/albums.
164 | However, this is really the best way to organize most resources anyway;
165 | see this {article}[http://weblog.jamisbuck.org/2007/2/5/nesting-resources].
166 |
167 | If you really need a deeply nested controller,
168 | it should be easy enough to set up on your own.
169 | Just override current_model.
170 | See the next section for more details.
171 |
172 | === Overriding Methods
173 |
174 | Not only are helper methods useful to the developer to use,
175 | they're used internally by the actions created by make_resourceful.
176 | Thus one of the main ways make_resourceful can be customized
177 | is by overriding accessors.
178 |
179 | For instance, if you want to only look up the 10 most recent records for +index+,
180 | you're override +current_objects+.
181 | If you wanted to use a different model than that suggested by the name of the controller,
182 | you'd override +current_model+.
183 |
184 | When you're overriding methods that do SQL lookups, though, be a little cautious.
185 | By default, these methods cache their values in instance variables
186 | so that multiple SQL queries aren't run on multiple calls.
187 | When overriding them, it's wise for you to do the same.
188 | For instance,
189 |
190 | def current_object
191 | @current_object ||= current_model.find_by_name(params[:name])
192 | end
193 |
194 | === For More Information...
195 |
196 | Haven't found all the information you need in the RDoc?
197 | Still a little confused about something?
198 | Don't despair, there are still more resources available!
199 |
200 | * Read the source code!
201 | It's very straightforward,
202 | and make_resourceful is built to encourage overriding methods
203 | and hacking the source.
204 | * Nathan Weizenbaum has some good blog posts about make_resourceful.
205 | They may be a little outdated, but they should still be useful and explanatory.
206 | * On nesting and associations: {here}[http://nex-3.com/posts/55-nesting-and-make_resourceful].
207 | * An overview of make_resourceful 0.2.0 and 0.2.2: {here}[http://localhost:3000/posts/54-make_resourceful-0-2-0].
208 | * On Resourceful::Builder#publish
209 | and Resourceful::Serialize:
210 | {here}[http://nex-3.com/posts/35-make_resourceful-the-basics-of-publish] and
211 | {here}[http://nex-3.com/posts/36-make_resourceful-publish-extras].
212 | * There's an excellent, active Google Group http://groups.google.com/group/make_resourceful
213 | where people will be happy to answer your questions.
214 |
215 | ---
216 |
217 | Copyright 2007-2010 Hampton Catlin and Nathan Weizenbaum.
218 | Contributions by:
219 |
220 | * Russell Norris
221 | * Jonathan Linowes
222 | * Cristi Balan
223 | * Mike Ferrier
224 | * James Golick
225 | * Don Petersen
226 | * Alex Ross
227 | * Tom Stuart
228 | * Glenn Powell
229 | * Johannes Jörg Schmidt
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake'
2 | require 'rdoc/task'
3 | require 'rspec/core/rake_task'
4 |
5 | desc 'Default: run specs.'
6 | task :default => :spec
7 |
8 | spec_files = Rake::FileList["spec/**/*_spec.rb"]
9 |
10 | desc "Run specs"
11 | RSpec::Core::RakeTask.new(:spec) do |t|
12 | t.pattern = "spec/**/*_spec.rb"
13 | t.rspec_opts = ["-c"]
14 | end
15 |
16 | desc "Generate code coverage"
17 | RSpec::Core::RakeTask.new(:coverage) do |t|
18 | t.spec_files = spec_files
19 | t.rcov = true
20 | t.rcov_opts = ['--exclude', 'spec,/var/lib/gems']
21 | end
22 |
23 | desc 'Generate documentation for the make_resourceful plugin.'
24 | Rake::RDocTask.new(:rdoc) do |rdoc|
25 | rdoc.rdoc_dir = 'rdoc'
26 | rdoc.title = 'make_resourceful'
27 | rdoc.options << '--line-numbers' << '--inline-source'
28 | rdoc.main = 'README'
29 | rdoc.rdoc_files.include(FileList.new('*').exclude(/[^A-Z0-9]/))
30 | rdoc.rdoc_files.include('lib/**/*.rb')
31 | end
32 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | * Generators need to be ported to the new syntax http://guides.rubyonrails.org/generators.html
2 | * Add better strong parameters support
3 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 2.0.0
2 |
--------------------------------------------------------------------------------
/lib/make_resourceful.rb:
--------------------------------------------------------------------------------
1 |
2 | class MakeResourcefulTie < Rails::Railtie
3 | initializer "my_railtie.configure_rails_initialization" do
4 | require File.join(File.dirname(__FILE__), 'resourceful/maker')
5 | ActionController::Base.extend Resourceful::Maker
6 | end
7 | end
--------------------------------------------------------------------------------
/lib/resourceful/base.rb:
--------------------------------------------------------------------------------
1 | # The module containing all the code for make_resourceful.
2 | #
3 | # For methods available in the +make_resourceful+ block,
4 | # see Resourceful::Builder.
5 | #
6 | # For helper methods and methods you can override
7 | # to customize the behavior of your actions,
8 | # see Resourceful::Default::Accessors
9 | # and Resourceful::Default::URLs.
10 | module Resourceful
11 | # We want to define some stuff before we load other modules
12 |
13 | # The default actions generated by make_resourceful.
14 | ACTIONS = [:index, :show, :edit, :update, :create, :new, :destroy]
15 |
16 | # The actions that modify the database.
17 | MODIFYING_ACTIONS = [:update, :create, :destroy]
18 |
19 | # The actions that act on multiple records.
20 | PLURAL_ACTIONS = [:index]
21 |
22 | # The actions that act on just one record.
23 | SINGULAR_ACTIONS = ACTIONS - PLURAL_ACTIONS
24 |
25 | # The actions that act on just one record.
26 | SINGULAR_PRELOADED_ACTIONS = SINGULAR_ACTIONS - [:new, :create]
27 | end
28 |
29 | require 'resourceful/default/accessors'
30 | require 'resourceful/default/responses'
31 | require 'resourceful/default/callbacks'
32 | require 'resourceful/default/urls'
33 |
34 | # All modules included by this module
35 | # are made available to the controller as accessors.
36 | module Resourceful::Base
37 | @@made_resourceful_callbacks = []
38 |
39 | # This method is meant to be called by included classes.
40 | # It takes a block of the same form as that given to Maker#make_resourceful.
41 | # The Maker will then run that block
42 | # along with the blocks given by the individual controllers.
43 | def self.made_resourceful(&block)
44 | if block
45 | @@made_resourceful_callbacks << block
46 | else
47 | @@made_resourceful_callbacks
48 | end
49 | end
50 |
51 | include Resourceful::Default::Accessors
52 | include Resourceful::Default::Responses
53 | include Resourceful::Default::Callbacks
54 | include Resourceful::Default::URLs
55 |
56 | # FIXME HACK
57 | # making methods assigned to controller private
58 | # prevents access from dispatcher.
59 | private *Resourceful::Default::Accessors.public_instance_methods
60 | private *Resourceful::Default::Responses.public_instance_methods
61 | private *Resourceful::Default::Callbacks.public_instance_methods
62 | private *Resourceful::Default::URLs.public_instance_methods
63 | end
64 |
--------------------------------------------------------------------------------
/lib/resourceful/builder.rb:
--------------------------------------------------------------------------------
1 | require 'resourceful/response'
2 | require 'resourceful/serialize'
3 | require 'resourceful/default/actions'
4 |
5 | module Resourceful
6 | # The Maker#make_resourceful block is evaluated in the context
7 | # of an instance of this class.
8 | # It provides various methods for customizing the behavior of the actions
9 | # built by make_resourceful.
10 | #
11 | # All instance methods of this class are available in the +make_resourceful+ block.
12 | class Builder
13 | # The klass of the controller on which the builder is working.
14 | attr :controller, true
15 |
16 | # The constructor is only meant to be called internally.
17 | #
18 | # This takes the klass (class object) of a controller
19 | # and constructs a Builder ready to apply the make_resourceful
20 | # additions to the controller.
21 | def initialize(kontroller)
22 | @controller = kontroller
23 | @inherited = !kontroller.resourceful_responses.blank?
24 | @action_module = Resourceful::Default::Actions.dup
25 | @ok_actions = []
26 | @callbacks = {:before => {}, :after => {}}
27 | @responses = {}
28 | @publish = {}
29 | @parents = []
30 | @shallow_parent = nil
31 | @custom_member_actions = []
32 | @custom_collection_actions = []
33 | @permitted_params = []
34 | end
35 |
36 | # This method is only meant to be called internally.
37 | #
38 | # Applies all the changes that have been declared
39 | # via the instance methods of this Builder
40 | # to the kontroller passed in to the constructor.
41 | def apply
42 | apply_publish
43 |
44 | kontroller = @controller
45 |
46 | Resourceful::ACTIONS.each do |action_named|
47 | # See if this is a method listed by #actions
48 | unless @ok_actions.include? action_named
49 | # If its not listed, then remove the method
50 | # No one can hit it... if its DEAD!
51 | @action_module.send :remove_method, action_named
52 | end
53 | end
54 |
55 | if kontroller.respond_to?(:hidden_actions)
56 | kontroller.hidden_actions.reject! &@ok_actions.method(:include?)
57 | end
58 | kontroller.send :include, @action_module
59 |
60 | merged_callbacks = kontroller.resourceful_callbacks.merge @callbacks
61 | merged_responses = kontroller.resourceful_responses.merge @responses
62 |
63 | kontroller.permitted_params = @permitted_params
64 | kontroller.resourceful_callbacks = merged_callbacks
65 | kontroller.resourceful_responses = merged_responses
66 | kontroller.made_resourceful = true
67 |
68 | kontroller.parent_controllers = @parents
69 | kontroller.shallow_parent = @shallow_parent
70 | kontroller.model_namespace = @model_namespace
71 | kontroller.before_action :load_object, :only => (@ok_actions & SINGULAR_PRELOADED_ACTIONS) + @custom_member_actions
72 | kontroller.before_action :load_objects, :only => (@ok_actions & PLURAL_ACTIONS) + @custom_collection_actions
73 | kontroller.before_action :load_parent_object, :only => @ok_actions + @custom_member_actions + @custom_collection_actions
74 | end
75 |
76 | # :call-seq:
77 | # actions(*available_actions)
78 | # actions :all
79 | #
80 | # Adds the default RESTful actions to the controller.
81 | #
82 | # If the only argument is :all,
83 | # adds all the actions listed in Resourceful::ACTIONS[link:classes/Resourceful.html]
84 | # (or Resourceful::SINGULAR_ACTIONS[link:classes/Resourceful.html]
85 | # for a singular controller).
86 | #
87 | # Otherwise, this adds all actions
88 | # whose names were passed as arguments.
89 | #
90 | # For example:
91 | #
92 | # actions :show, :new, :create
93 | #
94 | # This adds the +show+, +new+, and +create+ actions
95 | # to the controller.
96 | #
97 | # The available actions are defined in Default::Actions.
98 | def actions(*available_actions)
99 | # FIXME HACK
100 | # made all methods private, so plural?, too.
101 | # Did not want to make an exception for that and i do not like it to
102 | # come up on actions_methods.
103 | # TODO: maybe we can define plural? as class_method
104 | if available_actions.first == :all
105 | if controller.respond_to?(:new_without_capture)
106 | available_actions = controller.new_without_capture.send(:plural?) ? ACTIONS : SINGULAR_ACTIONS
107 | else
108 | available_actions = controller.new.send(:plural?) ? ACTIONS : SINGULAR_ACTIONS
109 | end
110 | end
111 |
112 | available_actions.each { |action| @ok_actions << action.to_sym }
113 | end
114 | alias build actions
115 |
116 | # :call-seq:
117 | # member_actions(*available_actions)
118 | # Registers custom member actions which will use the load_object before_action.
119 | # These actions are not created, but merely registered for filtering.
120 | def member_actions(*available_actions)
121 | available_actions.each { |action| @custom_member_actions << action.to_sym }
122 | end
123 |
124 | # :call-seq:
125 | # collection_actions(*available_actions)
126 | #
127 | # Registers custom collection actions which will use the load_objects before_action.
128 | # These actions are not created, but merely registered for filtering.
129 | def collection_actions(*available_actions)
130 | available_actions.each { |action| @custom_collection_actions << action.to_sym }
131 | end
132 |
133 | # :call-seq:
134 | # permitted_params(*params_allowed)
135 | #
136 | # When using strong parameters, specifies which params are permitted
137 | #
138 | # If the only argument is :all,
139 | # all parameters are permitted
140 | def permitted_params(*params_permitted)
141 | if params_permitted.first == :all
142 | @permitted_params = params_permitted.first
143 | else
144 | @permitted_params = [] unless @permitted_params.is_a?(Array)
145 | params_permitted.each { |param| @permitted_params << param.to_sym }
146 | end
147 | end
148 |
149 | # :call-seq:
150 | # before(*events) { ... }
151 | #
152 | # Sets up a block of code to run before one or more events.
153 | #
154 | # All the default actions can be used as +before+ events:
155 | # :index, :show, :create, :update, :new, :edit, and :destroy.
156 | #
157 | # +before+ events are run after any objects are loaded[link:classes/Resourceful/Default/Accessors.html#M000015],
158 | # but before any database operations or responses.
159 | #
160 | # For example:
161 | #
162 | # before :show, :edit do
163 | # @page_title = current_object.title
164 | # end
165 | #
166 | # This will set the @page_title variable
167 | # to the current object's title
168 | # for the show and edit actions.
169 | #
170 | # Successive before blocks for the same action will be chained and executed
171 | # in order when the event occurs.
172 | #
173 | # For example:
174 | #
175 | # before :show, :edit do
176 | # @page_title = current_object.title
177 | # end
178 | #
179 | # before :show do
180 | # @side_bar = true
181 | # end
182 | #
183 | # These before blocks will both be executed for the show action and in the
184 | # same order as they were defined.
185 | def before(*events, &block)
186 | add_callback :before, *events, &block
187 | end
188 |
189 | # :call-seq:
190 | # after(*events) { ... }
191 | #
192 | # Sets up a block of code to run after one or more events.
193 | #
194 | # There are two sorts of +after+ events.
195 | # :create, :update, and :destroy
196 | # are run after their respective database operations
197 | # have been completed successfully.
198 | # :create_fails, :update_fails, and :destroy_fails,
199 | # on the other hand,
200 | # are run after the database operations fail.
201 | #
202 | # +after+ events are run after the database operations
203 | # but before any responses.
204 | #
205 | # For example:
206 | #
207 | # after :create_fails, :update_fails do
208 | # current_object.password = nil
209 | # end
210 | #
211 | # This will nillify the password of the current object
212 | # if the object creation/modification failed.
213 | def after(*events, &block)
214 | add_callback :after, *events, &block
215 | end
216 |
217 | # :call-seq:
218 | # response_for(*actions) { ... }
219 | # response_for(*actions) { |format| ... }
220 | #
221 | # Sets up a block of code to run
222 | # instead of the default responses for one or more events.
223 | #
224 | # If the block takes a format parameter,
225 | # it has the same semantics as Rails' +respond_to+ method.
226 | # Various format methods are called on the format object
227 | # with blocks that say what to do for each format.
228 | # For example:
229 | #
230 | # response_for :index do |format|
231 | # format.html
232 | # format.atom do
233 | # headers['Content-Type'] = 'application/atom+xml; charset=utf-8'
234 | # render :action => 'atom', :layout => false
235 | # end
236 | # end
237 | #
238 | # This doesn't do anything special for the HTML
239 | # other than ensure that the proper view will be rendered,
240 | # but for ATOM it sets the proper content type
241 | # and renders the atom template.
242 | #
243 | # If you only need to set the HTML response,
244 | # you can omit the format parameter.
245 | # For example:
246 | #
247 | # response_for :new do
248 | # render :action => 'edit'
249 | # end
250 | #
251 | # This is the same as
252 | #
253 | # response_for :new do |format|
254 | # format.html { render :action => 'edit' }
255 | # end
256 | #
257 | # The default responses are defined by
258 | # Default::Responses.included[link:classes/Resourceful/Default/Responses.html#M000011].
259 | def response_for(*actions, &block)
260 | raise "Must specify one or more actions for response_for." if actions.empty?
261 |
262 | if block.arity < 1
263 | response_for(*actions) do |format|
264 | format.html(&block)
265 | end
266 | else
267 | response = Response.new
268 | block.call response
269 |
270 | actions.each do |action|
271 | @responses[action.to_sym] = response.formats
272 | end
273 | end
274 | end
275 |
276 | # :call-seq:
277 | # publish *formats, options = {}, :attributes => [ ... ]
278 | #
279 | # publish allows you to easily expose information about resourcess in a variety of formats.
280 | # The +formats+ parameter is a list of formats
281 | # in which to publish the resources.
282 | # The formats supported by default are +xml+, +yaml+, and +json+,
283 | # but other formats may be added by defining +to_format+ methods
284 | # for the Array and Hash classes
285 | # and registering the mime type with Rails' Mime::Type.register[http://api.rubyonrails.org/classes/Mime/Type.html#M001115].
286 | # See Resourceful::Serialize for more details..
287 | #
288 | # The :attributes option is mandatory.
289 | # It takes an array of attributes (as symbols) to make public.
290 | # These attributes can refer to any method on current_object;
291 | # they aren't limited to database fields.
292 | # For example:
293 | #
294 | # # posts_controller.rb
295 | # publish :yaml, :attributes => [:title, :created_at, :rendered_content]
296 | #
297 | # Then GET /posts/12.yaml would render
298 | #
299 | # ---
300 | # post:
301 | # title: Cool Stuff
302 | # rendered_content: |-
303 | #
This is a post.
304 | #
It's about really cool stuff.
305 | # created_at: 2007-04-28 04:32:08 -07:00
306 | #
307 | # The :attributes array can even contain attributes
308 | # that are themselves models.
309 | # In this case, you must use a hash to specify their attributes as well.
310 | # For example:
311 | #
312 | # # person_controller.rb
313 | # publish :xml, :json, :attributes => [
314 | # :name, :favorite_color, {
315 | # :pet_cat => [:name, :breed],
316 | # :hat => [:type]
317 | # }]
318 | #
319 | # Then GET /people/18.xml would render
320 | #
321 | #
322 | #
323 | # Nathan
324 | # blue
325 | #
326 | # Jasmine
327 | # panther
328 | #
329 | #
330 | # top
331 | #
332 | #
333 | #
334 | # publish will also allow the +index+ action
335 | # to render lists of objects.
336 | # An example would be too big,
337 | # but play with it a little on your own to see.
338 | #
339 | # publish takes only one optional option: only.
340 | # This specifies which action to publish the resources for.
341 | # By default, they're published for both +show+ and +index+.
342 | # For example:
343 | #
344 | # # cats_controller.rb
345 | # publish :json, :only => :index, :attributes => [:name, :breed]
346 | #
347 | # Then GET /cats.json would work, but GET /cats/294.json would fail.
348 | def publish(*formats)
349 | options = {
350 | :only => [:show, :index]
351 | }.merge(Hash === formats.last ? formats.pop : {})
352 | raise "Must specify :attributes option" unless options[:attributes]
353 |
354 | Array(options.delete(:only)).each do |action|
355 | @publish[action] ||= []
356 | formats.each do |format|
357 | format = format.to_sym
358 | @publish[action] << [format, proc do
359 | render_action = [:json, :xml].include?(format) ? format : :text
360 | render render_action => (plural_action? ? current_objects : current_object).serialize(format, options)
361 | end]
362 | end
363 | end
364 | end
365 |
366 | # Specifies parent resources for the current resource.
367 | # Each of these parents will be loaded automatically
368 | # if the proper id parameter is given.
369 | # For example,
370 | #
371 | # # cake_controller.rb
372 | # belongs_to :baker, :customer
373 | #
374 | # Then on GET /bakers/12/cakes,
375 | #
376 | # params[:baker_id] #=> 12
377 | # parent? #=> true
378 | # parent_name #=> "baker"
379 | # parent_model #=> Baker
380 | # parent_object #=> Baker.find(12)
381 | # current_objects #=> Baker.find(12).cakes
382 | #
383 | def belongs_to(*parents)
384 | options = parents.extract_options!
385 | @parents = parents.map(&:to_s)
386 | if options[:shallow]
387 | options[:shallow] = options[:shallow].to_s
388 | raise ArgumentError, ":shallow needs the name of a parent resource" unless @parents.include? options[:shallow]
389 | @shallow_parent = options[:shallow]
390 | end
391 | end
392 |
393 | # Specifies a namespace for the resource model. It can be given as a
394 | # Module::NameSpace, 'Module::NameSpace' (in a string), or
395 | # 'module/name_space' (underscored form).
396 | def model_namespace(ns)
397 | @model_namespace = ns.to_s.camelize
398 | end
399 |
400 | # This method is only meant to be called internally.
401 | #
402 | # Returns whether or not the Builder's controller
403 | # inherits make_resourceful settings from a parent controller.
404 | def inherited?
405 | @inherited
406 | end
407 |
408 | private
409 |
410 | def apply_publish
411 | @publish.each do |action, types|
412 | @responses[action.to_sym] ||= []
413 | @responses[action.to_sym] += types
414 | end
415 | end
416 |
417 | def add_callback(type, *events, &block)
418 | events.each do |event|
419 | @callbacks[type][event.to_sym] ||= []
420 | @callbacks[type][event.to_sym] << block
421 | end
422 | end
423 | end
424 | end
425 |
--------------------------------------------------------------------------------
/lib/resourceful/default/accessors.rb:
--------------------------------------------------------------------------------
1 | module Resourceful
2 | # This module contains various methods
3 | # that are available from actions and callbacks.
4 | # Default::Accessors and Default::URLs are the most useful to users;
5 | # the rest are mostly used internally.
6 | #
7 | # However, if you want to poke around the internals a little,
8 | # check out Default::Actions, which has the default Action definitions,
9 | # and Default::Responses.included, which defines the default response_for[link:classes/Resourceful/Builder.html#M000061] blocks.
10 | module Default
11 | # This module contains all sorts of useful methods
12 | # that allow access to the resources being worked with,
13 | # metadata about the controller and action,
14 | # and so forth.
15 | #
16 | # Many of these accessors call other accessors
17 | # and are called by the default make_resourceful actions[link:classes/Resourceful/Default/Actions.html].
18 | # This means that overriding one method
19 | # can affect everything else.
20 | #
21 | # This can be dangerous, but it can also be very powerful.
22 | # make_resourceful is designed to take advantage of overriding,
23 | # so as long as the new methods accomplish the same purpose as the old ones,
24 | # everything will just work.
25 | # Even if you make a small mistake,
26 | # it's hard to break the controller in any unexpected ways.
27 | #
28 | # For example, suppose your controller is called TagsController,
29 | # but your model is called PhotoTag.
30 | # All you have to do is override current_model_name:
31 | #
32 | # def current_model_name
33 | # "PhotoTag"
34 | # end
35 | #
36 | # Then current_model will return the PhotoTag model,
37 | # current_object will call PhotoTag.find,
38 | # and so forth.
39 | #
40 | # Overriding current_objects and current_object is particularly useful
41 | # for providing customized model lookup logic.
42 | module Accessors
43 | # Returns an array of all the objects of the model corresponding to the controller.
44 | # For UsersController, it essentially runs User.find(:all).
45 | #
46 | # However, there are a few important differences.
47 | # First, this method caches is results in the @current_objects instance variable.
48 | # That way, multiple calls won't run multiple queries.
49 | #
50 | # Second, this method uses the current_model accessor,
51 | # which provides a lot of flexibility
52 | # (see the documentation for current_model for details).
53 | def current_objects
54 | @current_objects ||= current_model.all
55 | end
56 |
57 | # Calls current_objects and stores
58 | # the result in an instance variable
59 | # named after the controller.
60 | #
61 | # This is called automatically by the default make_resourceful actions.
62 | # You shouldn't need to use it directly unless you're creating a new action.
63 | #
64 | # For example, in UsersController,
65 | # calling +load_objects+ sets @users = current_objects.
66 | def load_objects
67 | instance_variable_set("@#{instance_variable_name}", current_objects)
68 | end
69 |
70 | # Returns the object referenced by the id parameter
71 | # (or the newly-created object for the +new+ and +create+ actions).
72 | # For UsersController, it essentially runs User.find(params[:id]).
73 | #
74 | # However, there are a few important differences.
75 | # First, this method caches is results in the @current_objects instance variable.
76 | # That way, multiple calls won't run multiple queries.
77 | #
78 | # Second, this method uses the current_model accessor,
79 | # which provides a lot of flexibility
80 | # (see the documentation for current_model for details).
81 | #
82 | # Note that this is different for a singleton controller,
83 | # where there's only one resource per parent resource.
84 | # Then this just returns that resource.
85 | # For example, if Person has_one Hat,
86 | # then in HatsController current_object essentially runs Person.find(params[:person_id]).hat.
87 | def current_object
88 | @current_object ||= if !parent? || plural?
89 | current_model.find(params[:id]) if params[:id]
90 | else
91 | parent_object.send(instance_variable_name.singularize)
92 | end
93 | end
94 |
95 |
96 | # Calls current_object and stores
97 | # the result in an instance variable
98 | # named after the controller.
99 | #
100 | # This is called automatically by the default make_resourceful actions.
101 | # You shouldn't need to use it directly unless you're creating a new action.
102 | #
103 | # For example, in UsersController,
104 | # calling +load_object+ sets @user = current_object.
105 | def load_object
106 | instance_variable_set("@#{instance_variable_name.singularize}", current_object)
107 | end
108 |
109 | # Creates a new object of the type of the current model
110 | # with the current object's parameters.
111 | # +current_object+ then returns this object for this action
112 | # instead of looking up a new object.
113 | #
114 | # This is called automatically by the default make_resourceful actions.
115 | # You shouldn't need to use it directly unless you're creating a new action.
116 | #
117 | # Note that if a parent object exists,
118 | # the newly created object will automatically be a child of the parent object.
119 | # For example, on POST /people/4/things,
120 | #
121 | # build_object
122 | # current_object.person.id #=> 4
123 | #
124 | def build_object
125 | @current_object = if current_model.respond_to? :build
126 | current_model.build(object_parameters)
127 | else
128 | current_model.new(object_parameters).tap do |obj|
129 | if singular? && parent?
130 | obj.send("#{parent_name}_id=", parent_object.id)
131 | obj.send("#{parent_name}_type=", parent_object.class.to_s) if polymorphic_parent?
132 | end
133 | end
134 | end
135 | end
136 |
137 | def namespaced_model_name
138 | [self.class.model_namespace, current_model_name].compact.join('::')
139 | end
140 |
141 | # The string name of the current model.
142 | # By default, this is derived from the name of the controller.
143 | def current_model_name
144 | controller_name.singularize.camelize
145 | end
146 |
147 | # An array of namespaces under which the current controller is.
148 | # For example, in Admin::Content::PagesController:
149 | #
150 | # namespaces #=> [:admin, :content]
151 | #
152 | def namespaces
153 | @namespaces ||= self.class.name.split('::').slice(0...-1).map(&:underscore).map(&:to_sym)
154 | end
155 |
156 | # The name of the instance variable that load_object and load_objects should assign to.
157 | def instance_variable_name
158 | controller_name
159 | end
160 |
161 | # The class of the current model.
162 | # Note that if a parent object exists,
163 | # this instead returns the association object.
164 | # For example, in HatsController where Person has_many :hats,
165 | #
166 | # current_model #=> Person.find(params[:person_id]).hats
167 | #
168 | # This is useful because the association object uses duck typing
169 | # to act like a model class.
170 | # It supplies a find method that's automatically scoped
171 | # to ensure that the object returned is actually a child of the parent,
172 | # and so forth.
173 | def current_model
174 | if !parent? || singular?
175 | namespaced_model_name.constantize
176 | else
177 | parent_object.send(instance_variable_name)
178 | end
179 | end
180 |
181 | # Returns the hash passed as HTTP parameters
182 | # that defines the new (or updated) attributes
183 | # of the current object.
184 | # This is only meaningful for +create+ or +update+.
185 | def object_parameters
186 | if params.respond_to?(:permit) && self.class.permitted_params && params[:action].to_s != "new"
187 | permitable_method = if self.class.permitted_params == :all
188 | [:permit!]
189 | else
190 | [:permit, self.class.permitted_params]
191 | end
192 | params.require(namespaced_model_name.underscore.tr('/', '_')).send(*permitable_method)
193 | else
194 | params[namespaced_model_name.underscore.tr('/', '_')]
195 | end
196 | end
197 |
198 | # Returns a list of the names of all the potential parents of the current model.
199 | # For a non-nested controller, this is [].
200 | # For example, in HatsController where Rack has_many :hats and Person has_many :hats,
201 | #
202 | # parents #=> ["rack", "person"]
203 | #
204 | # Note that the parents must be declared via Builder#belongs_to.
205 | def parent_names
206 | self.class.parent_controllers
207 | end
208 |
209 | # Returns true if an appropriate parent id parameter has been supplied.
210 | # For example, in HatsController where Rack has_many :hats and Person has_many :hats,
211 | # if params[:rack_id] or params[:person_id] is given,
212 | #
213 | # parent? #=> true
214 | #
215 | # Otherwise, if both params[:rack_id] and params[:rack_id] are nil,
216 | #
217 | # parent? #=> false
218 | #
219 | # Note that parents must be declared via Builder#belongs_to.
220 | def parent?
221 | !!parent_name
222 | end
223 |
224 | # Returns true if no parent id parameter can be found _and_ a belongs_to
225 | # relationship on this controller was declared with a parent for shallow
226 | # routing.
227 | def shallow?
228 | self.class.shallow_parent &&
229 | (parent_name.nil? || parent_name == self.class.shallow_parent)
230 | end
231 |
232 | # Returns whether the parent (if it exists) is polymorphic
233 | def polymorphic_parent?
234 | !!polymorphic_parent_name
235 | end
236 |
237 | # Returns the name of the current parent object if a parent id is given,
238 | # or nil otherwise. For example, in HatsController where Rack has_many
239 | # :hats and Person has_many :hats, if params[:rack_id] is
240 | # given,
241 | #
242 | # parent_name #=> "rack"
243 | #
244 | # If params[:person_id] is given,
245 | #
246 | # parent_name #=> "person"
247 | #
248 | # If both params[:rack_id] and params[:person_id] are
249 | # nil,
250 | #
251 | # parent_name #=> nil
252 | #
253 | # There are several things to note about this method. First,
254 | # make_resourceful only supports single-level model nesting. Thus, if
255 | # neither params[:rack_id] nor params[:rack_id] are
256 | # nil, the return value of +parent_name+ is undefined.
257 | #
258 | # Second, don't use parent_name to check whether a parent id is given.
259 | # It's better to use the more semantic parent? method.
260 | #
261 | # Third, parent_name caches its return value in the
262 | # @parent_name variable, which you should keep in mind if
263 | # you're overriding it. However, because @parent_name == nil
264 | # could mean that there is no parent _or_ that the method hasn't been
265 | # run yet, it uses defined?(@parent_name) to do the caching
266 | # rather than @parent_name ||=. See the source code.
267 | #
268 | # Finally, note that parents must be declared via Builder#belongs_to.
269 | #
270 | # FIXME - Perhaps this logic should be moved to parent?() or another
271 | # init method
272 | def parent_name
273 | return @parent_name if defined?(@parent_name)
274 | @parent_name = parent_names.find { |name| params["#{name}_id"] }
275 | if @parent_name.nil?
276 | # get any polymorphic parents through :as association inspection
277 | names = params.keys.inject({}) do |hsh, key|
278 | hsh[key] = key.chomp("_id") if key.to_s =~ /_id$/
279 | hsh
280 | end
281 | names.each do |key, name|
282 | begin
283 | klass = name.camelize.constantize
284 | if association = klass.reflect_on_all_associations.detect { |association| association.options[:as] && parent_names.include?(association.options[:as].to_s) }
285 | @parent_name = name
286 | @polymorphic_parent_name = association.options[:as].to_s
287 | @parent_class_name = name.camelize
288 | @parent_object = klass.find(params[key])
289 | break
290 | end
291 | rescue
292 | end
293 | end
294 | else
295 | @parent_class_name = params["#{parent_name}_type"]
296 | @polymorphic_parent = !@parent_class_name.nil? # NEVER USED
297 | end
298 | @parent_name
299 | end
300 |
301 | def polymorphic_parent_name
302 | @polymorphic_parent_name
303 | end
304 |
305 | # Returns the class name of the current parent.
306 | # For example, in HatsController where Person has_many :hats,
307 | # if params[:person_id] is given,
308 | #
309 | # parent_class_name #=> 'Person'
310 | #
311 | # Note that parents must be declared via Builder#belongs_to.
312 | def parent_class_name
313 | parent_name # to init @parent_class_name
314 | @parent_class_name ||= parent_name.nil? ? nil : parent_name.camelize
315 | end
316 |
317 | # Returns the model class of the current parent.
318 | # For example, in HatsController where Person has_many :hats,
319 | # if params[:person_id] is given,
320 | #
321 | # parent_models #=> Person
322 | #
323 | # Note that parents must be declared via Builder#belongs_to.
324 | def parent_model
325 | parent_class_name.nil? ? nil : parent_class_name.constantize
326 | end
327 |
328 | # Returns the current parent object for the current object.
329 | # For example, in HatsController where Person has_many :hats,
330 | # if params[:person_id] is given,
331 | #
332 | # parent_object #=> Person.find(params[:person_id])
333 | #
334 | # Note that parents must be declared via Builder#belongs_to.
335 | #
336 | # Note also that the results of this method are cached
337 | # so that multiple calls don't result in multiple SQL queries.
338 | def parent_object
339 | @parent_object ||= parent_model.nil? ? nil : parent_model.find(params["#{parent_name}_id"])
340 | end
341 |
342 | # Assigns the current parent object, as given by parent_objects,
343 | # to its proper instance variable, as given by parent_name.
344 | #
345 | # This is automatically added as a before_action.
346 | # You shouldn't need to use it directly unless you're creating a new action.
347 | def load_parent_object
348 | instance_variable_set("@#{parent_name}", parent_object) if parent?
349 | instance_variable_set("@#{polymorphic_parent_name}", parent_object) if polymorphic_parent?
350 | end
351 |
352 | # Renders a 422 error if no parent id is given.
353 | # This is meant to be used with before_action
354 | # to ensure that some actions are only called with a parent id.
355 | # For example:
356 | #
357 | # before_action :ensure_parent_exists, :only => [:create, :update]
358 | #
359 | def ensure_parent_exists
360 | return true if parent?
361 | render :text => 'No parent id given', :status => 422
362 | return false
363 | end
364 |
365 | # Returns whether or not the database update in the +create+, +update+, and +destroy+
366 | # was completed successfully.
367 | def save_succeeded?
368 | @save_succeeded
369 | end
370 |
371 | # Declares that the current databse update was completed successfully.
372 | # Causes subsequent calls to save_succeeded? to return +true+.
373 | #
374 | # This is mostly meant to be used by the default actions,
375 | # but it can be used by user-defined actions as well.
376 | def save_succeeded!
377 | @save_succeeded = true
378 | end
379 |
380 | # Declares that the current databse update was not completed successfully.
381 | # Causes subsequent calls to save_succeeded? to return +false+.
382 | #
383 | # This is mostly meant to be used by the default actions,
384 | # but it can be used by user-defined actions as well.
385 | def save_failed!
386 | @save_succeeded = false
387 | end
388 |
389 | # Returns whether or not the current action acts upon multiple objects.
390 | # By default, the only such action is +index+.
391 | def plural_action?
392 | PLURAL_ACTIONS.include?(params[:action].to_sym)
393 | end
394 |
395 | # Returns whether or not the current action acts upon a single object.
396 | # By default, this is the case for all actions but +index+.
397 | def singular_action?
398 | !plural_action?
399 | end
400 |
401 | # Returns whether the controller is a singleton,
402 | # implying that there is only one such resource for each parent resource.
403 | #
404 | # Note that the way this is determined is based on the singularity of the controller name,
405 | # so it may yield false positives for oddly-named controllers and need to be overridden.
406 | #
407 | # TODO: maybe we can define plural? and singular? as class_methods,
408 | # so they are not visible to the world
409 | def singular?
410 | instance_variable_name.singularize == instance_variable_name
411 | end
412 |
413 | # Returns whether the controller is a normal plural controller,
414 | # implying that there are multiple resources for each parent resource.
415 | #
416 | # Note that the way this is determined is based on the singularity of the controller name,
417 | # so it may yield false negatives for oddly-named controllers.
418 | # If this is the case, the singular? method should be overridden.
419 | #
420 | # TODO: maybe we can define plural? and singular? as class_methods,
421 | # so they are not visible to the world
422 | def plural?
423 | !singular?
424 | end
425 | end
426 | end
427 | end
428 |
--------------------------------------------------------------------------------
/lib/resourceful/default/actions.rb:
--------------------------------------------------------------------------------
1 | module Resourceful
2 | module Default
3 | # Contains the definitions of the default resourceful actions.
4 | # These are made available with the Builder#actions method.
5 | #
6 | # These methods are very compact,
7 | # so the best way to understand them is just to look at their source.
8 | # Check out Resourceful::Accessors and Resourceful::Callbacks
9 | # for the documentation of the methods called within the actions.
10 | #
11 | # Along with each action is listed the RESTful method
12 | # which corresponds to the action.
13 | # The controller in the examples is FoosController,
14 | # and the id for single-object actions is 12.
15 | module Actions
16 | # GET /foos
17 | def index
18 | #load_objects
19 | before :index
20 | response_for :index
21 | end
22 |
23 | # GET /foos/12
24 | def show
25 | # NOTE - Moved this call to a more generic place
26 | #load_object
27 | before :show
28 | response_for :show
29 | rescue
30 | response_for :show_fails
31 | end
32 |
33 | # POST /foos
34 | def create
35 | build_object
36 | load_object
37 | before :create
38 | if current_object.save
39 | save_succeeded!
40 | after :create
41 | response_for :create
42 | else
43 | save_failed!
44 | after :create_fails
45 | response_for :create_fails
46 | end
47 | end
48 |
49 | # PUT /foos/12
50 | def update
51 | #load_object
52 | before :update
53 |
54 | begin
55 | result = current_object.update_attributes object_parameters
56 | rescue ActiveRecord::StaleObjectError
57 | current_object.reload
58 | result = false
59 | end
60 |
61 | if result
62 | save_succeeded!
63 | after :update
64 | response_for :update
65 | else
66 | save_failed!
67 | after :update_fails
68 | response_for :update_fails
69 | end
70 | end
71 |
72 | # GET /foos/new
73 | def new
74 | build_object
75 | load_object
76 | before :new
77 | response_for :new
78 | end
79 |
80 | # GET /foos/12/edit
81 | def edit
82 | #load_object
83 | before :edit
84 | response_for :edit
85 | end
86 |
87 | # DELETE /foos/12
88 | def destroy
89 | #load_object
90 | before :destroy
91 | if current_object.destroy
92 | after :destroy
93 | response_for :destroy
94 | else
95 | after :destroy_fails
96 | response_for :destroy_fails
97 | end
98 | end
99 | end
100 | end
101 | end
102 |
--------------------------------------------------------------------------------
/lib/resourceful/default/callbacks.rb:
--------------------------------------------------------------------------------
1 | require 'resourceful/builder'
2 |
3 | module Resourceful
4 | module Default
5 | # This module is mostly meant to be used by the make_resourceful default actions.
6 | # It provides various methods that declare where callbacks set in the +make_resourceful+ block,
7 | # like Builder#before and Builder#response_for,
8 | # should be called.
9 | module Callbacks
10 | # Calls any +before+ callbacks set in the +make_resourceful+ block for the given event.
11 | def before(event)
12 | resourceful_fire(:before, event.to_sym)
13 | end
14 |
15 | # Calls any +after+ callbacks set in the +make_resourceful+ block for the given event.
16 | def after(event)
17 | resourceful_fire(:after, event.to_sym)
18 | end
19 |
20 | # Calls any +response_for+ callbacks set in the +make_resourceful+ block for the given event.
21 | # Note that these aren't called directly,
22 | # but instead passed along to Rails' respond_to method.
23 | def response_for(event)
24 | if responses = self.class.resourceful_responses[event.to_sym]
25 | respond_to do |format|
26 | responses.each do |key, value|
27 | format.send(key, &scope(value))
28 | end
29 | end
30 | end
31 | end
32 |
33 | # Returns a block identical to the given block,
34 | # but in the context of the current controller.
35 | # The returned block accepts no arguments,
36 | # even if the given block accepted them.
37 | def scope(block)
38 | proc do
39 | instance_eval(&(block || proc {}))
40 | end
41 | end
42 |
43 | private
44 |
45 | def resourceful_fire(type, name)
46 | callbacks = self.class.resourceful_callbacks[type][name] || []
47 | callbacks.each { |callback| scope(callback).call }
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/resourceful/default/responses.rb:
--------------------------------------------------------------------------------
1 | module Resourceful
2 | module Default
3 | module Responses
4 | # Sets the default flash message.
5 | # This message can be overridden by passing in
6 | # an HTTP parameter of the form "_flash[type]" via POST or GET.
7 | #
8 | # You can use this to easily have multiple forms
9 | # post to the same create/edit/destroy actions
10 | # but display different flash notices -
11 | # without modifying the controller code at all.
12 | #
13 | # By default, the flash types are +notice+ when the database operation completes successfully
14 | # and +error+ when it fails.
15 | #
16 | #--
17 | # TODO: Move this out of here
18 | #++
19 | def set_default_flash(type, message)
20 | flash[type] ||= (params[:_flash] && params[:_flash][type]) || message
21 | end
22 |
23 | # Sets the default redirect
24 | # (the argument passed to +redirect_to+).
25 | # This message can be overridden by passing in
26 | # an HTTP parameter of the form "_redirect_on[status]" via POST or GET.
27 | #
28 | # You can use this to easily have multiple forms
29 | # post to the same create/edit/destroy actions
30 | # but redirect to different URLs -
31 | # without modifying the controller code at all.
32 | #
33 | # By default, the redirect statuses are +success+ when the database operation completes successfully
34 | # and +failure+ when it fails.
35 | # Use the :status option to specify which status to run the redirect for.
36 | # For example:
37 | #
38 | # set_default_redirect "/posts", :status => :failure
39 | #
40 | # This will run redirect_to params[:_redirect_on][:failure] if the parameter exists,
41 | # or redirect_to "/posts" otherwise.
42 | #
43 | #--
44 | # TODO: Move this out of here
45 | #++
46 | def set_default_redirect(to, options = {})
47 | status = options[:status] || :success
48 | redirect_to (params[:_redirect_on] && params[:_redirect_on][status]) || to
49 | end
50 |
51 | # This method is automatically run when this module is included in Resourceful::Base.
52 | # It sets up the default responses for the default actions.
53 | def self.included(base)
54 | base.made_resourceful do
55 | response_for(:show, :index, :edit, :new) do |format|
56 | format.html
57 | format.js
58 | end
59 |
60 | response_for(:show_fails) do |format|
61 | not_found = Proc.new { render :text => I18n.t('make_resourceful.show.fails', :default => "No item found"), :status => 404 }
62 | format.html ¬_found
63 | format.js ¬_found
64 | format.xml ¬_found
65 | end
66 |
67 | response_for(:create) do |format|
68 | format.html do
69 | set_default_flash :notice, I18n.t('make_resourceful.create.success', :default => "Create successful!")
70 | set_default_redirect object_path
71 | end
72 | format.js
73 | end
74 |
75 | response_for(:create_fails) do |format|
76 | format.html do
77 | set_default_flash :error, I18n.t('make_resourceful.create.fails', :default => "There was a problem!")
78 | render :action => :new, :status => 422
79 | end
80 | format.js
81 | end
82 |
83 | response_for(:update) do |format|
84 | format.html do
85 | set_default_flash :notice, I18n.t('make_resourceful.update.success', :default => "Save successful!")
86 | set_default_redirect object_path
87 | end
88 | format.js
89 | end
90 |
91 | response_for(:update_fails) do |format|
92 | format.html do
93 | set_default_flash :error, I18n.t('make_resourceful.update.fails', :default => "There was a problem saving!")
94 | render :action => :edit, :status => 422
95 | end
96 | format.js
97 | end
98 |
99 | response_for(:destroy) do |format|
100 | format.html do
101 | set_default_flash :notice, I18n.t('make_resourceful.destroy.success', :default => "Record deleted!")
102 | set_default_redirect objects_path
103 | end
104 | format.js
105 | end
106 |
107 | response_for(:destroy_fails) do |format|
108 | format.html do
109 | set_default_flash :error, I18n.t('make_resourceful.destroy.fails', :default => "There was a problem deleting!")
110 | set_default_redirect :back, :status => :failure
111 | end
112 | format.js
113 | end
114 | end
115 | end
116 | end
117 | end
118 | end
119 |
--------------------------------------------------------------------------------
/lib/resourceful/default/urls.rb:
--------------------------------------------------------------------------------
1 | module Resourceful
2 | module Default
3 | # This file contains various methods to make URL helpers less painful.
4 | # They provide methods analogous to the standard foo_url and foo_path helpers.
5 | # However, they use make_resourceful's knowledge of the structure of the controller
6 | # to allow you to avoid figuring out which method to call and which parent objects it should be passed.
7 | module URLs
8 | # This returns the path for the given object,
9 | # by default current_object[link:classes/Resourceful/Default/Accessors.html#M000012].
10 | # For example, in HatsController the following are equivalent:
11 | #
12 | # object_path #=> "/hats/12"
13 | # hat_path(@hat) #=> "/hats/12"
14 | #
15 | def object_path(object = current_object); object_route(object, 'path'); end
16 | # Same as object_path, but with the protocol and hostname.
17 | def object_url (object = current_object); object_route(object, 'url'); end
18 |
19 | # This is the same as object_path,
20 | # unless a parent exists.
21 | # Then it returns the nested path for the object.
22 | # For example, in HatsController where Person has_many :hats and params[:person_id] == 42,
23 | # the following are equivalent:
24 | #
25 | # nested_object_path #=> "/person/42/hats/12"
26 | # person_hat_path(@person, @hat) #=> "/person/42/hats/12"
27 | #
28 | def nested_object_path(object = current_object); nested_object_route(object, 'path'); end
29 | # Same as nested_object_path, but with the protocol and hostname.
30 | def nested_object_url (object = current_object); nested_object_route(object, 'url'); end
31 |
32 | # This returns the path for the edit action for the given object,
33 | # by default current_object[link:classes/Resourceful/Default/Accessors.html#M000012].
34 | # For example, in HatsController the following are equivalent:
35 | #
36 | # edit_object_path #=> "/hats/12/edit"
37 | # edit_person_hat_path(@person, @hat) #=> "/hats/12/edit"
38 | #
39 | def edit_object_path(object = current_object); edit_object_route(object, 'path'); end
40 | # Same as edit_object_path, but with the protocol and hostname.
41 | def edit_object_url (object = current_object); edit_object_route(object, 'url'); end
42 |
43 | # This returns the path for the collection of the current controller.
44 | # For example, in HatsController where Person has_many :hats and params[:person_id] == 42,
45 | # the following are equivalent:
46 | #
47 | # objects_path #=> "/people/42/hats"
48 | # person_hats_path(@person) #=> "/people/42/hats"
49 | #
50 | def objects_path; objects_route('path'); end
51 | # Same as objects_path, but with the protocol and hostname.
52 | def objects_url ; objects_route('url'); end
53 |
54 | # This returns the path for the new action for the current controller.
55 | # For example, in HatsController where Person has_many :hats and params[:person_id] == 42,
56 | # the following are equivalent:
57 | #
58 | # new_object_path #=> "/people/42/hats/new"
59 | # new_person_hat_path(@person) #=> "/people/42/hats/new"
60 | #
61 | def new_object_path; new_object_route('path'); end
62 | # Same as new_object_path, but with the protocol and hostname.
63 | def new_object_url ; new_object_route('url'); end
64 |
65 | # This returns the path for the parent object.
66 | #
67 | def parent_path(object = parent_object)
68 | instance_route(parent_class_name.underscore, object, 'path')
69 | end
70 | # Same as parent_path, but with the protocol and hostname.
71 | def parent_url(object = parent_object)
72 | instance_route(parent_class_name.underscore, object, 'url')
73 | end
74 |
75 | # This prefix is added to the Rails URL helper names
76 | # before they're called.
77 | # By default, it's the underscored list of namespaces of the current controller,
78 | # or nil if there are no namespaces defined.
79 | # However, it can be overridden if another prefix is needed.
80 | # Note that if this is overridden,
81 | # the new method should return a string ending in an underscore.
82 | #
83 | # For example, in Admin::Content::PagesController:
84 | #
85 | # url_helper_prefix #=> "admin_content_"
86 | #
87 | # Then object_path is the same as admin_content_page_path(current_object).
88 | def url_helper_prefix
89 | namespaces.empty? ? nil : "#{namespaces.join('_')}_"
90 | end
91 |
92 | # This prefix is added to the Rails URL helper names
93 | # for the make_resourceful collection URL helpers,
94 | # objects_path and new_object_path.
95 | # By default, it's the parent name followed by an underscore if a parent
96 | # is given, and the empty string otherwise.
97 | #
98 | # See also url_helper_prefix.
99 | def collection_url_prefix
100 | parent? ? "#{parent_class_name.underscore}_" : ''
101 | end
102 |
103 | private
104 |
105 | def object_route(object, type)
106 | instance_route(current_model_name.underscore, object, type)
107 | end
108 |
109 | def nested_object_route(object, type)
110 | return object_route(object, type) unless parent?
111 | send("#{url_helper_prefix}#{parent_class_name.underscore}_#{current_model_name.underscore}_#{type}", parent_object, object)
112 | end
113 |
114 | def edit_object_route(object, type)
115 | instance_route(current_model_name.underscore, object, type, "edit")
116 | end
117 |
118 | def objects_route(type)
119 | collection_route(current_model_name.pluralize.underscore, type)
120 | end
121 |
122 | def new_object_route(type)
123 | collection_route(current_model_name.underscore, type, "new")
124 | end
125 |
126 | def instance_route(name, object, type, action = nil)
127 | send("#{action ? action + '_' : ''}#{url_helper_prefix}#{collection_url_prefix unless shallow?}#{name}_#{type}", *(parent? && !shallow? ? [parent_object, object] : [object]))
128 | end
129 |
130 | def collection_route(name, type, action = nil)
131 | send("#{action ? action + '_' : ''}#{url_helper_prefix}#{collection_url_prefix}#{name}_#{type}",
132 | *(parent? ? [parent_object] : []))
133 | end
134 | end
135 | end
136 | end
137 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb:
--------------------------------------------------------------------------------
1 | class ResourcefulScaffoldGenerator < Rails::Generators::Base
2 | attr_reader :controller_class_path,
3 | :controller_file_path,
4 | :controller_class_nesting,
5 | :controller_class_nesting_depth,
6 | :controller_class_name,
7 | :controller_underscore_name,
8 | :controller_plural_name
9 | alias_method :controller_file_name, :controller_underscore_name
10 | alias_method :controller_table_name, :controller_plural_name
11 |
12 | def initialize(runtime_args, runtime_options = {})
13 | super
14 |
15 | base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@name.pluralize)
16 | @controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
17 |
18 | if @controller_class_nesting.empty?
19 | @controller_class_name = @controller_class_name_without_nesting
20 | else
21 | @controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
22 | end
23 | end
24 |
25 | def manifest
26 | record do |m|
27 | # Check for class naming collisions.
28 | m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
29 | m.class_collisions(class_path, "#{class_name}")
30 |
31 | # Controller, helper, views, and test directories.
32 | m.directory(File.join('app/models', class_path))
33 | m.directory(File.join('app/controllers', controller_class_path))
34 | m.directory(File.join('app/helpers', controller_class_path))
35 | m.directory(File.join('app/views', controller_class_path, controller_file_name))
36 | m.directory(File.join('test/functional', controller_class_path))
37 | m.directory(File.join('test/unit', class_path))
38 | m.directory(File.join('test/fixtures', class_path))
39 |
40 | # Views
41 | for action in scaffold_views
42 | m.template("view_#{action}.haml", File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.haml"))
43 | end
44 | m.template('view_partial.haml', File.join('app/views', controller_class_path, controller_file_name, "_#{singular_name}.html.haml"))
45 |
46 | # Helper
47 | m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
48 |
49 | # Model
50 | m.template('model.rb', File.join('app/models', class_path, "#{file_name}.rb"))
51 |
52 | unless options[:skip_migration]
53 | m.migration_template('migration.rb', 'db/migrate',
54 | :assigns => {
55 | :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}",
56 | :attributes => attributes
57 | },
58 | :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}")
59 | end
60 |
61 | # Controller
62 | m.template('controller.rb', File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb"))
63 |
64 | # Tests
65 | m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
66 | m.template('unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb"))
67 | m.template('fixtures.yml', File.join('test/fixtures', "#{table_name}.yml"))
68 |
69 | # Route
70 | m.route_resources controller_file_name
71 | end
72 | end
73 |
74 | protected
75 |
76 | def banner
77 | "Usage: #{$0} resourcefulscaffold ModelName [field:type, field:type]"
78 | end
79 |
80 | def scaffold_views
81 | %w[ index show new edit _form ]
82 | end
83 |
84 | def model_name
85 | class_name.demodulize
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/controller.rb:
--------------------------------------------------------------------------------
1 | class <%= controller_class_name %>Controller < ApplicationController
2 | make_resourceful do
3 | actions :all
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/fixtures.yml:
--------------------------------------------------------------------------------
1 | one:
2 | id: 1
3 | <% for attribute in attributes -%>
4 | <%= attribute.name %>: <%= attribute.default %>
5 | <% end -%>
6 | two:
7 | id: 2
8 | <% for attribute in attributes -%>
9 | <%= attribute.name %>: <%= attribute.default %>
10 | <% end -%>
11 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/functional_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '<%= '/..' * controller_class_nesting_depth %>/../test_helper'
2 | require '<%= controller_file_path %>_controller'
3 |
4 | # Re-raise errors caught by the controller.
5 | class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
6 |
7 | class <%= controller_class_name %>ControllerTest < ActionController::TestCase
8 |
9 | def test_should_get_index
10 | get :index
11 | assert_response :success
12 | assert assigns(:<%= table_name %>)
13 | end
14 |
15 | def test_should_get_new
16 | get :new
17 | assert_response :success
18 | end
19 |
20 | def test_should_create_<%= file_name %>
21 | old_count = <%= class_name %>.count
22 | post :create, :<%= file_name %> => { }
23 | assert_equal old_count + 1, <%= class_name %>.count
24 |
25 | assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
26 | end
27 |
28 | def test_should_show_<%= file_name %>
29 | get :show, :id => 1
30 | assert_response :success
31 | end
32 |
33 | def test_should_get_edit
34 | get :edit, :id => 1
35 | assert_response :success
36 | end
37 |
38 | def test_should_update_<%= file_name %>
39 | put :update, :id => 1, :<%= file_name %> => { }
40 | assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
41 | end
42 |
43 | def test_should_destroy_<%= file_name %>
44 | old_count = <%= class_name %>.count
45 | delete :destroy, :id => 1
46 | assert_equal old_count-1, <%= class_name %>.count
47 |
48 | assert_redirected_to <%= table_name %>_path
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/helper.rb:
--------------------------------------------------------------------------------
1 | module <%= controller_class_name %>Helper
2 | end
3 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/migration.rb:
--------------------------------------------------------------------------------
1 | class <%= migration_name %> < ActiveRecord::Migration
2 | def self.up
3 | create_table :<%= table_name %>, :force => true do |t|
4 | <% for attribute in attributes -%>
5 | t.column :<%= attribute.name %>, :<%= attribute.type %>
6 | <% end -%>
7 | end
8 | end
9 |
10 | def self.down
11 | drop_table :<%= table_name %>
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/model.rb:
--------------------------------------------------------------------------------
1 | class <%= class_name %> < ActiveRecord::Base
2 | end
3 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/unit_test.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
2 |
3 | class <%= class_name %>Test < Test::Unit::TestCase
4 | def test_truth
5 | assert true
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/view__form.haml:
--------------------------------------------------------------------------------
1 | <%- for attribute in attributes -%>
2 | %p
3 | %label{:for => "<%= singular_name %>_<%= attribute.name %>"} <%= attribute.column.human_name %>:
4 | = f.<%= attribute.field_type %> :<%= attribute.name %>
5 | <% end -%>
6 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/view_edit.haml:
--------------------------------------------------------------------------------
1 | %h1 Editing <%= singular_name %>
2 |
3 | = error_messages_for :<%= singular_name %>
4 |
5 | = form_for(:<%= singular_name %>, :url => object_url, :html => { :method => :put }) do |f|
6 | = render :partial => "form", :locals => {:f => f}
7 | %p= submit_tag "Update"
8 |
9 | = link_to 'Show', object_path
10 | |
11 | = link_to 'Back', objects_path
12 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/view_index.haml:
--------------------------------------------------------------------------------
1 | %h1 Listing <%= plural_name %>
2 |
3 | = render :partial => '<%= singular_name %>', :collection => current_objects
4 |
5 | = link_to 'New <%= singular_name %>', new_object_path
6 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/view_new.haml:
--------------------------------------------------------------------------------
1 | %h1 Creating <%= singular_name %>
2 |
3 | = error_messages_for :<%= singular_name %>
4 |
5 | = form_for(:<%= singular_name %>, :url => objects_url) do |f|
6 | = render :partial => "form", :locals => {:f => f}
7 | %p= submit_tag "Create"
8 |
9 | = link_to 'Back', objects_path
10 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/view_partial.haml:
--------------------------------------------------------------------------------
1 | %div[<%= singular_name %>]
2 | <% for attribute in attributes -%>
3 | %p.<%= attribute.name %>
4 | %strong <%= attribute.column.human_name %>
5 | = h <%= singular_name %>.<%= attribute.name %>
6 | <% end -%>
7 |
8 | = link_to 'Show', object_path(<%= singular_name %>)
9 | |
10 | = link_to 'Edit', edit_object_path(<%= singular_name %>)
11 | |
12 | = link_to 'Destroy', object_path(<%= singular_name %>), :confirm => 'Really destroy <%= singular_name %>?', :method => :delete
13 |
--------------------------------------------------------------------------------
/lib/resourceful/generators/resourceful_scaffold/templates/view_show.haml:
--------------------------------------------------------------------------------
1 | %h1 Viewing <%= singular_name %>
2 |
3 | %div[current_object]
4 | <% for attribute in attributes -%>
5 | %p.<%= attribute.name %>
6 | %strong <%= attribute.column.human_name %>
7 | = h current_object.<%= attribute.name %>
8 | <% end -%>
9 |
10 | = link_to 'Edit', edit_object_path
11 | |
12 | = link_to 'Destroy', object_path, :confirm => 'Really destroy <%= singular_name %>?', :method => :delete
13 | |
14 | = link_to 'Back', objects_path
15 |
--------------------------------------------------------------------------------
/lib/resourceful/maker.rb:
--------------------------------------------------------------------------------
1 | require 'resourceful/builder'
2 | require 'resourceful/base'
3 |
4 | module Resourceful
5 | # This module is extended by the ActionController::Base class object.
6 | # It provides the actual +make_resourceful+ method
7 | # and sets up the controller so that everything will work.
8 | module Maker
9 | # Called automatically on ActionController::Base.
10 | # Initializes various inheritable attributes.
11 | def self.extended(base)
12 | base.class_attribute :resourceful_callbacks
13 | base.class_attribute :resourceful_responses
14 | base.class_attribute :parent_controllers
15 | base.class_attribute :shallow_parent
16 | base.class_attribute :model_namespace
17 | base.class_attribute :made_resourceful
18 | base.class_attribute :permitted_params
19 |
20 | base.resourceful_callbacks = {}
21 | base.resourceful_responses = {}
22 | base.parent_controllers = []
23 | base.model_namespace = nil
24 | base.made_resourceful = false
25 | end
26 |
27 | # :call-seq:
28 | # make_resourceful(options = {}) { ... }
29 | #
30 | # This is the central method, and namesake, of make_resourceful.
31 | # It takes a block and evaluates it in the context of a Builder,
32 | # allowing the controller to be customized extensively.
33 | #
34 | # See Resourceful::Builder for documentation on the methods available
35 | # in the context of the block.
36 | #
37 | # The only option currently available is :include.
38 | # It takes an object that responds to to_proc
39 | # (or an array of such objects)
40 | # and evaluates that proc in the same context as the block.
41 | # For example:
42 | #
43 | # make_resourceful :include => proc { actions :all } do
44 | # before :show do
45 | # current_object.current_user = current_user
46 | # end
47 | # end
48 | #
49 | # This is the same as:
50 | #
51 | # make_resourceful do
52 | # actions :all
53 | # before :show do
54 | # current_object.current_user = current_user
55 | # end
56 | # end
57 | #
58 | def make_resourceful(options = {}, &block)
59 | # :stopdoc:
60 | include Resourceful::Base
61 | # :startdoc:
62 |
63 | builder = Resourceful::Builder.new(self)
64 | unless builder.inherited?
65 | Resourceful::Base.made_resourceful.each { |proc| builder.instance_eval(&proc) }
66 | end
67 | Array(options[:include]).each { |proc| builder.instance_eval(&proc) }
68 | builder.instance_eval(&block)
69 |
70 | builder.apply
71 |
72 | add_helpers
73 | end
74 |
75 | # Returns whether or not make_resourceful has been called
76 | # on this controller or any controllers it inherits from.
77 | def made_resourceful?
78 | self.class.made_resourceful
79 | end
80 |
81 | private
82 |
83 | def add_helpers
84 | helper_method(:object_path, :objects_path, :new_object_path, :edit_object_path,
85 | :object_url, :objects_url, :new_object_url, :edit_object_url,
86 | :parent_path, :parent_url,
87 | :nested_object_path, :nested_object_url,
88 | :current_objects, :current_object, :current_model, :current_model_name,
89 | :namespaces, :instance_variable_name, :parent_names, :parent_name,
90 | :parent?, :parent_model, :parent_object, :save_succeeded?)
91 | end
92 | end
93 | end
94 |
--------------------------------------------------------------------------------
/lib/resourceful/response.rb:
--------------------------------------------------------------------------------
1 | module Resourceful
2 | # This is the class of the object passed to the Builder#response_for method.
3 | # It shouldn't be used by users.
4 | #
5 | # The Response collects format procs
6 | # and returns them with the format method,
7 | # in the order they were given.
8 | # For example:
9 | #
10 | # response.html { redirect_to '/' }
11 | # response.xml { render :xml => current_object.to_xml }
12 | # response.js
13 | # response.formats #=> [[:html, #], [:xml, #], [:js, #]]
14 | #
15 | # Note that the :js response is the empty proc -
16 | # the same as proc {}.
17 | class Response # :nodoc:
18 | # Returns a list of pairs of formats and procs
19 | # representing the formats passed to the response object.
20 | # See class description.
21 | attr :formats
22 |
23 | # Returns a new Response with no format data.
24 | def initialize
25 | @formats = []
26 | end
27 |
28 | # Used to dispatch the individual format methods.
29 | def method_missing(name, &block)
30 | @formats.push([name, block || proc {}]) unless @formats.any? {|n,b| n == name}
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/resourceful/serialize.rb:
--------------------------------------------------------------------------------
1 | require 'resourceful/builder'
2 |
3 | module Resourceful
4 | # This module contains mixin modules
5 | # used to implement the object serialization
6 | # used for the Builder#publish method.
7 | # They can also be used to get serialized representations of objects
8 | # in other contexts.
9 | #
10 | # Serialization makes use of duck typing.
11 | # Each class that can be serialized
12 | # (just Array and ActiveRecord::Base by default)
13 | # implements the +serialize+ and +to_serializable+ methods.
14 | # These methods are implemented differently by the different classes,
15 | # but the semantics of the implementations are consistent,
16 | # so they can be used consistently.
17 | #
18 | # +to_serializable+ returns an object that can be directly serialized
19 | # with a call to +to_xml+, +to_yaml+, or +to_json+.
20 | # This object is either a hash or an array,
21 | # and all the elements are either values, like strings and integers,
22 | # or other serializable objects.
23 | # This is useful for getting a model into a simple data structure format.
24 | # The +attributes+ argument uses the same semantics
25 | # as the :attributes option for Builder#publish.
26 | # For example:
27 | #
28 | # c = Cake.new(:flavor => 'chocolate', :text => 'Happy birthday, Chris!')
29 | # c.recipient = User.new(:name => 'Chris', :password => 'not very secure')
30 | # c.to_serializable [
31 | # :flavor, :text,
32 | # :recipient => :name
33 | # ]
34 | #
35 | # This would return the Ruby hash
36 | #
37 | # { :flavor => 'chocolate', :text => 'Happy birthday, Chris!',
38 | # :user => {:name => 'Chris'} }
39 | #
40 | # +serialize+ takes a format (:xml, :yaml, or :json - see New Formats below)
41 | # and a hash of options.
42 | # The only option currently recognized is :attributes,
43 | # which has the same semantics
44 | # as the :attributes option for Builder#publish.
45 | # +serialize+ returns a string containing the target
46 | # serialized in the given format.
47 | # For example:
48 | #
49 | # c = CandyBag.new(:title => 'jellybag')
50 | # c.candies << Candy.new(:type => 'jellybean', :flavor => 'root beer')
51 | # c.candies << Candy.new(:type => 'jellybean', :flavor => 'pear')
52 | # c.candies << Candy.new(:type => 'licorice', :flavor => 'anisey')
53 | # c.serialize :xml, :attributes => [:title, {:candies => [:type, :flavor]}]
54 | #
55 | # This would return a Ruby string containing
56 | #
57 | #
58 | #
59 | # jellybag
60 | #
61 | #
62 | # jellybean
63 | # root beer
64 | #
65 | #
66 | # jellybean
67 | # pear
68 | #
69 | #
70 | # licorice
71 | # anisey
72 | #
73 | #
74 | #
75 | #
76 | module Serialize
77 |
78 | # Takes an attributes option in the form passed to Builder#publish
79 | # and returns a hash (or nil, if attributes is nil)
80 | # containing the same data,
81 | # but in a more consistent format.
82 | # All keys are converted to symbols,
83 | # and all lists are converted to hashes.
84 | # For example:
85 | #
86 | # Resourceful::Serialize.normalize_attributes([:foo, :bar, {"baz" => ["boom"]}])
87 | # #=> {"baz"=>["boom"], :foo=>nil, :bar=>nil}
88 | #
89 | def self.normalize_attributes(attributes) # :nodoc:
90 | return nil if attributes.nil?
91 | return {attributes.to_sym => nil} if String === attributes
92 | return {attributes => nil} if !attributes.respond_to?(:inject)
93 |
94 | attributes.inject({}) do |hash, attr|
95 | if Array === attr
96 | hash[attr[0]] = attr[1]
97 | hash
98 | else
99 | hash.merge normalize_attributes(attr)
100 | end
101 | end
102 | end
103 |
104 | # This module contains the definitions of +serialize+ and +to_serializable+
105 | # that are included in ActiveRecord::Base.
106 | module Model
107 | # :call-seq:
108 | # serialize format, options = {}, :attributes => [ ... ]
109 | #
110 | # See the module documentation for Serialize for details.
111 | def serialize(format, options)
112 | raise "Must specify :attributes option" unless options[:attributes]
113 | hash = self.to_serializable(options[:attributes])
114 | root = self.class.to_s.underscore
115 | if format == :xml
116 | hash.send("to_#{format}", :root => root)
117 | else
118 | {root => hash}.send("to_#{format}")
119 | end
120 | end
121 |
122 | # See the module documentation for Serialize for details.
123 | def to_serializable(attributes)
124 | raise "Must specify attributes for #{self.inspect}.to_serializable" if attributes.nil?
125 |
126 | Serialize.normalize_attributes(attributes).inject({}) do |hash, (key, value)|
127 | hash[key.to_s] = attr_hash_value(self.send(key), value)
128 | hash
129 | end
130 | end
131 |
132 | private
133 |
134 | # Given an attribute value
135 | # and a normalized (see above) attribute hash,
136 | # returns the serializable form of that attribute.
137 | def attr_hash_value(attr, sub_attributes)
138 | if attr.respond_to?(:to_serializable)
139 | attr.to_serializable(sub_attributes)
140 | else
141 | attr
142 | end
143 | end
144 | end
145 |
146 | # This module contains the definitions of +serialize+ and +to_serializable+
147 | # that are included in ActiveRecord::Base.
148 | module Array
149 | # :call-seq:
150 | # serialize format, options = {}, :attributes => [ ... ]
151 | #
152 | # See the module documentation for Serialize for details.
153 | def serialize(format, options)
154 | raise "Not all elements respond to to_serializable" unless all? { |e| e.respond_to? :to_serializable }
155 | raise "Must specify :attributes option" unless options[:attributes]
156 |
157 | serialized = map { |e| e.to_serializable(options[:attributes]) }
158 | root = first.class.to_s.pluralize.underscore
159 |
160 | if format == :xml
161 | serialized.send("to_#{format}", :root => root)
162 | else
163 | {root => serialized}.send("to_#{format}")
164 | end
165 | end
166 |
167 | # See the module documentation for Serialize for details.
168 | def to_serializable(attributes)
169 | if first.respond_to?(:to_serializable)
170 | attributes = Serialize.normalize_attributes(attributes)
171 | map { |e| e.to_serializable(attributes) }
172 | else
173 | self
174 | end
175 | end
176 | end
177 | end
178 | end
179 |
180 | if defined? ActiveModel
181 | class ActiveModel::Base; include Resourceful::Serialize::Model; end
182 | else
183 | class ActiveRecord::Base; include Resourceful::Serialize::Model; end
184 | end
185 | class Array; include Resourceful::Serialize::Array; end
186 |
--------------------------------------------------------------------------------
/make_resourceful.gemspec:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 |
3 | HAML_GEMSPEC = Gem::Specification.new do |spec|
4 | spec.name = 'make_resourceful'
5 | spec.summary = "An elegant, structured way to build ActionPack Controllers"
6 | spec.version = File.read(File.dirname(__FILE__) + '/VERSION').strip
7 | spec.authors = ['Hampton Catlin']
8 | spec.email = 'hcatlin@gmail.com'
9 | spec.description = <<-END
10 | Take back control of your Controllers. Make them awesome. Make them sleek. Make them resourceful.
11 | END
12 |
13 | spec.executables = []
14 | spec.files = Dir['lib/**/*', 'Rakefile', "Readme.rdoc", "VERSION"]
15 | spec.homepage = 'http://github.com/hcatlin/make_resourceful'
16 | spec.test_files = Dir['spec/**/*_spec.rb']
17 | end
18 |
--------------------------------------------------------------------------------
/spec/accessors_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Default::Accessors, "#current_objects" do
4 | include ControllerMocks
5 | before :each do
6 | mock_controller Resourceful::Default::Accessors
7 | @objects = stub_list 5, 'object'
8 | @model = stub
9 | @controller.stubs(:current_model).returns(@model)
10 | end
11 |
12 | it "should look up all objects in the current model" do
13 | @model.expects(:find).with(:all).returns(@objects)
14 | @controller.current_objects.should == @objects
15 | end
16 |
17 | it "should cache the result, so subsequent calls won't run multiple queries" do
18 | @model.expects(:find).once.returns(@objects)
19 | @controller.current_objects
20 | @controller.current_objects
21 | end
22 |
23 | it "shouldn't run a query if @current_objects is set" do
24 | @controller.instance_variable_set('@current_objects', @objects)
25 | @model.expects(:find).never
26 | @controller.current_objects.should == @objects
27 | end
28 | end
29 |
30 | describe Resourceful::Default::Accessors, "#load_objects" do
31 | include ControllerMocks
32 | before :each do
33 | mock_controller Resourceful::Default::Accessors
34 | @objects = stub_list 5, 'object'
35 | @controller.stubs(:current_objects).returns(@objects)
36 | @controller.stubs(:instance_variable_name).returns("posts")
37 | end
38 |
39 | it "should set the current instance variable to the object collection" do
40 | @controller.load_objects
41 | @controller.instance_variable_get('@posts').should == @objects
42 | end
43 | end
44 |
45 | describe Resourceful::Default::Accessors, "#current_object on a plural controller" do
46 | include ControllerMocks
47 | before :each do
48 | mock_controller Resourceful::Default::Accessors
49 | @controller.stubs(:plural?).returns(true)
50 | @controller.stubs(:params).returns(:id => "12")
51 |
52 | @object = stub
53 | @model = stub
54 | @controller.stubs(:current_model).returns(@model)
55 | end
56 |
57 | it "should look up the object specified by the :id parameter in the current model" do
58 | @model.expects(:find).with('12').returns(@object)
59 | @controller.current_object.should == @object
60 | end
61 |
62 | it "should cache the result, so subsequent calls won't run multiple queries" do
63 | @model.expects(:find).once.returns(@object)
64 | @controller.current_object
65 | @controller.current_object
66 | end
67 |
68 | it "shouldn't run a query if @current_object is set" do
69 | @controller.instance_variable_set('@current_object', @object)
70 | @model.expects(:find).never
71 | @controller.current_object.should == @object
72 | end
73 | end
74 |
75 | describe Resourceful::Default::Accessors, "#current_object on a singular controller" do
76 | include ControllerMocks
77 | before :each do
78 | mock_controller Resourceful::Default::Accessors
79 | @controller.stubs(:plural?).returns(false)
80 | @controller.stubs(:controller_name).returns("posts")
81 |
82 | @parent = stub('parent')
83 | @controller.stubs(:parent_object).returns(@parent)
84 | @controller.stubs(:parent?).returns(true)
85 |
86 | @object = stub
87 | end
88 |
89 | it "should return the instance object from parent object" do
90 | @parent.expects(:post).returns(@object)
91 | @controller.current_object.should == @object
92 | end
93 | end
94 |
95 | describe Resourceful::Default::Accessors, "#load_object" do
96 | include ControllerMocks
97 | before :each do
98 | mock_controller Resourceful::Default::Accessors
99 | @object = stub
100 | @controller.stubs(:current_object).returns(@object)
101 | @controller.stubs(:instance_variable_name).returns("posts")
102 | end
103 |
104 | it "should set the current singular instance variable to the current object" do
105 | @controller.load_object
106 | @controller.instance_variable_get('@post').should == @object
107 | end
108 | end
109 |
110 | describe Resourceful::Default::Accessors, "#build_object with a #build-able model" do
111 | include ControllerMocks
112 | before :each do
113 | mock_controller Resourceful::Default::Accessors
114 | @params = {:name => "Bob", :password => "hideously insecure"}
115 | @controller.stubs(:object_parameters).returns(@params)
116 |
117 | @object = stub
118 | @model = stub
119 | @controller.stubs(:current_model).returns(@model)
120 |
121 | @model.stubs(:build).returns(@object)
122 | end
123 |
124 | it "should return a new object built with current_model from the object parameters" do
125 | @model.expects(:build).with(@params).returns(@object)
126 | @controller.build_object.should == @object
127 | end
128 |
129 | it "should make current_object return the newly built object" do
130 | @controller.build_object
131 | @controller.current_object.should == @object
132 | end
133 | end
134 |
135 | describe Resourceful::Default::Accessors, "#build_object with a non-#build-able model" do
136 | include ControllerMocks
137 | before :each do
138 | mock_controller Resourceful::Default::Accessors
139 | @params = {:name => "Bob", :password => "hideously insecure"}
140 | @controller.stubs(:object_parameters).returns(@params)
141 |
142 | @controller.stubs(:singular?).returns(false)
143 | @controller.stubs(:parent?).returns(false)
144 |
145 | @object = stub
146 | @model = stub
147 | @controller.stubs(:current_model).returns(@model)
148 |
149 | @model.stubs(:new).returns(@object)
150 | end
151 |
152 | it "should return a new instance of the current_model built with the object parameters" do
153 | @model.expects(:new).with(@params).returns(@object)
154 | @controller.build_object.should == @object
155 | end
156 | end
157 |
158 | describe Resourceful::Default::Accessors, "#current_model_name" do
159 | include ControllerMocks
160 | before :each do
161 | mock_controller Resourceful::Default::Accessors
162 | @controller.stubs(:controller_name).returns("funky_posts")
163 | end
164 |
165 | it "should return the controller's name, singularized and camel-cased" do
166 | @controller.current_model_name.should == "FunkyPost"
167 | end
168 | end
169 |
170 | describe Resourceful::Default::Accessors, "#namespaces" do
171 | include ControllerMocks
172 | before :each do
173 | mock_controller Resourceful::Default::Accessors
174 | @kontroller.stubs(:name).returns("FunkyStuff::Admin::Posts")
175 | end
176 |
177 | it "should return an array of underscored symbols representing the namespaces of the controller class" do
178 | @controller.namespaces.should == [:funky_stuff, :admin]
179 | end
180 |
181 | it "should cache the result, so subsequent calls won't run multiple computations" do
182 | @kontroller.expects(:name).once.returns("Posts")
183 | @controller.namespaces
184 | @controller.namespaces
185 | end
186 | end
187 |
188 | describe Resourceful::Default::Accessors, "#instance_variable_name" do
189 | include ControllerMocks
190 | before :each do
191 | mock_controller Resourceful::Default::Accessors
192 | @controller.stubs(:controller_name).returns("posts")
193 | end
194 |
195 | it "should return controller_name" do
196 | @controller.instance_variable_name == "posts"
197 | end
198 | end
199 |
200 | describe Resourceful::Default::Accessors, "#current_model for a singular controller" do
201 | include ControllerMocks
202 | before :each do
203 | mock_controller Resourceful::Default::Accessors
204 | stub_const :Post
205 | @controller.stubs(:singular?).returns(true)
206 | @controller.stubs(:current_model_name).returns("Post")
207 |
208 | @parent = stub('parent')
209 | @controller.stubs(:parent_object).returns(@parent)
210 | @controller.stubs(:parent?).returns(true)
211 | end
212 |
213 | it "should return the constant named by current_model_name" do
214 | @controller.current_model.should == Post
215 | end
216 | end
217 |
218 | describe Resourceful::Default::Accessors, "#current_model for a plural controller with no parent" do
219 | include ControllerMocks
220 | before :each do
221 | mock_controller Resourceful::Default::Accessors
222 | stub_const :Post
223 | @controller.stubs(:singular?).returns(false)
224 | @controller.stubs(:current_model_name).returns("Post")
225 | @controller.stubs(:parent?).returns(false)
226 | end
227 |
228 | it "should return the constant named by current_model_name" do
229 | @controller.current_model.should == Post
230 | end
231 | end
232 |
233 | describe Resourceful::Default::Accessors, "#object_parameters" do
234 | include ControllerMocks
235 | before :each do
236 | mock_controller Resourceful::Default::Accessors
237 | @params = {"crazy_user" => {:name => "Hampton", :location => "Canada"}}
238 | @controller.stubs(:params).returns(@params)
239 | @controller.stubs(:current_model_name).returns("CrazyUser")
240 | end
241 |
242 | it "should return the element of the params hash with the name of the model" do
243 | @controller.object_parameters.should == @params["crazy_user"]
244 | end
245 | end
246 |
247 | describe Resourceful::Default::Accessors, " with two parent classes set on the controller class and one parent parameter supplied" do
248 | include ControllerMocks
249 | before :each do
250 | mock_controller Resourceful::Default::Accessors
251 | @parents = %w{post comment}
252 | @models = @parents.map(&:camelize).map(&method(:stub_const))
253 | @kontroller.parents = @parents
254 | @controller.stubs(:singular?).returns(false)
255 | @controller.stubs(:instance_variable_name).returns('lines')
256 |
257 | @params = HashWithIndifferentAccess.new :post_id => 12
258 | @controller.stubs(:params).returns(@params)
259 |
260 | @post = stub('Post')
261 | Post.stubs(:find).returns(@post)
262 |
263 | @model = stub
264 | end
265 |
266 | it "should return true for #parent?" do
267 | @controller.parent?.should be_true
268 | end
269 |
270 | it "should return the string names of all the parents for #parent_names" do
271 | @controller.parent_names.should == @parents
272 | end
273 |
274 | it "should return the string name of the current parent for #parent_name" do
275 | @controller.parent_name.should == 'post'
276 | end
277 |
278 | it "should return the model class for #parent_model" do
279 | @controller.parent_model.should == Post
280 | end
281 |
282 | it "should return the parent object for #parent_object" do
283 | Post.expects(:find).with(12).returns(@post)
284 | @controller.parent_object.should == @post
285 | end
286 |
287 | it "should cache the value of #parent_object so multiple calls won't cause multiple queries" do
288 | Post.expects(:find).returns(@post).once
289 | @controller.parent_object
290 | @controller.parent_object
291 | end
292 |
293 | it "should bind the parent object its proper instance variable" do
294 | @controller.load_parent_object
295 | @controller.instance_variable_get('@post').should == @post
296 | end
297 |
298 | it "should return the parent-scoped model for #current_model" do
299 | @post.stubs(:lines).returns(@model)
300 | @controller.current_model.should == @model
301 | end
302 |
303 | it "should return true for #ensure_parent_exists" do
304 | @controller.expects(:render).never
305 | @controller.ensure_parent_exists.should be_true
306 | end
307 | end
308 |
309 | describe Resourceful::Default::Accessors, " with two parent classes set on the controller class but no parent parameter supplied" do
310 | include ControllerMocks
311 | before :each do
312 | mock_controller Resourceful::Default::Accessors
313 | @parents = %w{post comment}
314 | @models = @parents.map(&:camelize).map(&method(:stub_const))
315 | @kontroller.parents = @parents
316 | @controller.stubs(:params).returns({})
317 | @controller.stubs(:controller_name).returns('line')
318 | stub_const('Line')
319 | end
320 |
321 | it "should return false for #parent?" do
322 | @controller.parent?.should be_false
323 | end
324 |
325 | it "should return nil for #parent_name" do
326 | @controller.parent_name.should be_nil
327 | end
328 |
329 | it "should return the unscoped model for #current_model" do
330 | @controller.current_model.should == Line
331 | end
332 |
333 | it "should return false and render a 422 error for #ensure_parent_exists" do
334 | @controller.expects(:render).with(has_entry(:status, 422))
335 | @controller.ensure_parent_exists.should be_false
336 | end
337 | end
338 |
339 | describe Resourceful::Default::Accessors, " with no parents" do
340 | include ControllerMocks
341 | before :each do
342 | mock_controller Resourceful::Default::Accessors
343 | @controller.stubs(:parents).returns([])
344 | @controller.stubs(:current_model_name).returns('Line')
345 | @controller.stubs(:params).returns({})
346 | stub_const 'Line'
347 | end
348 |
349 | it "should return false for #parent?" do
350 | @controller.parent?.should be_false
351 | end
352 |
353 | it "should return nil for #parent_name" do
354 | @controller.parent_name.should be_nil
355 | end
356 |
357 | it "should return the unscoped model for #current_model" do
358 | @controller.current_model.should == Line
359 | end
360 | end
361 |
362 | describe Resourceful::Default::Accessors, " for a singular controller with a parent" do
363 | include ControllerMocks
364 | before :each do
365 | mock_controller Resourceful::Default::Accessors
366 | @controller.stubs(:singular?).returns(true)
367 |
368 | @model = stub_model('Thing')
369 | @model.send(:attr_accessor, :person_id)
370 | @controller.stubs(:current_model).returns(@model)
371 |
372 | @person = stub_model('Person')
373 | @person.stubs(:id).returns 42
374 | @controller.stubs(:parent_object).returns(@person)
375 | @controller.stubs(:parent_name).returns('person')
376 | @controller.stubs(:parent?).returns(true)
377 |
378 | @controller.stubs(:object_parameters).returns :thinginess => 12, :bacon => true
379 | end
380 |
381 | it "should set assign the parent's id to a newly built object" do
382 | thing = @controller.build_object
383 | thing.thinginess.should == 12
384 | thing.person_id.should == @person.id
385 | end
386 | end
387 |
388 | describe Resourceful::Default::Accessors, "#save_succeeded!" do
389 | include ControllerMocks
390 | before :each do
391 | mock_controller Resourceful::Default::Accessors
392 | @controller.save_succeeded!
393 | end
394 |
395 | it "should make #save_succeeded? return true" do
396 | @controller.save_succeeded?.should be_true
397 | end
398 | end
399 |
400 | describe Resourceful::Default::Accessors, "#save_failed!" do
401 | include ControllerMocks
402 | before :each do
403 | mock_controller Resourceful::Default::Accessors
404 | @controller.save_failed!
405 | end
406 |
407 | it "should make #save_succeeded? return false" do
408 | @controller.save_succeeded?.should be_false
409 | end
410 | end
411 |
412 | describe Resourceful::Default::Accessors, " for a plural action" do
413 | include ControllerMocks
414 | before :each do
415 | mock_controller Resourceful::Default::Accessors
416 | @controller.stubs(:params).returns :action => "index"
417 | end
418 |
419 | it "should know it's a plural action" do
420 | @controller.should be_a_plural_action
421 | end
422 |
423 | it "should know it's not a singular action" do
424 | @controller.should_not be_a_singular_action
425 | end
426 | end
427 |
428 | describe Resourceful::Default::Accessors, " for a singular action" do
429 | include ControllerMocks
430 | before :each do
431 | mock_controller Resourceful::Default::Accessors
432 | @controller.stubs(:params).returns :action => "show"
433 | end
434 |
435 | it "should know it's not a plural action" do
436 | @controller.should_not be_a_plural_action
437 | end
438 |
439 | it "should know it's a singular action" do
440 | @controller.should be_a_singular_action
441 | end
442 | end
443 |
444 | describe Resourceful::Default::Accessors, " for a singular controller" do
445 | include ControllerMocks
446 | before :each do
447 | mock_controller Resourceful::Default::Accessors
448 | @controller.stubs(:instance_variable_name).returns "post"
449 | end
450 |
451 | it "should know it's not plural" do
452 | @controller.should_not be_plural
453 | end
454 |
455 | it "should know it's singular" do
456 | @controller.should be_singular
457 | end
458 | end
459 |
460 | describe Resourceful::Default::Accessors, " for a plural controller" do
461 | include ControllerMocks
462 | before :each do
463 | mock_controller Resourceful::Default::Accessors
464 | @controller.stubs(:instance_variable_name).returns "posts"
465 | end
466 |
467 | it "should know it's plural" do
468 | @controller.should be_plural
469 | end
470 |
471 | it "should know it's not singular" do
472 | @controller.should_not be_singular
473 | end
474 | end
475 |
--------------------------------------------------------------------------------
/spec/actions_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Default::Actions, " index action" do
4 | include ControllerMocks
5 | before :each do
6 | mock_controller Resourceful::Default::Actions
7 | [:load_objects, :before, :response_for].each(&@controller.method(:stubs))
8 | end
9 |
10 | after(:each) { @controller.index }
11 |
12 | it "should load the object collection" do
13 | #@controller.expects(:load_objects)
14 | end
15 |
16 | it "should call the before :index callback" do
17 | @controller.expects(:before).with(:index)
18 | end
19 |
20 | it "should run the response for index" do
21 | @controller.expects(:response_for).with(:index)
22 | end
23 | end
24 |
25 | describe Resourceful::Default::Actions, " show action" do
26 | include ControllerMocks
27 | before :each do
28 | mock_controller Resourceful::Default::Actions
29 | [:load_object, :before, :response_for].each(&@controller.method(:stubs))
30 | end
31 |
32 | after(:each) { @controller.show }
33 |
34 | it "should load the instance object" do
35 | #@controller.expects(:load_object)
36 | end
37 |
38 | it "should call the before :show callback" do
39 | @controller.expects(:before).with(:show)
40 | end
41 |
42 | it "should run the response for show" do
43 | @controller.expects(:response_for).with(:show)
44 | end
45 |
46 | it "should run the response for show failing if an exception is raised" do
47 | @controller.stubs(:response_for).with(:show).raises("Oh no!")
48 | @controller.expects(:response_for).with(:show_fails)
49 | end
50 | end
51 |
52 | describe Resourceful::Default::Actions, " successful create action" do
53 | include ControllerMocks
54 | before :each do
55 | mock_controller Resourceful::Default::Actions
56 | [:build_object, :load_object, :before, :after,
57 | :save_succeeded!, :response_for].each(&@controller.method(:stubs))
58 | @object = stub :save => true
59 | @controller.stubs(:current_object).returns(@object)
60 | end
61 |
62 | after(:each) { @controller.create }
63 |
64 | it "should build the object from the POSTed parameters" do
65 | @controller.expects(:build_object)
66 | end
67 |
68 | it "should load the instance object" do
69 | @controller.expects(:load_object)
70 | end
71 |
72 | it "should call the before :create callback" do
73 | @controller.expects(:before).with(:create)
74 | end
75 |
76 | it "should try to save the object" do
77 | @object.expects(:save).returns(true)
78 | end
79 |
80 | it "should record the successful save" do
81 | @controller.expects(:save_succeeded!)
82 | end
83 |
84 | it "should call the after :create callback" do
85 | @controller.expects(:after).with(:create)
86 | end
87 |
88 | it "should run the response for create" do
89 | @controller.expects(:response_for).with(:create)
90 | end
91 | end
92 |
93 | describe Resourceful::Default::Actions, " unsuccessful create action" do
94 | include ControllerMocks
95 | before :each do
96 | mock_controller Resourceful::Default::Actions
97 | [:build_object, :load_object, :before, :after,
98 | :save_failed!, :response_for].each(&@controller.method(:stubs))
99 | @object = stub :save => false
100 | @controller.stubs(:current_object).returns(@object)
101 | end
102 |
103 | after(:each) { @controller.create }
104 |
105 | it "should record the unsuccessful save" do
106 | @controller.expects(:save_failed!)
107 | end
108 |
109 | it "should call the after :create_fails callback" do
110 | @controller.expects(:after).with(:create_fails)
111 | end
112 |
113 | it "should run the response for create failing" do
114 | @controller.expects(:response_for).with(:create_fails)
115 | end
116 | end
117 |
118 | describe Resourceful::Default::Actions, " successful update action" do
119 | include ControllerMocks
120 | before :each do
121 | mock_controller Resourceful::Default::Actions
122 | [:load_object, :before, :after, :object_parameters,
123 | :save_succeeded!, :response_for].each(&@controller.method(:stubs))
124 | @object = stub :update_attributes => true
125 | @controller.stubs(:current_object).returns(@object)
126 | end
127 |
128 | after(:each) { @controller.update }
129 |
130 | it "should load the instance object" do
131 | #@controller.expects(:load_object)
132 | end
133 |
134 | it "should call the before :update callback" do
135 | @controller.expects(:before).with(:update)
136 | end
137 |
138 | it "should try to update the object with the POSTed attributes" do
139 | @controller.expects(:object_parameters).returns(:params => "stuff")
140 | @object.expects(:update_attributes).with(:params => "stuff").returns(true)
141 | end
142 |
143 | it "should record the successful save" do
144 | @controller.expects(:save_succeeded!)
145 | end
146 |
147 | it "should call the after :update callback" do
148 | @controller.expects(:after).with(:update)
149 | end
150 |
151 | it "should run the response for update" do
152 | @controller.expects(:response_for).with(:update)
153 | end
154 | end
155 |
156 | describe Resourceful::Default::Actions, " unsuccessful update action" do
157 | include ControllerMocks
158 | before :each do
159 | mock_controller Resourceful::Default::Actions
160 | [:load_object, :before, :after, :object_parameters,
161 | :save_failed!, :response_for].each(&@controller.method(:stubs))
162 | @object = stub :update_attributes => false
163 | @controller.stubs(:current_object).returns(@object)
164 | end
165 |
166 | after(:each) { @controller.update }
167 |
168 | it "should record the unsuccessful save" do
169 | @controller.expects(:save_failed!)
170 | end
171 |
172 | it "should call the after :update_fails callback" do
173 | @controller.expects(:after).with(:update_fails)
174 | end
175 |
176 | it "should run the response for update failing" do
177 | @controller.expects(:response_for).with(:update_fails)
178 | end
179 | end
180 |
181 | describe Resourceful::Default::Actions, " unsuccessful update action because of StaleObjectError" do
182 | include ControllerMocks
183 | before :each do
184 | mock_controller Resourceful::Default::Actions
185 | [:load_object, :before, :after, :object_parameters,
186 | :save_failed!, :response_for].each(&@controller.method(:stubs))
187 | @object = stub_model("Thing")
188 | @object.stubs(:update_attributes).raises(ActiveRecord::StaleObjectError)
189 | @object.expects(:reload)
190 | @controller.stubs(:current_object).returns(@object)
191 | end
192 |
193 | after(:each) { @controller.update }
194 |
195 | it "should record the unsuccessful save" do
196 | @controller.expects(:save_failed!)
197 | end
198 |
199 | it "should call the after :update_fails callback" do
200 | @controller.expects(:after).with(:update_fails)
201 | end
202 |
203 | it "should run the response for update failing" do
204 | @controller.expects(:response_for).with(:update_fails)
205 | end
206 | end
207 |
208 | describe Resourceful::Default::Actions, " new action" do
209 | include ControllerMocks
210 | before :each do
211 | mock_controller Resourceful::Default::Actions
212 | [:build_object, :load_object,
213 | :before, :response_for].each(&@controller.method(:stubs))
214 | end
215 |
216 | after(:each) { @controller.new }
217 |
218 | it "should build the object from the POSTed parameters" do
219 | @controller.expects(:build_object)
220 | end
221 |
222 | it "should load the instance object" do
223 | @controller.expects(:load_object)
224 | end
225 |
226 | it "should call the before :new callback" do
227 | @controller.expects(:before).with(:new)
228 | end
229 |
230 | it "should run the response for new" do
231 | @controller.expects(:response_for).with(:new)
232 | end
233 | end
234 |
235 | describe Resourceful::Default::Actions, " edit action" do
236 | include ControllerMocks
237 | before :each do
238 | mock_controller Resourceful::Default::Actions
239 | [:load_object, :before, :response_for].each(&@controller.method(:stubs))
240 | end
241 |
242 | after(:each) { @controller.edit }
243 |
244 | it "should load the instance object" do
245 | #@controller.expects(:load_object)
246 | end
247 |
248 | it "should call the before :edit callback" do
249 | @controller.expects(:before).with(:edit)
250 | end
251 |
252 | it "should run the response for edit" do
253 | @controller.expects(:response_for).with(:edit)
254 | end
255 | end
256 |
257 | describe Resourceful::Default::Actions, " successful destroy action" do
258 | include ControllerMocks
259 | before :each do
260 | mock_controller Resourceful::Default::Actions
261 | [:load_object, :before,
262 | :after, :response_for].each(&@controller.method(:stubs))
263 | @object = stub :destroy => true
264 | @controller.stubs(:current_object).returns(@object)
265 | end
266 |
267 | after(:each) { @controller.destroy }
268 |
269 | it "should load the instance object" do
270 | #@controller.expects(:load_object)
271 | end
272 |
273 | it "should call the before :destroy callback" do
274 | @controller.expects(:before).with(:destroy)
275 | end
276 |
277 | it "should try to destroy the object" do
278 | @object.expects(:destroy).returns(true)
279 | end
280 |
281 | it "should call the after :destroy callback" do
282 | @controller.expects(:after).with(:destroy)
283 | end
284 |
285 | it "should run the response for destroy" do
286 | @controller.expects(:response_for).with(:destroy)
287 | end
288 | end
289 |
290 | describe Resourceful::Default::Actions, " unsuccessful destroy action" do
291 | include ControllerMocks
292 | before :each do
293 | mock_controller Resourceful::Default::Actions
294 | [:load_object, :before,
295 | :after, :response_for].each(&@controller.method(:stubs))
296 | @object = stub :destroy => false
297 | @controller.stubs(:current_object).returns(@object)
298 | end
299 |
300 | after(:each) { @controller.destroy }
301 |
302 | it "should call the after :destroy_fails callback" do
303 | @controller.expects(:after).with(:destroy_fails)
304 | end
305 |
306 | it "should run the response for destroy failing" do
307 | @controller.expects(:response_for).with(:destroy_fails)
308 | end
309 | end
310 |
311 |
--------------------------------------------------------------------------------
/spec/base_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Base, ".made_resourceful" do
4 | before(:all) { @original_blocks = Resourceful::Base.made_resourceful.dup }
5 | before(:each) { Resourceful::Base.made_resourceful.replace [] }
6 | after(:all) { Resourceful::Base.made_resourceful.replace @original_blocks }
7 |
8 | it "should store blocks when called with blocks and return them when called without a block" do
9 | 5.times { Resourceful::Base.made_resourceful(&should_be_called) }
10 | Resourceful::Base.made_resourceful.each(&:call)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/builder_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Builder, " applied without any modification" do
4 | include ControllerMocks
5 | before :each do
6 | mock_kontroller
7 | create_builder
8 | end
9 |
10 | it "should remove all resourceful actions" do
11 | @kontroller.expects(:send).with do |name, action_module|
12 | name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).empty?
13 | end
14 | @builder.apply
15 | end
16 |
17 | it "shouldn't un-hide any actions" do
18 | @builder.apply
19 | @kontroller.hidden_actions.should == Resourceful::ACTIONS
20 | end
21 |
22 | it "shouldn't set any callbacks" do
23 | @builder.apply
24 | callbacks.should == {:before => {}, :after => {}}
25 | end
26 |
27 | it "shouldn't set any responses" do
28 | @builder.apply
29 | responses.should be_empty
30 | end
31 |
32 | it "shouldn't set any parents" do
33 | @builder.apply
34 | parents.should be_empty
35 | end
36 |
37 | it "should set the controller as made_resourceful" do
38 | @builder.apply
39 | @kontroller.made_resourceful.should be_true
40 | end
41 |
42 | it "should set load_parent_object as a before_action for no actions" do
43 | @kontroller.expects(:before_action).with(:load_parent_object, :only => [])
44 | @builder.apply
45 | end
46 | end
47 |
48 | describe Resourceful::Builder, " with some actions set" do
49 | include ControllerMocks
50 | before :each do
51 | mock_kontroller
52 | create_builder
53 | @actions = [:show, :index, :new, :create]
54 | @builder.actions *@actions
55 | end
56 |
57 | it "should include the given actions" do
58 | @kontroller.expects(:send).with do |name, action_module|
59 | name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).sort ==
60 | @actions.map(&:to_s).sort
61 | end
62 | @builder.apply
63 | end
64 |
65 | it "should un-hide the given actions" do
66 | @builder.apply
67 | (@kontroller.hidden_actions & @actions).should be_empty
68 | end
69 |
70 | it "should set load_parent_object as a before_action for the given actions" do
71 | @kontroller.expects(:before_action).with(:load_parent_object, :only => [:show, :index, :new, :create])
72 | @builder.apply
73 | end
74 | end
75 |
76 | describe Resourceful::Builder, " with all actions set for a plural controller" do
77 | include ControllerMocks
78 | before :each do
79 | mock_kontroller
80 | @kontroller.class_eval { def plural?; true; end }
81 | create_builder
82 | @builder.actions :all
83 | end
84 |
85 | it "should include all actions" do
86 | @kontroller.expects(:send).with do |name, action_module|
87 | name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).sort ==
88 | Resourceful::ACTIONS.map(&:to_s).sort
89 | end
90 | @builder.apply
91 | end
92 | end
93 |
94 | describe Resourceful::Builder, " with all actions set for a singular controller" do
95 | include ControllerMocks
96 | before :each do
97 | mock_kontroller
98 | @kontroller.class_eval { def plural?; false; end }
99 | create_builder
100 | @builder.actions :all
101 | end
102 |
103 | it "should include all singular actions" do
104 | @kontroller.expects(:send).with do |name, action_module|
105 | name == :include && (action_module.instance_methods & Resourceful::ACTIONS.map(&:to_s)).sort ==
106 | Resourceful::SINGULAR_ACTIONS.map(&:to_s).sort
107 | end
108 | @builder.apply
109 | end
110 | end
111 |
112 | describe Resourceful::Builder, " with several before and after callbacks set" do
113 | include ControllerMocks
114 | before :each do
115 | mock_kontroller
116 | create_builder
117 | @builder.before(:create, :update, 'destroy', &(should_be_called { times(3) }))
118 | @builder.after('index', &should_be_called)
119 | @builder.after(:update, &should_be_called)
120 | @builder.apply
121 | end
122 |
123 | it "should save the callbacks as the :resourceful_callbacks inheritable_attribute" do
124 | callbacks[:before][:create].each(&:call)
125 | callbacks[:before][:update].each(&:call)
126 | callbacks[:before][:destroy].each(&:call)
127 | callbacks[:after][:index].each(&:call)
128 | callbacks[:after][:update].each(&:call)
129 | end
130 | end
131 |
132 | describe Resourceful::Builder, " with chained before and after callbacks" do
133 | include ControllerMocks
134 | before :each do
135 | mock_kontroller
136 | create_builder
137 | @before_value = ''
138 | @builder.before(:index, &lambda { @before_value += 'A' })
139 | @builder.before(:index, &lambda { @before_value += 'B' })
140 |
141 | @after_value = ''
142 | @builder.after(:index, &lambda { @after_value += 'A' })
143 | @builder.after(:index, &lambda { @after_value += 'B' })
144 | @builder.apply
145 | end
146 |
147 | it "should save as array in the :resourceful_callbacks inheritable_attribute and execute in order" do
148 | callbacks[:before][:index].each { |callback| callback.call }
149 | @before_value.should == 'AB'
150 | callbacks[:after][:index].each { |callback| callback.call }
151 | @after_value.should == 'AB'
152 | end
153 | end
154 |
155 | describe Resourceful::Builder, " with responses set for several formats" do
156 | include ControllerMocks
157 | before :each do
158 | mock_kontroller
159 | create_builder
160 | @builder.response_for('create') do |f|
161 | f.html(&should_be_called)
162 | f.js(&should_be_called)
163 | f.yaml(&should_be_called)
164 | f.xml(&should_be_called)
165 | f.txt(&should_be_called)
166 | end
167 | @builder.response_for(:remove_failed, 'update') do |f|
168 | f.yaml(&(should_be_called { times(2) }))
169 | f.png(&(should_be_called { times(2) }))
170 | end
171 | @builder.apply
172 | end
173 |
174 | it "should save the responses as the :resourceful_responses inheritable_attribute" do
175 | responses[:create].map(&:first).should == [:html, :js, :yaml, :xml, :txt]
176 | responses[:create].map(&:last).each(&:call)
177 |
178 | responses[:remove_failed].map(&:first).should == [:yaml, :png]
179 | responses[:remove_failed].map(&:last).each(&:call)
180 |
181 | responses[:update].map(&:first).should == [:yaml, :png]
182 | responses[:update].map(&:last).each(&:call)
183 | end
184 | end
185 |
186 | describe Resourceful::Builder, " with a response set for the default format" do
187 | include ControllerMocks
188 | before :each do
189 | mock_kontroller
190 | create_builder
191 | @builder.response_for('index', &should_be_called)
192 | @builder.apply
193 | end
194 |
195 | it "should save the response as a response for HTML in the :resourceful_responses inheritable_attribute" do
196 | responses[:index].map(&:first).should == [:html]
197 | responses[:index].map(&:last).each(&:call)
198 | end
199 | end
200 |
201 | describe Resourceful::Builder, " with a response set for no actions" do
202 | include ControllerMocks
203 | before :each do
204 | mock_kontroller
205 | create_builder
206 | end
207 |
208 | it "should raise an error" do
209 | lambda { @builder.response_for {} }.should raise_error("Must specify one or more actions for response_for.")
210 | end
211 | end
212 |
213 | describe Resourceful::Builder, " publishing without an attributes hash" do
214 | include ControllerMocks
215 | before :each do
216 | mock_kontroller
217 | create_builder
218 | end
219 |
220 | it "should raise an error" do
221 | proc { @builder.publish :xml, :yaml }.should raise_error("Must specify :attributes option")
222 | end
223 | end
224 |
225 | describe Resourceful::Builder, " publishing several formats" do
226 | include ControllerMocks
227 | before :each do
228 | mock_kontroller
229 | create_builder
230 |
231 | @model = stub_model("Thing")
232 | @kontroller.stubs(:current_object).returns(@model)
233 |
234 | @models = (1..5).map { stub_model("Thing") }
235 | @kontroller.stubs(:current_objects).returns(@models)
236 |
237 | @builder.publish :yaml, :json, 'xml', :additional => 'option', :attributes => [:name, :stuff]
238 | @builder.apply
239 | end
240 |
241 | it "should add a list of types as responses for index and show" do
242 | responses[:index].map(&:first).should == [:yaml, :json, :xml]
243 | responses[:show].map(&:first).should == [:yaml, :json, :xml]
244 | end
245 |
246 | it "should respond by rendering the serialized model with the proper type, passing along un-recognized options" do
247 | @model.expects(:serialize).with(:yaml, :additional => 'option', :attributes => [:name, :stuff]).returns('serialized')
248 | @kontroller.expects(:render).with(:text => 'serialized')
249 | @kontroller.instance_eval(&responses[:index].find { |type, _| type == :yaml }[1])
250 | end
251 |
252 | it "should respond render XML and JSON with the proper action" do
253 | @model.expects(:serialize).with(:xml, :additional => 'option', :attributes => [:name, :stuff]).returns('XML serialized')
254 | @model.expects(:serialize).with(:json, :additional => 'option', :attributes => [:name, :stuff]).returns('JSON serialized')
255 | @kontroller.expects(:render).with(:xml => 'XML serialized')
256 | @kontroller.expects(:render).with(:json => 'JSON serialized')
257 |
258 | @kontroller.instance_eval(&responses[:index].find { |type, _| type == :xml }[1])
259 | @kontroller.instance_eval(&responses[:index].find { |type, _| type == :json }[1])
260 | end
261 |
262 | it "should render current_objects if the action is plural" do
263 | @kontroller.stubs(:plural_action?).returns(true)
264 | @models.expects(:serialize).with(:yaml, :additional => 'option', :attributes => [:name, :stuff]).returns('serialized')
265 | @kontroller.expects(:render).with(:text => 'serialized')
266 | @kontroller.instance_eval(&responses[:index].find { |type, _| type == :yaml }[1])
267 | end
268 | end
269 |
270 | describe Resourceful::Builder, " publishing only to #show" do
271 | include ControllerMocks
272 | before :each do
273 | mock_kontroller
274 | create_builder
275 |
276 | @model = stub_model("Thing")
277 | @kontroller.stubs(:current_object).returns(@model)
278 |
279 | @builder.publish :json, :yaml, :only => :show, :attributes => [:name, :stuff]
280 | @builder.apply
281 | end
282 |
283 | it "should add responses for show" do
284 | responses[:show].map(&:first).should == [:json, :yaml]
285 | end
286 |
287 | it "shouldn't add responses for index" do
288 | responses[:index].should be_nil
289 | end
290 |
291 | it "shouldn't pass the :only option to the serialize call" do
292 | @model.expects(:serialize).with(:yaml, :attributes => [:name, :stuff])
293 | @kontroller.stubs(:render)
294 | @kontroller.instance_eval(&responses[:show].find { |type, _| type == :yaml }[1])
295 | end
296 | end
297 |
298 | describe Resourceful::Builder, " publishing in addition to other responses" do
299 | include ControllerMocks
300 | before :each do
301 | mock_kontroller
302 | create_builder
303 |
304 | @builder.response_for(:index) {}
305 | @builder.publish :json, :yaml, :attributes => [:name, :stuff]
306 | @builder.response_for :show do |f|
307 | f.html {}
308 | f.js {}
309 | end
310 | @builder.apply
311 | end
312 |
313 | it "should add published responses in addition to pre-existing ones" do
314 | responses[:show].map(&:first).should == [:html, :js, :json, :yaml]
315 | responses[:index].map(&:first).should == [:html, :json, :yaml]
316 | end
317 | end
318 |
319 | describe Resourceful::Builder, " belonging to several parents" do
320 | include ControllerMocks
321 | before :each do
322 | mock_kontroller
323 | create_builder
324 |
325 | @builder.belongs_to :post, :blat, :stang
326 | @builder.apply
327 | end
328 |
329 | it "should save the parents as the :parents inheritable_attribute" do
330 | parents.should == ['post', 'blat', 'stang']
331 | end
332 | end
333 |
--------------------------------------------------------------------------------
/spec/callbacks_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Default::Callbacks, " with a few callbacks" do
4 | include ControllerMocks
5 | before :each do
6 | mock_controller Resourceful::Default::Callbacks
7 | end
8 |
9 | it "should fire the :before callback with the given name when #before is called" do
10 | callbacks[:before] = { :create => [ should_be_called ] }
11 | @controller.before(:create)
12 | end
13 |
14 | it "should fire the :after callback with the given name when #after is called" do
15 | callbacks[:after] = { :index => [ should_be_called ] }
16 | @controller.after("index")
17 | end
18 | end
19 |
20 | describe Resourceful::Default::Callbacks, " with a few responses" do
21 | include ControllerMocks
22 | before :each do
23 | mock_controller Resourceful::Default::Callbacks
24 | responses[:create_failed] = [[:html, nil], [:js, nil]]
25 | responses[:create] = [[:html, proc { "create html" }], [:xml, proc { @xml }]]
26 | @controller.instance_variable_set('@xml', 'create XML')
27 | @response = Resourceful::Response.new
28 | end
29 |
30 | it "should respond to each format with a call to the given block when #response_for is called" do
31 | @controller.expects(:respond_to).yields(@response)
32 | @controller.response_for(:create_failed)
33 | @response.formats[0][0].should == :html
34 | @response.formats[0][1].call.should be_nil
35 |
36 | @response.formats[1][0].should == :js
37 | @response.formats[1][1].call.should be_nil
38 | end
39 |
40 | it "should properly scope blocks when #response_for is called" do
41 | @controller.expects(:respond_to).yields(@response)
42 | @controller.response_for(:create)
43 | @response.formats[0][0].should == :html
44 | @response.formats[0][1].call.should == "create html"
45 |
46 | @response.formats[1][0].should == :xml
47 |
48 | # This value comes from the instance variable in @controller.
49 | # Having it be "create XML" ensures that the block was properly scoped.
50 | @response.formats[1][1].call.should == "create XML"
51 | end
52 | end
53 |
54 | describe Resourceful::Default::Callbacks, "#scope" do
55 | include ControllerMocks
56 | before(:each) { mock_controller Resourceful::Default::Callbacks }
57 |
58 | it "should re-bind the block to the controller's context" do
59 | block = proc { @var }
60 | @controller.instance_variable_set('@var', 'value')
61 |
62 | block.call.should == nil
63 | @controller.scope(block).call.should == 'value'
64 | end
65 |
66 | it "should make the block empty if it's passed in as nil" do
67 | @controller.scope(nil).call.should == nil
68 | end
69 | end
70 |
71 |
72 |
--------------------------------------------------------------------------------
/spec/integration_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe "ThingsController", "with all the resourceful actions" do
4 |
5 | before :each do
6 | mock_resourceful do
7 | actions :all
8 | end
9 | @objects = stub_list(5, 'Thing') do |t|
10 | [:destroy, :save, :update_attributes].each { |m| t.stubs(m).returns(true) }
11 | t.stubs(:to_param).returns('12')
12 | end
13 | @object = @objects.first
14 | Thing.stubs(:find).returns(@object)
15 | Thing.stubs(:new).returns(@object)
16 | end
17 |
18 | ## Default responses
19 |
20 | (Resourceful::ACTIONS - Resourceful::MODIFYING_ACTIONS).each(&method(:should_render_html))
21 | Resourceful::ACTIONS.each(&method(:should_render_js))
22 | Resourceful::ACTIONS.each(&method(:shouldnt_render_xml))
23 |
24 | ## Specs for #index
25 |
26 | it "should find all records on GET /things" do
27 | Thing.expects(:find).with(:all).returns(@objects)
28 | get :index
29 | end
30 |
31 | it "should return a list of objects for #current_objects after GET /things" do
32 | Thing.stubs(:find).returns(@objects)
33 | get :index
34 | current_objects.should == @objects
35 | end
36 |
37 | it "should assign @things to a list of objects for GET /things" do
38 | Thing.stubs(:find).returns(@objects)
39 | get :index
40 | assigns(:things).should == @objects
41 | end
42 |
43 | ## Specs for #show
44 |
45 | it "should find the record with id 12 on GET /things/12" do
46 | Thing.expects(:find).with('12').returns(@object)
47 | get :show, :id => 12
48 | end
49 |
50 | it "should return an object for #current_object after GET /things/12" do
51 | Thing.stubs(:find).returns(@object)
52 | get :show, :id => 12
53 | current_object.should == @object
54 | end
55 |
56 | it "should assign @thing to an object for GET /things/12" do
57 | Thing.stubs(:find).returns(@object)
58 | get :show, :id => 12
59 | assigns(:thing).should == @object
60 | end
61 |
62 | ## Specs for #edit
63 |
64 | it "should find the record with id 12 on GET /things/12/edit" do
65 | Thing.expects(:find).with('12').returns(@object)
66 | get :edit, :id => 12
67 | end
68 |
69 | it "should return an object for #current_object after GET /things/12/edit" do
70 | Thing.stubs(:find).returns(@object)
71 | get :edit, :id => 12
72 | current_object.should == @object
73 | end
74 |
75 | it "should assign @thing to an object for GET /things/12/edit" do
76 | Thing.stubs(:find).returns(@object)
77 | get :edit, :id => 12
78 | assigns(:thing).should == @object
79 | end
80 |
81 | ## Specs for #new
82 |
83 | it "should create a new object from params[:thing] for GET /things/new" do
84 | Thing.expects(:new).with('name' => "Herbert the thing").returns(@object)
85 | get :new, :thing => {:name => "Herbert the thing"}
86 | end
87 |
88 | it "should create a new object even if there aren't any params for GET /things/new" do
89 | Thing.expects(:new).with(nil).returns(@object)
90 | get :new
91 | end
92 |
93 | it "should return the new object for #current_object after GET /things/new" do
94 | Thing.stubs(:new).returns(@object)
95 | get :new
96 | current_object.should == @object
97 | end
98 |
99 | it "should assign @thing to the new object for GET /things/new" do
100 | Thing.stubs(:new).returns(@object)
101 | get :new
102 | assigns(:thing).should == @object
103 | end
104 |
105 | ## Specs for #create
106 |
107 | it "should create a new object from params[:thing] for POST /things" do
108 | Thing.expects(:new).with('name' => "Herbert the thing").returns(@object)
109 | post :create, :thing => {:name => "Herbert the thing"}
110 | end
111 |
112 | it "should create a new object even if there aren't any params for POST /things" do
113 | Thing.expects(:new).with(nil).returns(@object)
114 | post :create
115 | end
116 |
117 | it "should return the new object for #current_object after POST /things" do
118 | Thing.stubs(:new).returns(@object)
119 | post :create
120 | current_object.should == @object
121 | end
122 |
123 | it "should assign @thing to the new object for POST /things" do
124 | Thing.stubs(:new).returns(@object)
125 | post :create
126 | assigns(:thing).should == @object
127 | end
128 |
129 | it "should save the new object for POST /things" do
130 | Thing.stubs(:new).returns(@object)
131 | @object.expects(:save)
132 | post :create
133 | end
134 |
135 | it "should set an appropriate flash notice for a successful POST /things" do
136 | Thing.stubs(:new).returns(@object)
137 | post :create
138 | flash[:notice].should == "Create successful!"
139 | end
140 |
141 | it "should redirect to the new object for a successful POST /things" do
142 | Thing.stubs(:new).returns(@object)
143 | post :create
144 | response.should redirect_to('/things/12')
145 | end
146 |
147 | it "should set an appropriate flash error for an unsuccessful POST /things" do
148 | Thing.stubs(:new).returns(@object)
149 | @object.stubs(:save).returns(false)
150 | post :create
151 | flash[:error].should == "There was a problem!"
152 | end
153 |
154 | it "should give a failing response for an unsuccessful POST /things" do
155 | Thing.stubs(:new).returns(@object)
156 | @object.stubs(:save).returns(false)
157 | post :create
158 | response.should_not be_success
159 | response.code.should == '422'
160 | end
161 |
162 | it "should render the #new template for an unsuccessful POST /things" do
163 | Thing.stubs(:new).returns(@object)
164 | @object.stubs(:save).returns(false)
165 | post :create
166 | response.body.should include('New object')
167 | end
168 |
169 | ## Specs for #update
170 |
171 | it "should find the record with id 12 on PUT /things/12" do
172 | Thing.expects(:find).with('12').returns(@object)
173 | put :update, :id => 12
174 | end
175 |
176 | it "should return an object for #current_object after PUT /things/12" do
177 | Thing.stubs(:find).returns(@object)
178 | put :update, :id => 12
179 | current_object.should == @object
180 | end
181 |
182 | it "should assign @thing to an object for PUT /things/12" do
183 | Thing.stubs(:find).returns(@object)
184 | put :update, :id => 12
185 | assigns(:thing).should == @object
186 | end
187 |
188 | it "should update the new object for PUT /things/12" do
189 | Thing.stubs(:find).returns(@object)
190 | @object.expects(:update_attributes).with('name' => "Jorje")
191 | put :update, :id => 12, :thing => {:name => "Jorje"}
192 | end
193 |
194 | it "should set an appropriate flash notice for a successful PUT /things/12" do
195 | Thing.stubs(:find).returns(@object)
196 | put :update, :id => 12
197 | flash[:notice].should == "Save successful!"
198 | end
199 |
200 | it "should redirect to the updated object for a successful PUT /things/12" do
201 | Thing.stubs(:find).returns(@object)
202 | put :update, :id => 12
203 | response.should redirect_to('/things/12')
204 | end
205 |
206 | it "should set an appropriate flash error for an unsuccessful PUT /things/12" do
207 | Thing.stubs(:find).returns(@object)
208 | @object.stubs(:update_attributes).returns(false)
209 | put :update, :id => 12
210 | flash[:error].should == "There was a problem saving!"
211 | end
212 |
213 | it "should give a failing response for an unsuccessful PUT /things/12" do
214 | Thing.stubs(:find).returns(@object)
215 | @object.stubs(:update_attributes).returns(false)
216 | put :update, :id => 12
217 | response.should_not be_success
218 | response.code.should == '422'
219 | end
220 |
221 | it "should render the #edit template for an unsuccessful PUT /things/12" do
222 | Thing.stubs(:find).returns(@object)
223 | @object.stubs(:update_attributes).returns(false)
224 | put :update, :id => 12
225 | response.body.should include('Editting object')
226 | end
227 |
228 | ## Specs for #destroy
229 |
230 | it "should find the record with id 12 on DELETE /things/12" do
231 | Thing.expects(:find).with('12').returns(@object)
232 | delete :destroy, :id => 12
233 | end
234 |
235 | it "should return an object for #current_object after DELETE /things/12" do
236 | Thing.stubs(:find).returns(@object)
237 | delete :destroy, :id => 12
238 | current_object.should == @object
239 | end
240 |
241 | it "should assign @thing to an object for DELETE /things/12" do
242 | Thing.stubs(:find).returns(@object)
243 | delete :destroy, :id => 12
244 | assigns(:thing).should == @object
245 | end
246 |
247 | it "should destroy the new object for DELETE /things/12" do
248 | Thing.stubs(:find).returns(@object)
249 | @object.expects(:destroy)
250 | delete :destroy, :id => 12
251 | end
252 |
253 | it "should set an appropriate flash notice for a successful DELETE /things/12" do
254 | Thing.stubs(:find).returns(@object)
255 | delete :destroy, :id => 12
256 | flash[:notice].should == "Record deleted!"
257 | end
258 |
259 | it "should redirect to the object list for a successful DELETE /things/12" do
260 | Thing.stubs(:find).returns(@object)
261 | delete :destroy, :id => 12
262 | response.should redirect_to('/things')
263 | end
264 |
265 | it "should set an appropriate flash error for an unsuccessful DELETE /things/12" do
266 | Thing.stubs(:find).returns(@object)
267 | @object.stubs(:destroy).returns(false)
268 | delete :destroy, :id => 12
269 | flash[:error].should == "There was a problem deleting!"
270 | end
271 |
272 | it "should give a failing response for an unsuccessful DELETE /things/12" do
273 | Thing.stubs(:find).returns(@object)
274 | @object.stubs(:destroy).returns(false)
275 | delete :destroy, :id => 12
276 | response.should_not be_success
277 | end
278 |
279 | it "should redirect to the previous page for an unsuccessful DELETE /things/12" do
280 | Thing.stubs(:find).returns(@object)
281 | @object.stubs(:destroy).returns(false)
282 | delete :destroy, :id => 12
283 | response.should redirect_to(:back)
284 | end
285 | end
286 |
287 | describe "ThingsController", "with several parent objects", :type => :integration do
288 | before :each do
289 | mock_resourceful do
290 | actions :all
291 | belongs_to :person, :category
292 | end
293 | stub_const 'Person'
294 | stub_const 'Category'
295 |
296 | @objects = stub_list(5, 'Thing') do |t|
297 | t.stubs(:save).returns(true)
298 | end
299 | @object = @objects.first
300 | @person = stub('Person')
301 | @category = stub('Category')
302 | @fake_model = stub('parent_object.things')
303 | end
304 |
305 | ## No parent ids
306 |
307 | it "should find all things on GET /things" do
308 | Thing.expects(:find).with(:all).returns(@objects)
309 | get :index
310 | current_objects.should == @objects
311 | end
312 |
313 | it "should find the thing with id 12 regardless of scoping on GET /things/12" do
314 | Thing.expects(:find).with('12').returns(@object)
315 | get :show, :id => 12
316 | current_object.should == @object
317 | end
318 |
319 | it "should create a new thing without a person on POST /things" do
320 | Thing.expects(:new).with('name' => "Lamp").returns(@object)
321 | post :create, :thing => {:name => "Lamp"}
322 | current_object.should == @object
323 | end
324 |
325 | ## Person ids
326 |
327 | it "should assign the proper parent variables and accessors to the person with id 4 for GET /people/4/things" do
328 | Person.stubs(:find).returns(@person)
329 | @person.stubs(:things).returns(@fake_model)
330 | @fake_model.stubs(:find).with(:all).returns(@objects)
331 | get :index, :person_id => 4
332 | controller.instance_eval("parent_object").should == @person
333 | assigns(:person).should == @person
334 | end
335 |
336 | it "should find all the things belonging to the person with id 4 on GET /people/4/things" do
337 | Person.expects(:find).with('4').returns(@person)
338 | @person.expects(:things).at_least_once.returns(@fake_model)
339 | @fake_model.expects(:find).with(:all).returns(@objects)
340 | get :index, :person_id => 4
341 | current_objects.should == @objects
342 | end
343 |
344 | it "should find the thing with id 12 if it belongs to the person with id 4 on GET /person/4/things/12" do
345 | Person.expects(:find).with('4').returns(@person)
346 | @person.expects(:things).at_least_once.returns(@fake_model)
347 | @fake_model.expects(:find).with('12').returns(@object)
348 | get :show, :person_id => 4, :id => 12
349 | current_object.should == @object
350 | end
351 |
352 | it "should create a new thing belonging to the person with id 4 on POST /person/4/things" do
353 | Person.expects(:find).with('4').returns(@person)
354 | @person.expects(:things).at_least_once.returns(@fake_model)
355 | @fake_model.expects(:build).with('name' => 'Lamp').returns(@object)
356 | post :create, :person_id => 4, :thing => {:name => "Lamp"}
357 | current_object.should == @object
358 | end
359 |
360 | ## Category ids
361 |
362 | it "should assign the proper parent variables and accessors to the category with id 4 for GET /people/4/things" do
363 | Category.stubs(:find).returns(@category)
364 | @category.stubs(:things).returns(@fake_model)
365 | @fake_model.stubs(:find).with(:all).returns(@objects)
366 | get :index, :category_id => 4
367 | controller.instance_eval("parent_object").should == @category
368 | assigns(:category).should == @category
369 | end
370 |
371 | it "should find all the things belonging to the category with id 4 on GET /people/4/things" do
372 | Category.expects(:find).with('4').returns(@category)
373 | @category.expects(:things).at_least_once.returns(@fake_model)
374 | @fake_model.expects(:find).with(:all).returns(@objects)
375 | get :index, :category_id => 4
376 | current_objects.should == @objects
377 | end
378 |
379 | it "should find the thing with id 12 if it belongs to the category with id 4 on GET /category/4/things/12" do
380 | Category.expects(:find).with('4').returns(@category)
381 | @category.expects(:things).at_least_once.returns(@fake_model)
382 | @fake_model.expects(:find).with('12').returns(@object)
383 | get :show, :category_id => 4, :id => 12
384 | current_object.should == @object
385 | end
386 |
387 | it "should create a new thing belonging to the category with id 4 on POST /category/4/things" do
388 | Category.expects(:find).with('4').returns(@category)
389 | @category.expects(:things).at_least_once.returns(@fake_model)
390 | @fake_model.expects(:build).with('name' => 'Lamp').returns(@object)
391 | post :create, :category_id => 4, :thing => {:name => "Lamp"}
392 | current_object.should == @object
393 | end
394 | end
395 |
--------------------------------------------------------------------------------
/spec/maker_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Maker, "when extended" do
4 | include ControllerMocks
5 | before(:each) { mock_kontroller }
6 |
7 | it "should create an empty, inheritable callbacks hash" do
8 | @kontroller.resourceful_callbacks.should == {}
9 | end
10 |
11 | it "should create an empty, inheritable responses hash" do
12 | @kontroller.resourceful_responses.should == {}
13 | end
14 |
15 | it "should create an empty, inheritable parents array" do
16 | @kontroller.parents.should == []
17 | end
18 |
19 | it "should create a made_resourceful variable set to false" do
20 | @kontroller.made_resourceful.should be_false
21 | end
22 |
23 | it "should create a made_resourceful? method on the controller that returns the variable" do
24 | @kontroller.should_not be_made_resourceful
25 | @kontroller.made_resourceful = true
26 | @kontroller.should be_made_resourceful
27 | end
28 | end
29 |
30 | describe Resourceful::Maker, "when made_resourceful" do
31 | include ControllerMocks
32 | before(:each) do
33 | mock_kontroller
34 | mock_builder
35 | end
36 |
37 | it "should include Resourceful::Base" do
38 | @kontroller.expects(:include).with(Resourceful::Base)
39 | @kontroller.make_resourceful {}
40 | end
41 |
42 | it "should use Resourceful::Builder to build the controller" do
43 | Resourceful::Builder.expects(:new).with(@kontroller).returns(@builder)
44 | @kontroller.make_resourceful {}
45 | end
46 |
47 | it "should evaluate the made_resourceful callbacks in the context of the builder" do
48 | procs = (1..5).map { should_be_called { with(@builder) } }
49 | Resourceful::Base.stubs(:made_resourceful).returns(procs)
50 | @kontroller.make_resourceful {}
51 | end
52 |
53 | it "should evaluate the :include callback in the context of the builder" do
54 | @kontroller.make_resourceful(:include => should_be_called { with(@builder) }) {}
55 | end
56 |
57 | it "should evaluate the given block in the context of the builder" do
58 | @kontroller.make_resourceful(&(should_be_called { with(@builder) }))
59 | end
60 | end
61 |
62 | describe Resourceful::Maker, "when made_resourceful with an inherited controller" do
63 | include ControllerMocks
64 | before(:each) do
65 | mock_kontroller
66 | mock_builder :inherited
67 | end
68 |
69 | it "should include Resourceful::Base" do
70 | @kontroller.expects(:include).with(Resourceful::Base)
71 | @kontroller.make_resourceful {}
72 | end
73 |
74 | it "should use Resourceful::Builder to build the controller" do
75 | Resourceful::Builder.expects(:new).with(@kontroller).returns(@builder)
76 | @kontroller.make_resourceful {}
77 | end
78 |
79 | it "should not evaluate the made_resourceful callbacks in the context of the builder" do
80 | Resourceful::Base.expects(:made_resourceful).never
81 | @kontroller.make_resourceful {}
82 | end
83 |
84 | it "should evaluate the :include callback in the context of the builder" do
85 | @kontroller.make_resourceful(:include => should_be_called { with(@builder) }) {}
86 | end
87 |
88 | it "should evaluate the given block in the context of the builder" do
89 | @kontroller.make_resourceful(&(should_be_called { with(@builder) }))
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/spec/response_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Response, "when first created" do
4 | before(:each) { @response = Resourceful::Response.new }
5 |
6 | it "should have an empty formats array" do
7 | @response.formats.should == []
8 | end
9 | end
10 |
11 | describe Resourceful::Response, "with a few formats" do
12 | before :each do
13 | @response = Resourceful::Response.new
14 | @response.html
15 | @response.js {'javascript'}
16 | @response.xml {'xml'}
17 | end
18 |
19 | it "should store the formats and blocks" do
20 | @response.formats.should have_any {|f,p| f == :js && p.call == 'javascript'}
21 | @response.formats.should have_any {|f,p| f == :xml && p.call == 'xml'}
22 | end
23 |
24 | it "should give formats without a block an empty block" do
25 | @response.formats.should have_any {|f,p| f == :html && Proc === p && p.call.nil?}
26 | end
27 |
28 | it "shouldn't allow duplicate formats" do
29 | @response.js {'not javascript'}
30 | @response.formats.should have_any {|f,p| f == :js && p.call == 'javascript'}
31 | @response.formats.should_not have_any {|f,p| f == :js && p.call == 'not javascript'}
32 | end
33 |
34 | it "should keep the formats in sorted order" do
35 | @response.formats.map(&:first).should == [:html, :js, :xml]
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/spec/responses_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe 'Resourceful::Default::Responses', " with a _flash parameter for :error" do
4 | include ControllerMocks
5 | before :each do
6 | mock_controller Resourceful::Default::Responses
7 | @flash = {}
8 | @controller.stubs(:flash).returns(@flash)
9 | @params = {:_flash => {:error => 'Oh no, an error!'}}
10 | @controller.stubs(:params).returns(@params)
11 | end
12 |
13 | it "should set the flash for :error to the parameter's value when set_default_flash is called on :error" do
14 | @controller.set_default_flash(:error, "Aw there's no error!")
15 | @flash[:error].should == 'Oh no, an error!'
16 | end
17 |
18 | it "should set the flash for :message to the default value when set_default_flash is called on :message" do
19 | @controller.set_default_flash(:message, "All jim dandy!")
20 | @flash[:message].should == 'All jim dandy!'
21 | end
22 |
23 | it "shouldn't set the flash for :error when set_default_flash is called on :message" do
24 | @controller.set_default_flash(:message, "All jim dandy!")
25 | @flash[:error].should be_nil
26 | end
27 | end
28 |
29 | describe 'Resourceful::Default::Responses', " with a _redirect parameter on :failure" do
30 | include ControllerMocks
31 | before :each do
32 | mock_controller Resourceful::Default::Responses
33 | @params = {:_redirect_on => {:failure => 'http://hamptoncatlin.com/'}}
34 | @controller.stubs(:params).returns(@params)
35 | end
36 |
37 | it "should set the redirect for :failure to the parameter's value when set_default_redirect is called on :failure" do
38 | @controller.expects(:redirect_to).with('http://hamptoncatlin.com/')
39 | @controller.set_default_redirect(:back, :status => :failure)
40 | end
41 |
42 | it "should set the redirect for :success to the default value when set_default_redirect is called on :success" do
43 | @controller.expects(:redirect_to).with(:back)
44 | @controller.set_default_redirect(:back, :status => :success)
45 | end
46 |
47 | it "shouldn't set the redirect for :failure when set_default_redirect is called on :success" do
48 | @controller.expects(:redirect_to).with(:back)
49 | @controller.expects(:redirect_to).with('http://hamptoncatlin.com/').never
50 | @controller.set_default_redirect(:back, :status => :success)
51 | end
52 |
53 | it "should set the default redirect for :success by default" do
54 | @controller.expects(:redirect_to).with(:back)
55 | @controller.set_default_redirect(:back)
56 | end
57 | end
58 |
59 | describe 'Resourceful::Default::Responses', ' for show' do
60 | include ControllerMocks
61 | before :each do
62 | mock_kontroller
63 | create_builder
64 | made_resourceful(Resourceful::Default::Responses)
65 | @builder.apply
66 | end
67 |
68 | it "should have an empty HTML response" do
69 | responses[:show].find { |f, p| f == :html }[1].call.should == nil
70 | end
71 |
72 | it "should have an empty JS response" do
73 | responses[:show].find { |f, p| f == :js }[1].call.should == nil
74 | end
75 | end
76 |
77 | describe 'Resourceful::Default::Responses', ' for index' do
78 | include ControllerMocks
79 | before :each do
80 | mock_kontroller
81 | create_builder
82 | made_resourceful(Resourceful::Default::Responses)
83 | @builder.apply
84 | end
85 |
86 | it "should have an empty HTML response" do
87 | responses[:index].find { |f, p| f == :html }[1].call.should == nil
88 | end
89 |
90 | it "should have an empty JS response" do
91 | responses[:index].find { |f, p| f == :js }[1].call.should == nil
92 | end
93 | end
94 |
95 | describe 'Resourceful::Default::Responses', ' for edit' do
96 | include ControllerMocks
97 | before :each do
98 | mock_kontroller
99 | create_builder
100 | made_resourceful(Resourceful::Default::Responses)
101 | @builder.apply
102 | end
103 |
104 | it "should have an empty HTML response" do
105 | responses[:edit].find { |f, p| f == :html }[1].call.should == nil
106 | end
107 |
108 | it "should have an empty JS response" do
109 | responses[:edit].find { |f, p| f == :js }[1].call.should == nil
110 | end
111 | end
112 |
113 | describe 'Resourceful::Default::Responses', ' for new' do
114 | include ControllerMocks
115 | before :each do
116 | mock_kontroller
117 | create_builder
118 | made_resourceful(Resourceful::Default::Responses)
119 | @builder.apply
120 | end
121 |
122 | it "should have an empty HTML response" do
123 | responses[:new].find { |f, p| f == :html }[1].call.should == nil
124 | end
125 |
126 | it "should have an empty JS response" do
127 | responses[:new].find { |f, p| f == :js }[1].call.should == nil
128 | end
129 | end
130 |
131 | describe 'Resourceful::Default::Responses', ' for show_fails' do
132 | include ControllerMocks
133 | before :each do
134 | mock_controller Resourceful::Default::Callbacks
135 | create_builder
136 | made_resourceful(Resourceful::Default::Responses)
137 | @builder.apply
138 | end
139 |
140 | it "should give a 404 error for HTML" do
141 | @controller.expects(:render).with(:text => "No item found", :status => 404)
142 | @controller.scope(responses[:show_fails].find { |f, p| f == :html }[1]).call
143 | end
144 |
145 | it "should give a 404 error for JS" do
146 | @controller.expects(:render).with(:text => "No item found", :status => 404)
147 | @controller.scope(responses[:show_fails].find { |f, p| f == :js }[1]).call
148 | end
149 |
150 | it "should give a 404 error for XML" do
151 | @controller.expects(:render).with(:text => "No item found", :status => 404)
152 | @controller.scope(responses[:show_fails].find { |f, p| f == :xml }[1]).call
153 | end
154 | end
155 |
156 | describe 'Resourceful::Default::Responses', ' for create' do
157 | include ControllerMocks
158 | before :each do
159 | mock_controller Resourceful::Default::Callbacks
160 | create_builder
161 | made_resourceful(Resourceful::Default::Responses)
162 | @builder.apply
163 |
164 | [:set_default_flash, :set_default_redirect, :object_path].each(&@controller.method(:stubs))
165 | end
166 |
167 | it "should have an empty JS response" do
168 | responses[:create].find { |f, p| f == :js }[1].call.should == nil
169 | end
170 |
171 | it "should flash a success message to :notice by default for HTML" do
172 | @controller.expects(:set_default_flash).with(:notice, "Create successful!")
173 | @controller.scope(responses[:create].find { |f, p| f == :html }[1]).call
174 | end
175 |
176 | it "should redirect to object_path by default for HTML" do
177 | @controller.stubs(:object_path).returns("/posts/12")
178 | @controller.expects(:set_default_redirect).with("/posts/12")
179 | @controller.scope(responses[:create].find { |f, p| f == :html }[1]).call
180 | end
181 | end
182 |
183 | describe 'Resourceful::Default::Responses', ' for create_fails' do
184 | include ControllerMocks
185 | before :each do
186 | mock_controller Resourceful::Default::Callbacks
187 | create_builder
188 | made_resourceful(Resourceful::Default::Responses)
189 | @builder.apply
190 |
191 | [:set_default_flash, :render].each(&@controller.method(:stubs))
192 | end
193 |
194 | it "should have an empty JS response" do
195 | responses[:create_fails].find { |f, p| f == :js }[1].call.should == nil
196 | end
197 |
198 | it "should flash a failure message to :error by default for HTML" do
199 | @controller.expects(:set_default_flash).with(:error, "There was a problem!")
200 | @controller.scope(responses[:create_fails].find { |f, p| f == :html }[1]).call
201 | end
202 |
203 | it "should render new with a 422 error for HTML" do
204 | @controller.expects(:render).with(:action => :new, :status => 422)
205 | @controller.scope(responses[:create_fails].find { |f, p| f == :html }[1]).call
206 | end
207 | end
208 |
209 | describe 'Resourceful::Default::Responses', ' for update' do
210 | include ControllerMocks
211 | before :each do
212 | mock_controller Resourceful::Default::Callbacks
213 | create_builder
214 | made_resourceful(Resourceful::Default::Responses)
215 | @builder.apply
216 |
217 | [:set_default_flash, :set_default_redirect, :object_path].each(&@controller.method(:stubs))
218 | end
219 |
220 | it "should have an empty JS response" do
221 | responses[:update].find { |f, p| f == :js }[1].call.should == nil
222 | end
223 |
224 | it "should flash a success message to :notice by default for HTML" do
225 | @controller.expects(:set_default_flash).with(:notice, "Save successful!")
226 | @controller.scope(responses[:update].find { |f, p| f == :html }[1]).call
227 | end
228 |
229 | it "should redirect to object_path by default for HTML" do
230 | @controller.stubs(:object_path).returns("/posts/12")
231 | @controller.expects(:set_default_redirect).with("/posts/12")
232 | @controller.scope(responses[:update].find { |f, p| f == :html }[1]).call
233 | end
234 | end
235 |
236 | describe 'Resourceful::Default::Responses', ' for update_fails' do
237 | include ControllerMocks
238 | before :each do
239 | mock_controller Resourceful::Default::Callbacks
240 | create_builder
241 | made_resourceful(Resourceful::Default::Responses)
242 | @builder.apply
243 |
244 | [:set_default_flash, :render].each(&@controller.method(:stubs))
245 | end
246 |
247 | it "should have an empty JS response" do
248 | responses[:update_fails].find { |f, p| f == :js }[1].call.should == nil
249 | end
250 |
251 | it "should flash a failure message to :error by default for HTML" do
252 | @controller.expects(:set_default_flash).with(:error, "There was a problem saving!")
253 | @controller.scope(responses[:update_fails].find { |f, p| f == :html }[1]).call
254 | end
255 |
256 | it "should render edit with a 422 error for HTML" do
257 | @controller.expects(:render).with(:action => :edit, :status => 422)
258 | @controller.scope(responses[:update_fails].find { |f, p| f == :html }[1]).call
259 | end
260 | end
261 |
262 |
263 | describe 'Resourceful::Default::Responses', ' for destroy' do
264 | include ControllerMocks
265 | before :each do
266 | mock_controller Resourceful::Default::Callbacks
267 | create_builder
268 | made_resourceful(Resourceful::Default::Responses)
269 | @builder.apply
270 |
271 | [:set_default_flash, :set_default_redirect, :objects_path].each(&@controller.method(:stubs))
272 | end
273 |
274 | it "should have an empty JS response" do
275 | responses[:destroy].find { |f, p| f == :js }[1].call.should == nil
276 | end
277 |
278 | it "should flash a success message to :notice by default for HTML" do
279 | @controller.expects(:set_default_flash).with(:notice, "Record deleted!")
280 | @controller.scope(responses[:destroy].find { |f, p| f == :html }[1]).call
281 | end
282 |
283 | it "should redirect to objects_path by default for HTML" do
284 | @controller.stubs(:objects_path).returns("/posts")
285 | @controller.expects(:set_default_redirect).with("/posts")
286 | @controller.scope(responses[:destroy].find { |f, p| f == :html }[1]).call
287 | end
288 | end
289 |
290 | describe 'Resourceful::Default::Responses', ' for destroy_fails' do
291 | include ControllerMocks
292 | before :each do
293 | mock_controller Resourceful::Default::Callbacks
294 | create_builder
295 | made_resourceful(Resourceful::Default::Responses)
296 | @builder.apply
297 |
298 | [:set_default_flash, :set_default_redirect, :render].each(&@controller.method(:stubs))
299 | end
300 |
301 | it "should have an empty JS response" do
302 | responses[:destroy_fails].find { |f, p| f == :js }[1].call.should == nil
303 | end
304 |
305 | it "should flash a failure message to :error by default for HTML" do
306 | @controller.expects(:set_default_flash).with(:error, "There was a problem deleting!")
307 | @controller.scope(responses[:destroy_fails].find { |f, p| f == :html }[1]).call
308 | end
309 |
310 | it "should redirect back on failure by default for HTML" do
311 | @controller.expects(:set_default_redirect).with(:back, :status => :failure)
312 | @controller.scope(responses[:destroy_fails].find { |f, p| f == :html }[1]).call
313 | end
314 | end
315 |
--------------------------------------------------------------------------------
/spec/rspec-rails/LICENSE:
--------------------------------------------------------------------------------
1 | All the code in this directory comes from the rspec-rails plugin.
2 | We've pilfered it as needed to make the make_resourceful specs easier to write.
3 | It was made available by its authors under the following license terms:
4 |
5 | (The MIT License)
6 |
7 | ====================================================================
8 | ==== RSpec, RSpec-Rails
9 | Copyright (c) 2005-2008 The RSpec Development Team
10 | ====================================================================
11 | ==== ARTS
12 | Copyright (c) 2006 Kevin Clark, Jake Howerton
13 | ====================================================================
14 | ==== ZenTest
15 | Copyright (c) 2001-2006 Ryan Davis, Eric Hodel, Zen Spider Software
16 | ====================================================================
17 | ==== AssertSelect
18 | Copyright (c) 2006 Assaf Arkin
19 | ====================================================================
20 |
21 | Permission is hereby granted, free of charge, to any person obtaining a copy of
22 | this software and associated documentation files (the "Software"), to deal in
23 | the Software without restriction, including without limitation the rights to
24 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
25 | of the Software, and to permit persons to whom the Software is furnished to do
26 | so, subject to the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be included in all
29 | copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 | SOFTWARE.
38 |
--------------------------------------------------------------------------------
/spec/rspec-rails/redirect_to.rb:
--------------------------------------------------------------------------------
1 | module Spec
2 | module Rails
3 | module Matchers
4 |
5 | class RedirectTo #:nodoc:
6 |
7 | def initialize(request, expected)
8 | @expected = expected
9 | @request = request
10 | end
11 |
12 | def matches?(response)
13 | @redirected = response.redirect?
14 | @actual = response.redirect_url
15 | return false unless @redirected
16 | if @expected.instance_of? Hash
17 | return false unless @actual =~ %r{^\w+://#{@request.host}}
18 | return false unless actual_redirect_to_valid_route
19 | return actual_hash == expected_hash
20 | else
21 | return @actual == expected_url
22 | end
23 | end
24 |
25 | def actual_hash
26 | hash_from_url @actual
27 | end
28 |
29 | def expected_hash
30 | hash_from_url expected_url
31 | end
32 |
33 | def actual_redirect_to_valid_route
34 | actual_hash
35 | end
36 |
37 | def hash_from_url(url)
38 | query_hash(url).merge(path_hash(url)).with_indifferent_access
39 | end
40 |
41 | def path_hash(url)
42 | path = url.sub(%r{^\w+://#{@request.host}(?::\d+)?}, "").split("?", 2)[0]
43 | ActionController::Routing::Routes.recognize_path path
44 | end
45 |
46 | def query_hash(url)
47 | query = url.split("?", 2)[1] || ""
48 | QueryParameterParser.parse_query_parameters(query, @request)
49 | end
50 |
51 | def expected_url
52 | case @expected
53 | when Hash
54 | return ActionController::UrlRewriter.new(@request, {}).rewrite(@expected)
55 | when :back
56 | return @request.env['HTTP_REFERER']
57 | when %r{^\w+://.*}
58 | return @expected
59 | else
60 | return "http://#{@request.host}" + (@expected.split('')[0] == '/' ? '' : '/') + @expected
61 | end
62 | end
63 |
64 | def failure_message
65 | if @redirected
66 | return %Q{expected redirect to #{@expected.inspect}, got redirect to #{@actual.inspect}}
67 | else
68 | return %Q{expected redirect to #{@expected.inspect}, got no redirect}
69 | end
70 | end
71 |
72 | def negative_failure_message
73 | return %Q{expected not to be redirected to #{@expected.inspect}, but was} if @redirected
74 | end
75 |
76 | def description
77 | "redirect to #{@actual.inspect}"
78 | end
79 |
80 | class QueryParameterParser
81 | def self.parse_query_parameters(query, request)
82 | if defined?(CGIMethods)
83 | CGIMethods.parse_query_parameters(query)
84 | else
85 | request.class.parse_query_parameters(query)
86 | end
87 | end
88 | end
89 | end
90 |
91 | # :call-seq:
92 | # response.should redirect_to(url)
93 | # response.should redirect_to(:action => action_name)
94 | # response.should redirect_to(:controller => controller_name, :action => action_name)
95 | # response.should_not redirect_to(url)
96 | # response.should_not redirect_to(:action => action_name)
97 | # response.should_not redirect_to(:controller => controller_name, :action => action_name)
98 | #
99 | # Passes if the response is a redirect to the url, action or controller/action.
100 | # Useful in controller specs (integration or isolation mode).
101 | #
102 | # == Examples
103 | #
104 | # response.should redirect_to("path/to/action")
105 | # response.should redirect_to("http://test.host/path/to/action")
106 | # response.should redirect_to(:action => 'list')
107 | def redirect_to(opts)
108 | RedirectTo.new(request, opts)
109 | end
110 | end
111 |
112 | end
113 | end
114 |
--------------------------------------------------------------------------------
/spec/rspec-rails/render_template.rb:
--------------------------------------------------------------------------------
1 | module Spec
2 | module Rails
3 | module Matchers
4 |
5 | class RenderTemplate #:nodoc:
6 |
7 | def initialize(expected, controller)
8 | @controller = controller
9 | @expected = expected
10 | end
11 |
12 | def matches?(response)
13 |
14 | if response.respond_to?(:rendered_file)
15 | @actual = response.rendered_file
16 | else
17 | @actual = response.rendered_template.to_s
18 | end
19 | return false if @actual.blank?
20 | given_controller_path, given_file = path_and_file(@actual)
21 | expected_controller_path, expected_file = path_and_file(@expected)
22 | given_controller_path == expected_controller_path && given_file.match(expected_file)
23 | end
24 |
25 | def failure_message
26 | "expected #{@expected.inspect}, got #{@actual.inspect}"
27 | end
28 |
29 | def negative_failure_message
30 | "expected not to render #{@expected.inspect}, but did"
31 | end
32 |
33 | def description
34 | "render template #{@expected.inspect}"
35 | end
36 |
37 | private
38 | def path_and_file(path)
39 | parts = path.split('/')
40 | file = parts.pop
41 | controller = parts.empty? ? current_controller_path : parts.join('/')
42 | return controller, file
43 | end
44 |
45 | def controller_path_from(path)
46 | parts = path.split('/')
47 | parts.pop
48 | parts.join('/')
49 | end
50 |
51 | def current_controller_path
52 | @controller.class.to_s.underscore.gsub(/_controller$/,'')
53 | end
54 |
55 | end
56 |
57 | # :call-seq:
58 | # response.should render_template(path)
59 | # response.should_not render_template(path)
60 | #
61 | # Passes if the specified template is rendered by the response.
62 | # Useful in controller specs (integration or isolation mode).
63 | #
64 | # path can include the controller path or not. It
65 | # can also include an optional extension (no extension assumes .rhtml).
66 | #
67 | # Note that partials must be spelled with the preceding underscore.
68 | #
69 | # == Examples
70 | #
71 | # response.should render_template('list')
72 | # response.should render_template('same_controller/list')
73 | # response.should render_template('other_controller/list')
74 | #
75 | # #rjs
76 | # response.should render_template('list.rjs')
77 | # response.should render_template('same_controller/list.rjs')
78 | # response.should render_template('other_controller/list.rjs')
79 | #
80 | # #partials
81 | # response.should render_template('_a_partial')
82 | # response.should render_template('same_controller/_a_partial')
83 | # response.should render_template('other_controller/_a_partial')
84 | def render_template(path)
85 | RenderTemplate.new(path.to_s, @controller)
86 | end
87 |
88 | end
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/spec/serialize_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative './spec_helper'
2 |
3 | describe Resourceful::Serialize, ".normalize_attributes" do
4 | def expect_normalize(object)
5 | expect(Resourceful::Serialize.normalize_attributes(object))
6 | end
7 |
8 | it "should return nil if given nil" do
9 | expect_normalize(nil).to be_nil
10 | end
11 |
12 | it "should return a basic hash if given a non-injectable attribute" do
13 | expect_normalize(:foo).to eq({:foo => nil})
14 | expect_normalize(12).to eq({12 => nil})
15 | end
16 |
17 | it "should return a basic hash with a symbol key if given a string attribute" do
18 | expect_normalize("foo").to eq({:foo => nil})
19 | end
20 |
21 | it "should preserve hashes" do
22 | expect_normalize({:foo => nil, :bar => nil, :baz => nil}).to eq({:foo => nil, :bar => nil, :baz => nil})
23 | expect_normalize({:foo => 3, :bar => 1, :baz => 4}).to eq({:foo => 3, :bar => 1, :baz => 4})
24 | expect_normalize({:foo => 3, :bar => 1, :baz => [:foo, :bar]}).to eq({:foo => 3, :bar => 1, :baz => [:foo, :bar]})
25 | end
26 |
27 | it "should merge injectable attributes into one big hash" do
28 | expect_normalize([:foo, :bar, :baz]).to eq({:foo => nil, :bar => nil, :baz => nil})
29 | expect_normalize(
30 | [:foo, :bar, {:baz => nil}, :boom, {:bop => nil, :blat => nil}]
31 | ).to eq(
32 | {:foo => nil, :bar => nil, :baz => nil, :boom => nil, :bop => nil, :blat => nil}
33 | )
34 | expect_normalize(
35 | [:foo, :bar, {:baz => 12}, :boom, {:bop => "foo", :blat => [:fee, :fi, :fo]}]
36 | ).to eq(
37 | {:foo => nil, :bar => nil, :baz => 12, :boom => nil, :bop => "foo", :blat => [:fee, :fi, :fo]}
38 | )
39 | end
40 | end
41 |
42 | describe Array, " of non-serializable objects" do
43 | before :each do
44 | @array = [1, 2, 3, 4, "foo"]
45 | end
46 |
47 | it "should return itself for #to_serializable" do
48 | @array.to_serializable(nil).should == @array
49 | end
50 |
51 | it "should raise an error for #serialize" do
52 | lambda { @array.serialize(:yaml, :attributes => [:foo]) }.should raise_error("Not all elements respond to to_serializable")
53 | end
54 | end
55 |
56 | describe Array, " of serializable objects" do
57 | before :each do
58 | @cat = stub_model("Cat")
59 | @dog = stub_model("Dog")
60 | @array = %w{brown yellow green}.zip(%w{rex rover fido}).
61 | map { |c, d| @cat.new(:fur => c, :friend => @dog.new(:name => d)) }
62 | end
63 |
64 | it "should return an array of serializable hashes for #to_serializable" do
65 | @array.to_serializable([:fur]).should == [{'fur' => 'brown'}, {'fur' => 'yellow'}, {'fur' => 'green'}]
66 | end
67 |
68 | it "should follow deep attributes for #to_serializable" do
69 | @array.to_serializable([:fur, {:friend => :name}]).should ==
70 | [{'fur' => 'brown', 'friend' => {'name' => 'rex'}},
71 | {'fur' => 'yellow', 'friend' => {'name' => 'rover'}},
72 | {'fur' => 'green', 'friend' => {'name' => 'fido'}}]
73 | end
74 |
75 | it "should raise an error if #serialize is called without the :attributes option" do
76 | lambda { @array.serialize(:yaml, {}) }.should raise_error("Must specify :attributes option")
77 | end
78 |
79 | it "should serialize to a hash with a pluralized root for #serialize" do
80 | YAML.load(@array.serialize(:yaml, :attributes => [:fur, {:friend => :name}])).should ==
81 | {"cats" => [{'fur' => 'brown', 'friend' => {'name' => 'rex'}},
82 | {'fur' => 'yellow', 'friend' => {'name' => 'rover'}},
83 | {'fur' => 'green', 'friend' => {'name' => 'fido'}}]}
84 | end
85 |
86 | it "should serialize to an XML document with a pluralized root for #serialize(:xml, ...)" do
87 | doc = REXML::Document.new(@array.serialize(:xml, :attributes => [:fur, {:friend => :name}]),
88 | :ignore_whitespace_nodes => :all)
89 | doc.root.name.should == "cats"
90 | cats = doc.get_elements('/cats/cat')
91 | cats.size.should == 3
92 | cats.zip(%w{brown yellow green}, %w{rex rover fido}) do |cat, fur, dog|
93 | cat.children.find { |e| e.name == "fur" }.text.should == fur
94 | cat.children.find { |e| e.name == "friend" }.children[0].text.should == dog
95 | end
96 | end
97 | end
98 |
99 | describe ActiveModel::Base, " with a few attributes and an association" do
100 | before :each do
101 | @person = stub_model("Person")
102 | @party_hat = stub_model("PartyHat")
103 | @model = @person.new(:name => "joe", :eye_color => "blue", :hairs => 567,
104 | :party_hat => @party_hat.new(:color => 'blue', :size => 12, :pattern => 'stripey'))
105 | end
106 |
107 | it "should raise an error if #to_serializable is called without attributes" do
108 | lambda { @model.to_serializable(nil) }.should raise_error("Must specify attributes for #.to_serializable")
109 | end
110 |
111 | it "should return an attributes hash for #to_serializable" do
112 | @model.to_serializable([:name, :hairs, {:party_hat => [:color, :size]}]).should ==
113 | {'name' => 'joe', 'hairs' => 567, 'party_hat' => {
114 | 'color' => 'blue', 'size' => 12
115 | }}
116 | end
117 |
118 | it "should raise an error if #serialize is called without the :attributes option" do
119 | lambda { @model.serialize(:yaml, {}) }.should raise_error("Must specify :attributes option")
120 | end
121 |
122 | it "should serialize to a hash for #serialize" do
123 | YAML.load(@model.serialize(:yaml, :attributes => [:hairs, :eye_color, {:party_hat => :size}])).should ==
124 | {"person" => {'hairs' => 567, 'eye_color' => 'blue', 'party_hat' => {'size' => 12}}}
125 | end
126 |
127 | it "should serialize to an XML document for #serialize(:xml, ...)" do
128 | doc = REXML::Document.new(@model.serialize(:xml, :attributes => [:name, :eye_color, {:party_hat => :pattern}]),
129 | :ignore_whitespace_nodes => :all)
130 | doc.root.name.should == "person"
131 | doc.root.children.find { |e| e.name == "name" }.text.should == "joe"
132 | doc.root.children.find { |e| e.name == "eye-color" }.text.should == "blue"
133 |
134 | hat = doc.root.children.find { |e| e.name == "party-hat" }
135 | hat.children.find { |e| e.name == "pattern" }.text.should == "stripey"
136 | end
137 | end
138 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | $: << File.dirname(__FILE__) + '/../lib'
2 |
3 |
4 | require 'rubygems'
5 |
6 | require 'rack'
7 | require "rails/all"
8 | require 'rack/test'
9 | require 'rails/test_help'
10 | require 'rspec/rails'
11 | require 'active_model'
12 | require 'support/helper_methods'
13 | require 'support/integration_helpers'
14 | require_relative '../lib/make_resourceful'
15 | require_relative '../lib/resourceful/base'
16 |
17 | RSpec.configure do |config|
18 | config.mock_with :mocha
19 | config.include HelperMethods
20 | config.include IntegrationHelpers
21 | end
22 |
23 | module MetaClass
24 | def metaclass
25 | class << self; self; end
26 | end
27 | end
28 |
29 | def should_be_called(&block)
30 | pstub = stub
31 | pstub.expects(:call).instance_eval(&(block || proc {}))
32 | proc { |*args| pstub.call(*args) }
33 | end
34 |
35 | def stub_model(name)
36 | model = Class.new do
37 | include Resourceful::Serialize::Model
38 |
39 | def self.to_s
40 | @name
41 | end
42 |
43 | def initialize(attrs = {})
44 | attrs.each do |k, v|
45 | self.stubs(k).returns(v)
46 | end
47 | end
48 |
49 | def inspect
50 | "#<#{self.class.send(:instance_variable_get, '@name')}>"
51 | end
52 | end
53 | model.send(:instance_variable_set, '@name', name)
54 | model
55 | end
56 |
57 | def stub_const(name)
58 | unless Object.const_defined?(name)
59 | obj = Object.new
60 | obj.extend MetaClass
61 | obj.metaclass.send(:define_method, :to_s) { name.to_s }
62 | obj.metaclass.send(:alias_method, :inspect, :to_s)
63 | Object.const_set(name, obj)
64 | end
65 | Object.const_get(name)
66 | end
67 |
68 | def stub_list(size, name = nil, &block)
69 | list = Array.new(size) { |i| name ? stub("#{name}_#{i}") : stub }
70 | list.each(&block) if block
71 | list
72 | end
73 |
74 | module Spec::Matchers
75 | def have_any(&proc)
76 | satisfy { |a| a.any?(&proc) }
77 | end
78 | end
79 |
80 | module ControllerMocks
81 | def mock_kontroller(*to_extend)
82 | options = to_extend.last.is_a?(Hash) ? to_extend.slice!(-1) : {}
83 | @kontroller = Class.new
84 | @kontroller.extend Resourceful::Maker
85 | to_extend.each(&@kontroller.method(:extend))
86 |
87 | @hidden_actions = Resourceful::ACTIONS.dup
88 | @kontroller.stubs(:hidden_actions).returns(@hidden_actions)
89 | @kontroller.stubs(:plural_action?).returns(false)
90 | @kontroller.stubs(:include)
91 | @kontroller.stubs(:before_action)
92 | @kontroller.stubs(:helper_method)
93 | end
94 |
95 | def mock_controller(*to_extend)
96 | mock_kontroller
97 | @controller = @kontroller.new
98 | to_extend.each(&@controller.method(:extend))
99 | end
100 |
101 | def mock_builder(inherited = false)
102 | @builder = stub
103 | @builder.stubs(:response_for)
104 | @builder.stubs(:apply)
105 | @builder.stubs(:instance_eval).yields(@buildercc )
106 | @builder.stubs(:inherited?).returns(inherited)
107 | Resourceful::Base.stubs(:made_resourceful).returns([])
108 | Resourceful::Builder.stubs(:new).returns(@builder)
109 | end
110 |
111 | def create_builder
112 | @builder = Resourceful::Builder.new(@kontroller)
113 | class << @builder
114 | alias_method :made_resourceful, :instance_eval
115 | end
116 | end
117 |
118 | def responses
119 | @kontroller.resourceful_responses
120 | end
121 |
122 | def callbacks
123 | @kontroller.resourceful_callbacks
124 | end
125 |
126 | def parents
127 | @kontroller.parents
128 | end
129 |
130 | # Evaluates the made_resourceful block of mod (a module)
131 | # in the context of @builder.
132 | # @builder should be initialized via create_builder.
133 | def made_resourceful(mod)
134 | mod.included(@builder)
135 | end
136 | end
137 |
138 | module RailsMocks
139 | attr_reader :response, :request, :controller, :kontroller
140 |
141 | def included(mod)
142 | require 'ruby-debug'
143 | debugger
144 | end
145 |
146 | def mock_resourceful(options = {}, &block)
147 | options = {
148 | :name => "things"
149 | }.merge options
150 |
151 | init_kontroller options
152 | init_routes options
153 |
154 | stub_const(options[:name].singularize.camelize)
155 | kontroller.make_resourceful(&block)
156 |
157 | init_controller options
158 | end
159 |
160 | def assigns(name)
161 | controller.instance_variable_get("@#{name}")
162 | end
163 |
164 | def redirect_to(opts)
165 | Spec::Rails::Matchers::RedirectTo.new(request, opts)
166 | end
167 |
168 | def render_template(path)
169 | Spec::Rails::Matchers::RenderTemplate.new(path.to_s, @controller)
170 | end
171 |
172 | private
173 |
174 | def init_kontroller(options)
175 | @kontroller = Class.new ActionController::Base
176 | @kontroller.extend Resourceful::Maker
177 | @kontroller.extend MetaClass
178 |
179 | @kontroller.metaclass.send(:define_method, :controller_name) { options[:name] }
180 | @kontroller.metaclass.send(:define_method, :controller_path) { options[:name] }
181 | @kontroller.metaclass.send(:define_method, :inspect) { "#{options[:name].camelize}Controller" }
182 | @kontroller.metaclass.send(:alias_method, :to_s, :inspect)
183 |
184 | @kontroller.send(:define_method, :controller_name) { options[:name] }
185 | @kontroller.send(:define_method, :controller_path) { options[:name] }
186 | @kontroller.send(:define_method, :inspect) { "#<#{options[:name].camelize}Controller>" }
187 | @kontroller.send(:alias_method, :to_s, :inspect)
188 | @kontroller.send(:include, ControllerMethods)
189 | @kontroller.send(:view_paths=, [File.join(File.dirname(__FILE__), 'views')])
190 |
191 | @kontroller
192 | end
193 |
194 | def init_routes(options)
195 | ActionController::Routing::Routes.clear!
196 | route_block = options[:routes] || proc { |map| map.resources options[:name] }
197 | ActionController::Routing::Routes.draw(&route_block)
198 | end
199 |
200 | def init_controller(options)
201 | @controller = kontroller.new
202 | @request = ActionController::TestRequest.new
203 | @response = ActionController::TestResponse.new
204 |
205 | @controller.request = @request
206 | @controller.response = @response
207 | @request.accept = '*/*'
208 | @request.env['HTTP_REFERER'] = 'http://test.host'
209 |
210 | @controller
211 | end
212 |
213 | def action_params(action, params = {})
214 | params.merge case action
215 | when :show, :edit, :destroy, {:id => 12}
216 | when :update, {:id => 12, :thing => {}}
217 | when :create, {:thing => {}}
218 | else {}
219 | end
220 | end
221 |
222 | def action_method(action)
223 | method case action
224 | when :index, :show, :edit, :new, :get
225 | when :update, :put
226 | when :create, :post
227 | when :destroy, :delete
228 | end
229 | end
230 |
231 | module ControllerMethods
232 | # From rspec-rails ControllerExampleGroup
233 |
234 | def render(options=nil, deprecated_status_or_extra_options=nil, &block)
235 | if ::Rails::VERSION::STRING >= '2.0.0' && deprecated_status_or_extra_options.nil?
236 | deprecated_status_or_extra_options = {}
237 | end
238 |
239 | unless block_given?
240 | if @template.respond_to?(:finder)
241 | (class << @template.finder; self; end).class_eval do
242 | define_method :file_exists? do; true; end
243 | end
244 | else
245 | (class << @template; self; end).class_eval do
246 | define_method :file_exists? do; true; end
247 | end
248 | end
249 | (class << @template; self; end).class_eval do
250 | define_method :render_file do |*args|
251 | @first_render ||= args[0] unless args[0] =~ /^layouts/
252 | @_first_render ||= args[0] unless args[0] =~ /^layouts/
253 | end
254 |
255 | define_method :_pick_template do |*args|
256 | @_first_render ||= args[0] unless args[0] =~ /^layouts/
257 | PickedTemplate.new
258 | end
259 | end
260 | end
261 |
262 | super(options, deprecated_status_or_extra_options, &block)
263 | end
264 |
265 | class PickedTemplate
266 | def render_template(*ignore_args); end
267 | def render_partial(*ignore_args); end
268 | end
269 | end
270 |
271 | end
272 |
273 |
--------------------------------------------------------------------------------
/spec/support/helper_methods.rb:
--------------------------------------------------------------------------------
1 | module HelperMethods
2 | def should_render_html(action)
3 | it "should render HTML by default for #{action_string(action)}" do
4 | action_method(action)[action, action_params(action)]
5 | response.body.should include("as HTML")
6 | response.content_type.should == 'text/html'
7 | end
8 | end
9 |
10 | def should_render_js(action)
11 | it "should render JS for #{action_string(action)}" do
12 | action_method(action)[action, action_params(action, :format => 'js')]
13 | response.body.should include("insert(\"#{action}")
14 | response.should be_success
15 | response.content_type.should == 'text/javascript'
16 | end
17 | end
18 |
19 | def shouldnt_render_xml(action)
20 | it "shouldn't render XML for #{action_string(action)}" do
21 | action_method(action)[action, action_params(action, :format => 'xml')]
22 | response.should_not be_success
23 | response.code.should == '406'
24 | end
25 | end
26 |
27 | def action_string(action)
28 | case action
29 | when :index, "GET /things"
30 | when :show, "GET /things/12"
31 | when :edit, "GET /things/12/edit"
32 | when :update, "PUT /things/12"
33 | when :create, "POST /things"
34 | when :new, "GET /things/new"
35 | when :destroy, "DELETE /things/12"
36 | end
37 | end
38 | end
--------------------------------------------------------------------------------
/spec/support/integration_helpers.rb:
--------------------------------------------------------------------------------
1 | module IntegrationHelpers
2 | # Need this helper, because we made current_objects private
3 | def current_objects
4 | controller.instance_eval("current_objects")
5 | end
6 |
7 | # Need this helper, because we made current_object private
8 | def current_object
9 | controller.instance_eval("current_object")
10 | end
11 | end
--------------------------------------------------------------------------------
/spec/urls_spec.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/spec_helper'
2 |
3 | describe Resourceful::Default::URLs, " for a controller with no parents or namespaces" do
4 | include ControllerMocks
5 | before :each do
6 | mock_controller Resourceful::Default::URLs
7 | @object = stub_model('Thing')
8 | @controller.stubs(:current_object).returns(@object)
9 |
10 | @controller.stubs(:current_model_name).returns('Thing')
11 | @controller.stubs(:parent?).returns(false)
12 | @controller.stubs(:namespaces).returns([])
13 | end
14 |
15 | it "should return nil for #url_helper_prefix" do
16 | @controller.url_helper_prefix.should be_nil
17 | end
18 |
19 | it "should return the empty string for #collection_url_prefix" do
20 | @controller.collection_url_prefix.should == ""
21 | end
22 |
23 | it "should get the path of current_object with #object_path" do
24 | @controller.expects(:send).with('thing_path', @object)
25 | @controller.object_path
26 | end
27 |
28 | it "should get the url of current_object with #object_url" do
29 | @controller.expects(:send).with('thing_url', @object)
30 | @controller.object_url
31 | end
32 |
33 | it "should get the path of the passed object with #object_path" do
34 | model = stub_model('Thing')
35 | @controller.expects(:send).with('thing_path', model)
36 | @controller.object_path(model)
37 | end
38 |
39 | it "should get the url of the passed object with #object_url" do
40 | model = stub_model('Thing')
41 | @controller.expects(:send).with('thing_url', model)
42 | @controller.object_url(model)
43 | end
44 |
45 | it "should get the path of current_object with #nested_object_path" do
46 | @controller.expects(:send).with('thing_path', @object)
47 | @controller.nested_object_path
48 | end
49 |
50 | it "should get the url of current_object with #nested_object_url" do
51 | @controller.expects(:send).with('thing_url', @object)
52 | @controller.nested_object_url
53 | end
54 |
55 | it "should get the path of the passed object with #nested_object_path" do
56 | model = stub_model('Thing')
57 | @controller.expects(:send).with('thing_path', model)
58 | @controller.nested_object_path(model)
59 | end
60 |
61 | it "should get the url of the passed object with #nested_object_url" do
62 | model = stub_model('Thing')
63 | @controller.expects(:send).with('thing_url', model)
64 | @controller.nested_object_url(model)
65 | end
66 |
67 | it "should get the edit path of current_object with #edit_object_path" do
68 | @controller.expects(:send).with('edit_thing_path', @object)
69 | @controller.edit_object_path
70 | end
71 |
72 | it "should get the edit url of current_object with #edit_object_url" do
73 | @controller.expects(:send).with('edit_thing_url', @object)
74 | @controller.edit_object_url
75 | end
76 |
77 | it "should get the edit path of the passed object with #edit_object_path" do
78 | model = stub_model('Thing')
79 | @controller.expects(:send).with('edit_thing_path', model)
80 | @controller.edit_object_path(model)
81 | end
82 |
83 | it "should get the edit url of the passed object with #edit_object_url" do
84 | model = stub_model('Thing')
85 | @controller.expects(:send).with('edit_thing_url', model)
86 | @controller.edit_object_url(model)
87 | end
88 |
89 | it "should get the plural path of the current model with #objects_path" do
90 | @controller.expects(:send).with('things_path')
91 | @controller.objects_path
92 | end
93 |
94 | it "should get the plural url of the current model with #objects_url" do
95 | @controller.expects(:send).with('things_url')
96 | @controller.objects_url
97 | end
98 |
99 | it "should get the new path of the current model with #new_object_path" do
100 | @controller.expects(:send).with('new_thing_path')
101 | @controller.new_object_path
102 | end
103 |
104 | it "should get the new url of the current model with #new_object_url" do
105 | @controller.expects(:send).with('new_thing_url')
106 | @controller.new_object_url
107 | end
108 | end
109 |
110 | describe Resourceful::Default::URLs, " for a controller with a parent object" do
111 | include ControllerMocks
112 | before :each do
113 | mock_controller Resourceful::Default::URLs
114 | @object = stub_model('Thing')
115 | @controller.stubs(:current_object).returns(@object)
116 |
117 | @controller.stubs(:current_model_name).returns('Thing')
118 |
119 | @person = stub_model('Person')
120 | @controller.stubs(:parent_object).returns(@person)
121 | @controller.stubs(:parent_name).returns('person')
122 | @controller.stubs(:parent?).returns(true)
123 | @controller.stubs(:parent_class_name).returns('Person')
124 | @controller.stubs(:namespaces).returns([])
125 | end
126 |
127 | it "should return nil for #url_helper_prefix" do
128 | @controller.url_helper_prefix.should be_nil
129 | end
130 |
131 | it "should return the underscored parent name for #collection_url_prefix" do
132 | @controller.collection_url_prefix.should == "person_"
133 | end
134 |
135 | it "should get the path of current_object with #object_path" do
136 | @controller.expects(:send).with('person_thing_path', @person, @object)
137 | @controller.object_path
138 | end
139 |
140 | it "should get the nested path of current_object with #nested_object_path" do
141 | @controller.expects(:send).with('person_thing_path', @person, @object)
142 | @controller.nested_object_path
143 | end
144 |
145 | it "should get the nested url of current_object with #nested_object_url" do
146 | @controller.expects(:send).with('person_thing_url', @person, @object)
147 | @controller.nested_object_url
148 | end
149 |
150 | it "should get the nested path of the passed object with #nested_object_path" do
151 | object = stub_model('Thing')
152 | @controller.expects(:send).with('person_thing_path', @person, object)
153 | @controller.nested_object_path object
154 | end
155 |
156 | it "should get the nested url of the passed object with #nested_object_url" do
157 | object = stub_model('Thing')
158 | @controller.expects(:send).with('person_thing_url', @person, object)
159 | @controller.nested_object_url object
160 | end
161 |
162 | it "should get the plural path of the current model and its parent with #objects_path" do
163 | @controller.expects(:send).with('person_things_path', @person)
164 | @controller.objects_path
165 | end
166 |
167 | it "should get the edit path of the current model with #edit_object_path" do
168 | @controller.expects(:send).with('edit_person_thing_path', @person, @object)
169 | @controller.edit_object_path
170 | end
171 |
172 | it "should get the new path of the current model and its parent with #new_object_path" do
173 | @controller.expects(:send).with('new_person_thing_path', @person)
174 | @controller.new_object_path
175 | end
176 |
177 | it "should get the path of the parent_object with #parent_path" do
178 | pending
179 | @controller.expects(:send).with('person_path', @person)
180 | @controller.parent_path
181 | end
182 |
183 | it "should get the url of the parent_object with #parent_url" do
184 | pending
185 | @controller.expects(:send).with('person_url', @person)
186 | @controller.parent_url
187 | end
188 |
189 | it "should get the path of the passed object with #parent_path" do
190 | pending
191 | model = stub_model('Person')
192 | @controller.expects(:send).with('person_path', model)
193 | @controller.parent_path model
194 | end
195 |
196 | it "should get the url of the passed object with #parent_url" do
197 | pending
198 | model = stub_model('Person')
199 | @controller.expects(:send).with('person_url', model)
200 | @controller.parent_url model
201 | end
202 | end
203 |
204 | describe Resourceful::Default::URLs, " for a controller within a namespace" do
205 | include ControllerMocks
206 | before :each do
207 | mock_controller Resourceful::Default::URLs
208 | @object = stub_model('Thing')
209 | @controller.stubs(:current_object).returns(@object)
210 |
211 | @controller.stubs(:current_model_name).returns('Thing')
212 |
213 | @controller.stubs(:parent?).returns(false)
214 | @controller.stubs(:namespaces).returns([:admin, :main])
215 | end
216 |
217 | it "should return the underscored list of namespaces for #url_helper_prefix" do
218 | @controller.url_helper_prefix.should == "admin_main_"
219 | end
220 |
221 | it "should get the namespaced path of current_object with #object_path" do
222 | @controller.expects(:send).with('admin_main_thing_path', @object)
223 | @controller.object_path
224 | end
225 |
226 | it "should get the namespaced plural path of the current model with #objects_path" do
227 | @controller.expects(:send).with('admin_main_things_path')
228 | @controller.objects_path
229 | end
230 |
231 | it "should get the edit path of the current model with #edit_object_path" do
232 | @controller.expects(:send).with('edit_admin_main_thing_path', @object)
233 | @controller.edit_object_path
234 | end
235 |
236 | it "should get the new path of the current model with #new_object_path" do
237 | @controller.expects(:send).with('new_admin_main_thing_path')
238 | @controller.new_object_path
239 | end
240 | end
241 |
242 | describe Resourceful::Default::URLs, " for a controller with a parent object and within a namespace" do
243 | include ControllerMocks
244 | before :each do
245 | mock_controller Resourceful::Default::URLs
246 | @object = stub_model('Thing')
247 | @controller.stubs(:current_object).returns(@object)
248 |
249 | @controller.stubs(:current_model_name).returns('Thing')
250 |
251 | @person = stub_model('Person')
252 | @controller.stubs(:parent_object).returns(@person)
253 | @controller.stubs(:parent_name).returns('person')
254 | @controller.stubs(:parent?).returns(true)
255 | @controller.stubs(:parent_class_name).returns('Person')
256 | @controller.stubs(:namespaces).returns([:admin, :main])
257 | end
258 |
259 | it "should return the underscored list of namespaces for #url_helper_prefix" do
260 | @controller.url_helper_prefix.should == "admin_main_"
261 | end
262 |
263 | it "should get the namespaced path of current_object with #object_path" do
264 | @controller.expects(:send).with('admin_main_person_thing_path', @person, @object)
265 | @controller.object_path
266 | end
267 |
268 | it "should get the namespaced plural path of the current model and its parent with #objects_path" do
269 | @controller.expects(:send).with('admin_main_person_things_path', @person)
270 | @controller.objects_path
271 | end
272 |
273 | it "should get the edit path of the current model with #edit_object_path" do
274 | @controller.expects(:send).with('edit_admin_main_person_thing_path', @person, @object)
275 | @controller.edit_object_path
276 | end
277 |
278 | it "should get the new path of the current model and its parent with #new_object_path" do
279 | @controller.expects(:send).with('new_admin_main_person_thing_path', @person)
280 | @controller.new_object_path
281 | end
282 | end
283 |
--------------------------------------------------------------------------------
/spec/views/things/create.rjs:
--------------------------------------------------------------------------------
1 | page.insert_html 'test', 'create'
--------------------------------------------------------------------------------
/spec/views/things/destroy.rjs:
--------------------------------------------------------------------------------
1 | page.insert_html 'test', 'destroy'
--------------------------------------------------------------------------------
/spec/views/things/edit.html.erb:
--------------------------------------------------------------------------------
1 | Notice: <%= flash[:notice]%>
2 | Error: <%= flash[:error]%>
3 |
4 | Editting object as HTML
--------------------------------------------------------------------------------
/spec/views/things/edit.rjs:
--------------------------------------------------------------------------------
1 | page.insert_html 'test', 'edit'
--------------------------------------------------------------------------------
/spec/views/things/index.html.erb:
--------------------------------------------------------------------------------
1 | Notice: <%= flash[:notice]%>
2 | Error: <%= flash[:error]%>
3 |
4 | Objects as HTML
--------------------------------------------------------------------------------
/spec/views/things/index.rjs:
--------------------------------------------------------------------------------
1 | page.insert_html 'test', 'index'
--------------------------------------------------------------------------------
/spec/views/things/new.html.erb:
--------------------------------------------------------------------------------
1 | Notice: <%= flash[:notice]%>
2 | Error: <%= flash[:error]%>
3 |
4 | New object as HTML
--------------------------------------------------------------------------------
/spec/views/things/new.rjs:
--------------------------------------------------------------------------------
1 | page.insert_html 'test', 'new'
--------------------------------------------------------------------------------
/spec/views/things/show.html.erb:
--------------------------------------------------------------------------------
1 | Notice: <%= flash[:notice]%>
2 | Error: <%= flash[:error]%>
3 |
4 | Showing object as HTML
--------------------------------------------------------------------------------
/spec/views/things/show.rjs:
--------------------------------------------------------------------------------
1 | page.insert_html 'test', 'show'
--------------------------------------------------------------------------------
/spec/views/things/update.rjs:
--------------------------------------------------------------------------------
1 | page.insert_html 'test', 'update'
--------------------------------------------------------------------------------