snusnu-merb_resource_controller 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 +306 -0
- data/Rakefile +81 -0
- data/TODO +7 -0
- data/lib/merb_resource_controller/action_timeout_support.rb +53 -0
- data/lib/merb_resource_controller/actions.rb +169 -0
- data/lib/merb_resource_controller/identity_map_support.rb +20 -0
- data/lib/merb_resource_controller/resource_controller.rb +160 -0
- data/lib/merb_resource_controller/resource_proxy.rb +317 -0
- data/lib/merb_resource_controller.rb +29 -0
- data/spec/mrc_test_app/Rakefile +52 -0
- data/spec/mrc_test_app/app/controllers/application.rb +6 -0
- data/spec/mrc_test_app/app/controllers/articles.rb +3 -0
- data/spec/mrc_test_app/app/controllers/community/comments.rb +9 -0
- data/spec/mrc_test_app/app/controllers/community/ratings.rb +9 -0
- data/spec/mrc_test_app/app/controllers/editors.rb +7 -0
- data/spec/mrc_test_app/app/models/article.rb +19 -0
- data/spec/mrc_test_app/app/models/editor.rb +11 -0
- data/spec/mrc_test_app/app/views/articles/edit.html.erb +13 -0
- data/spec/mrc_test_app/app/views/articles/index.html.erb +25 -0
- data/spec/mrc_test_app/app/views/articles/new.html.erb +12 -0
- data/spec/mrc_test_app/app/views/articles/show.html.erb +8 -0
- data/spec/mrc_test_app/app/views/community/comments/edit.html.erb +12 -0
- data/spec/mrc_test_app/app/views/community/comments/index.html.erb +25 -0
- data/spec/mrc_test_app/app/views/community/comments/new.html.erb +3 -0
- data/spec/mrc_test_app/app/views/community/comments/show.html.erb +3 -0
- data/spec/mrc_test_app/app/views/community/ratings/edit.html.erb +11 -0
- data/spec/mrc_test_app/app/views/community/ratings/index.html.erb +25 -0
- data/spec/mrc_test_app/app/views/community/ratings/show.html.erb +3 -0
- data/spec/mrc_test_app/app/views/editors/edit.html.erb +12 -0
- data/spec/mrc_test_app/app/views/editors/new.html.erb +12 -0
- data/spec/mrc_test_app/app/views/editors/show.html.erb +7 -0
- data/spec/mrc_test_app/config/database.yml +33 -0
- data/spec/mrc_test_app/config/environments/development.rb +15 -0
- data/spec/mrc_test_app/config/environments/rake.rb +11 -0
- data/spec/mrc_test_app/config/environments/test.rb +12 -0
- data/spec/mrc_test_app/config/init.rb +36 -0
- data/spec/mrc_test_app/config/rack.rb +11 -0
- data/spec/mrc_test_app/config/router.rb +50 -0
- data/spec/mrc_test_app/spec/lib/resource_proxy_spec.rb +292 -0
- data/spec/mrc_test_app/spec/request/article_comments_spec.rb +208 -0
- data/spec/mrc_test_app/spec/request/article_editor_spec.rb +202 -0
- data/spec/mrc_test_app/spec/request/articles_spec.rb +208 -0
- data/spec/mrc_test_app/spec/request/comments_spec.rb +221 -0
- data/spec/mrc_test_app/spec/spec.opts +2 -0
- data/spec/mrc_test_app/spec/spec_helper.rb +206 -0
- metadata +166 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
module Merb
|
2
|
+
module ResourceController
|
3
|
+
|
4
|
+
class ResourceControllerException < Exception; end
|
5
|
+
class WebMethodsNotAvailable < ResourceControllerException; end
|
6
|
+
class InvalidRoute < ResourceControllerException; end
|
7
|
+
|
8
|
+
module Mixin
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def controlling(name, options = {})
|
13
|
+
options = { :flash => true }.merge!(options)
|
14
|
+
@resource_proxy = Merb::ResourceController::ResourceProxy.new(name, options)
|
15
|
+
yield @resource_proxy if block_given?
|
16
|
+
class_inheritable_reader :resource_proxy
|
17
|
+
include InstanceMethods
|
18
|
+
include FlashSupport if options[:flash]
|
19
|
+
@resource_proxy.registered_actions.each do |a|
|
20
|
+
include Merb::ResourceController::Actions.const_get("#{a[:name].to_s.camel_case}")
|
21
|
+
show_action(a[:name])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
module InstanceMethods
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def resource_proxy
|
32
|
+
self.class.resource_proxy
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def singleton_controller?
|
37
|
+
resource_proxy.singleton_resource?
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_parent?
|
41
|
+
resource_proxy.has_parent? && has_parent_param?
|
42
|
+
end
|
43
|
+
|
44
|
+
def has_parent_param?
|
45
|
+
!!parent_param
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def parent
|
50
|
+
resource_proxy.path_to_resource(params)[-2].last
|
51
|
+
end
|
52
|
+
|
53
|
+
# TODO refactor so that no additional queries are necessary
|
54
|
+
def parents
|
55
|
+
resource_proxy.parents.map do |parent|
|
56
|
+
parent[:class].get(params[parent[:key]])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def parent_param
|
61
|
+
params[resource_proxy.parent_key]
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def load_resource
|
66
|
+
path = resource_proxy.path_to_resource(params)
|
67
|
+
# puts "path_to_resource: #{path.inspect}"
|
68
|
+
path.each do |pc|
|
69
|
+
# puts "setting @#{pc[0]} to #{pc[1].inspect}" if pc[1]
|
70
|
+
instance_variable_set("@#{pc[0]}", pc[1]) if pc[1]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def requested_resource
|
75
|
+
resource_proxy.path_to_resource(params).last.last
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def set_collection(obj)
|
80
|
+
instance_variable_set("@#{collection_name}", obj)
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_member(obj)
|
84
|
+
instance_variable_set("@#{member_name}", obj)
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def collection_name(resource = nil)
|
89
|
+
resource_proxy.collection_name(resource)
|
90
|
+
end
|
91
|
+
|
92
|
+
def member_name(resource = nil)
|
93
|
+
resource_proxy.member_name(resource)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def collection
|
98
|
+
instance_variable_get("@#{collection_name}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def member
|
102
|
+
instance_variable_get("@#{member_name}")
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def new_member(attributes = {})
|
107
|
+
resource_proxy.new_member(params, attributes)
|
108
|
+
end
|
109
|
+
|
110
|
+
def member_params(attributes = {})
|
111
|
+
resource_proxy.member_params(params, attributes)
|
112
|
+
end
|
113
|
+
|
114
|
+
def parent_params
|
115
|
+
resource_proxy.parent_params(params)
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def flash_supported?
|
120
|
+
self.kind_of?(FlashSupport)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
module FlashSupport
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
def successful_create_messages
|
130
|
+
{ :notice => "#{member.class.name} was successfully created" }
|
131
|
+
end
|
132
|
+
|
133
|
+
def failed_create_messages
|
134
|
+
{ :error => "Failed to create new #{member.class.name}" }
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def successful_update_messages
|
139
|
+
{ :notice => "#{member.class.name} was successfully updated" }
|
140
|
+
end
|
141
|
+
|
142
|
+
def failed_update_messages
|
143
|
+
{ :error => "Failed to update #{member.class.name}" }
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def successful_destroy_messages
|
148
|
+
{ :notice => "#{member.class.name} was successfully destroyed" }
|
149
|
+
end
|
150
|
+
|
151
|
+
def failed_destroy_messages
|
152
|
+
{ :error => "Failed to destroy #{member.class.name}" }
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,317 @@
|
|
1
|
+
module Merb
|
2
|
+
module ResourceController
|
3
|
+
|
4
|
+
class ResourceProxy
|
5
|
+
|
6
|
+
DEFAULT_NESTING_OPTIONS = {
|
7
|
+
:singleton => false,
|
8
|
+
:fully_qualified => false
|
9
|
+
}
|
10
|
+
|
11
|
+
DEFAULT_OPTIONS = DEFAULT_NESTING_OPTIONS.merge({
|
12
|
+
:defaults => true,
|
13
|
+
:use => :all
|
14
|
+
})
|
15
|
+
|
16
|
+
attr_reader :resource, :parents, :registered_methods
|
17
|
+
|
18
|
+
def initialize(resource, options = {})
|
19
|
+
options = DEFAULT_OPTIONS.merge(options)
|
20
|
+
@resource, @singleton = load_resource(resource), !!options[:singleton]
|
21
|
+
@fully_qualified = !!options[:fully_qualified]
|
22
|
+
@actions, @registered_methods, @parents = [], [], []
|
23
|
+
@specific_methods_registered = options[:use] != :all
|
24
|
+
register_default_actions! if options[:defaults]
|
25
|
+
register_methods!(options[:use])
|
26
|
+
end
|
27
|
+
|
28
|
+
def action(name, options = {})
|
29
|
+
@actions << { :name => name.to_sym }.merge(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def actions(*names)
|
33
|
+
names.each { |n| @actions << n.is_a?(Hash) ? n : { :name => n.to_sym } }
|
34
|
+
end
|
35
|
+
|
36
|
+
def registered_actions
|
37
|
+
@actions
|
38
|
+
end
|
39
|
+
|
40
|
+
# ----------------------------------------------------------------------------------------
|
41
|
+
# # one level nestings
|
42
|
+
# ----------------------------------------------------------------------------------------
|
43
|
+
# r.belongs_to :article # assumes :key => :article_id
|
44
|
+
# r.belongs_to :article, :key => :foo # override :key => :foo
|
45
|
+
# ----------------------------------------------------------------------------------------
|
46
|
+
# # multi level nestings (array item ordering reflects nesting strategy)
|
47
|
+
# ----------------------------------------------------------------------------------------
|
48
|
+
# r.belongs_to [ :article, :post ] # assumes :key => :article_id and :key => :post_id
|
49
|
+
# r.belongs_to [ [ :article, :key => :foo ], :post ]
|
50
|
+
# r.belongs_to [ [ :article, :key => :foo ], [ :post, :key => :bar ] ] ]
|
51
|
+
# ----------------------------------------------------------------------------------------
|
52
|
+
|
53
|
+
def belongs_to(parent, options = {})
|
54
|
+
case parent
|
55
|
+
when Symbol, String then
|
56
|
+
options = DEFAULT_NESTING_OPTIONS.merge(:key => key_name(parent)).merge(options)
|
57
|
+
@parents << { :name => parent, :class => load_resource(parent) }.merge(options)
|
58
|
+
when Array then
|
59
|
+
parent.each do |p|
|
60
|
+
case p
|
61
|
+
when Symbol, String then
|
62
|
+
options = DEFAULT_NESTING_OPTIONS.merge(:key => key_name(p))
|
63
|
+
@parents << { :name => p, :class => load_resource(p) }.merge(options)
|
64
|
+
when Array then
|
65
|
+
if (p[0].is_a?(Symbol) || p[0].is_a?(String)) && p[1].is_a?(Hash)
|
66
|
+
options = DEFAULT_NESTING_OPTIONS.merge(:key => key_name(p[0])).merge(p[1])
|
67
|
+
@parents << { :name => p[0], :class => load_resource(p[0]) }.merge(options)
|
68
|
+
else
|
69
|
+
raise ArgumentError, "use [ Symbol|String, Hash ] to denote one of multiple parents"
|
70
|
+
end
|
71
|
+
else
|
72
|
+
raise ArgumentError, "parent must be Symbol, String or Array but was #{p.class}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
else
|
76
|
+
raise ArgumentError, "parent must be Symbol, String or Array but was #{parent.class}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def belongs_to?(parent)
|
81
|
+
@parents.any? { |h| h[:name] == parent }
|
82
|
+
end
|
83
|
+
|
84
|
+
def has_parent?
|
85
|
+
!@parents.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
def has_parents?
|
89
|
+
@parents.size > 1
|
90
|
+
end
|
91
|
+
|
92
|
+
def fully_qualified?
|
93
|
+
@fully_qualified
|
94
|
+
end
|
95
|
+
|
96
|
+
def singleton_resource?
|
97
|
+
@singleton
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def path_to_resource(params)
|
102
|
+
nesting_strategy_instance(nesting_strategy_template(params)).map do |i|
|
103
|
+
[ i[3] ? member_name(i[0], i[2]) : i[1] ? member_name(i[0], i[2]) : collection_name(i[0], i[2]), i[4] ]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def nesting_strategy_instance(nst, idx = 0)
|
108
|
+
if nst[idx]
|
109
|
+
if idx == 0
|
110
|
+
if nst[idx][3]
|
111
|
+
if nst[idx][1]
|
112
|
+
raise "Toplevel singleton resources are not supported"
|
113
|
+
else
|
114
|
+
nst[idx] = nst[idx] + [ nst[idx][0].get(nst[idx][3]), nra(nst[idx][0], nst) ]
|
115
|
+
nesting_strategy_instance(nst, idx + 1)
|
116
|
+
end
|
117
|
+
else
|
118
|
+
nst[idx] = nst[idx] + [ nst[idx][0].all, nra(nst[idx][0], nst) ]
|
119
|
+
nesting_strategy_instance(nst, idx + 1)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
if nst[idx][3]
|
123
|
+
if nst[idx][1]
|
124
|
+
nst[idx] = nst[idx] + [ nst[idx - 1][4].send(nst[idx - 1][5]), nra(nst[idx][0], nst) ]
|
125
|
+
nesting_strategy_instance(nst, idx + 1)
|
126
|
+
else
|
127
|
+
nst[idx] = nst[idx] + [ nst[idx - 1][4].send(nst[idx - 1][5]).get(nst[idx][3]), nra(nst[idx][0], nst) ]
|
128
|
+
nesting_strategy_instance(nst, idx + 1)
|
129
|
+
end
|
130
|
+
else
|
131
|
+
nst[idx] = nst[idx] + [ nst[idx - 1][4].send(nst[idx - 1][5]), nra(nst[idx][0], nst) ]
|
132
|
+
nesting_strategy_instance(nst, idx + 1)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
else
|
136
|
+
nst
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# nested_resource_accessor
|
141
|
+
def nra(member, nst)
|
142
|
+
member = member.is_a?(Class) ? member : member.class
|
143
|
+
return nil unless idx = nst.map { |el| el[0] }.index(member)
|
144
|
+
if child = nst[idx + 1]
|
145
|
+
model, singleton, fully_qualified, id = child[0], child[1], child[2], child[3]
|
146
|
+
if id
|
147
|
+
collection_name(model, fully_qualified)
|
148
|
+
else
|
149
|
+
singleton ? member_name(model, fully_qualified) : collection_name(model, fully_qualified)
|
150
|
+
end
|
151
|
+
else
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def nesting_strategy_template(params)
|
158
|
+
idx = -1
|
159
|
+
nesting_strategy_params(params).map do |nsp|
|
160
|
+
nesting_strategy[idx += 1] << nsp
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def nesting_strategy_params(params)
|
165
|
+
parent_param_values(params) << params["id"]
|
166
|
+
end
|
167
|
+
|
168
|
+
def nesting_strategy
|
169
|
+
parent_resources << [ @resource, @singleton, fully_qualified? ]
|
170
|
+
end
|
171
|
+
|
172
|
+
def nesting_level
|
173
|
+
nesting_strategy.size
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
# all parent parameters
|
178
|
+
def parent_param_values(params)
|
179
|
+
parent_keys.map { |k| params[k] }
|
180
|
+
end
|
181
|
+
|
182
|
+
# the immediate parent parameter
|
183
|
+
def parent_param_value(params)
|
184
|
+
parent_param_values(params).last
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def new_member(params, attributes = {})
|
189
|
+
resource.new(member_params(params, attributes))
|
190
|
+
end
|
191
|
+
|
192
|
+
def member_params(params, attributes = {})
|
193
|
+
if attrs = params[member_name]
|
194
|
+
has_parent? ? parent_params(params).merge!(attrs).merge!(attributes) : attrs.merge!(attributes)
|
195
|
+
else
|
196
|
+
has_parent? ? parent_params(params).merge!(attributes) : attributes
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def parent_params(params)
|
201
|
+
parent_keys.inject({}) do |hash, key|
|
202
|
+
key = key.to_sym
|
203
|
+
hash[key] = params[key] if resource.properties.map { |p| p.name }.include?(key.to_sym)
|
204
|
+
hash
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
# all parent resources
|
210
|
+
def parent_resources
|
211
|
+
@parents.map { |h| [ h[:class], h[:singleton], h[:fully_qualified] ] }
|
212
|
+
end
|
213
|
+
|
214
|
+
# the immediate parent resource
|
215
|
+
def parent_resource
|
216
|
+
parent_resources.last
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
# all parent resource keys
|
221
|
+
def parent_keys
|
222
|
+
@parents.map { |h| h[:key] }
|
223
|
+
end
|
224
|
+
|
225
|
+
# the immediate parent resource key
|
226
|
+
def parent_key
|
227
|
+
@parents.last ? @parents.last[:key] : nil
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
def collection_name(resource = nil, fully_qualified = false)
|
232
|
+
if fully_qualified
|
233
|
+
Extlib::Inflection.tableize((resource || @resource).name).to_sym
|
234
|
+
else
|
235
|
+
Extlib::Inflection.demodulize((resource || @resource).name).pluralize.snake_case.to_sym
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def member_name(resource = nil, fully_qualified = false)
|
240
|
+
collection_name(resource, fully_qualified).to_s.singularize.to_sym
|
241
|
+
end
|
242
|
+
|
243
|
+
def key_name(resource = nil)
|
244
|
+
Extlib::Inflection.foreign_key(resource || @resource)
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
def specific_methods_registered?
|
249
|
+
@specific_methods_registered && !@registered_methods.empty?
|
250
|
+
end
|
251
|
+
|
252
|
+
def method_registered?(name)
|
253
|
+
specific_methods_registered? ? registered_methods.map { |m| m[:name] }.include?(name.to_sym) : true
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
def method_missing(name, *args, &block)
|
258
|
+
return super unless method_registered?(name)
|
259
|
+
@resource.send(name, *args, &block)
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
private
|
264
|
+
|
265
|
+
def load_resource(r)
|
266
|
+
case r
|
267
|
+
when Symbol
|
268
|
+
Module.find_const(r.to_s.singular.camel_case)
|
269
|
+
when String
|
270
|
+
Module.find_const(r.include?('::') ? r : r.singular.camel_case)
|
271
|
+
when Class
|
272
|
+
r
|
273
|
+
else
|
274
|
+
raise "resource must be either a Symbol, a String or a Class"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def register_default_actions!
|
279
|
+
action :index unless @singleton
|
280
|
+
[ :show, :new, :edit, :create, :update, :destroy ].each do |a|
|
281
|
+
action(a)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
# KEEP THESE for later
|
287
|
+
|
288
|
+
def register_methods!(methods)
|
289
|
+
@registered_methods = case methods
|
290
|
+
when :all then []
|
291
|
+
when :web_methods then @resource.web_methods
|
292
|
+
when Array then methods
|
293
|
+
else raise
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# def raise_if_invalid_options!(options)
|
298
|
+
# if options[:use] == :web_methods && !@resource.respond_to?(:web_methods)
|
299
|
+
# raise WebMethodsNotAvailable, "require 'dm-is-online' if you want to use web_methods"
|
300
|
+
# end
|
301
|
+
# meth = options[:to]
|
302
|
+
# if options[:use] == :all
|
303
|
+
# msg = "merb_resource_controller: #{@resource}.public_methods.include?(:#{meth}) == false"
|
304
|
+
# raise InvalidRoute, msg unless @resource.public_methods.include?(meth)
|
305
|
+
# elsif options[:use] == :web_methods
|
306
|
+
# msg = "merb_resource_controller: #{@resource}.web_methods.include?(:#{meth}) == false"
|
307
|
+
# raise InvalidRoute, msg unless @resource.web_methods.include?(meth)
|
308
|
+
# else
|
309
|
+
# msg = "merb_resource_controller: Invalid option[:use] = #{options[:use]}, using :all instead"
|
310
|
+
# Merb::Logger.warn(msg)
|
311
|
+
# end
|
312
|
+
# end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# make sure we're running inside Merb
|
2
|
+
if defined?(Merb::Plugins)
|
3
|
+
|
4
|
+
# Merb gave me a Merb::Plugins.config hash
|
5
|
+
# i felt free to put my stuff in my piece of it
|
6
|
+
Merb::Plugins.config[:merb_resource_controller] = {
|
7
|
+
:identity_map => true,
|
8
|
+
:action_timeout => true
|
9
|
+
}
|
10
|
+
|
11
|
+
Merb::BootLoader.before_app_loads do
|
12
|
+
# require code that must be loaded before the application
|
13
|
+
mrc = File.join(File.dirname(__FILE__), 'merb_resource_controller')
|
14
|
+
require mrc / 'resource_proxy'
|
15
|
+
require mrc / 'actions'
|
16
|
+
require mrc / 'resource_controller'
|
17
|
+
if Merb::Plugins.config[:merb_resource_controller][:identity_map]
|
18
|
+
require mrc / 'identity_map_support'
|
19
|
+
end
|
20
|
+
if Merb::Plugins.config[:merb_resource_controller][:action_timeout]
|
21
|
+
require mrc / 'action_timeout_support'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Merb::BootLoader.after_app_loads do
|
26
|
+
# code that can be required after the application loads
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
|
4
|
+
require 'merb-core'
|
5
|
+
require 'merb-core/tasks/merb'
|
6
|
+
|
7
|
+
include FileUtils
|
8
|
+
|
9
|
+
# Load the basic runtime dependencies; this will include
|
10
|
+
# any plugins and therefore plugin rake tasks.
|
11
|
+
init_env = ENV['MERB_ENV'] || 'rake'
|
12
|
+
Merb.load_dependencies(:environment => init_env)
|
13
|
+
|
14
|
+
# Get Merb plugins and dependencies
|
15
|
+
Merb::Plugins.rakefiles.each { |r| require r }
|
16
|
+
|
17
|
+
# Load any app level custom rakefile extensions from lib/tasks
|
18
|
+
tasks_path = File.join(File.dirname(__FILE__), "lib", "tasks")
|
19
|
+
rake_files = Dir["#{tasks_path}/*.rake"]
|
20
|
+
rake_files.each{|rake_file| load rake_file }
|
21
|
+
|
22
|
+
desc "Start runner environment"
|
23
|
+
task :merb_env do
|
24
|
+
Merb.start_environment(:environment => init_env, :adapter => 'runner')
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'spec/rake/spectask'
|
28
|
+
|
29
|
+
desc 'Run specifications'
|
30
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
31
|
+
|
32
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
33
|
+
t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
|
34
|
+
|
35
|
+
begin
|
36
|
+
t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
|
37
|
+
t.rcov_opts << '--exclude' << 'spec'
|
38
|
+
t.rcov_opts << '--text-summary'
|
39
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
40
|
+
rescue Exception
|
41
|
+
# rcov not installed
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Default: run spec examples'
|
47
|
+
task :default => 'spec'
|
48
|
+
|
49
|
+
##############################################################################
|
50
|
+
# ADD YOUR CUSTOM TASKS IN /lib/tasks
|
51
|
+
# NAME YOUR RAKE FILES file_name.rake
|
52
|
+
##############################################################################
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Article
|
2
|
+
|
3
|
+
include DataMapper::Resource
|
4
|
+
|
5
|
+
property :id, Serial
|
6
|
+
property :title, String, :nullable => false, :length => (3..80)
|
7
|
+
property :body, String
|
8
|
+
|
9
|
+
property :editor_id, Integer
|
10
|
+
|
11
|
+
belongs_to :editor
|
12
|
+
|
13
|
+
has n, :comments, :class_name => "Community::Comment"
|
14
|
+
|
15
|
+
def editor_name
|
16
|
+
editor ? editor.name : "Anonymous"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<h2>Edit Article</h2>
|
2
|
+
|
3
|
+
<%= form_for(@article, :action => resource(@article), :method => :put) do %>
|
4
|
+
|
5
|
+
<p><%= text_field :title, :label => "Title" %></p>
|
6
|
+
<p><%= text_field :body, :label => "Body" %></p>
|
7
|
+
<p><%= submit "Update" %></p>
|
8
|
+
|
9
|
+
<% end =%>
|
10
|
+
|
11
|
+
<%= link_to 'Show Editor', resource(@article, :editor) %> |
|
12
|
+
<%= link_to 'Show Article', resource(@article) %> |
|
13
|
+
<%= link_to 'All Articles', resource(:articles) %>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<h1>Listing Articles</h1>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<th>Title</th>
|
6
|
+
<th>Editor</th>
|
7
|
+
<th>Body</th>
|
8
|
+
|
9
|
+
<th colspan="3">Actions</th>
|
10
|
+
</tr>
|
11
|
+
|
12
|
+
<% @articles.each do |article| %>
|
13
|
+
<tr>
|
14
|
+
<td><%=h article.title %></td>
|
15
|
+
<td><%=h article.editor_name %></td>
|
16
|
+
<td><%=h article.body %></td>
|
17
|
+
|
18
|
+
<td><%= link_to 'Show', resource(article) %></td>
|
19
|
+
<td><%= link_to 'Edit', resource(article, :edit) %></td>
|
20
|
+
<td><%= delete_button(article, "Delete #{article.title}") %></td>
|
21
|
+
</tr>
|
22
|
+
<% end %>
|
23
|
+
</table>
|
24
|
+
|
25
|
+
<%= link_to 'New Article', resource(:articles, :new) %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h2>New Article</h2>
|
2
|
+
|
3
|
+
<%= form_for(@article, :action => resource(:articles)) do %>
|
4
|
+
|
5
|
+
<p><%= text_field :title, :label => "Title" %></p>
|
6
|
+
<p><%= text_area :body, :label => "Body" %></p>
|
7
|
+
|
8
|
+
<p><%= submit "Create" %></p>
|
9
|
+
|
10
|
+
<% end =%>
|
11
|
+
|
12
|
+
<%= link_to 'All Articles', resource(:articles) %>
|