resourcelogic 0.0.11
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/.gitignore +7 -0
- data/CHANGELOG.rdoc +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +167 -0
- data/Rakefile +49 -0
- data/VERSION.yml +4 -0
- data/init.rb +1 -0
- data/lib/resourcelogic.rb +16 -0
- data/lib/resourcelogic/accessors.rb +53 -0
- data/lib/resourcelogic/action_options.rb +40 -0
- data/lib/resourcelogic/actions.rb +174 -0
- data/lib/resourcelogic/aliases.rb +134 -0
- data/lib/resourcelogic/base.rb +130 -0
- data/lib/resourcelogic/child.rb +55 -0
- data/lib/resourcelogic/context.rb +51 -0
- data/lib/resourcelogic/context_options.rb +5 -0
- data/lib/resourcelogic/failable_action_options.rb +25 -0
- data/lib/resourcelogic/parent.rb +142 -0
- data/lib/resourcelogic/response_collector.rb +32 -0
- data/lib/resourcelogic/scope.rb +36 -0
- data/lib/resourcelogic/self.rb +148 -0
- data/lib/resourcelogic/sibling.rb +20 -0
- data/lib/resourcelogic/singleton.rb +55 -0
- data/lib/resourcelogic/sub_views.rb +98 -0
- data/lib/resourcelogic/urligence.rb +128 -0
- data/resourcelogic.gemspec +62 -0
- metadata +90 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Resourcelogic
|
2
|
+
class ResponseCollector
|
3
|
+
|
4
|
+
attr_reader :responses
|
5
|
+
|
6
|
+
delegate :clear, :to => :responses
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@responses = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method_name, &block)
|
13
|
+
existing = self[method_name]
|
14
|
+
if existing
|
15
|
+
existing[0] = method_name
|
16
|
+
existing[1] = block || nil
|
17
|
+
else
|
18
|
+
@responses << [method_name, block || nil]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](symbol)
|
23
|
+
@responses.find { |method, block| method == symbol }
|
24
|
+
end
|
25
|
+
|
26
|
+
def dup
|
27
|
+
returning ResponseCollector.new do |duplicate|
|
28
|
+
duplicate.instance_variable_set(:@responses, responses.dup)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Resourcelogic
|
2
|
+
module Scope
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
add_acts_as_resource_module(Methods)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Methods
|
10
|
+
def self.included(klass)
|
11
|
+
klass.class_eval do
|
12
|
+
attr_accessor :scoping, :scoping_parent
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def scope
|
18
|
+
return @scope if defined?(@scope)
|
19
|
+
|
20
|
+
if parent?
|
21
|
+
@scope = parent_object.send(parent_scope_name)
|
22
|
+
else
|
23
|
+
@scope = model
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def parent_scope
|
28
|
+
parent_model
|
29
|
+
end
|
30
|
+
|
31
|
+
def parent_scope_name
|
32
|
+
@parent_scope_name ||= model_name.to_s.pluralize
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Resourcelogic
|
2
|
+
module Self
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
extend Config
|
6
|
+
add_acts_as_resource_module(UrlParts)
|
7
|
+
add_acts_as_resource_module(Reflection)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Config
|
12
|
+
def model_name(value = nil)
|
13
|
+
rw_config(:model_name, value, model_name_from_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def object_name(value = nil)
|
17
|
+
rw_config(:object_name, value, model_name_from_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def model_name_from_name
|
22
|
+
name.underscore.split("/").first.gsub(/_controller/, "").singularize.underscore.to_sym
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module UrlParts
|
27
|
+
private
|
28
|
+
# Used internally to provide the options to smart_url from Urligence.
|
29
|
+
#
|
30
|
+
def collection_url_parts(action = nil, url_params = {})
|
31
|
+
[action] + contexts_url_parts + [route_name.to_s.pluralize.to_sym, url_params]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Used internally to provide the options to smart_url from Urligence.
|
35
|
+
#
|
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
|
54
|
+
else
|
55
|
+
obj = object_or_params.first
|
56
|
+
end
|
57
|
+
[obj, url_params]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module Reflection
|
62
|
+
def self.included(klass)
|
63
|
+
klass.helper_method :model_name, :collection, :object, :path_name, :route_name
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
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
|
77
|
+
end
|
78
|
+
|
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
|
89
|
+
end
|
90
|
+
|
91
|
+
# Convenience method for the class level model_name method
|
92
|
+
def model_name
|
93
|
+
@model_name ||= self.class.model_name.to_sym
|
94
|
+
end
|
95
|
+
|
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
|
103
|
+
end
|
104
|
+
|
105
|
+
# The collection for the resource.
|
106
|
+
def collection # :doc:
|
107
|
+
@collection ||= scope.all
|
108
|
+
end
|
109
|
+
|
110
|
+
# The current paremter than contains the object identifier.
|
111
|
+
def id # :doc:
|
112
|
+
params[:id]
|
113
|
+
end
|
114
|
+
|
115
|
+
def id?
|
116
|
+
params.key?(:id)
|
117
|
+
end
|
118
|
+
|
119
|
+
# The parameter hash that contains the object attributes.
|
120
|
+
def attributes
|
121
|
+
params[object_name]
|
122
|
+
end
|
123
|
+
|
124
|
+
def attributes?
|
125
|
+
params.key?(object_name)
|
126
|
+
end
|
127
|
+
|
128
|
+
# The current member being used. If no param is present, it will look for
|
129
|
+
# a current_#{object_name} method. For example, if you have a UsersController
|
130
|
+
# and its a singleton controller, meaning no identifier is needed, this will
|
131
|
+
# look for a current_user method. If this is not the behavioru you want, simply
|
132
|
+
# overwrite this method.
|
133
|
+
def object # :doc:
|
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)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Resourcelogic
|
2
|
+
module Sibling
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
add_acts_as_resource_module(Urls)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Urls
|
10
|
+
private
|
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]
|
13
|
+
end
|
14
|
+
|
15
|
+
def sibling_collection_url_parts(action = nil, sibling_name = nil, url_params = {})
|
16
|
+
[action] + contexts_url_parts + [sibling_name, url_params]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Nested and Polymorphic Resource Helpers
|
2
|
+
#
|
3
|
+
module Resourcelogic
|
4
|
+
module Singleton
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
add_acts_as_resource_module(Methods)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Methods
|
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
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_object
|
29
|
+
if singleton? && parent?
|
30
|
+
scope.send("build_#{model_name}")
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def scope
|
37
|
+
if singleton? && parent?
|
38
|
+
parent_object
|
39
|
+
else
|
40
|
+
super
|
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
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Nested and Polymorphic Resource Helpers
|
2
|
+
#
|
3
|
+
module Resourcelogic
|
4
|
+
module SubViews
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
extend Config
|
8
|
+
add_acts_as_resource_module(Methods)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Config
|
13
|
+
def namespace_views_by_context(value = nil)
|
14
|
+
rw_config(:namespace_views_by_context, value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def namespace_views_by_context?
|
18
|
+
!namespace_views_by_context.blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
def namespace_views_by_route_alias(value = nil)
|
22
|
+
rw_config(:namespace_views_by_route_alias, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def namespace_views_by_route_alias?
|
26
|
+
!namespace_views_by_route_alias.blank?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Methods
|
31
|
+
def self.included(klass)
|
32
|
+
klass.helper_method :namespace_views_by_route_alias, :namespace_views_by_route_alias?,
|
33
|
+
:namespace_views_by_context, :namespace_views_by_context?, :sub_template_name
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def namespace_views_by_context?
|
38
|
+
self.class.namespace_views_by_context?
|
39
|
+
end
|
40
|
+
|
41
|
+
def namespace_views_by_context
|
42
|
+
self.class.namespace_views_by_context
|
43
|
+
end
|
44
|
+
|
45
|
+
def namespace_views_by_route_alias?
|
46
|
+
self.class.namespace_views_by_route_alias? && route_name != model_name
|
47
|
+
end
|
48
|
+
|
49
|
+
def namespace_views_by_route_alias
|
50
|
+
self.class.namespace_views_by_route_alias
|
51
|
+
end
|
52
|
+
|
53
|
+
def sub_template_name(name)
|
54
|
+
path_parts = [controller_name]
|
55
|
+
|
56
|
+
if namespace_views_by_context?
|
57
|
+
context_folder_name = namespace_views_by_context.is_a?(Hash) && namespace_views_by_context.key?(context) ?
|
58
|
+
namespace_views_by_context[context] : context
|
59
|
+
path_parts << (context_folder_name || "root")
|
60
|
+
end
|
61
|
+
|
62
|
+
if namespace_views_by_route_alias?
|
63
|
+
route_alias_folder_name = namespace_views_by_route_alias.is_a?(Hash) && namespace_views_by_route_alias.key?(route_name) ?
|
64
|
+
namespace_views_by_route_alias[route_name] : route_name
|
65
|
+
path_parts << (route_alias_folder_name).to_s.pluralize
|
66
|
+
end
|
67
|
+
|
68
|
+
path_parts << name
|
69
|
+
path_parts.join("/")
|
70
|
+
end
|
71
|
+
|
72
|
+
def default_template_name(action_name = self.action_name)
|
73
|
+
if namespace_views_by_context? || namespace_views_by_route_alias?
|
74
|
+
sub_template_name(action_name)
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Partials
|
82
|
+
def _pick_partial_template(partial_path)
|
83
|
+
if respond_to?(:namespace_views_by_context?) && respond_to?(:namespace_views_by_route_alias?) &&
|
84
|
+
(namespace_views_by_context? || namespace_views_by_route_alias?) && !partial_path.include?("/")
|
85
|
+
partial_path = sub_template_name(partial_path)
|
86
|
+
end
|
87
|
+
super
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
module ActionView
|
95
|
+
class Base
|
96
|
+
include Resourcelogic::SubViews::Partials
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module Resourcelogic
|
2
|
+
module Urligence
|
3
|
+
def self.included(klass)
|
4
|
+
klass.helper_method :smart_url, :smart_path, :hash_for_smart_url, :hash_for_smart_path, :method_missing
|
5
|
+
end
|
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)
|
12
|
+
end
|
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
|
+
end
|
20
|
+
|
21
|
+
def hash_for_smart_url(*url_parts)
|
22
|
+
urligence(*url_parts.unshift(:hash_for).push(:url).push({:type => :hash}))
|
23
|
+
end
|
24
|
+
|
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
|
37
|
+
else
|
38
|
+
url_params = method_arguments.extract_options!
|
39
|
+
params = {}
|
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)
|
44
|
+
params.merge!((obj.is_a?(Array)) ? {key => obj[1].to_param} : {key => obj.to_param})
|
45
|
+
end
|
46
|
+
|
47
|
+
params.merge!(url_params)
|
48
|
+
send method_name_fragments.join("_"), params
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# The point of this method is to replace any object if a url param is passed. For example:
|
53
|
+
#
|
54
|
+
# [:admin, [:user, user_object], {:user_id => 4}]
|
55
|
+
#
|
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.
|
58
|
+
#
|
59
|
+
# This also pluralizes path names if the obj is nil. Example:
|
60
|
+
#
|
61
|
+
# [:user, nil]
|
62
|
+
#
|
63
|
+
# No param was passed to replace the nil object as in the example above, so it should be:
|
64
|
+
#
|
65
|
+
# :users
|
66
|
+
#
|
67
|
+
# This is needed for contextual development. Take modifying a resource from another, but a
|
68
|
+
# parent resource is not needed. For example:
|
69
|
+
#
|
70
|
+
# payments/credit_cards
|
71
|
+
#
|
72
|
+
# You can manage and select a credit card from the credit cards resource but a payment object
|
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|
|
80
|
+
if !object.is_a?(Array)
|
81
|
+
# It's not an array, just copy it over
|
82
|
+
new_url_parts << object
|
83
|
+
else
|
84
|
+
# Let's try to
|
85
|
+
klass = object.first.to_s.camelize.constantize rescue nil
|
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])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
new_url_parts
|
98
|
+
end
|
99
|
+
|
100
|
+
def extract_method_name_fragments(url_parts)
|
101
|
+
fragments = url_parts.collect do |obj|
|
102
|
+
if obj.is_a?(Symbol)
|
103
|
+
obj
|
104
|
+
elsif obj.is_a?(Array)
|
105
|
+
obj.first
|
106
|
+
elsif !obj.is_a?(Hash)
|
107
|
+
obj.class.name.underscore.to_sym
|
108
|
+
end
|
109
|
+
end
|
110
|
+
fragments.compact
|
111
|
+
end
|
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
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|