snusnu-merb_resource_controller 0.1.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.
- 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
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Martin Gamsjaeger
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
h2. merb_resource_controller
|
2
|
+
|
3
|
+
A merb plugin that provides the default @CRUD@ actions for controllers and allows for easy customization
|
4
|
+
of the generated actions. @merb_resource_controller@ only supports @datamapper@ so far. Implementing support
|
5
|
+
for @active_record@ would be trivial, but I have no need for it.
|
6
|
+
|
7
|
+
Currently, @merb_resource_controller@ needs an extra specification inside the controller to define the nesting strategy
|
8
|
+
to use. This shouldn't really be necessary, since this information could be provided by the @Merb::Router@.
|
9
|
+
I'm planning to have a look into this soon! With that in place, nested resource_controllers could be _3 liners_
|
10
|
+
that just do what you would expect them to do.
|
11
|
+
|
12
|
+
To get you started with @merb_resource_controller@, all you have to do is extend it into your controller.
|
13
|
+
Most probably you will want extend it into the @Application@ controller, which will make the @controlling@
|
14
|
+
method available to all your application's controllers.
|
15
|
+
|
16
|
+
<pre>
|
17
|
+
<code>
|
18
|
+
class Application < Merb::Controller
|
19
|
+
extend Merb::ResourceController::Mixin::ClassMethods
|
20
|
+
end
|
21
|
+
</code>
|
22
|
+
</pre>
|
23
|
+
|
24
|
+
And here is a quick example showing a controller for a standard toplevel resource with all standard @CRUD@ actions.
|
25
|
+
|
26
|
+
<pre>
|
27
|
+
<code>
|
28
|
+
class Articles < Application
|
29
|
+
controlling :articles
|
30
|
+
end
|
31
|
+
</code>
|
32
|
+
</pre>
|
33
|
+
|
34
|
+
If you don't want all standard @CRUD@ actions to be generated, give @merb_resource_controller@ the exact list
|
35
|
+
of actions you want your controller to have.
|
36
|
+
|
37
|
+
<pre>
|
38
|
+
<code>
|
39
|
+
class Articles < Application
|
40
|
+
controlling :articles do |a|
|
41
|
+
a.action :create
|
42
|
+
a.action :update
|
43
|
+
end
|
44
|
+
end
|
45
|
+
</code>
|
46
|
+
</pre>
|
47
|
+
|
48
|
+
Alternatively, you can also add more than one action at once using the @actions@ method available on the @ResourceProxy@
|
49
|
+
|
50
|
+
<pre>
|
51
|
+
<code>
|
52
|
+
class Articles < Application
|
53
|
+
controlling :articles do |a|
|
54
|
+
a.actions :create, :update
|
55
|
+
end
|
56
|
+
end
|
57
|
+
</code>
|
58
|
+
</pre>
|
59
|
+
|
60
|
+
|
61
|
+
If you are using models that are nested inside some @module@ (maybe you're developing a @slice@ ?), you need to
|
62
|
+
provide the fully qualified class name to the @controlling@ method. Per default, @merb_resource_controller@ assumes
|
63
|
+
that you don't want to use the fully qualified name to generate your instance variables and association calls.
|
64
|
+
|
65
|
+
<pre>
|
66
|
+
<code>
|
67
|
+
class Ratings < Application
|
68
|
+
controlling "Community::Rating"
|
69
|
+
end
|
70
|
+
</code>
|
71
|
+
</pre>
|
72
|
+
|
73
|
+
This will initialize the instance variable @@ratings@ (or @@rating@ respectively). If this resource is nested below
|
74
|
+
another resource (see next section), this also means that the @:ratings@ (or @:rating@ respectively) association will
|
75
|
+
be used to navigate from the parent resource down to this resource.
|
76
|
+
|
77
|
+
If however, you want to use fully qualified names for your instance variables and association calls, you can explicitly
|
78
|
+
pass @:fully_qualified => true@ to the @controlling@ method (@false@ is the default).
|
79
|
+
|
80
|
+
<pre>
|
81
|
+
<code>
|
82
|
+
class Ratings < Application
|
83
|
+
controlling "Community::Rating", :fully_qualified => true
|
84
|
+
end
|
85
|
+
</code>
|
86
|
+
</pre>
|
87
|
+
|
88
|
+
This will initialize the instance variable @@community_ratings@ (or @@community_rating@ respectively). If this resource
|
89
|
+
is nested below another resource (see next section), this also means that the @:community_ratings@ (or
|
90
|
+
@:community_rating@ respectively) association will be used to navigate from the parent resource down to this resource.
|
91
|
+
|
92
|
+
h2. Nested Resources
|
93
|
+
|
94
|
+
@merb_resource_controller@ will automatically recognize if it is accessed from a toplevel or a nested url, and
|
95
|
+
will _adjust_ its actions accordingly. It will also initialize all the instance variables
|
96
|
+
you would expect for your chosen nesting strategy. This means, that when you have an URL like
|
97
|
+
@/articles/1/comments@, you will have an @@article@ variable pointing to @Article.get(1)@ and a @@comments@ variable
|
98
|
+
pointing to @Article.get(1).comments@.
|
99
|
+
|
100
|
+
It's important to note, that no database operations will be performed more than once!
|
101
|
+
So in the case of @/articles/1/comments/1/ratings@, @merb_resource_controller@ will do exactly what you would
|
102
|
+
expect it to do: It will issue @Article.get(1).comments.get(1).ratings@, initializing
|
103
|
+
@@article@, @@comment@ and @@ratings@ with the intermediate results on it's way down the association chain.
|
104
|
+
|
105
|
+
Speaking of an _association chain_, it is worth mentioning that it is by no means necessary that your
|
106
|
+
underlying models are connected via _real datamapper associations_ (like @has n, ...@ or @belongs_to@).
|
107
|
+
The only requirement is, that the object returned from a call to the nested _collection_ @respond_to?(:get)@
|
108
|
+
(in most cases it will be a @DataMapper::Collection@ anyway).
|
109
|
+
|
110
|
+
|
111
|
+
<pre>
|
112
|
+
</code>
|
113
|
+
class Comments < Application
|
114
|
+
controlling :comments do |c|
|
115
|
+
c.belongs_to :article
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Ratings < Application
|
120
|
+
controlling :ratings do |r|
|
121
|
+
r.belongs_to [ :article, :comment ]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
</code>
|
125
|
+
</pre>
|
126
|
+
|
127
|
+
h2. Nested Singleton Resources
|
128
|
+
|
129
|
+
First of all, singleton resources (currently) must always be nested below at least one parent resource.
|
130
|
+
|
131
|
+
This is mostly because I somehow can't really imagine a usecase for a real toplevel singleton resource? I mean, there
|
132
|
+
must be _some_ information on _how_ to load that _one_ resource. Since we're mostly dealing with database backends here,
|
133
|
+
we should probably need some kind of _key_ to identify exactly _one_ dataset, but we don't get that _key_ from a resource
|
134
|
+
like @/profile@ (_where_ is the @:id@ param here?). So my guess is, that most of the toplevel singleton resources that
|
135
|
+
_are_ actually used, really are nested below some kind of resource that's stored in the @session@, most probably the
|
136
|
+
authenticated @user@ object. If this is the case, it would be perfectly valid, to just _really nest_ those singletons
|
137
|
+
below their real parent, and probably add a route alias to keep up the disguise. If you can come up with a usecase
|
138
|
+
where you have a real toplevel singleton resource that needs _no key_ to be loaded, just don't use
|
139
|
+
@merb_resource_controller@. Really, it's that simple! If however, I'm completely on the wrong track here, I would be glad
|
140
|
+
to be informed about it, so that I can fix things up.
|
141
|
+
|
142
|
+
The example code below behaves almost exactly like described in the section on _Nested Resources_ above. The only
|
143
|
+
two differences are, that 1) @no index action@ will be generated and that 2) the initialized instance variables will be
|
144
|
+
named @@article@ and @@editor@ if you have a singleton resource nested like @/articles/1/editor@. Just like you would
|
145
|
+
expect, no?
|
146
|
+
|
147
|
+
<pre>
|
148
|
+
<code>
|
149
|
+
class Editors < Application
|
150
|
+
|
151
|
+
controlling :editor, :singleton => true do |e|
|
152
|
+
e.belongs_to :article
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
</code>
|
157
|
+
</pre>
|
158
|
+
|
159
|
+
h2. DataMapper's Identity Map
|
160
|
+
|
161
|
+
Including @Merb::ResourceController::DM::IdentityMapSupport@ into any of your controller will wrap all actions
|
162
|
+
inside a dm repository block in order to be able to use DM's identitiy map feature. For more information on that topic,
|
163
|
+
have a look at "DataMapper's Identity Map":http://datamapper.org/doku.php?id=docs:identity_map
|
164
|
+
|
165
|
+
Feel free to include this module into _single_ controllers instead of including it into @Application@ controller, which
|
166
|
+
enables identity maps for _every_ controller action in your app.
|
167
|
+
|
168
|
+
<pre>
|
169
|
+
<code>
|
170
|
+
class Application < Merb::Controller
|
171
|
+
include Merb::ResourceController::DM::IdentityMapSupport
|
172
|
+
end
|
173
|
+
</code>
|
174
|
+
</pre>
|
175
|
+
|
176
|
+
h2. Action Timeouts
|
177
|
+
|
178
|
+
Extending @Merb::ResourceController::ActionTimeout@ into any of your controllers, will give you the @set_action_timeout@
|
179
|
+
method. If you specify an action_timeout greater or equal to @1@ and you have the @system_timer@ gem installed, the
|
180
|
+
action will timeout after the given amount of seconds. For a more detailed explanation on the topic have a look at
|
181
|
+
"Battling Wedged Mongrels with a Request Timeout":http://adam.blog.heroku.com/past/2008/6/17/battling_wedged_mongrels_with_a/
|
182
|
+
|
183
|
+
Feel free to set action timeouts for _single_ controllers instead of doing so in @Application@ controller, which sets
|
184
|
+
the same action timeout for _every_ controller action in your app.
|
185
|
+
|
186
|
+
<pre>
|
187
|
+
<code>
|
188
|
+
class Application < Merb::Controller
|
189
|
+
extend Merb::ResourceController::ActionTimeout
|
190
|
+
set_action_timeout 1
|
191
|
+
end
|
192
|
+
</code>
|
193
|
+
</pre>
|
194
|
+
|
195
|
+
h2. Defined Actions
|
196
|
+
|
197
|
+
All the above controllers will have the following actions defined. Feel free to override them, to customize
|
198
|
+
how the controllers behave. Of course you are also free to override all the methods used by these defined actions.
|
199
|
+
|
200
|
+
<pre>
|
201
|
+
<code>
|
202
|
+
def index
|
203
|
+
load_resource
|
204
|
+
display requested_resource
|
205
|
+
end
|
206
|
+
|
207
|
+
def show
|
208
|
+
load_resource
|
209
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
210
|
+
display requested_resource
|
211
|
+
end
|
212
|
+
|
213
|
+
def new
|
214
|
+
only_provides :html
|
215
|
+
load_resource
|
216
|
+
set_member(new_member)
|
217
|
+
display member
|
218
|
+
end
|
219
|
+
|
220
|
+
def edit
|
221
|
+
only_provides :html
|
222
|
+
load_resource
|
223
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
224
|
+
display requested_resource
|
225
|
+
end
|
226
|
+
|
227
|
+
def create
|
228
|
+
load_resource
|
229
|
+
set_member(new_member(params[member_name]))
|
230
|
+
if member.save
|
231
|
+
options = flash_supported? ? { :message => successful_create_messages } : {}
|
232
|
+
redirect redirect_on_successful_create, options
|
233
|
+
else
|
234
|
+
message.merge!(failed_create_messages) if flash_supported?
|
235
|
+
render :new
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def update
|
240
|
+
load_resource
|
241
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
242
|
+
if requested_resource.update_attributes(params[member_name])
|
243
|
+
options = flash_supported? ? { :message => successful_update_messages } : {}
|
244
|
+
redirect redirect_on_successful_update, options
|
245
|
+
else
|
246
|
+
message.merge!(failed_update_messages) if flash_supported?
|
247
|
+
display requested_resource, :edit
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def destroy
|
252
|
+
load_resource
|
253
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
254
|
+
if requested_resource.destroy
|
255
|
+
options = flash_supported? ? { :message => successful_destroy_messages } : {}
|
256
|
+
redirect redirect_on_successful_destroy, options
|
257
|
+
else
|
258
|
+
raise Merb::ControllerExceptions::InternalServerError
|
259
|
+
end
|
260
|
+
end
|
261
|
+
</code>
|
262
|
+
</pre>
|
263
|
+
|
264
|
+
h2. Additional Helper Methods
|
265
|
+
|
266
|
+
In addition to the actions and the methods they are using, you will probably want to override the redirection targets
|
267
|
+
These are the default method implementations you can override to this effect:
|
268
|
+
|
269
|
+
<pre>
|
270
|
+
<code>
|
271
|
+
def redirect_on_successful_create
|
272
|
+
target = singleton_controller? ? member_name : member
|
273
|
+
resource(*(has_parent? ? parents + [ target ] : [ target ]))
|
274
|
+
end
|
275
|
+
|
276
|
+
def redirect_on_successful_update
|
277
|
+
target = singleton_controller? ? member_name : member
|
278
|
+
resource(*(has_parent? ? parents + [ target ] : [ target ]))
|
279
|
+
end
|
280
|
+
|
281
|
+
def redirect_on_successful_destroy
|
282
|
+
if singleton_controller?
|
283
|
+
has_parent? ? resource(parent) : '/'
|
284
|
+
else
|
285
|
+
resource(*(has_parent? ? parents + [ collection_name ] : [ collection_name ]))
|
286
|
+
end
|
287
|
+
end
|
288
|
+
</code>
|
289
|
+
</pre>
|
290
|
+
|
291
|
+
h2. More Information
|
292
|
+
|
293
|
+
Have a look at @Merb::ResourceController::Actions@, @Merb::ResourceController::Mixin::InstanceMethods@ and
|
294
|
+
@Merb::ResourceController::Mixin::FlashSupport@ inside @lib/merb_resource_controller/actions.rb@ and
|
295
|
+
@lib/merb_resource_controller/resource_controller.rb@ to see what methods will be available inside your controllers.
|
296
|
+
If you want to see where the _real work gets done_, have a look at @lib/merb_resource_controller/resource_proxy.rb@
|
297
|
+
|
298
|
+
Of course, you should also have a look at the specs, to get a more concrete idea of how @merb_resource_controller@
|
299
|
+
behaves under the given circumstances.
|
300
|
+
|
301
|
+
h2. TODO
|
302
|
+
|
303
|
+
# Infer route nesting strategy from @Merb::Router@
|
304
|
+
# Customizable support for @provides@ and @only_provides@
|
305
|
+
# Support for user stamps (aka created_by and updated_by)
|
306
|
+
# Support for pagination once an _official_ merb pagination solution exists
|
data/Rakefile
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
require 'merb-core'
|
5
|
+
require 'merb-core/tasks/merb'
|
6
|
+
require "spec/rake/spectask"
|
7
|
+
|
8
|
+
GEM_NAME = "merb_resource_controller"
|
9
|
+
GEM_VERSION = "0.1.0"
|
10
|
+
AUTHOR = "Martin Gamsjaeger"
|
11
|
+
EMAIL = "gamsnjaga@gmail.com"
|
12
|
+
HOMEPAGE = "http://merbivore.com/"
|
13
|
+
SUMMARY = "A merb plugin that provides the default restful actions for controllers."
|
14
|
+
|
15
|
+
spec = Gem::Specification.new do |s|
|
16
|
+
|
17
|
+
s.rubyforge_project = 'merb_resource_controller'
|
18
|
+
s.name = GEM_NAME
|
19
|
+
s.version = GEM_VERSION
|
20
|
+
s.platform = Gem::Platform::RUBY
|
21
|
+
s.has_rdoc = false
|
22
|
+
s.extra_rdoc_files = [ "LICENSE", 'TODO']
|
23
|
+
s.summary = SUMMARY
|
24
|
+
s.description = s.summary
|
25
|
+
s.author = AUTHOR
|
26
|
+
s.email = EMAIL
|
27
|
+
s.homepage = HOMEPAGE
|
28
|
+
s.require_path = 'lib'
|
29
|
+
|
30
|
+
# be extra picky so that no coverage info and such gets included
|
31
|
+
s.files = %w(LICENSE README.textile Rakefile TODO) +
|
32
|
+
Dir.glob("{lib,spec}/**/*.rb") +
|
33
|
+
Dir.glob("{spec}/**") +
|
34
|
+
Dir.glob("{spec}/**/*.rb") +
|
35
|
+
Dir.glob("{spec}/**/*.yml") +
|
36
|
+
Dir.glob("{spec}/**/*.opts") +
|
37
|
+
Dir.glob("{spec}/**/*.html.erb") +
|
38
|
+
[ "spec/mrc_test_app/Rakefile"]
|
39
|
+
|
40
|
+
# runtime dependencies
|
41
|
+
s.add_dependency('merb-core', '~> 1.0')
|
42
|
+
|
43
|
+
# development dependencies
|
44
|
+
# if these are desired, install with:
|
45
|
+
# gem install merb_resource_controller --development
|
46
|
+
s.add_development_dependency('merb-assets', '~>1.0')
|
47
|
+
s.add_development_dependency('merb-helpers', '~>1.0')
|
48
|
+
s.add_development_dependency('dm-core', '~>0.9.8')
|
49
|
+
s.add_development_dependency('dm-validations', '~>0.9.8')
|
50
|
+
s.add_development_dependency('dm-serializer', '~>0.9.8')
|
51
|
+
s.add_development_dependency('dm-constraints', '~>0.9.8')
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
56
|
+
pkg.gem_spec = spec
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "install the plugin as a gem"
|
60
|
+
task :install do
|
61
|
+
Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "Uninstall the gem"
|
65
|
+
task :uninstall do
|
66
|
+
Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "Create a gemspec file"
|
70
|
+
task :gemspec do
|
71
|
+
File.open("#{GEM_NAME}.gemspec", "w") do |file|
|
72
|
+
file.puts spec.to_ruby
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
desc 'Default: run spec examples'
|
77
|
+
task :spec do
|
78
|
+
puts "!!! README !!! Run 'rake spec' from the './spec/mrc_test_app' directory to run all specs for merb_resource_controller."
|
79
|
+
end
|
80
|
+
|
81
|
+
task :default => 'spec'
|
data/TODO
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'system_timer'
|
2
|
+
|
3
|
+
module Merb
|
4
|
+
module ResourceController
|
5
|
+
|
6
|
+
# To be extended into controllers
|
7
|
+
module ActionTimeout
|
8
|
+
|
9
|
+
def self.for?(controller)
|
10
|
+
self.for(controller) > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.for(controller)
|
14
|
+
(@controllers ||= {})[Application] || @controllers[controller] || 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.register(controller, seconds)
|
18
|
+
(@controllers ||= {})[controller] = seconds
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def set_action_timeout(seconds)
|
23
|
+
if seconds >= 1
|
24
|
+
ActionTimeout.register(self, seconds)
|
25
|
+
include InstanceMethods
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
|
31
|
+
# taken from:
|
32
|
+
# http://datamapper.org/doku.php?id=docs:identity_map
|
33
|
+
def _call_action(*args)
|
34
|
+
if Merb::Plugins.config[:merb_resource_controller][:action_timeout]
|
35
|
+
return super unless ActionTimeout.for?(self.class)
|
36
|
+
timeout = ActionTimeout.for(self.class)
|
37
|
+
Merb.logger.info "SystemTimer.timeout for #{args[0]}: #{timeout} seconds"
|
38
|
+
# more information on system_timer:
|
39
|
+
# http://adam.blog.heroku.com/past/2008/6/17/battling_wedged_mongrels_with_a/
|
40
|
+
SystemTimer.timeout(timeout) do
|
41
|
+
super
|
42
|
+
end
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module Merb
|
2
|
+
module ResourceController
|
3
|
+
|
4
|
+
module Actions
|
5
|
+
|
6
|
+
module Index
|
7
|
+
|
8
|
+
# def index
|
9
|
+
# @articles = Article.all
|
10
|
+
# display @articles
|
11
|
+
# end
|
12
|
+
|
13
|
+
def index
|
14
|
+
load_resource
|
15
|
+
display requested_resource
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module Show
|
21
|
+
|
22
|
+
# def show(id)
|
23
|
+
# @article = Article.get(id)
|
24
|
+
# raise NotFound unless @article
|
25
|
+
# display @article
|
26
|
+
# end
|
27
|
+
|
28
|
+
def show
|
29
|
+
load_resource
|
30
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
31
|
+
display requested_resource
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
module New
|
37
|
+
|
38
|
+
# def new
|
39
|
+
# only_provides :html
|
40
|
+
# @article = Article.new
|
41
|
+
# display @article
|
42
|
+
# end
|
43
|
+
|
44
|
+
def new
|
45
|
+
only_provides :html
|
46
|
+
load_resource
|
47
|
+
set_member(new_member)
|
48
|
+
display member
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
module Edit
|
54
|
+
|
55
|
+
# def edit(id)
|
56
|
+
# only_provides :html
|
57
|
+
# @article = Article.get(id)
|
58
|
+
# raise NotFound unless @article
|
59
|
+
# display @article
|
60
|
+
# end
|
61
|
+
|
62
|
+
def edit
|
63
|
+
only_provides :html
|
64
|
+
load_resource
|
65
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
66
|
+
display requested_resource
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
module Create
|
72
|
+
|
73
|
+
# def create(article)
|
74
|
+
# @article = Article.new(article)
|
75
|
+
# if @article.save
|
76
|
+
# redirect resource(@article), :message => { :notice => "Article was successfully created" }
|
77
|
+
# else
|
78
|
+
# message[:error] = "Article failed to be created"
|
79
|
+
# render :new
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
|
83
|
+
def create
|
84
|
+
load_resource
|
85
|
+
set_member(new_member)
|
86
|
+
if member.save
|
87
|
+
options = flash_supported? ? { :message => successful_create_messages } : {}
|
88
|
+
redirect redirect_on_successful_create, options
|
89
|
+
else
|
90
|
+
message.merge!(failed_create_messages) if flash_supported?
|
91
|
+
render :new, :status => 406
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def redirect_on_successful_create
|
96
|
+
target = singleton_controller? ? member_name : member
|
97
|
+
resource(*(has_parent? ? parents + [ target ] : [ target ]))
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
module Update
|
103
|
+
|
104
|
+
# def update(id, article)
|
105
|
+
# @article = Article.get(id)
|
106
|
+
# raise NotFound unless @article
|
107
|
+
# if @article.update_attributes(article)
|
108
|
+
# redirect resource(@article), :message => { :notice => "Article was successfully updated" }
|
109
|
+
# else
|
110
|
+
# display @article, :edit
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
|
114
|
+
def update
|
115
|
+
load_resource
|
116
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
117
|
+
if requested_resource.update_attributes(params[member_name])
|
118
|
+
options = flash_supported? ? { :message => successful_update_messages } : {}
|
119
|
+
redirect redirect_on_successful_update, options
|
120
|
+
else
|
121
|
+
message.merge!(failed_update_messages) if flash_supported?
|
122
|
+
display requested_resource, :edit, :status => 406
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def redirect_on_successful_update
|
127
|
+
target = singleton_controller? ? member_name : member
|
128
|
+
resource(*(has_parent? ? parents + [ target ] : [ target ]))
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
module Destroy
|
134
|
+
|
135
|
+
# def destroy(id)
|
136
|
+
# @article = Article.get(id)
|
137
|
+
# raise NotFound unless @article
|
138
|
+
# if @article.destroy
|
139
|
+
# redirect resource(:articles)
|
140
|
+
# else
|
141
|
+
# raise InternalServerError
|
142
|
+
# end
|
143
|
+
# end
|
144
|
+
|
145
|
+
def destroy
|
146
|
+
load_resource
|
147
|
+
raise Merb::ControllerExceptions::NotFound unless requested_resource
|
148
|
+
if requested_resource.destroy
|
149
|
+
options = flash_supported? ? { :message => successful_destroy_messages } : {}
|
150
|
+
redirect redirect_on_successful_destroy, options
|
151
|
+
else
|
152
|
+
raise Merb::ControllerExceptions::InternalServerError
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def redirect_on_successful_destroy
|
157
|
+
if singleton_controller?
|
158
|
+
has_parent? ? resource(parent) : '/'
|
159
|
+
else
|
160
|
+
resource(*(has_parent? ? parents + [ collection_name ] : [ collection_name ]))
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Merb
|
2
|
+
module ResourceController
|
3
|
+
module DM
|
4
|
+
|
5
|
+
# include this into controllers
|
6
|
+
module IdentityMapSupport
|
7
|
+
|
8
|
+
# taken from:
|
9
|
+
# http://datamapper.org/doku.php?id=docs:identity_map
|
10
|
+
def _call_action(*)
|
11
|
+
repository do |r| # enable identity_map
|
12
|
+
Merb.logger.info "Using DM Identity map inside #{r.name} repository block"
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|