resourcelogic 0.9.0 → 0.10.0

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.
@@ -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