resourcelogic 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,181 +3,145 @@ module Resourcelogic
3
3
  def self.included(klass)
4
4
  klass.class_eval do
5
5
  extend Config
6
- add_acts_as_resource_module(Urls)
6
+ add_acts_as_resource_module(UrlParts)
7
7
  add_acts_as_resource_module(Reflection)
8
8
  end
9
9
  end
10
10
 
11
11
  module Config
12
12
  def model_name(value = nil)
13
- config(:model_name, value, controller_name.singularize.underscore)
13
+ rw_config(:model_name, value, model_name_from_name)
14
14
  end
15
15
 
16
16
  def object_name(value = nil)
17
- config(:object_name, value, controller_name.singularize.underscore)
18
- end
19
-
20
- def route_name(value = nil)
21
- config(:route_name, value, controller_name.singularize.underscore)
22
- end
23
- end
24
-
25
- module Urls
26
- def self.included(klass)
27
- klass.helper_method :object_url, :object_path, :hash_for_object_url, :hash_for_object_path, :edit_object_url, :edit_object_path, :hash_for_edit_object_url,
28
- :hash_for_edit_object_path, :new_object_url, :new_object_path, :hash_for_new_object_url, :hash_for_new_object_path, :collection_url, :collection_path,
29
- :hash_for_collection_url, :hash_for_collection_path
17
+ rw_config(:object_name, value, model_name_from_name)
30
18
  end
31
19
 
32
20
  private
33
- def object_url(*objects) # :doc:
34
- smart_url *object_url_options(nil, objects)
35
- end
36
-
37
- def object_path(*objects)
38
- smart_path *object_url_options(nil, objects)
39
- end
40
-
41
- def hash_for_object_url(*objects)
42
- hash_for_smart_url *object_url_options(nil, objects)
43
- end
44
-
45
- def hash_for_object_path(*objects)
46
- hash_for_smart_path *object_url_options(nil, objects)
47
- end
48
-
49
- def edit_object_url(*objects)
50
- smart_url *object_url_options(:edit, objects)
51
- end
52
-
53
- def edit_object_path(*objects)
54
- smart_path *object_url_options(:edit, objects)
55
- end
56
-
57
- def hash_for_edit_object_url(*objects)
58
- hash_for_smart_url *object_url_options(:edit, objects)
59
- end
60
-
61
- def hash_for_edit_object_path(*objects)
62
- hash_for_smart_path *object_url_options(:edit, objects)
63
- end
64
-
65
- def new_object_url(url_params = {})
66
- smart_url *new_object_url_options(url_params)
67
- end
68
-
69
- def new_object_path(url_params = {})
70
- smart_path *new_object_url_options(url_params)
71
- end
72
-
73
- def hash_for_new_object_url(url_params = {})
74
- hash_for_smart_url *new_object_url_options(url_params)
75
- end
76
-
77
- def hash_for_new_object_path(url_params = {})
78
- hash_for_smart_path *new_object_url_options(url_params)
79
- end
80
-
81
- def collection_url(url_params = {})
82
- smart_url *collection_url_options(url_params)
83
- end
84
-
85
- def collection_path(url_params = {})
86
- smart_path *collection_url_options(url_params)
87
- end
88
-
89
- def hash_for_collection_url(url_params = {})
90
- hash_for_smart_url *collection_url_options(url_params)
91
- end
92
-
93
- def hash_for_collection_path(url_params = {})
94
- hash_for_smart_path *collection_url_options(url_params)
21
+ def model_name_from_name
22
+ name.underscore.split("/").first.gsub(/_controller/, "").singularize.underscore.to_sym
95
23
  end
24
+ end
96
25
 
26
+ module UrlParts
27
+ private
97
28
  # Used internally to provide the options to smart_url from Urligence.
98
29
  #
99
- def collection_url_options(url_params = {})
100
- namespaces + [parent_url_options, route_name.to_s.pluralize.to_sym, url_params]
30
+ def collection_url_parts(action = nil, url_params = {})
31
+ [action] + contexts_url_parts + [route_name.to_s.pluralize.to_sym, url_params]
101
32
  end
102
-
33
+
103
34
  # Used internally to provide the options to smart_url from Urligence.
104
35
  #
105
- def object_url_options(action_prefix = nil, alternate_object_or_params = nil)
106
- alternate_object = nil
107
- url_params = nil
108
- case alternate_object_or_params
109
- when Array
110
- url_params = alternate_object_or_params.last if alternate_object_or_params.last.is_a?(Hash)
111
- alternate_object = alternate_object_or_params.first
112
- when Hash
113
- url_params = alternate_object_or_params
36
+ def object_url_parts(action = nil, *alternate_object_or_params)
37
+ alternate_object, url_params = identify_object_or_params(alternate_object_or_params)
38
+ url_object = alternate_object
39
+ url_object = object if url_object.nil? && object && !object.new_record?
40
+ object_parts = url_object ?
41
+ [route_name, url_object] :
42
+ (action == :new || singleton? ? route_name : route_name.to_s.pluralize.to_sym)
43
+ [action] + contexts_url_parts + [object_parts, url_params]
44
+ end
45
+
46
+ def identify_object_or_params(object_or_params)
47
+ obj = nil
48
+ url_params = {}
49
+ if object_or_params.size > 1
50
+ url_params = object_or_params.last if object_or_params.last.is_a?(Hash)
51
+ obj = object_or_params.first
52
+ elsif object_or_params.first.is_a?(Hash)
53
+ url_params = object_or_params.first
114
54
  else
115
- alternate_object = alternate_object_or_params
55
+ obj = object_or_params.first
116
56
  end
117
-
118
- [action_prefix] + namespaces + [parent_url_options, [route_name.to_sym, alternate_object || (param ? object : nil)], url_params]
119
- end
120
-
121
- # Used internally to provide the options to smart_url from Urligence.
122
- #
123
- def new_object_url_options(url_params = {})
124
- [:new] + namespaces + [parent_url_options, route_name.to_sym, url_params]
57
+ [obj, url_params]
125
58
  end
126
59
  end
127
60
 
128
61
  module Reflection
129
62
  def self.included(klass)
130
- klass.helper_method :model_name, :collection, :object
63
+ klass.helper_method :model_name, :collection, :object, :path_name, :route_name
131
64
  end
132
65
 
133
66
  private
134
- # Convenience method for the class level model_name method
135
- def model_name
136
- self.class.model_name
67
+ def path_name
68
+ return @path_name if defined?(@path_name)
69
+ path_parts = request.path.split("/")
70
+ path_parts.reverse.each do |path_part|
71
+ next if path_part.blank?
72
+ if model_name_from_path_part(path_part) == model_name
73
+ return @path_name = path_part.to_sym
74
+ end
75
+ end
76
+ @path_name = nil
137
77
  end
138
78
 
139
- # Convenience method for the class level object_name method
140
- def object_name
141
- self.class.object_name
79
+ def route_name
80
+ return @route_name if defined?(@route_name)
81
+ path_parts = request.path.split("/")
82
+ path_parts.reverse.each do |path_part|
83
+ next if path_part.blank?
84
+ if model_name_from_path_part(path_part) == model_name
85
+ return @route_name = route_name_from_path_part(path_part)
86
+ end
87
+ end
88
+ @route_name = model_name
142
89
  end
143
90
 
144
- # Convenience method for the class level route_name method
145
- def route_name
146
- self.class.route_name
91
+ # Convenience method for the class level model_name method
92
+ def model_name
93
+ @model_name ||= self.class.model_name.to_sym
147
94
  end
148
95
 
149
- # The current model for the resource.
150
- def model # :doc:
151
- model_name.to_s.camelize.constantize
96
+ def model
97
+ @model ||= model_name.to_s.camelize.constantize
98
+ end
99
+
100
+ # Convenience method for the class level object_name method
101
+ def object_name
102
+ @object_name ||= self.class.object_name.to_sym
152
103
  end
153
104
 
154
105
  # The collection for the resource.
155
106
  def collection # :doc:
156
- end_of_association_chain.all
107
+ @collection ||= scope.all
157
108
  end
158
109
 
159
110
  # The current paremter than contains the object identifier.
160
- def param # :doc:
161
- params[:id]
111
+ def id # :doc:
112
+ params[:id]
113
+ end
114
+
115
+ def id?
116
+ params.key?(:id)
162
117
  end
163
118
 
164
119
  # The parameter hash that contains the object attributes.
165
- def object_params
166
- params["#{object_name}"]
120
+ def attributes
121
+ params[object_name]
167
122
  end
168
-
123
+
124
+ def attributes?
125
+ params.key?(object_name)
126
+ end
127
+
169
128
  # The current member being used. If no param is present, it will look for
170
129
  # a current_#{object_name} method. For example, if you have a UsersController
171
130
  # and its a singleton controller, meaning no identifier is needed, this will
172
131
  # look for a current_user method. If this is not the behavioru you want, simply
173
132
  # overwrite this method.
174
133
  def object # :doc:
175
- return @object if defined?(@object)
176
- if param.nil? && respond_to?("current_#{object_name}", true)
177
- @object = send("current_#{object_name}")
178
- else
179
- @object = end_of_association_chain.find(param)
180
- end
134
+ @object ||= id? ? find_object : build_object
135
+ end
136
+
137
+ def build_object
138
+ obj = scope.respond_to?(:build) ? scope.build : scope.new
139
+ obj.attributes = attributes
140
+ obj
141
+ end
142
+
143
+ def find_object
144
+ scope.find(id)
181
145
  end
182
146
  end
183
147
  end
@@ -7,55 +7,13 @@ module Resourcelogic
7
7
  end
8
8
 
9
9
  module Urls
10
- def self.included(klass)
11
- klass.helper_method :new_sibling_url, :new_sibling_path, :edit_sibling_url, :edit_sibling_path,
12
- :sibling_url, :sibling_path, :sibling_collection_url, :sibling_collection_path
13
- end
14
-
15
10
  private
16
- def new_sibling_url(sibling_name, url_params = {})
17
- smart_url *new_sibling_url_options(sibling_name, url_params)
18
- end
19
-
20
- def new_sibling_path(sibling_name, url_params = {})
21
- smart_path *new_sibling_url_options(sibling_name, url_params)
22
- end
23
-
24
- def edit_sibling_url(sibling, url_params = {})
25
- smart_url *sibling_url_options(sibling, :edit, url_params)
26
- end
27
-
28
- def edit_sibling_path(sibling, url_params = {})
29
- smart_path *sibling_url_options(sibling, :edit, url_params)
30
- end
31
-
32
- def sibling_url(sibling, url_params = {})
33
- smart_url *sibling_url_options(sibling, url_params)
34
- end
35
-
36
- def sibling_path(sibling, url_params = {})
37
- smart_path *sibling_url_options(sibling, url_params)
38
- end
39
-
40
- def sibling_collection_url(sibling_name, url_params = {})
41
- smart_url *sibling_collection_url_options(sibling_name, url_params)
42
- end
43
-
44
- def sibling_collection_path(sibling_name, url_params = {})
45
- smart_path *sibling_collection_url_options(sibling_name, url_params)
46
- end
47
-
48
- def new_sibling_url_options(sibling_name, url_params = {})
49
- [:new] + namespaces + [parent_url_options, sibling_name, url_params]
50
- end
51
-
52
- def sibling_url_options(sibling, *action_prefix_or_params)
53
- action_prefix, url_params = identify_action_prefix_or_params(action_prefix_or_params)
54
- [action_prefix] + namespaces + [parent_url_options, [sibling.class.name.underscore.to_sym, sibling], url_params]
11
+ def sibling_url_parts(action = nil, sibling = nil, url_params = {})
12
+ [action] + contexts_url_parts + [sibling.is_a?(Symbol) ? sibling : [sibling.class.name.underscore.to_sym, sibling], url_params]
55
13
  end
56
14
 
57
- def sibling_collection_url_options(sibling_name, url_params = {})
58
- namespaces + [parent_url_options, sibling_name, url_params]
15
+ def sibling_collection_url_parts(action = nil, sibling_name = nil, url_params = {})
16
+ [action] + contexts_url_parts + [sibling_name, url_params]
59
17
  end
60
18
  end
61
19
  end
@@ -4,70 +4,52 @@ module Resourcelogic
4
4
  module Singleton
5
5
  def self.included(klass)
6
6
  klass.class_eval do
7
- extend Config
8
- end
9
- end
10
-
11
- module Config
12
- def singleton(value = nil)
13
- include Methods if value == true
14
- config(:singleton, value)
15
- end
16
-
17
- def singleton?
18
- singleton == true
7
+ add_acts_as_resource_module(Methods)
19
8
  end
20
9
  end
21
10
 
22
11
  module Methods
23
- def self.included(klass)
24
- klass.class_eval do
25
- methods_to_undefine = [:index, :collection, :load_collection, :collection_url,
26
- :collection_path, :hash_for_collection_url, :hash_for_collection_path]
27
- methods_to_undefine.each { |method| undef_method(method) if method_defined?(method) }
12
+ def object
13
+ return @object if defined?(@object)
14
+
15
+ if singleton?
16
+ if !parent? && respond_to?("current_#{model_name}", true)
17
+ @object = send("current_#{model_name}")
18
+ elsif parent? && parent_object.send(model_name)
19
+ @object = parent_object.send(model_name)
20
+ else
21
+ super
22
+ end
23
+ else
24
+ super
28
25
  end
29
26
  end
30
27
 
31
- private
32
- # Used to fetch the current object in a singleton controller.
33
- #
34
- # By defult this method is able to fetch the current object for resources nested with the :has_one association only. (i.e. /users/1/image # => @user.image)
35
- # In other cases you should override this method and provide your custom code to fetch a singleton resource object, like using a session hash.
36
- #
37
- # class AccountsController < ResourceController::Singleton
38
- # private
39
- # def object
40
- # @object ||= Account.find(session[:account_id])
41
- # end
42
- # end
43
- #
44
- def object
45
- @object ||= parent? ? end_of_association_chain : nil
46
- end
47
-
48
- # Returns the :has_one association proxy of the parent. (i.e. /users/1/image # => @user.image)
49
- #
50
- def parent_association
51
- @parent_association ||= parent_object.send(model_name.to_sym)
52
- end
53
-
54
- # Used internally to provide the options to smart_url in a singleton controller.
55
- #
56
- def object_url_options(action_prefix = nil, alternate_object = nil)
57
- [action_prefix] + namespaces + [parent_url_options, route_name.to_sym]
58
- end
59
-
60
- # Builds the object, but doesn't save it, during the new, and create action.
61
- #
62
- def build_object
63
- @object ||= singleton_build_object_base.send parent? ? "build_#{model_name}".to_sym : :new, object_params
28
+ def build_object
29
+ if singleton? && parent?
30
+ scope.send("build_#{model_name}")
31
+ else
32
+ super
64
33
  end
65
-
66
- # Singleton controllers don't build off of association proxy, so we can't use end_of_association_chain here
67
- #
68
- def singleton_build_object_base
69
- parent? ? parent_object : model
34
+ end
35
+
36
+ def scope
37
+ if singleton? && parent?
38
+ parent_object
39
+ else
40
+ super
70
41
  end
42
+ end
43
+
44
+ # Route alises can only be used for singleton, like account => user. Otherwise the urligence wont work because there is no account model.
45
+ def object_url_parts(action = nil, *alternate_object_or_params)
46
+ singleton? ? ([action] + contexts_url_parts + [route_name]) : super
47
+ end
48
+
49
+ # Override me with true to make singleton
50
+ def singleton?
51
+ false
52
+ end
71
53
  end
72
54
  end
73
55
  end
@@ -1,57 +1,60 @@
1
1
  module Resourcelogic
2
2
  module Urligence
3
- def smart_url(*objects)
4
- url_params = objects.extract_options!
5
- objects.push(:url)
6
- objects.push(url_params)
7
- urligence(*objects)
3
+ def self.included(klass)
4
+ klass.helper_method :smart_url, :smart_path, :hash_for_smart_url, :hash_for_smart_path, :method_missing
8
5
  end
9
-
10
- def smart_path(*objects)
11
- url_params = objects.extract_options!
12
- objects.push(:path)
13
- objects.push(url_params)
14
- urligence(*objects)
6
+
7
+ def smart_url(*url_parts)
8
+ url_params = url_parts.extract_options!
9
+ url_parts.push(:url)
10
+ url_parts.push(url_params)
11
+ urligence(*url_parts)
15
12
  end
16
-
17
- def hash_for_smart_url(*objects)
18
- urligence(*objects.unshift(:hash_for).push(:url).push({:type => :hash}))
13
+
14
+ def smart_path(*url_parts)
15
+ url_params = url_parts.extract_options!
16
+ url_parts.push(:path)
17
+ url_parts.push(url_params)
18
+ urligence(*url_parts)
19
19
  end
20
-
21
- def hash_for_smart_path(*objects)
22
- urligence(*objects.unshift(:hash_for).push(:path).push({:type => :hash}))
20
+
21
+ def hash_for_smart_url(*url_parts)
22
+ urligence(*url_parts.unshift(:hash_for).push(:url).push({:type => :hash}))
23
23
  end
24
-
25
- def urligence(*objects)
26
- objects = cleanup_url_objects(objects)
27
- url_fragments = extract_url_fragments(objects)
28
- url_objects = extract_url_objects(objects)
29
24
 
30
- begin
31
- if objects.first != :hash_for
32
- send url_fragments.join("_"), *url_objects
25
+ def hash_for_smart_path(*url_parts)
26
+ urligence(*url_parts.unshift(:hash_for).push(:path).push({:type => :hash}))
27
+ end
28
+
29
+ private
30
+ def urligence(*url_parts)
31
+ url_parts = cleanup_url_parts(url_parts)
32
+ method_name_fragments = extract_method_name_fragments(url_parts)
33
+ method_arguments = extract_method_arguments(url_parts)
34
+
35
+ if url_parts.first != :hash_for
36
+ send method_name_fragments.join("_"), *method_arguments
33
37
  else
34
- url_params = url_objects.extract_options!
38
+ url_params = method_arguments.extract_options!
35
39
  params = {}
36
- url_objects.each_with_index do |obj, i|
37
- key = i == (url_objects.size - 1) ? :id : (obj.is_a?(Array) ? "#{obj.first}_id".to_sym : "#{obj.class.name.underscore}_id".to_sym)
40
+ method_arguments.each_with_index do |obj, i|
41
+ key = i == (method_arguments.size - 1) ?
42
+ :id :
43
+ (obj.is_a?(Array) ? "#{obj.first}_id".to_sym : "#{obj.class.name.underscore}_id".to_sym)
38
44
  params.merge!((obj.is_a?(Array)) ? {key => obj[1].to_param} : {key => obj.to_param})
39
45
  end
40
-
46
+
41
47
  params.merge!(url_params)
42
- send url_fragments.join("_"), params
48
+ send method_name_fragments.join("_"), params
43
49
  end
44
- rescue NoMethodError
45
- raise objects.inspect
46
50
  end
47
- end
48
-
49
- private
51
+
50
52
  # The point of this method is to replace any object if a url param is passed. For example:
51
53
  #
52
- # [:user, user_object], {:user_id => 4}
54
+ # [:admin, [:user, user_object], {:user_id => 4}]
53
55
  #
54
- # The "user_object" should be replaced by user with id 4.
56
+ # The "user_object" should be replaced by user with id 4, since we are explicitly saying we want to use User.find(4).
57
+ # The last part is the url_params.
55
58
  #
56
59
  # This also pluralizes path names if the obj is nil. Example:
57
60
  #
@@ -67,31 +70,35 @@ module Resourcelogic
67
70
  # payments/credit_cards
68
71
  #
69
72
  # You can manage and select a credit card from the credit cards resource but a payment object
70
- # is not needed.
71
- def cleanup_url_objects(objects)
72
- objects = objects.compact
73
- url_params = objects.last.is_a?(Hash) ? objects.last : {}
74
- non_symbol_object_total = objects.select { |object| !object.is_a?(Symbol) }.size - 1
75
- non_symbol_object_count = 0
76
- new_objects = []
77
- objects.each do |object|
78
- non_symbol_object_count += 1 if !object.is_a?(Symbol)
73
+ # is not needed. In a sense you are just creating a "payments" context.
74
+ def cleanup_url_parts(url_parts)
75
+ url_parts = url_parts.compact
76
+ url_params = url_parts.last.is_a?(Hash) ? url_parts.last : {}
77
+ new_url_parts = []
78
+
79
+ url_parts.each do |object|
79
80
  if !object.is_a?(Array)
80
- new_objects << object
81
+ # It's not an array, just copy it over
82
+ new_url_parts << object
81
83
  else
84
+ # Let's try to
82
85
  klass = object.first.to_s.camelize.constantize rescue nil
83
- klass_name = klass ? klass.name.underscore : nil
84
- key = (non_symbol_object_count == non_symbol_object_total) ? :id : "#{object.first}_id".to_sym
85
- obj = (url_params.key?(key) ? ((!klass && url_params[key]) || (url_params[key] && klass.find(url_params.delete(key)))) : object[1])
86
- new_objects << (obj.nil? ? object.first.to_s.pluralize.to_sym : [object.first, obj])
86
+ #klass_name = klass ? klass.name.underscore : nil
87
+ params_key = "#{object.first}_id".to_sym
88
+ obj = if url_params.key?(params_key)
89
+ (!klass && url_params[params_key]) || (url_params[params_key] && klass.find(url_params.delete(params_key)))
90
+ else
91
+ object[1]
92
+ end
93
+ new_url_parts << [object.first, obj]
94
+ #new_url_parts << (obj.nil? ? object.first.to_s.pluralize.to_sym : [object.first, obj])
87
95
  end
88
96
  end
89
- new_objects
97
+ new_url_parts
90
98
  end
91
-
92
-
93
- def extract_url_fragments(objects)
94
- fragments = objects.collect do |obj|
99
+
100
+ def extract_method_name_fragments(url_parts)
101
+ fragments = url_parts.collect do |obj|
95
102
  if obj.is_a?(Symbol)
96
103
  obj
97
104
  elsif obj.is_a?(Array)
@@ -102,9 +109,20 @@ module Resourcelogic
102
109
  end
103
110
  fragments.compact
104
111
  end
105
-
106
- def extract_url_objects(objects)
107
- objects.flatten.select { |obj| !obj.is_a?(Symbol) }
112
+
113
+ def extract_method_arguments(url_parts)
114
+ url_parts.flatten.select { |obj| !obj.is_a?(Symbol) }
115
+ end
116
+
117
+ def method_missing(method, *args, &block)
118
+ if method.to_s =~ /^((.*)_)?(child_collection|parent_collection|sibling|sibling_collection)_(path|url)$/ || method.to_s =~ /^((.*)_)?(child|collection|object|parent)_(path|url)$/
119
+ action = $2.blank? ? nil : $2.to_sym
120
+ target = $3
121
+ url_type = $4
122
+ send("smart_#{url_type}", *send("#{target}_url_parts", action, *args))
123
+ else
124
+ super
125
+ end
108
126
  end
109
127
  end
110
128
  end