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