make_resourceful_rails2 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/Rakefile +31 -0
  2. data/VERSION +1 -0
  3. data/lib/make_resourceful.rb +2 -0
  4. data/lib/resourceful/base.rb +63 -0
  5. data/lib/resourceful/builder.rb +405 -0
  6. data/lib/resourceful/default/accessors.rb +418 -0
  7. data/lib/resourceful/default/actions.rb +101 -0
  8. data/lib/resourceful/default/callbacks.rb +51 -0
  9. data/lib/resourceful/default/responses.rb +118 -0
  10. data/lib/resourceful/default/urls.rb +136 -0
  11. data/lib/resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb +87 -0
  12. data/lib/resourceful/generators/resourceful_scaffold/templates/controller.rb +5 -0
  13. data/lib/resourceful/generators/resourceful_scaffold/templates/fixtures.yml +10 -0
  14. data/lib/resourceful/generators/resourceful_scaffold/templates/functional_test.rb +50 -0
  15. data/lib/resourceful/generators/resourceful_scaffold/templates/helper.rb +2 -0
  16. data/lib/resourceful/generators/resourceful_scaffold/templates/migration.rb +13 -0
  17. data/lib/resourceful/generators/resourceful_scaffold/templates/model.rb +2 -0
  18. data/lib/resourceful/generators/resourceful_scaffold/templates/unit_test.rb +7 -0
  19. data/lib/resourceful/generators/resourceful_scaffold/templates/view__form.haml +5 -0
  20. data/lib/resourceful/generators/resourceful_scaffold/templates/view_edit.haml +11 -0
  21. data/lib/resourceful/generators/resourceful_scaffold/templates/view_index.haml +5 -0
  22. data/lib/resourceful/generators/resourceful_scaffold/templates/view_new.haml +9 -0
  23. data/lib/resourceful/generators/resourceful_scaffold/templates/view_partial.haml +12 -0
  24. data/lib/resourceful/generators/resourceful_scaffold/templates/view_show.haml +14 -0
  25. data/lib/resourceful/maker.rb +92 -0
  26. data/lib/resourceful/response.rb +33 -0
  27. data/lib/resourceful/serialize.rb +185 -0
  28. data/spec/accessors_spec.rb +474 -0
  29. data/spec/actions_spec.rb +310 -0
  30. data/spec/base_spec.rb +12 -0
  31. data/spec/builder_spec.rb +332 -0
  32. data/spec/callbacks_spec.rb +71 -0
  33. data/spec/integration_spec.rb +394 -0
  34. data/spec/maker_spec.rb +91 -0
  35. data/spec/response_spec.rb +37 -0
  36. data/spec/responses_spec.rb +314 -0
  37. data/spec/serialize_spec.rb +133 -0
  38. data/spec/urls_spec.rb +282 -0
  39. metadata +114 -0
@@ -0,0 +1,2 @@
1
+ module <%= controller_class_name %>Helper
2
+ end
@@ -0,0 +1,13 @@
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
@@ -0,0 +1,2 @@
1
+ class <%= class_name %> < ActiveRecord::Base
2
+ end
@@ -0,0 +1,7 @@
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
@@ -0,0 +1,5 @@
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 -%>
@@ -0,0 +1,11 @@
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
@@ -0,0 +1,5 @@
1
+ %h1 Listing <%= plural_name %>
2
+
3
+ = render :partial => '<%= singular_name %>', :collection => current_objects
4
+
5
+ = link_to 'New <%= singular_name %>', new_object_path
@@ -0,0 +1,9 @@
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
@@ -0,0 +1,12 @@
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
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,92 @@
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 :parents
15
+ base.class_attribute :shallow_parent
16
+ base.class_attribute :model_namespace
17
+ base.class_attribute :made_resourceful
18
+
19
+ base.resourceful_callbacks = {}
20
+ base.resourceful_responses = {}
21
+ base.parents = []
22
+ base.model_namespace = nil
23
+ base.made_resourceful = false
24
+ end
25
+
26
+ # :call-seq:
27
+ # make_resourceful(options = {}) { ... }
28
+ #
29
+ # This is the central method, and namesake, of make_resourceful.
30
+ # It takes a block and evaluates it in the context of a Builder,
31
+ # allowing the controller to be customized extensively.
32
+ #
33
+ # See Resourceful::Builder for documentation on the methods available
34
+ # in the context of the block.
35
+ #
36
+ # The only option currently available is <tt>:include</tt>.
37
+ # It takes an object that responds to to_proc
38
+ # (or an array of such objects)
39
+ # and evaluates that proc in the same context as the block.
40
+ # For example:
41
+ #
42
+ # make_resourceful :include => proc { actions :all } do
43
+ # before :show do
44
+ # current_object.current_user = current_user
45
+ # end
46
+ # end
47
+ #
48
+ # This is the same as:
49
+ #
50
+ # make_resourceful do
51
+ # actions :all
52
+ # before :show do
53
+ # current_object.current_user = current_user
54
+ # end
55
+ # end
56
+ #
57
+ def make_resourceful(options = {}, &block)
58
+ # :stopdoc:
59
+ include Resourceful::Base
60
+ # :startdoc:
61
+
62
+ builder = Resourceful::Builder.new(self)
63
+ unless builder.inherited?
64
+ Resourceful::Base.made_resourceful.each { |proc| builder.instance_eval(&proc) }
65
+ end
66
+ Array(options[:include]).each { |proc| builder.instance_eval(&proc) }
67
+ builder.instance_eval(&block)
68
+
69
+ builder.apply
70
+
71
+ add_helpers
72
+ end
73
+
74
+ # Returns whether or not make_resourceful has been called
75
+ # on this controller or any controllers it inherits from.
76
+ def made_resourceful?
77
+ self.class.made_resourceful
78
+ end
79
+
80
+ private
81
+
82
+ def add_helpers
83
+ helper_method(:object_path, :objects_path, :new_object_path, :edit_object_path,
84
+ :object_url, :objects_url, :new_object_url, :edit_object_url,
85
+ :parent_path, :parent_url,
86
+ :nested_object_path, :nested_object_url,
87
+ :current_objects, :current_object, :current_model, :current_model_name,
88
+ :namespaces, :instance_variable_name, :parent_names, :parent_name,
89
+ :parent?, :parent_model, :parent_object, :save_succeeded?)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,33 @@
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, #<Proc>], [:xml, #<Proc>], [:js, #<Proc>]]
14
+ #
15
+ # Note that the <tt>:js</tt> response is the empty proc -
16
+ # the same as <tt>proc {}</tt>.
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
@@ -0,0 +1,185 @@
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 <tt>:attributes</tt> 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 (<tt>:xml</tt>, <tt>:yaml</tt>, or <tt>:json</tt> - see New Formats below)
41
+ # and a hash of options.
42
+ # The only option currently recognized is <tt>:attributes</tt>,
43
+ # which has the same semantics
44
+ # as the <tt>:attributes</tt> 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
+ # <?xml version="1.0" encoding="UTF-8"?>
58
+ # <candy-bag>
59
+ # <title>jellybag</title>
60
+ # <candies>
61
+ # <candy>
62
+ # <type>jellybean</type>
63
+ # <flavor>root beer</flavor>
64
+ # </candy>
65
+ # <candy>
66
+ # <type>jellybean</type>
67
+ # <flavor>pear</flavor>
68
+ # </candy>
69
+ # <candy>
70
+ # <type>licorice</type>
71
+ # <flavor>anisey</flavor>
72
+ # </candy>
73
+ # </candies>
74
+ # </candy-bag>
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
@@ -0,0 +1,474 @@
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