sinatra_resource 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/examples/datacatalog/lib/resource.rb +2 -2
- data/examples/datacatalog/lib/roles.rb +1 -1
- data/examples/datacatalog/resources/categories_sources.rb +43 -0
- data/examples/datacatalog/test/helpers/lib/request_helpers.rb +10 -2
- data/examples/datacatalog/test/helpers/resource_test_helper.rb +1 -1
- data/examples/datacatalog/test/helpers/shared/api_keys.rb +4 -4
- data/examples/datacatalog/test/helpers/shared/model_counts.rb +81 -0
- data/examples/datacatalog/test/helpers/shared/status_codes.rb +7 -3
- data/examples/datacatalog/test/helpers/test_helper.rb +2 -9
- data/examples/datacatalog/test/resources/categories/categories_delete_test.rb +5 -17
- data/examples/datacatalog/test/resources/categories/categories_get_many_test.rb +5 -2
- data/examples/datacatalog/test/resources/categories/categories_get_one_test.rb +4 -3
- data/examples/datacatalog/test/resources/categories/categories_post_test.rb +27 -43
- data/examples/datacatalog/test/resources/categories/categories_put_test.rb +9 -15
- data/examples/datacatalog/test/resources/categories_sources/categories_sources_delete_test.rb +148 -0
- data/examples/datacatalog/test/resources/categories_sources/categories_sources_get_many_test.rb +92 -0
- data/examples/datacatalog/test/resources/categories_sources/categories_sources_get_one_test.rb +95 -0
- data/examples/datacatalog/test/resources/categories_sources/categories_sources_post_test.rb +187 -0
- data/examples/datacatalog/test/resources/categories_sources/categories_sources_put_test.rb +323 -0
- data/examples/datacatalog/test/resources/sources/sources_delete_test.rb +5 -17
- data/examples/datacatalog/test/resources/sources/sources_get_many_test.rb +5 -2
- data/examples/datacatalog/test/resources/sources/sources_get_one_test.rb +4 -3
- data/examples/datacatalog/test/resources/sources/sources_post_test.rb +22 -35
- data/examples/datacatalog/test/resources/sources/sources_put_test.rb +12 -18
- data/examples/datacatalog/test/resources/users/users_delete_test.rb +10 -22
- data/examples/datacatalog/test/resources/users/users_get_many_test.rb +5 -2
- data/examples/datacatalog/test/resources/users/users_get_one_test.rb +4 -3
- data/examples/datacatalog/test/resources/users/users_post_test.rb +15 -32
- data/examples/datacatalog/test/resources/users/users_put_test.rb +15 -23
- data/lib/builder/action_definitions.rb +60 -0
- data/lib/builder/helpers.rb +53 -26
- data/lib/builder/mongo_helpers.rb +61 -10
- data/lib/builder.rb +136 -38
- data/lib/resource.rb +99 -16
- data/lib/sinatra_resource.rb +6 -6
- data/notes/permissions.mdown +6 -6
- data/sinatra_resource.gemspec +17 -2
- metadata +17 -2
data/lib/builder/helpers.rb
CHANGED
@@ -11,11 +11,13 @@ module SinatraResource
|
|
11
11
|
#
|
12
12
|
# @param [MongoMapper::Document] document
|
13
13
|
#
|
14
|
+
# @param [Hash] resource_config
|
15
|
+
#
|
14
16
|
# @return [Hash<String => Object>]
|
15
|
-
def build_resource(role, document)
|
17
|
+
def build_resource(role, document, resource_config)
|
16
18
|
resource = {}
|
17
|
-
|
18
|
-
if authorized?(:read, role, property)
|
19
|
+
resource_config[:properties].each_pair do |property, hash|
|
20
|
+
if authorized?(:read, role, resource_config, property)
|
19
21
|
resource[property.to_s] = value(property, document, hash)
|
20
22
|
end
|
21
23
|
end
|
@@ -29,10 +31,12 @@ module SinatraResource
|
|
29
31
|
#
|
30
32
|
# @param [String] api_key
|
31
33
|
#
|
34
|
+
# @param [Hash] resource_config
|
35
|
+
#
|
32
36
|
# @return [Array<Hash<String => Object>>]
|
33
|
-
def build_resources(documents)
|
37
|
+
def build_resources(documents, resource_config)
|
34
38
|
documents.map do |document|
|
35
|
-
build_resource(lookup_role(document), document)
|
39
|
+
build_resource(lookup_role(document), document, resource_config)
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
@@ -44,10 +48,17 @@ module SinatraResource
|
|
44
48
|
# @param [Symbol] role
|
45
49
|
# a role (such as :anonymous, :basic, or :admin)
|
46
50
|
#
|
51
|
+
# @param [Hash] resource_config
|
52
|
+
#
|
53
|
+
# @param [Boolean] leaf
|
54
|
+
# If a simple resource, should be true.
|
55
|
+
# If a nested resource, are we at the 'end' (the leaf)?
|
56
|
+
#
|
47
57
|
# @return [undefined]
|
48
|
-
def check_params(action, role)
|
58
|
+
def check_params(action, role, resource_config, leaf)
|
59
|
+
return unless leaf
|
49
60
|
params_check_action(action)
|
50
|
-
params_check_action_and_role(action, role)
|
61
|
+
params_check_action_and_role(action, role, resource_config)
|
51
62
|
end
|
52
63
|
|
53
64
|
# Halt unless the current role has permission to carry out +action+
|
@@ -58,10 +69,12 @@ module SinatraResource
|
|
58
69
|
# @param [Symbol] role
|
59
70
|
# a role (such as :anonymous, :basic, or :admin)
|
60
71
|
#
|
72
|
+
# @param [Hash] resource_config
|
73
|
+
#
|
61
74
|
# @return [undefined]
|
62
|
-
def check_permission(action, role)
|
63
|
-
before_authorization(action, role)
|
64
|
-
unless authorized?(action, role)
|
75
|
+
def check_permission(action, role, resource_config)
|
76
|
+
before_authorization(action, role, resource_config)
|
77
|
+
unless authorized?(action, role, resource_config)
|
65
78
|
error 401, convert(body_for(:unauthorized))
|
66
79
|
end
|
67
80
|
end
|
@@ -85,12 +98,12 @@ module SinatraResource
|
|
85
98
|
# @param [Object] object
|
86
99
|
#
|
87
100
|
# @return [String]
|
88
|
-
def display(action, object)
|
101
|
+
def display(action, object, resource_config)
|
89
102
|
case action
|
90
103
|
when :read
|
91
104
|
when :create
|
92
105
|
response.status = 201
|
93
|
-
path =
|
106
|
+
path = resource_config[:path] + %(/#{object["id"]})
|
94
107
|
response.headers['Location'] = full_uri(path)
|
95
108
|
when :update
|
96
109
|
when :delete
|
@@ -118,8 +131,8 @@ module SinatraResource
|
|
118
131
|
# @param [String, nil] id
|
119
132
|
#
|
120
133
|
# @return [Symbol]
|
121
|
-
def get_role(id=nil)
|
122
|
-
lookup_role(id ?
|
134
|
+
def get_role(model, id=nil)
|
135
|
+
lookup_role(id ? model.find_by_id(id) : nil)
|
123
136
|
end
|
124
137
|
|
125
138
|
# Return the minimum role required for +action+, and, if specified,
|
@@ -128,13 +141,17 @@ module SinatraResource
|
|
128
141
|
# @param [Symbol] action
|
129
142
|
# :read, :create, :update, or :delete
|
130
143
|
#
|
144
|
+
# @param [Hash] resource_config
|
145
|
+
#
|
146
|
+
# @param [Symbol, nil] property
|
147
|
+
#
|
131
148
|
# @return [Symbol]
|
132
149
|
# a role (such as :anonymous, :basic, or :admin)
|
133
|
-
def minimum_role(action, property=nil)
|
150
|
+
def minimum_role(action, resource_config, property=nil)
|
134
151
|
if property.nil?
|
135
|
-
|
152
|
+
resource_config[:permission][to_read_or_modify(action)]
|
136
153
|
else
|
137
|
-
|
154
|
+
resource_config[:properties][property][to_r_or_w(action)]
|
138
155
|
end || :anonymous
|
139
156
|
end
|
140
157
|
|
@@ -148,18 +165,20 @@ module SinatraResource
|
|
148
165
|
# @param [Symbol] action
|
149
166
|
# :read, :create, :update, or :delete
|
150
167
|
#
|
151
|
-
# @param [
|
168
|
+
# @param [Hash] resource_config
|
169
|
+
#
|
170
|
+
# @param [Symbol, nil] property
|
152
171
|
# a property of a resource
|
153
172
|
#
|
154
173
|
# @return [Boolean]
|
155
|
-
def authorized?(action, role, property=nil)
|
156
|
-
klass =
|
174
|
+
def authorized?(action, role, resource_config, property=nil)
|
175
|
+
klass = resource_config[:roles]
|
157
176
|
klass.validate_role(role)
|
158
|
-
klass.satisfies?(role, minimum_role(action, property))
|
177
|
+
klass.satisfies?(role, minimum_role(action, resource_config, property))
|
159
178
|
end
|
160
179
|
|
161
180
|
# Application-level hook that runs as part of +check_permission+,
|
162
|
-
# before +authorized?(action, role)+ is called.
|
181
|
+
# before +authorized?(action, role, resource_config)+ is called.
|
163
182
|
#
|
164
183
|
# For example, an application might want to throw custom errors
|
165
184
|
# in certain situations before +authorized?+ runs.
|
@@ -172,8 +191,10 @@ module SinatraResource
|
|
172
191
|
# @param [Symbol] role
|
173
192
|
# a role (such as :anonymous, :basic, or :admin)
|
174
193
|
#
|
194
|
+
# @param [Hash] resource_config
|
195
|
+
#
|
175
196
|
# @return [String]
|
176
|
-
def before_authorization(action, role)
|
197
|
+
def before_authorization(action, role, resource_config)
|
177
198
|
raise NotImplementedError
|
178
199
|
end
|
179
200
|
|
@@ -201,7 +222,7 @@ module SinatraResource
|
|
201
222
|
when :not_found
|
202
223
|
""
|
203
224
|
when :unauthorized
|
204
|
-
""
|
225
|
+
{ "errors" => "unauthorized_api_key" }
|
205
226
|
end
|
206
227
|
end
|
207
228
|
|
@@ -221,6 +242,10 @@ module SinatraResource
|
|
221
242
|
# @param [Symbol] action
|
222
243
|
# :read, :create, :update, or :delete
|
223
244
|
#
|
245
|
+
# @param [Boolean] leaf
|
246
|
+
# If a simple resource, should be true.
|
247
|
+
# If a nested resource, are we at the 'end' (the leaf)?
|
248
|
+
#
|
224
249
|
# @return [undefined]
|
225
250
|
def params_check_action(action)
|
226
251
|
case action
|
@@ -251,11 +276,13 @@ module SinatraResource
|
|
251
276
|
# @param [Symbol] role
|
252
277
|
# a role (such as :anonymous, :basic, or :admin)
|
253
278
|
#
|
279
|
+
# @param [Hash] resource_config
|
280
|
+
#
|
254
281
|
# @return [undefined]
|
255
|
-
def params_check_action_and_role(action, role)
|
282
|
+
def params_check_action_and_role(action, role, resource_config)
|
256
283
|
invalid = []
|
257
284
|
params.each_pair do |property, value|
|
258
|
-
invalid << property if !authorized?(action, role, property.intern)
|
285
|
+
invalid << property if !authorized?(action, role, resource_config, property.intern)
|
259
286
|
end
|
260
287
|
unless invalid.empty?
|
261
288
|
error 400, convert(body_for(:invalid_params, invalid))
|
@@ -3,12 +3,29 @@ module SinatraResource
|
|
3
3
|
class Builder
|
4
4
|
|
5
5
|
module MongoHelpers
|
6
|
+
|
7
|
+
|
8
|
+
# Make sure that +parent+ document is related to the +child+ document
|
9
|
+
# by way of +association+. If not, return 404 Not Found.
|
10
|
+
#
|
11
|
+
# @param [MongoMapper::Document] parent
|
12
|
+
#
|
13
|
+
# @param [Symbol] association
|
14
|
+
#
|
15
|
+
# @param [String] child_id
|
16
|
+
#
|
17
|
+
# @return [MongoMapper::Document]
|
18
|
+
def check_related?(parent, association, child_id)
|
19
|
+
unless parent.send(association).find { |x| x.id == child_id }
|
20
|
+
error 404, convert(body_for(:not_found))
|
21
|
+
end
|
22
|
+
end
|
6
23
|
|
7
24
|
# Create a document from params. If not valid, returns 400.
|
8
25
|
#
|
9
26
|
# @return [MongoMapper::Document]
|
10
|
-
def create_document!
|
11
|
-
document =
|
27
|
+
def create_document!(model)
|
28
|
+
document = model.new(params)
|
12
29
|
unless document.valid?
|
13
30
|
error 400, convert(body_for(:invalid_document, document))
|
14
31
|
end
|
@@ -23,8 +40,8 @@ module SinatraResource
|
|
23
40
|
# @param [String] id
|
24
41
|
#
|
25
42
|
# @return [MongoMapper::Document]
|
26
|
-
def delete_document!(id)
|
27
|
-
document = find_document!(id)
|
43
|
+
def delete_document!(model, id)
|
44
|
+
document = find_document!(model, id)
|
28
45
|
document.destroy
|
29
46
|
document
|
30
47
|
end
|
@@ -34,8 +51,8 @@ module SinatraResource
|
|
34
51
|
# @param [String] id
|
35
52
|
#
|
36
53
|
# @return [MongoMapper::Document]
|
37
|
-
def find_document!(id)
|
38
|
-
document =
|
54
|
+
def find_document!(model, id)
|
55
|
+
document = model.find_by_id(id)
|
39
56
|
unless document
|
40
57
|
error 404, convert(body_for(:not_found))
|
41
58
|
end
|
@@ -48,15 +65,49 @@ module SinatraResource
|
|
48
65
|
# a class that includes MongoMapper::Document
|
49
66
|
#
|
50
67
|
# @return [Array<MongoMapper::Document>]
|
51
|
-
def find_documents!
|
52
|
-
|
68
|
+
def find_documents!(model)
|
69
|
+
model.find(:all)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Delegates to application, who should use custom logic to related
|
73
|
+
# +parent+ and +child+.
|
74
|
+
#
|
75
|
+
# @param [MongoMapper::Document] parent
|
76
|
+
#
|
77
|
+
# @param [MongoMapper::Document] child
|
78
|
+
#
|
79
|
+
# @param [Hash] resource_config
|
80
|
+
#
|
81
|
+
# @return [MongoMapper::Document] child document
|
82
|
+
def make_related(parent, child, resource_config)
|
83
|
+
proc = resource_config[:relation][:create]
|
84
|
+
proc.call(parent, child) if proc
|
85
|
+
child
|
86
|
+
end
|
87
|
+
|
88
|
+
# Select only the +children+ that are related to the +parent+ by
|
89
|
+
# way of the +association+.
|
90
|
+
#
|
91
|
+
# @param [MongoMapper::Document] parent
|
92
|
+
#
|
93
|
+
# @param [Symbol] association
|
94
|
+
#
|
95
|
+
# @param [Array<MongoMapper::Document>] children
|
96
|
+
#
|
97
|
+
# @return [MongoMapper::Document]
|
98
|
+
def select_related(parent, association, children)
|
99
|
+
children.select do |child|
|
100
|
+
parent.send(association).find { |x| x.id == child.id }
|
101
|
+
end
|
102
|
+
# TODO: this has O^2 complexity because of the nesting.
|
103
|
+
# I think it is reducible to O.
|
53
104
|
end
|
54
105
|
|
55
106
|
# Update a document with +id+ from params. If not valid, returns 400.
|
56
107
|
#
|
57
108
|
# @return [MongoMapper::Document]
|
58
|
-
def update_document!(id)
|
59
|
-
document =
|
109
|
+
def update_document!(model, id)
|
110
|
+
document = model.update(id, params)
|
60
111
|
unless document.valid?
|
61
112
|
error 400, convert(body_for(:invalid_document, document))
|
62
113
|
end
|
data/lib/builder.rb
CHANGED
@@ -3,7 +3,17 @@ module SinatraResource
|
|
3
3
|
class Builder
|
4
4
|
|
5
5
|
def initialize(klass)
|
6
|
-
@klass
|
6
|
+
@klass = klass
|
7
|
+
|
8
|
+
@resource_config = @klass.resource_config
|
9
|
+
@child_association = @resource_config[:child_association]
|
10
|
+
@model = @resource_config[:model]
|
11
|
+
@parent = @resource_config[:parent]
|
12
|
+
@path = @resource_config[:path]
|
13
|
+
if @parent
|
14
|
+
@parent_resource_config = @parent.resource_config
|
15
|
+
@parent_model = @parent_resource_config[:model]
|
16
|
+
end
|
7
17
|
end
|
8
18
|
|
9
19
|
def build
|
@@ -16,64 +26,152 @@ module SinatraResource
|
|
16
26
|
end
|
17
27
|
|
18
28
|
def build_get_one
|
19
|
-
@
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
model = @model
|
30
|
+
resource_config = @resource_config
|
31
|
+
if !@parent
|
32
|
+
@klass.get '/:id/?' do
|
33
|
+
id = params.delete("id")
|
34
|
+
role = get_role(model, id)
|
35
|
+
document = document_for_get_one(role, model, resource_config, true, id, nil, nil)
|
36
|
+
resource = build_resource(role, document, resource_config)
|
37
|
+
display(:read, resource, resource_config)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
association = @child_association
|
41
|
+
parent_model = @parent_model
|
42
|
+
parent_resource_config = @parent_resource_config
|
43
|
+
path = @path
|
44
|
+
@parent.get "/:parent_id/#{path}/:id/?" do
|
45
|
+
id = params.delete("id")
|
46
|
+
parent_id = params.delete("parent_id")
|
47
|
+
parent_role = get_role(parent_model, parent_id)
|
48
|
+
parent_document = document_for_get_one(parent_role, parent_model, parent_resource_config, false, parent_id, nil, nil)
|
49
|
+
# ------
|
50
|
+
role = get_role(model, id)
|
51
|
+
document = document_for_get_one(role, model, resource_config, true, id, parent_document, association)
|
52
|
+
resource = build_resource(role, document, resource_config)
|
53
|
+
display(:read, resource, resource_config)
|
54
|
+
end
|
27
55
|
end
|
28
56
|
end
|
29
57
|
|
30
58
|
def build_get_many
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
59
|
+
model = @model
|
60
|
+
resource_config = @resource_config
|
61
|
+
if !@parent
|
62
|
+
@klass.get '/?' do
|
63
|
+
role = get_role(model)
|
64
|
+
documents = documents_for_get_many(role, model, resource_config, true, nil, nil)
|
65
|
+
resources = build_resources(documents, resource_config)
|
66
|
+
display(:read, resources, resource_config)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
association = @child_association
|
70
|
+
parent_model = @parent_model
|
71
|
+
parent_resource_config = @parent_resource_config
|
72
|
+
path = @path
|
73
|
+
@parent.get "/:parent_id/#{path}/?" do
|
74
|
+
parent_id = params.delete("parent_id")
|
75
|
+
parent_role = get_role(parent_model, parent_id)
|
76
|
+
parent_document = document_for_get_one(parent_role, parent_model, parent_resource_config, false, parent_id, nil, nil)
|
77
|
+
# ------
|
78
|
+
role = get_role(model)
|
79
|
+
documents = documents_for_get_many(role, model, resource_config, true, parent_document, association)
|
80
|
+
resources = build_resources(documents, resource_config)
|
81
|
+
display(:read, resources, resource_config)
|
82
|
+
end
|
38
83
|
end
|
39
84
|
end
|
40
85
|
|
41
86
|
def build_post
|
42
|
-
@
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
87
|
+
model = @model
|
88
|
+
resource_config = @resource_config
|
89
|
+
if !@parent
|
90
|
+
@klass.post '/?' do
|
91
|
+
role = get_role(model)
|
92
|
+
document = document_for_post(role, model, resource_config, true, nil, nil)
|
93
|
+
resource = build_resource(role, document, resource_config)
|
94
|
+
display(:create, resource, resource_config)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
association = @child_association
|
98
|
+
parent_model = @parent_model
|
99
|
+
parent_resource_config = @parent_resource_config
|
100
|
+
path = @path
|
101
|
+
@parent.post "/:parent_id/#{path}/?" do
|
102
|
+
parent_id = params.delete("parent_id")
|
103
|
+
parent_role = get_role(parent_model, parent_id)
|
104
|
+
parent_document = document_for_get_one(parent_role, parent_model, parent_resource_config, false, parent_id, nil, nil)
|
105
|
+
# ------
|
106
|
+
role = get_role(model)
|
107
|
+
document = document_for_post(role, model, resource_config, true, parent_document, association)
|
108
|
+
resource = build_resource(role, document, resource_config)
|
109
|
+
display(:create, resource, resource_config)
|
110
|
+
end
|
49
111
|
end
|
50
112
|
end
|
51
113
|
|
52
114
|
def build_put
|
53
|
-
@
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
115
|
+
model = @model
|
116
|
+
resource_config = @resource_config
|
117
|
+
if !@parent
|
118
|
+
@klass.put '/:id/?' do
|
119
|
+
id = params.delete("id")
|
120
|
+
role = get_role(model, id)
|
121
|
+
document = document_for_put(role, model, resource_config, true, id, nil, nil)
|
122
|
+
resource = build_resource(role, document, resource_config)
|
123
|
+
display(:update, resource, resource_config)
|
124
|
+
end
|
125
|
+
else
|
126
|
+
association = @child_association
|
127
|
+
parent_model = @parent_model
|
128
|
+
parent_resource_config = @parent_resource_config
|
129
|
+
path = @path
|
130
|
+
@parent.put "/:parent_id/#{path}/:id/?" do
|
131
|
+
id = params.delete("id")
|
132
|
+
parent_id = params.delete("parent_id")
|
133
|
+
parent_role = get_role(parent_model, parent_id)
|
134
|
+
parent_document = document_for_get_one(parent_role, parent_model, parent_resource_config, false, parent_id, id, id)
|
135
|
+
# ------
|
136
|
+
role = get_role(model, id)
|
137
|
+
document = document_for_put(role, model, resource_config, true, id, parent_document, association)
|
138
|
+
resource = build_resource(role, document, resource_config)
|
139
|
+
display(:update, resource, resource_config)
|
140
|
+
end
|
61
141
|
end
|
62
142
|
end
|
63
143
|
|
64
144
|
def build_delete
|
65
|
-
@
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
145
|
+
model = @model
|
146
|
+
resource_config = @resource_config
|
147
|
+
if !@parent
|
148
|
+
@klass.delete '/:id/?' do
|
149
|
+
id = params.delete("id")
|
150
|
+
role = get_role(model, id)
|
151
|
+
document_for_delete(role, model, resource_config, true, id, nil, nil)
|
152
|
+
display(:delete, "", resource_config)
|
153
|
+
end
|
154
|
+
else
|
155
|
+
association = @child_association
|
156
|
+
parent_model = @parent_model
|
157
|
+
parent_resource_config = @parent_resource_config
|
158
|
+
path = @path
|
159
|
+
@parent.delete "/:parent_id/#{path}/:id/?" do
|
160
|
+
id = params.delete("id")
|
161
|
+
parent_id = params.delete("parent_id")
|
162
|
+
parent_role = get_role(parent_model, parent_id)
|
163
|
+
parent_document = document_for_get_one(parent_role, parent_model, parent_resource_config, false, parent_id, nil, nil)
|
164
|
+
# ------
|
165
|
+
role = get_role(model, id)
|
166
|
+
document_for_delete(role, model, resource_config, true, id, parent_document, association)
|
167
|
+
display(:delete, "", resource_config)
|
168
|
+
end
|
72
169
|
end
|
73
170
|
end
|
74
171
|
|
75
172
|
def build_helpers
|
76
173
|
@klass.helpers do
|
174
|
+
include ActionDefinitions
|
77
175
|
include Helpers
|
78
176
|
include MongoHelpers
|
79
177
|
end
|