hookercookerman-merb-resource-scope 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.textile +297 -0
- data/Rakefile +82 -0
- data/TODO +3 -0
- data/lib/dependencies.rb +1 -0
- data/lib/merb-resource-scope.rb +25 -0
- data/lib/merb-resource-scope/controller/actions.rb +67 -0
- data/lib/merb-resource-scope/controller/helpers.rb +252 -0
- data/lib/merb-resource-scope/controller/scoped_resource_mixin.rb +300 -0
- data/lib/merb-resource-scope/controller/singleton_actions.rb +25 -0
- data/lib/merb-resource-scope/merbtasks.rb +6 -0
- data/lib/merb-resource-scope/router/resource_specification.rb +144 -0
- data/lib/merb-resource-scope/specification.rb +36 -0
- data/spec/app.rb +7 -0
- data/spec/controller/_build_enclosing_scope_spec.rb +107 -0
- data/spec/controller/scope_resource_mixin_spec.rb +0 -0
- data/spec/integration/admin_posts_controller_spec.rb +158 -0
- data/spec/integration/campaign_post_comments_controller_spec.rb +158 -0
- data/spec/integration/campaign_posts_controller_spec.rb +73 -0
- data/spec/integration/campaigns_controller_spec.rb +43 -0
- data/spec/integration/images_spec.rb +17 -0
- data/spec/integration/myhome_controller_spec.rb +29 -0
- data/spec/integration/myhome_posts_comment_controller_spec.rb +44 -0
- data/spec/integration/myhome_posts_controller_spec.rb +55 -0
- data/spec/integration/profile_images_controller_spec.rb +61 -0
- data/spec/integration/tags_controller_spec.rb +34 -0
- data/spec/integration/user_posts_controller_spec.rb +71 -0
- data/spec/request_with_controller.rb +103 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/specification_spec.rb +1 -0
- metadata +88 -0
@@ -0,0 +1,252 @@
|
|
1
|
+
module MerbResourceScope
|
2
|
+
module Controller
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
class UrlGenerator
|
6
|
+
attr_accessor :resources, :name_prefixes, :resources_with_specs, :named_route
|
7
|
+
|
8
|
+
def initialize(name_prefixes, resources_with_specs, &block)
|
9
|
+
@resources = []
|
10
|
+
@name_prefixes, @resources_with_specs = name_prefixes, resources_with_specs
|
11
|
+
yield self if block_given?
|
12
|
+
@named_route = generate_named_route
|
13
|
+
end
|
14
|
+
|
15
|
+
# this is so we can extend out current name_route
|
16
|
+
# will be used in congunction of the url helpers
|
17
|
+
# enclosing_resource_url{|route| route.add(:comments)}
|
18
|
+
#
|
19
|
+
# @param args<VarArgs> : this takes 3 parameter really, first one
|
20
|
+
# is a symbol of the named_route you want to add, so lets
|
21
|
+
# say we had enclosing_resource_url of /users/1/
|
22
|
+
# and we wanted to add tags on to this url we can simple
|
23
|
+
# enclosing_resource_url{|route| route.add(:tags)}
|
24
|
+
# and this will be /users/1/tags
|
25
|
+
# You can also add custom routes as well then as well
|
26
|
+
# add(:tags, :pending)
|
27
|
+
# Also add a resource as well, add(:tag, @tag)
|
28
|
+
# Also do a new this way as well add(:tag, :new)
|
29
|
+
# And finally add(:tag, :edit, @tag) Where the @tag
|
30
|
+
# has to be the last option
|
31
|
+
#
|
32
|
+
# @examples
|
33
|
+
# enclosing_resource_url{|route| route.add(:comments)}
|
34
|
+
# resource_url(current_resource){|route| route.add(:tag, @tag)}
|
35
|
+
# enclosing_resource_url{|route| route.add(:post, :edit, @post)}
|
36
|
+
#
|
37
|
+
#
|
38
|
+
def add(*args)
|
39
|
+
name_prefixes.flatten!
|
40
|
+
name_prefixes << args.shift
|
41
|
+
args.each do |arg|
|
42
|
+
if arg.is_a?(Symbol)
|
43
|
+
name_prefixes.unshift(arg.to_s)
|
44
|
+
else
|
45
|
+
resources << arg
|
46
|
+
end
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :with, :add
|
52
|
+
|
53
|
+
def generate_named_route
|
54
|
+
if resources_with_specs
|
55
|
+
none_singletons = resources_with_specs.reject{|rs| rs[1].singleton}
|
56
|
+
resources << none_singletons.map{|rs| rs[0]}
|
57
|
+
end
|
58
|
+
generated_named_route = name_prefixes.flatten.compact.join('_').intern
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# TODO tidy refactor these methods pretty please
|
63
|
+
# if you want the current resources url then use this method its the way forward
|
64
|
+
# url is generated from building up a named route and then parsing the named
|
65
|
+
# route to the url method, so remember this url is buit using named routes!!
|
66
|
+
#
|
67
|
+
# so if we have a path of /users/1/posts/2
|
68
|
+
# the current_resources_url would be /users/1/posts
|
69
|
+
# remeber the url generated will be already scoped
|
70
|
+
# You can pass a custom route to generate a custom route
|
71
|
+
# and the params just like the url method
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# current_resources_url(:pending, {:q => "cool"})
|
75
|
+
# current_resources_url(:notpending, {:lots => "yes", :lot => "ofparams"})
|
76
|
+
# current_resources_url
|
77
|
+
#
|
78
|
+
#
|
79
|
+
# @param<*args> : pass in a list of custom named routes, and param hash
|
80
|
+
#
|
81
|
+
# @return<String> : the generate url
|
82
|
+
def current_resources_url(*args, &block)
|
83
|
+
options = extract_options_from_args!(args) || {}
|
84
|
+
name_prefixes = []
|
85
|
+
name_prefixes << _current_specification.name_prefix
|
86
|
+
name_prefixes << Extlib::Inflection.pluralize(_current_specification.resource_name)
|
87
|
+
name_prefixes.unshift(args)
|
88
|
+
generator = UrlGenerator.new(name_prefixes, _enclosing_resources_with_spec, &block)
|
89
|
+
url(generator.named_route, *[generator.resources, options].flatten)
|
90
|
+
end
|
91
|
+
alias_method :resources_url, :current_resources_url
|
92
|
+
|
93
|
+
|
94
|
+
# generates a new resource url that is automatically scoped where you are
|
95
|
+
#
|
96
|
+
#
|
97
|
+
# lets just say we were at this url users/1/posts
|
98
|
+
# by using new_current_resource_url we would basically generate
|
99
|
+
# new_user_post named route
|
100
|
+
# which inturn would generate /users/1/posts/new
|
101
|
+
#
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# new_current_resource_url({:awantaparam=> "yesyoucan"})
|
105
|
+
# new_current_resource_url
|
106
|
+
#
|
107
|
+
#
|
108
|
+
# @param options<Hash> : Pass in a hash of params
|
109
|
+
#
|
110
|
+
# @return<String> : the generate url
|
111
|
+
def new_current_resource_url(options = {}, &block)
|
112
|
+
name_prefixes = []
|
113
|
+
name_prefixes << _current_specification.name_prefix
|
114
|
+
name_prefixes << _current_specification.resource_name
|
115
|
+
name_prefixes.unshift("new")
|
116
|
+
generator = UrlGenerator.new(name_prefixes, _enclosing_resources_with_spec, &block)
|
117
|
+
url(generator.named_route, *[generator.resources, options].flatten)
|
118
|
+
end
|
119
|
+
alias_method :new_resource_url, :new_current_resource_url
|
120
|
+
|
121
|
+
|
122
|
+
# generates the current_resource_url cool
|
123
|
+
#
|
124
|
+
#
|
125
|
+
# lets just say we were at this url users/1/posts/1
|
126
|
+
# then via to using the current_resource_url we would do
|
127
|
+
# current_resource_url(@post) for singleton resources
|
128
|
+
# just pass in the resource_name ie. for /myhome/proile_image
|
129
|
+
# we would have current_resource_url(:myhome)
|
130
|
+
#
|
131
|
+
#
|
132
|
+
# @example
|
133
|
+
# current_resource_url(@post)
|
134
|
+
# current_resource_url(@post, :my => "params")
|
135
|
+
# current_resource_url(:myhome)
|
136
|
+
#
|
137
|
+
# @param options<Hash> : Pass in a hash of params
|
138
|
+
#
|
139
|
+
# @return<String> : the generate url
|
140
|
+
def current_resource_url(current_resource, *args, &block)
|
141
|
+
name_prefixes = []
|
142
|
+
options = extract_options_from_args!(args) || {}
|
143
|
+
resource_specs = _resources_with_specs.dup
|
144
|
+
name_prefixes << _current_specification.name_prefix
|
145
|
+
unless current_resource.is_a?(Symbol) && _current_specification.singleton
|
146
|
+
resource_specs << [current_resource, _current_specification]
|
147
|
+
name_prefixes << _current_specification.resource_name
|
148
|
+
else
|
149
|
+
name_prefixes << current_resource
|
150
|
+
end
|
151
|
+
name_prefixes.unshift(args)
|
152
|
+
generator = UrlGenerator.new(name_prefixes, resource_specs, &block)
|
153
|
+
url(generator.named_route, *[generator.resources, options].flatten)
|
154
|
+
end
|
155
|
+
alias_method :resource_url, :current_resource_url
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
# generates the enclosing_resource_url cool
|
160
|
+
#
|
161
|
+
#
|
162
|
+
# lets just say we were at this url users/1/posts/1
|
163
|
+
# then via to using the current_resource_url we would do
|
164
|
+
# current_resource_url(@post) for singleton resources
|
165
|
+
# just pass in the resource_name ie. for /myhome/proile_image
|
166
|
+
# we would have current_resource_url(:myhome)
|
167
|
+
#
|
168
|
+
#
|
169
|
+
# @example
|
170
|
+
# enclosing_resource_url(:edit)
|
171
|
+
# enclosing_resource_url(:edit, :my => "param")
|
172
|
+
# enclosing_resource_url(:whatever)
|
173
|
+
# enclosing_resource_url(:my => "params")
|
174
|
+
# enclosing_resource_url(:myhome)
|
175
|
+
#
|
176
|
+
# @param args<Var> : Pass in custom route, then params basically
|
177
|
+
#
|
178
|
+
# @return<String> : the generate url
|
179
|
+
def enclosing_resource_url(*args, &block)
|
180
|
+
if _enclosing_specification
|
181
|
+
options = extract_options_from_args!(args) || {}
|
182
|
+
name_prefixes = []
|
183
|
+
name_prefixes << _enclosing_specification.name_prefix
|
184
|
+
name_prefixes << _enclosing_specification.resource_name
|
185
|
+
name_prefixes.unshift(args)
|
186
|
+
generator = UrlGenerator.new(name_prefixes, _resources_with_specs, &block)
|
187
|
+
url(generator.named_route, *[generator.resources, options].flatten)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
# generates the enclosing_resources_url cool
|
193
|
+
#
|
194
|
+
#
|
195
|
+
# lets just say we were at this url users/1/posts/1
|
196
|
+
# then via to using the enclosing_resources_url
|
197
|
+
# we would get the url of /users
|
198
|
+
# which inturn would be using :users named_route
|
199
|
+
#
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# enclosing_resources_url
|
203
|
+
# enclosing_resources_url(:pending)
|
204
|
+
# enclosing_resources_url(:pending, :my => "param")
|
205
|
+
# @param args<Var> : Pass in custom route, then params basically
|
206
|
+
#
|
207
|
+
# @return<String> : the generate url
|
208
|
+
def enclosing_resources_url(*args, &block)
|
209
|
+
if _enclosing_specification
|
210
|
+
options = extract_options_from_args!(args) || {}
|
211
|
+
name_prefixes = []
|
212
|
+
name_prefixes << _enclosing_specification.name_prefix
|
213
|
+
name_prefixes << Extlib::Inflection.pluralize(_enclosing_specification.resource_name)
|
214
|
+
name_prefixes.unshift(args)
|
215
|
+
generator = UrlGenerator.new(name_prefixes, _enclosing_resources_with_spec, &block)
|
216
|
+
url(generator.named_route, *[generator.resources, options].flatten)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# generates the new_enclosing_resource_url cool
|
221
|
+
#
|
222
|
+
#
|
223
|
+
# lets just say we were at this url users/1/posts/1
|
224
|
+
# then via to using the new_enclosing_resource_url
|
225
|
+
# we would get the url of /users/new
|
226
|
+
# which inturn would be using :new_user named_route
|
227
|
+
#
|
228
|
+
#
|
229
|
+
# @example
|
230
|
+
# new_enclosing_resource_url(:edit)
|
231
|
+
# new_enclosing_resource_url(:edit, :my => "param")
|
232
|
+
# new_enclosing_resource_url(:whatever)
|
233
|
+
# new_enclosing_resource_url(:my => "params")
|
234
|
+
# new_enclosing_resource_url(:myhome)
|
235
|
+
#
|
236
|
+
# @param args<Var> : Pass in custom route, then params basically
|
237
|
+
#
|
238
|
+
# @return<String> : the generate url
|
239
|
+
def new_enclosing_resource_url(options = {}, &block)
|
240
|
+
if _enclosing_specification
|
241
|
+
name_prefixes = []
|
242
|
+
name_prefixes << _enclosing_specification.name_prefix
|
243
|
+
name_prefixes << _enclosing_specification.resource_name
|
244
|
+
name_prefixes.unshift("new")
|
245
|
+
generator = UrlGenerator.new(name_prefixes, _enclosing_resources_with_spec, &block)
|
246
|
+
url(generator.named_route, *[generator.resources, options].flatten)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
module MerbResourceScope
|
2
|
+
module Controller
|
3
|
+
|
4
|
+
module ScopedResourceMixin
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
attr_accessor :_enclosing_scope, :_enclosing_specifications, :_resources_with_specs, :_enclosing_resources_with_spec, :_current_specification, :_enclosing_specification
|
8
|
+
attr_accessor :current_resources, :current_resource, :resource_scope
|
9
|
+
extend ScopedResourceMixin::ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def build_resource_scope(options = {})
|
15
|
+
include InstanceMethods
|
16
|
+
options.only(:build_scope, :singleton, :actions)
|
17
|
+
filter_options = options.delete(:build_scope)
|
18
|
+
add_filter(_before_filters, :_build_resource_scope, filter_options || {})
|
19
|
+
|
20
|
+
unless options[:actions] == false
|
21
|
+
actions ||= options[:singleton] ? Merb::Plugins.config[:merb_resource_scope][:singleton_actions] : Merb::Plugins.config[:merb_resource_scope][:actions]
|
22
|
+
include_actions actions, options.delete(:actions) || {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
# we only want to include actions that we want!
|
28
|
+
def include_actions(mixin, options = {})
|
29
|
+
mixin = mixin.dup
|
30
|
+
if only = options[:only]
|
31
|
+
only = Array(options[:only])
|
32
|
+
mixin.instance_methods.each {|m| mixin.send(:undef_method, m) unless only.include?(m.intern)}
|
33
|
+
elsif except = options[:exclude]
|
34
|
+
except = Array(options[:exclude])
|
35
|
+
mixin.instance_methods.each {|m| mixin.send(:undef_method, m) if except.include?(m.intern)}
|
36
|
+
end
|
37
|
+
include mixin
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Proxy class to provide a consistent API for resource_scope. passing all method calls
|
42
|
+
# to the current_scope
|
43
|
+
class ResourceScope
|
44
|
+
instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? respond_to? should should_not instance_variable_set instance_variable_get instance_eval].include?(m) }
|
45
|
+
attr_reader :controller
|
46
|
+
|
47
|
+
def initialize(controller)
|
48
|
+
@controller = controller
|
49
|
+
end
|
50
|
+
|
51
|
+
def _enclosing_resource
|
52
|
+
@_enclosing_resource ||= controller.enclosing_resource
|
53
|
+
end
|
54
|
+
|
55
|
+
def _current_specification
|
56
|
+
@_current_specification ||= controller._current_specification
|
57
|
+
end
|
58
|
+
|
59
|
+
def respond_to?(method)
|
60
|
+
super || current_scope.respond_to?(method)
|
61
|
+
end
|
62
|
+
|
63
|
+
def current_scope
|
64
|
+
@current_scope ||= _enclosing_resource ? _enclosing_resource.send(_current_specification.association_method) : _current_specification.klass
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_missing(method, *args, &block)
|
68
|
+
if current_scope.respond_to?(method)
|
69
|
+
current_scope.send(method, *args, &block)
|
70
|
+
else
|
71
|
+
super
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module InstanceMethods
|
77
|
+
|
78
|
+
# the before filter, when this before filter is run; it will build the resource_scope
|
79
|
+
def _build_resource_scope
|
80
|
+
if specifications = request.resource_specifications
|
81
|
+
@_current_specification = specifications.pop
|
82
|
+
@_enclosing_specifications = specifications
|
83
|
+
@_resources_with_specs = []
|
84
|
+
|
85
|
+
# if we dont want to load all the enclosing resource and resources
|
86
|
+
# we can specify a depth to which build the the scope from
|
87
|
+
if scope_depth = @_current_specification.scope_depth
|
88
|
+
@_enclosing_specifications.slice!(0..scope_depth-1)
|
89
|
+
end
|
90
|
+
|
91
|
+
_generate_enclosing_scope
|
92
|
+
@resource_scope ||= MerbResourceScope::Controller::ScopedResourceMixin::ResourceScope.new(self)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def enclosing_resource
|
97
|
+
return nil if @_enclosing_specifications.size == 0
|
98
|
+
if @_resources_with_specs.size == @_enclosing_specifications.size+1
|
99
|
+
@_resources_with_specs.slice(-2)[0]
|
100
|
+
else
|
101
|
+
@_resources_with_specs.last && @_resources_with_specs.last[0]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# When we find the current_resource we want its resource with its specification
|
106
|
+
# to be added; essentially so we can know what the correct enclosing resource is
|
107
|
+
# but we dont want to add it if its a new_record as its not a resource just yet
|
108
|
+
# TODO need looking AT basically!! as create and destroy actions show not hold
|
109
|
+
# the resource OR put into the resource as they are essentiall not correct
|
110
|
+
# as it should be current_resource_url(@new_resource) Basically YEAH!
|
111
|
+
|
112
|
+
# if you want to find resources in another way just override this in your
|
113
|
+
# controller;
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# def find_resources
|
117
|
+
# @resource_scope.all :conditions => {:pending => true}
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# @api overwritable
|
121
|
+
def find_resources
|
122
|
+
@resource_scope.all
|
123
|
+
end
|
124
|
+
|
125
|
+
# if you want to find a resource in another then this default way
|
126
|
+
# then you can override in the controller,
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# def find_resource
|
130
|
+
# @resource_scope.first :conditions => {:user_id => 1, :pending => false}
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# @param id<String>|<Integer> the params to use to find a resource could be permalink
|
134
|
+
#
|
135
|
+
# @api overwritable
|
136
|
+
def find_resource(id = params[@_current_specification.permalink])
|
137
|
+
if find_method = @_current_specification.find_method
|
138
|
+
find_method.is_a?(Proc) ? self.instance_eval(&find_method) : self.send(find_method)
|
139
|
+
else
|
140
|
+
@_current_specification.singleton ? @resource_scope : @resource_scope.first(:conditions => {@_current_specification.permalink => id})
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# if you have other requirments on how you would like to create a new resource
|
145
|
+
# then you can override this at the controller level, using the @resource_scope
|
146
|
+
# Also if you wish to have different attributes set you can just add them as well
|
147
|
+
# dont really want to go into it but I cannot use the proxy here as a send then a
|
148
|
+
# method call basically stops the dependent attributes from being set! strange but
|
149
|
+
# true
|
150
|
+
# @example
|
151
|
+
# def new_resource
|
152
|
+
# @resource_scope.different_new {:whatever => :egg}
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
# @api overwritable
|
156
|
+
def new_resource(attributes = (params[@_current_specification.resource_name] || {}))
|
157
|
+
if enclosing_resource
|
158
|
+
if @_current_specification.singleton
|
159
|
+
return @_current_specification.klass.new attributes.merge!(_enclosing_specification.association_method.to_sym => enclosing_resource)
|
160
|
+
end
|
161
|
+
enclosing_resource.send(@_current_specification.association_method).build attributes
|
162
|
+
else
|
163
|
+
@_current_specification.klass.new attributes
|
164
|
+
end
|
165
|
+
end
|
166
|
+
#
|
167
|
+
# we set the current_resource to have the current specification resource_name method
|
168
|
+
def current_resource=(resource)
|
169
|
+
instance_variable_set("@#{@_current_specification.resource_name}", resource)
|
170
|
+
@current_resource = resource
|
171
|
+
end
|
172
|
+
|
173
|
+
# we set the current_resource to have the current specification resource_name method
|
174
|
+
def current_resources=(resources)
|
175
|
+
instance_variable_set("@#{@_current_specification.association_method}", resources)
|
176
|
+
@current_resources = resources
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
# this is the work horse of how the enclosing_scope is built,
|
181
|
+
# it will use all the enclosing enclosing_specifications to work out what the
|
182
|
+
# enclosing scope is.
|
183
|
+
#
|
184
|
+
# On its first run it will determine whether
|
185
|
+
# it should just use the singleton method or the find_method of the specification,
|
186
|
+
# or just go with the standand User.get(params[:id])
|
187
|
+
#
|
188
|
+
# Futher iterations will just try to find the resource via method calls
|
189
|
+
# and if the key of the specification is in the params then that resource
|
190
|
+
# will also be found
|
191
|
+
#
|
192
|
+
# so for a typical url say users/1/posts/1/comments
|
193
|
+
# it would go something like this, setting the enclosing_scope as it goes
|
194
|
+
# 1. enclosing_scope = User - via enclosing_specifications.first.klass_name
|
195
|
+
# 2. enclosing_scope = User.get(params[:user_id]) - via collect_resource_via_params(@enclosing_scope, specification)
|
196
|
+
# 3. enclosing_scope = User.get(params[:user_id]).posts.get(params[:post_id]) - via collect_resource_via_params(@enclosing_scope, specification)
|
197
|
+
# 4. thats it! now our enclosing_scope is where we want it
|
198
|
+
#
|
199
|
+
# This will also handle enclosing depth witch can default to the number of
|
200
|
+
# enclosing specification but can get it from current
|
201
|
+
# specification and if its been set to say 1 then my inclosing_specifications
|
202
|
+
# will only be set to just that COOL
|
203
|
+
def _generate_enclosing_scope
|
204
|
+
@_enclosing_specifications.each_with_index do |specification, index|
|
205
|
+
if specification.singleton || specification.find_method
|
206
|
+
@_enclosing_scope = _collect_resource_via_method(@_enclosing_scope, specification)
|
207
|
+
else
|
208
|
+
@_enclosing_scope = _collect_resource_via_params(@_enclosing_scope, specification)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# collect a resource either with the specification.find_method or the association method
|
214
|
+
# we also want to store the found resource or resources along with the specification
|
215
|
+
# that was used to find them for later use; mainly in route generation
|
216
|
+
#
|
217
|
+
# @param enclosing_scope<Object>|<Objects> aka ur orm object
|
218
|
+
# @param specification<Specification>
|
219
|
+
#
|
220
|
+
# @return <Object>|<Objects> aka ur orm object
|
221
|
+
def _collect_resource_via_method(enclosing_scope, specification)
|
222
|
+
if specification.find_method
|
223
|
+
return _collect_resource_via_find_method(specification)
|
224
|
+
else
|
225
|
+
resource_or_resources = enclosing_scope.send(specification.association_method)
|
226
|
+
@_resources_with_specs << [resource_or_resources, specification]
|
227
|
+
instance_variable_set("@#{specification.association_method}", resource_or_resources)
|
228
|
+
end
|
229
|
+
resource_or_resources
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
# collect a resource either with the specification.find_method
|
234
|
+
# we also want to store the found resource or resources along with the specification
|
235
|
+
# that was used to find them for later use; mainly in route generation and to find
|
236
|
+
# enclosing resources or resource
|
237
|
+
#
|
238
|
+
# @param specification<Specification>
|
239
|
+
#
|
240
|
+
# @return <Object>|<Objects> aka ur orm object
|
241
|
+
def _collect_resource_via_find_method(specification)
|
242
|
+
resource_or_resources = if specification.find_method.is_a?(Proc)
|
243
|
+
self.instance_eval(&specification.find_method)
|
244
|
+
else
|
245
|
+
self.send(specification.find_method)
|
246
|
+
end
|
247
|
+
|
248
|
+
unless _resource_is_a_collection?(specification, resource_or_resources)
|
249
|
+
@_resources_with_specs << [resource_or_resources, specification]
|
250
|
+
instance_variable_set("@#{specification.resource_name}", resource_or_resources)
|
251
|
+
end
|
252
|
+
|
253
|
+
resource_or_resources
|
254
|
+
end
|
255
|
+
|
256
|
+
# collect a resource with the specification using the params and the resource_name
|
257
|
+
# we also want to store the found resource along with the specification
|
258
|
+
#
|
259
|
+
# @param enclosing_scope<Object>|<Objects> aka ur orm object
|
260
|
+
# @param specification<Specification>
|
261
|
+
#
|
262
|
+
# @return <Object>|<Objects>|enclosing_scope aka ur orm object
|
263
|
+
def _collect_resource_via_params(enclosing_scope, specification)
|
264
|
+
if !params.keys.include?(specification.foreign_key.to_s)
|
265
|
+
return enclosing_scope
|
266
|
+
else
|
267
|
+
|
268
|
+
resource = if enclosing_scope
|
269
|
+
enclosing_scope.send(specification.association_method).first(:conditions => {specification.permalink => params[specification.foreign_key]})
|
270
|
+
else
|
271
|
+
specification.klass.first(:conditions => {specification.permalink => params[specification.foreign_key]})
|
272
|
+
end
|
273
|
+
|
274
|
+
@_resources_with_specs << [resource, specification]
|
275
|
+
instance_variable_set("@#{specification.resource_name}", resource)
|
276
|
+
resource
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# This is by default set to use datamapper
|
281
|
+
# You can override this at the application controller level
|
282
|
+
# then it will use this for all controllers; or if you have
|
283
|
+
# specific requirement on a controller;then just change it in there
|
284
|
+
#
|
285
|
+
# @api overwritable
|
286
|
+
def _resource_is_a_collection?(specification, resource_or_resources)
|
287
|
+
resource_or_resources.kind_of?(::DataMapper::Collection)
|
288
|
+
end
|
289
|
+
|
290
|
+
def _enclosing_resources_with_specs
|
291
|
+
@_resources_with_specs.slice(0..-2)
|
292
|
+
end
|
293
|
+
|
294
|
+
def _enclosing_specification
|
295
|
+
@_enclosing_specifications.last
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|