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.
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/lib/make_resourceful.rb +2 -0
- data/lib/resourceful/base.rb +63 -0
- data/lib/resourceful/builder.rb +405 -0
- data/lib/resourceful/default/accessors.rb +418 -0
- data/lib/resourceful/default/actions.rb +101 -0
- data/lib/resourceful/default/callbacks.rb +51 -0
- data/lib/resourceful/default/responses.rb +118 -0
- data/lib/resourceful/default/urls.rb +136 -0
- data/lib/resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb +87 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/controller.rb +5 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/fixtures.yml +10 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/functional_test.rb +50 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/helper.rb +2 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/migration.rb +13 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/model.rb +2 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/unit_test.rb +7 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view__form.haml +5 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_edit.haml +11 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_index.haml +5 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_new.haml +9 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_partial.haml +12 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_show.haml +14 -0
- data/lib/resourceful/maker.rb +92 -0
- data/lib/resourceful/response.rb +33 -0
- data/lib/resourceful/serialize.rb +185 -0
- data/spec/accessors_spec.rb +474 -0
- data/spec/actions_spec.rb +310 -0
- data/spec/base_spec.rb +12 -0
- data/spec/builder_spec.rb +332 -0
- data/spec/callbacks_spec.rb +71 -0
- data/spec/integration_spec.rb +394 -0
- data/spec/maker_spec.rb +91 -0
- data/spec/response_spec.rb +37 -0
- data/spec/responses_spec.rb +314 -0
- data/spec/serialize_spec.rb +133 -0
- data/spec/urls_spec.rb +282 -0
- metadata +114 -0
@@ -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,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,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
|