magic-resource 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +475 -0
- data/lib/generators/magic_resource/install_generator.rb +17 -0
- data/lib/generators/magic_resource/templates/config/locales/en/resources.yml +11 -0
- data/lib/generators/magic_resource/templates/magic-resource.rb +38 -0
- data/lib/magic-resource.rb +76 -0
- data/lib/magic-resource/container.rb +288 -0
- data/lib/magic-resource/controller.rb +73 -0
- data/lib/magic-resource/feature.rb +33 -0
- data/lib/magic-resource/helper.rb +11 -0
- data/lib/magic-resource/path_with_http_verb.rb +13 -0
- data/lib/magic-resource/railtie.rb +1 -0
- data/lib/magic-resource/version.rb +3 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 88c7e7680c76cf4d34bb5b067c01a80d731fb55d
|
4
|
+
data.tar.gz: 211494d054116ea4373f63f49f31eeba5b20bcca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0f22e8a2cc3675a69c97453792b260b5abd919878e3304e6b48b91535b87ed11a779c083e7ad762e0da1822184e141f8ffa8aac23a52d58875f2a09f467ce353
|
7
|
+
data.tar.gz: f2f8eafac949a968111a00cd084d198c307f82faae122bdc07d6339fa5e13afbf79eadc3b8373e6fbf1f01b219b34618c84b1b276b878c19e857580002286d2f
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Sergey Tokarenko
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,475 @@
|
|
1
|
+
MagicResource
|
2
|
+
==============
|
3
|
+
|
4
|
+
MagicResource is a Rails plugin which introduces the resource-based ideology for WEB applications development.
|
5
|
+
|
6
|
+
## Getting Started
|
7
|
+
|
8
|
+
Add to your Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'magic-resource', git: 'git@gitlab.anahoret.com:anadea/magic-resource.git', tag: 'v0.0.3'
|
12
|
+
```
|
13
|
+
|
14
|
+
Run the bundle command to install it.
|
15
|
+
|
16
|
+
Run the generator:
|
17
|
+
```console
|
18
|
+
rails generate magic_resource:install
|
19
|
+
```
|
20
|
+
|
21
|
+
Include to controller, typically to ApplicationController:
|
22
|
+
```ruby
|
23
|
+
class ApplicationController < ActionController::Base
|
24
|
+
include MagicResource::Controller
|
25
|
+
```
|
26
|
+
|
27
|
+
## Resource abstraction
|
28
|
+
It is a quite naturally when we affecting to something, we always affecting to specific object or to the scope of alike objects. Just like the calling the instance method or class method. Even when we want to do something like `turn off all electrical devices in the house`, it's better and more natural to trigger the `turn_off_the_devices` method on `House.belongs_to(me)` object, rather than to implement inplace logic which will find all the devices, and do something with them.
|
29
|
+
|
30
|
+
Lets think like that when building the RoR application. Any time when the browser makes the request to the server, it affects the specific object, or to the scope of alike object. In RoR terms, each call to the server affects to specific Model instance, or to the scope of such Model.
|
31
|
+
|
32
|
+
Each Model has `resource_name`, typically that is the model's class name, underscored and pluralized. For Property model that will be `:properties`.
|
33
|
+
|
34
|
+
Then, lets add the `resource_context` when we affecting to the Model. It is just a Symbol identity. It should have just a logical sense, but not tied to any terms from the application like user roles, application areas, menu items etc.
|
35
|
+
|
36
|
+
For example, lets say that `admin` users should have an access to all crud actions for the Property model, and `guest` users should have acccess to show and filterable index actions only. It's better to say that Property model can be affected under `:crud` and `:search` contexts, rather than `:admin` and `:guest` ones. Then, if `admin` role will be renamed to `superadmin`, if crud actions will be available for `manager` as well etc - `:crud` context name will not loose the sense, rather than `:admin` context.
|
37
|
+
|
38
|
+
The affecting to the Model is always has some context, it can't be blank. Lets say that default context for any Model is `:crud`.
|
39
|
+
|
40
|
+
Each resource context means the separated controller and the special folder for templates. To finish the picture, lets add the single localization file, then say:
|
41
|
+
> Resource is the container-like abstraction, which includes the Model, the contexts to be applied when affecting the Model, the scope of one-per-context Controllers, the scope of templates to be rendered with the Model, and single localization file.
|
42
|
+
|
43
|
+
## Sources structure
|
44
|
+
### Controllers
|
45
|
+
Controllers should be placed to `app/controllers/#{resource_name}/#{resource_context}_controller.rb` and named accordingly. For Property model and :crud context it should be placed to `app/controllers/properties/crud_controller.rb` and contains something like:
|
46
|
+
```ruby
|
47
|
+
module Properties
|
48
|
+
class CrudController < ApplicationController
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
### Models
|
54
|
+
Nothing specific, place and name them as always.
|
55
|
+
|
56
|
+
### Views
|
57
|
+
There are the general folder for resource templates, named `app/views/#{resource_name}/`. In the root of this folter should be placed the templates which you normally want to name `shared`. In folters like `app/views/#{resource_name}/#{resource_context}/` should be placed the context-specific templates.
|
58
|
+
|
59
|
+
The typical scope of templates for Property model under :crud context should looks like:
|
60
|
+
* app/views/properties/crud/edit.html.haml
|
61
|
+
* app/views/properties/crud/index.html.haml
|
62
|
+
* app/views/properties/crud/new.html.haml
|
63
|
+
* app/views/properties/crud/show.html.haml
|
64
|
+
* app/views/properties/_form_attrs.html.haml
|
65
|
+
* app/views/properties/_list_item.html.haml
|
66
|
+
* app/views/properties/_form_search_attrs.html.haml
|
67
|
+
* app/views/properties/_view.html.haml
|
68
|
+
|
69
|
+
### Locales
|
70
|
+
The single locale file should be placed to `config/locales/en/#{resource_name}.yml`. For Property model it should be placed to `config/locales/en/properties.yml` and contains something like:
|
71
|
+
```yml
|
72
|
+
en:
|
73
|
+
properties:
|
74
|
+
button_edit: 'Edit Property'
|
75
|
+
```
|
76
|
+
|
77
|
+
## Magic
|
78
|
+
When we speaking about the `magic`, we mean some smart and powerful instruments. Just like:
|
79
|
+
```ruby
|
80
|
+
redirect_to user
|
81
|
+
```
|
82
|
+
|
83
|
+
But, uncontrolled usage of such things is not good:
|
84
|
+
```ruby
|
85
|
+
redirect_to my_favorite_variable
|
86
|
+
```
|
87
|
+
Where we go? Lets guess that to `users#show` action, and we going to rename the action or controller - how we can find all places in the sources which redirects to `users#show` in this way?
|
88
|
+
|
89
|
+
Lets define, that the `magic` is good when it:
|
90
|
+
* invariantly predictable
|
91
|
+
* searchable in the sources
|
92
|
+
* can be banned partially or totally in whole project
|
93
|
+
|
94
|
+
## General rules
|
95
|
+
Lets separate the controller's action and templates to two group - collection and member, dependent when we working with the single Model instance of with Model's scope.
|
96
|
+
|
97
|
+
### Set the resource object
|
98
|
+
In `collection` controller actions call `resources=` method:
|
99
|
+
```ruby
|
100
|
+
def index
|
101
|
+
self.resources = Property.all
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
In `member` controller actions call `resource=` method:
|
106
|
+
```ruby
|
107
|
+
def show
|
108
|
+
self.resource = Property.find(params[:id])
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
Controller will set the resource context automatically by controller's class name.
|
113
|
+
|
114
|
+
### Get the resource object
|
115
|
+
In 'collection' controller actions and templates call 'resources' method:
|
116
|
+
```ruby
|
117
|
+
resources.size
|
118
|
+
```
|
119
|
+
|
120
|
+
In 'member' controller actions and templates call 'resource' method:
|
121
|
+
```ruby
|
122
|
+
resource.full_name
|
123
|
+
```
|
124
|
+
|
125
|
+
### Magic patterns
|
126
|
+
|
127
|
+
The full pattern looks like `#{resource_name}::#{resource_context}#{separator}#{identifier}`. Dependent on the magic method, it changes the separator and identifier sense:
|
128
|
+
```ruby
|
129
|
+
# the way to controller's action
|
130
|
+
r.path_to 'users::search#show'
|
131
|
+
|
132
|
+
# the template path
|
133
|
+
r.render 'users::search/view'
|
134
|
+
|
135
|
+
# the translation key
|
136
|
+
r.t 'users::search.button_search'
|
137
|
+
```
|
138
|
+
|
139
|
+
When the context name is skipped, it means the inheritance of current context for current object, or apllying the default context to other object:
|
140
|
+
```ruby
|
141
|
+
r.path_to 'users#show' # inherit the current context
|
142
|
+
|
143
|
+
r(resource).path_to 'users#show' # the same as
|
144
|
+
r(resource).path_to 'users::crud#show'
|
145
|
+
```
|
146
|
+
|
147
|
+
MagicResource will check is the object's type matching to specified resource name:
|
148
|
+
```ruby
|
149
|
+
r(Property).path_to 'users#show' # raise exception
|
150
|
+
```
|
151
|
+
|
152
|
+
For current object is possible to skip the resource name:
|
153
|
+
```ruby
|
154
|
+
r.path_to '#index'
|
155
|
+
```
|
156
|
+
|
157
|
+
For current object is possible to specify just identifier:
|
158
|
+
```ruby
|
159
|
+
r.path_to :show
|
160
|
+
```
|
161
|
+
|
162
|
+
### Access to the magic
|
163
|
+
Typically you want to inherit the current resource object and resource_context for the magic. To do this, in controller or template call `r` method. You are able to use short patterns in magic calls, as well as combined and full patterns:
|
164
|
+
```ruby
|
165
|
+
r.path_to :show
|
166
|
+
r.path_to 'properties#show'
|
167
|
+
r.path_to '::search#show'
|
168
|
+
r.path_to 'properties::search#show'
|
169
|
+
```
|
170
|
+
|
171
|
+
Sometimes, you want to make a magic with some `other object`. To do this, in controller or template call `r` method with object as parameter. Once the `other object` is specified, you are forced to use only full patterns in magic calls:
|
172
|
+
```ruby
|
173
|
+
r(resource.user).path_to 'users#show'
|
174
|
+
r(resource.user).path_to 'users::search#show'
|
175
|
+
```
|
176
|
+
|
177
|
+
When you pass the current resource as parameter, it anyway means that the `other object` is specified - you loose the current context and must use the full patterns in magic calls:
|
178
|
+
```ruby
|
179
|
+
r(resource).path_to 'properties#show'
|
180
|
+
r(resource).path_to 'properties::search#show'
|
181
|
+
```
|
182
|
+
|
183
|
+
When you don't have the specific object, pass the class as parameter. It again means the `other object` rules:
|
184
|
+
```ruby
|
185
|
+
r(User).path_to 'users#index'
|
186
|
+
```
|
187
|
+
|
188
|
+
You can pass the form object as parameter, in this case the object inside it will be a the `other object`:
|
189
|
+
```haml
|
190
|
+
= r.form_for :update do |f|
|
191
|
+
= f.simple_fields_for :user do |uf|
|
192
|
+
= r(ff).render 'users/form_attrs', f: ff
|
193
|
+
```
|
194
|
+
|
195
|
+
## Magic methods
|
196
|
+
### r.context?
|
197
|
+
Check is the resource context is in the args list:
|
198
|
+
```ruby
|
199
|
+
r.context?(:crud, :search) # true
|
200
|
+
```
|
201
|
+
|
202
|
+
### r.t
|
203
|
+
The call looks like:
|
204
|
+
```ruby
|
205
|
+
r.t :button_edit
|
206
|
+
r(user).t 'users::crud.button_edit', {interpolation_option: 'foo'}
|
207
|
+
```
|
208
|
+
|
209
|
+
It will search the translation in next order:
|
210
|
+
* users.crud.button_edit
|
211
|
+
* users.button_edit
|
212
|
+
* resources.crud.button_edit
|
213
|
+
* resources.button_edit
|
214
|
+
|
215
|
+
The method will not accept dotted keys like `.my.dotted.key`. But why do you want it actually? '.my_dotted_key' has the same count of chars, and it's better to separate the locales section by white space rather than to produce the one more subtree for nothing.
|
216
|
+
|
217
|
+
### r.render
|
218
|
+
The call looks like:
|
219
|
+
```ruby
|
220
|
+
r.render :form_attrs, f: f
|
221
|
+
r(user).render 'users::crud/form_attrs', f: f
|
222
|
+
```
|
223
|
+
|
224
|
+
It will search the template in next order:
|
225
|
+
* users/crud/_form_attrs
|
226
|
+
* users/_form_attrs
|
227
|
+
* resources/crud/_form_attrs
|
228
|
+
* resources/_form_attrs
|
229
|
+
|
230
|
+
The method will apply the specified object and context as `current` inside the target template.
|
231
|
+
|
232
|
+
The method will not accept the subfolder `/form/attrs` templates just like r.t method.
|
233
|
+
|
234
|
+
The accepts the form object as second param, and passes it as `f` local parameter:
|
235
|
+
```haml
|
236
|
+
= r.form_for :update do |f|
|
237
|
+
= r.render :form_attrs, f
|
238
|
+
```
|
239
|
+
|
240
|
+
It's good to render the other resource template, rather than mass calling to it's attributes or magic methods:
|
241
|
+
```haml
|
242
|
+
that is bad:
|
243
|
+
= r(resource.user).link_to 'users#show', resource.user.name
|
244
|
+
= resource.user.role
|
245
|
+
= resource.user.last_logged_in
|
246
|
+
|
247
|
+
that is good:
|
248
|
+
= r(resource.user).render 'users#view'
|
249
|
+
|
250
|
+
and then in 'app/views/users/_view'
|
251
|
+
= r.link_to :show, resource.name
|
252
|
+
= resource.role
|
253
|
+
= resource.last_logged_in
|
254
|
+
```
|
255
|
+
|
256
|
+
In general, when you see in your partial the massive definition of full magic pattern - it's time to move this part to other resource's partial.
|
257
|
+
|
258
|
+
### r.render_collection
|
259
|
+
The call looks like:
|
260
|
+
```ruby
|
261
|
+
r.render_collection :list_item
|
262
|
+
r(users).render_collection 'users::crud/list_item'
|
263
|
+
```
|
264
|
+
|
265
|
+
Just calls r.render for collection items, with current context inheritance in first sample.
|
266
|
+
|
267
|
+
### r.render_collection_build
|
268
|
+
The call looks like:
|
269
|
+
```ruby
|
270
|
+
r.render_collection_build :new_form
|
271
|
+
r(users).render_collection_build 'users::crud/new_form'
|
272
|
+
```
|
273
|
+
|
274
|
+
Calls the r.render for 'object.soft_build' object (look at `stokarenko/association-soft-build` gem), with current context inheritance in first sample.
|
275
|
+
|
276
|
+
### r.path_to
|
277
|
+
The call looks like:
|
278
|
+
```ruby
|
279
|
+
r.path_to :show
|
280
|
+
r(user).path_to 'users::crud#show'
|
281
|
+
```
|
282
|
+
|
283
|
+
Returns the URL to specific controller/action.
|
284
|
+
|
285
|
+
To pass the URL parameters call it in hashable way:
|
286
|
+
```ruby
|
287
|
+
r.path_to show: {foo: :bar}
|
288
|
+
r(user).path_to 'users::crud#show' => {foo: :bar}
|
289
|
+
```
|
290
|
+
|
291
|
+
### r.redirect_to
|
292
|
+
Can be used only in controller.
|
293
|
+
|
294
|
+
The call looks like:
|
295
|
+
```ruby
|
296
|
+
r.redirect_to :show
|
297
|
+
r(user).redirect_to 'users::crud#show'
|
298
|
+
```
|
299
|
+
|
300
|
+
Redirects to specific controller/action.
|
301
|
+
|
302
|
+
To pass the URL parameters call it in hashable way:
|
303
|
+
```ruby
|
304
|
+
r.redirect_to show: {foo: :bar}
|
305
|
+
r(user).redirect_to 'users::crud#show' => {foo: :bar}
|
306
|
+
```
|
307
|
+
|
308
|
+
### r.link_to
|
309
|
+
The call looks like:
|
310
|
+
```ruby
|
311
|
+
r.link_to :edit
|
312
|
+
r(user).link_to 'users::crud#edit', 'Special edit button'
|
313
|
+
```
|
314
|
+
|
315
|
+
The method will build the link html tag to specific controller/action.
|
316
|
+
|
317
|
+
It will accept the URL parameters in hashable way, just like `r.path_to` do.
|
318
|
+
|
319
|
+
It will automatically apply the HTTP verb for the link.
|
320
|
+
|
321
|
+
It will use `:button_#{action}` as the label param by default, then...
|
322
|
+
|
323
|
+
It will apply `r.t` method if the label param is a Symbol.
|
324
|
+
|
325
|
+
It will use string label param for label as it is.
|
326
|
+
|
327
|
+
It will automatically set `:confirm` param to `true` for `:destroy` actions, then
|
328
|
+
|
329
|
+
It will set `:confirm` param to `:confirm_destroy` if it is true, then
|
330
|
+
|
331
|
+
It will apply r.t method for confirmation message if `:confirm` param if is a Symbol.
|
332
|
+
|
333
|
+
It will apply `:confirm` string param as confirmation message as it is.
|
334
|
+
|
335
|
+
It will accept the block to be used as label parameter.
|
336
|
+
|
337
|
+
### r.link_to_if
|
338
|
+
The call looks like:
|
339
|
+
```ruby
|
340
|
+
r.link_to_if true, :edit
|
341
|
+
r(user).link_to_if false, 'users::crud#edit'
|
342
|
+
```
|
343
|
+
|
344
|
+
Works just like regular `link_to_if` but uses r.link_to on success.
|
345
|
+
|
346
|
+
It will accept the URL parameters in hashable way, just like `r.path_to` do.
|
347
|
+
|
348
|
+
It will accept the block to be used as label parameter.
|
349
|
+
|
350
|
+
### r.link_to_unless
|
351
|
+
The call looks like:
|
352
|
+
```ruby
|
353
|
+
r.link_to_unless false, :edit
|
354
|
+
r(user).link_to_unless true, 'users::crud#edit'
|
355
|
+
```
|
356
|
+
|
357
|
+
Works just like regular `link_to_unless` but uses r.link_to on success.
|
358
|
+
|
359
|
+
It will accept the URL parameters in hashable way, just like `r.path_to` do.
|
360
|
+
|
361
|
+
It will accept the block to be used as label parameter.
|
362
|
+
|
363
|
+
### r.form_for
|
364
|
+
The call looks like:
|
365
|
+
```ruby
|
366
|
+
r.form_for :update do |f|
|
367
|
+
end
|
368
|
+
|
369
|
+
r(user).form_for 'users::crud#update' do |f|
|
370
|
+
end
|
371
|
+
```
|
372
|
+
|
373
|
+
Build the form to specific controller/action.
|
374
|
+
|
375
|
+
It will accept the URL parameters in hashable way, just like `r.path_to` do.
|
376
|
+
|
377
|
+
It automatically set the HTTP verb.
|
378
|
+
|
379
|
+
It uses the default `:simple` form helper prefix, so the form will be built with `simple_form_for` helper.
|
380
|
+
|
381
|
+
It takes the second param as the form helper prefix to be used.
|
382
|
+
|
383
|
+
It supports Ransack, just pass Ransack::Search object as second param.
|
384
|
+
|
385
|
+
It takes the logical `#save` action, which will be transformed to `#create` or `#update` actions dependent on the object's persistency status.
|
386
|
+
|
387
|
+
### r.content_for
|
388
|
+
This method is too much magical, and disabled by default. Activate it in configuration if you really need it.
|
389
|
+
|
390
|
+
The usage looks like:
|
391
|
+
```haml
|
392
|
+
- r(resource.user).content_for 'users:header_class', 'strong'
|
393
|
+
- r(resource.user).content_for 'users:header_prefix' do
|
394
|
+
some super prefix
|
395
|
+
|
396
|
+
= r(resource.user).render 'users::crud/view'
|
397
|
+
```
|
398
|
+
|
399
|
+
Then, in `app/views/users/crud/_view` template:
|
400
|
+
```haml
|
401
|
+
.header{class: r.content(:header_class) || :default}
|
402
|
+
= r.content(:header_prefix)
|
403
|
+
the body
|
404
|
+
```
|
405
|
+
|
406
|
+
The same can be done by locals passing to render, but sometimes we need to forward such locals deeply and deeply to other render calls. Looks like `r.content_for` method is a better solution for that...
|
407
|
+
|
408
|
+
## Configuration
|
409
|
+
MagicResource generator will prepare the `config/initializers/magic-resource.rb` config file.
|
410
|
+
Please take a look on it:
|
411
|
+
```ruby
|
412
|
+
MagicResource.setup do |config|
|
413
|
+
## Uncoment the line to change the context to be used as default.
|
414
|
+
## Default value is `:crud`.
|
415
|
+
# config.default_context = :manage
|
416
|
+
|
417
|
+
## Uncoment the line to change default form helper prefix.
|
418
|
+
## Default value is `:simple`.
|
419
|
+
# config.default_form_type = :default
|
420
|
+
|
421
|
+
## Change the method to adjust the level or resource magic.
|
422
|
+
## possible methods are `no_magic`, `try_magic?` and `full_magic!`
|
423
|
+
## Thechnically such methods just setting all following parameters to
|
424
|
+
## `:by_exception`, `:by_warning` and `false` respectively.
|
425
|
+
config.no_magic
|
426
|
+
|
427
|
+
############################
|
428
|
+
#### For all next parameters
|
429
|
+
#### the correct values are: `[false, :by_exception, :by_warning]`.
|
430
|
+
|
431
|
+
## Will require to specify the resource name in helper's pattern when resource is "other".
|
432
|
+
## Can be useful for fast development or experiments. But typically it's better
|
433
|
+
## to switch it off later and define the resource names everywhere,
|
434
|
+
## because the magic becames to be not predictable and searchable.
|
435
|
+
# config.force_resource_name_definition = :by_exception
|
436
|
+
|
437
|
+
## Will require the correct resource assignation in controller
|
438
|
+
## Means the situation when inside Users::CrudController you trying to do
|
439
|
+
## self.resource = Property.first
|
440
|
+
# config.assert_resource_name_in_controller = :by_exception
|
441
|
+
|
442
|
+
## Will disable content_for helper.
|
443
|
+
## It is too much magical...
|
444
|
+
## But in some cases it is the better than other solutions.
|
445
|
+
## Try to avoid it.
|
446
|
+
## Activate it if you really need it.
|
447
|
+
# config.disable_content_for_helper = :by_exception
|
448
|
+
|
449
|
+
end
|
450
|
+
```
|
451
|
+
|
452
|
+
## Changes
|
453
|
+
### v0.0.3
|
454
|
+
* Added the possibility to pass URL params to routable helpers.
|
455
|
+
* Not ActiveModel-like classes can be a resource.
|
456
|
+
* `r` method will extract the `object` and use it as resource when form object received.
|
457
|
+
* `r.render` accepts the form object as second param, and passes it as `f` local parameter.
|
458
|
+
* Added `r.link_to_unless` helper.
|
459
|
+
* `r.link_to`-like helpers accept the block as label.
|
460
|
+
* Added `r.redirect_to` controller helper.
|
461
|
+
|
462
|
+
## TODO
|
463
|
+
* Implement resourcable Coccon, and make it less painful.
|
464
|
+
* Leave r object in controller as it was set there (?).
|
465
|
+
* Apply the passing of translation interpolation params as Hash (?).
|
466
|
+
* Separate the helpers for controller and view.
|
467
|
+
* Resource controllers.
|
468
|
+
* Automatic preload in controllers, bases on statistic of association calls in templates.
|
469
|
+
* inverse_of as default in ActiveModel (?).
|
470
|
+
* Resource tests (?).
|
471
|
+
* ActiveRecord translations.
|
472
|
+
* cancancan integration.
|
473
|
+
* Single-file navigation for whole application (?).
|
474
|
+
* Routes definition helper.
|
475
|
+
* Implement resources default templates, and configuration to turn them off.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MagicResource
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
desc 'Copy MagicResource default files'
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def copy_initializer
|
8
|
+
template 'magic-resource.rb', 'config/initializers/magic-resource.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
def copy_locales
|
12
|
+
directory 'config/locales'
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
MagicResource.setup do |config|
|
2
|
+
## Uncoment the line to change the context to be used as default.
|
3
|
+
## Default value is `:crud`.
|
4
|
+
# config.default_context = :manage
|
5
|
+
|
6
|
+
## Uncoment the line to change default form helper prefix.
|
7
|
+
## Default value is `:simple`.
|
8
|
+
# config.default_form_type = :default
|
9
|
+
|
10
|
+
## Change the method to adjust the level or resource magic.
|
11
|
+
## possible methods are `no_magic`, `try_magic?` and `full_magic!`
|
12
|
+
## Thechnically such methods just setting all following parameters to
|
13
|
+
## `:by_exception`, `:by_warning` and `false` respectively.
|
14
|
+
config.no_magic
|
15
|
+
|
16
|
+
############################
|
17
|
+
#### For all next parameters
|
18
|
+
#### the correct values are: `<%= MagicResource::MAGIC_VALID_VALUES.inspect %>`.
|
19
|
+
|
20
|
+
## Will require to specify the resource name in helper's pattern when resource is "other".
|
21
|
+
## Can be useful for fast development or experiments. But typically it's better
|
22
|
+
## to switch it off later and define the resource names everywhere,
|
23
|
+
## because the magic becames to be not predictable and searchable.
|
24
|
+
# config.force_resource_name_definition = :by_exception
|
25
|
+
|
26
|
+
## Will require the correct resource assignation in controller
|
27
|
+
## Means the situation when inside Users::CrudController you trying to do
|
28
|
+
## self.resource = Property.first
|
29
|
+
# config.assert_resource_name_in_controller = :by_exception
|
30
|
+
|
31
|
+
## Will disable content_for helper.
|
32
|
+
## It is too much magical...
|
33
|
+
## But in some cases it is the better than other solutions.
|
34
|
+
## Try to avoid it.
|
35
|
+
## Activate it if you really need it.
|
36
|
+
# config.disable_content_for_helper = :by_exception
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'magic-resource/version'
|
2
|
+
|
3
|
+
require 'ruby-features'
|
4
|
+
require 'activerecord-devkit'
|
5
|
+
|
6
|
+
require 'magic-resource/railtie'
|
7
|
+
|
8
|
+
module MagicResource
|
9
|
+
autoload :Controller, 'magic-resource/controller'
|
10
|
+
autoload :Helper, 'magic-resource/helper'
|
11
|
+
autoload :Container, 'magic-resource/container'
|
12
|
+
autoload :PathWithHttpVerb, 'magic-resource/path_with_http_verb'
|
13
|
+
|
14
|
+
module Generators
|
15
|
+
autoload :InstallGenerator, 'generators/magic_resource/install_generator'
|
16
|
+
end
|
17
|
+
|
18
|
+
MAGIC_PARAMETERS = %w(
|
19
|
+
force_resource_name_definition
|
20
|
+
assert_resource_name_in_controller
|
21
|
+
disable_content_for_helper
|
22
|
+
).freeze
|
23
|
+
MAGIC_VALID_VALUES = [false, :by_exception, :by_warning].freeze
|
24
|
+
|
25
|
+
mattr_accessor :default_context
|
26
|
+
@@default_context = :crud
|
27
|
+
|
28
|
+
mattr_accessor :default_form_type
|
29
|
+
@@default_form_type = :simple
|
30
|
+
|
31
|
+
MAGIC_PARAMETERS.each do |param|
|
32
|
+
mattr_reader param.to_sym
|
33
|
+
class_variable_set(:"@@#{param}", :by_exception)
|
34
|
+
|
35
|
+
define_singleton_method(:"#{param}=") do |value|
|
36
|
+
raise ArgumentError.new(
|
37
|
+
"Unknown value: #{value.inspect}. Valid values are: #{MAGIC_VALID_VALUES.inspect}"
|
38
|
+
) unless MAGIC_VALID_VALUES.include?(value)
|
39
|
+
class_variable_set(:"@@#{param}", value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.setup
|
44
|
+
yield self
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.no_magic
|
48
|
+
set_all_magic_parameters(:by_exception)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.try_magic?
|
52
|
+
set_all_magic_parameters(:by_warning)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.full_magic!
|
56
|
+
set_all_magic_parameters(false)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.set_all_magic_parameters(value)
|
60
|
+
MAGIC_PARAMETERS.each do |param|
|
61
|
+
public_send(:"#{param}=", value)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.logger
|
66
|
+
::Rails.logger
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.assert_restriction(restriction_type, message)
|
70
|
+
message.prepend('MagicResource: ')
|
71
|
+
case class_variable_get(:"@@#{restriction_type}")
|
72
|
+
when :by_exception then raise(message)
|
73
|
+
when :by_warning then logger.warn(message)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,288 @@
|
|
1
|
+
module MagicResource
|
2
|
+
class Container
|
3
|
+
|
4
|
+
CONTEXT_PATTERNS = {
|
5
|
+
route: '#',
|
6
|
+
translation: '.',
|
7
|
+
template: '/',
|
8
|
+
content: ':'
|
9
|
+
}.inject({}){ |mem, (type, separator)|
|
10
|
+
mem[type] = {
|
11
|
+
separator: separator,
|
12
|
+
regexp: /^(?<name>\w+)?#{'(?:::(?<context>\w+))?' unless type == :content}(?:#{Regexp.quote(separator)}(?<target>\w+#{'\??' if type == :translation}))$/
|
13
|
+
}; mem
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
attr_reader :name, :context, :collection, :item
|
17
|
+
alias resources collection
|
18
|
+
alias resource item
|
19
|
+
|
20
|
+
def context?(*args)
|
21
|
+
args.include?(context)
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(partial, *args)
|
25
|
+
options = args.extract_options!
|
26
|
+
if args.present? && form = args.shift
|
27
|
+
raise 'Form object containing wrong resource' unless form.object == rs
|
28
|
+
options[:f] = form unless options.has_key?(:f)
|
29
|
+
end
|
30
|
+
|
31
|
+
partial, ct = extract_context(partial, :template)
|
32
|
+
|
33
|
+
lookup_templates = [name, :resources].
|
34
|
+
flat_map{|lc| ["#{lc}/#{ct}", lc] }.
|
35
|
+
map{|l| "#{l}/#{partial}"}
|
36
|
+
|
37
|
+
template = lookup_templates.find{|t| helper.lookup_context.template_exists?(t, [], true)}
|
38
|
+
raise("Can't find #{lookup_templates[0]} template for #{name} resource") unless template
|
39
|
+
|
40
|
+
with_context(ct){helper.with_resource_container(self){helper.render(template, options)}}
|
41
|
+
end
|
42
|
+
|
43
|
+
def render_collection(partial, options = {})
|
44
|
+
partial, ct = extract_context(partial, :template)
|
45
|
+
collection.map{|r| helper.r(r).render("#{name}::#{ct}/#{partial}", options)}.join.html_safe
|
46
|
+
end
|
47
|
+
|
48
|
+
def t(key, options = {})
|
49
|
+
key, ct = extract_context(key, :translation)
|
50
|
+
|
51
|
+
lookup_keys = [name, :resources].
|
52
|
+
flat_map{|lc| ["#{lc}.#{ct}", lc] }.
|
53
|
+
map{|l| :"#{l}.#{key}"}
|
54
|
+
|
55
|
+
helper.t(
|
56
|
+
lookup_keys[0],
|
57
|
+
options.merge(
|
58
|
+
default: lookup_keys[1..-1] + Array.wrap(options[:default])
|
59
|
+
)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def render_collection_build(partial, options = {})
|
64
|
+
partial, ct = extract_context(partial, :template)
|
65
|
+
helper.r(resources.soft_build).render("#{name}::#{ct}/#{partial}", options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def link_to(action, *args, &block)
|
69
|
+
options = args.extract_options!
|
70
|
+
|
71
|
+
action, ct, route_args = extract_route_context(action)
|
72
|
+
with_context(ct) do
|
73
|
+
url, method = route_to(action, route_args)
|
74
|
+
link_options = {}
|
75
|
+
|
76
|
+
link_options[:method] = method unless method == :get
|
77
|
+
|
78
|
+
confirm = options.delete(:confirm){method === :delete}
|
79
|
+
confirm = :confirm_destroy if confirm === true
|
80
|
+
confirm = t(confirm) if confirm.kind_of?(Symbol)
|
81
|
+
link_options[:data] = {confirm: confirm} if confirm
|
82
|
+
|
83
|
+
label = label_for_link(action, args.first, &block)
|
84
|
+
|
85
|
+
helper.link_to(label, url, link_options.deep_merge(options))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
#Used in controller and view
|
90
|
+
def path_to(action)
|
91
|
+
action, ct, route_args = extract_route_context(action)
|
92
|
+
with_context(ct) do
|
93
|
+
route_to(action, route_args).first
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#Used in controller
|
98
|
+
def redirect_to(action, options = {})
|
99
|
+
helper.redirect_to path_to(action), options
|
100
|
+
end
|
101
|
+
|
102
|
+
def link_to_if(condition, action, *args, &block)
|
103
|
+
if condition
|
104
|
+
link_to(action, *args, &block)
|
105
|
+
else
|
106
|
+
action, ct = extract_route_context(action)
|
107
|
+
args.extract_options!
|
108
|
+
with_context(ct) do
|
109
|
+
label_for_link(action, args.first, &block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def link_to_unless(condition, *args, &block)
|
115
|
+
link_to_if(!condition, *args, &block)
|
116
|
+
end
|
117
|
+
|
118
|
+
def form_for(action, *args, &block)
|
119
|
+
options = args.extract_options!
|
120
|
+
form_type = args.shift
|
121
|
+
|
122
|
+
action, ct, route_args = extract_route_context(action)
|
123
|
+
with_context(ct) do
|
124
|
+
url, method = route_to(action, route_args)
|
125
|
+
|
126
|
+
form_type ||= MagicResource.default_form_type
|
127
|
+
form_type, obj = form_type.kind_of?(Ransack::Search) ?
|
128
|
+
[:search, form_type] :
|
129
|
+
[form_type, item]
|
130
|
+
helper.public_send(:"#{form_type}_form_for", obj, {url: url, method: method}.merge(options), &block)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def content_for(index, *args, &block)
|
135
|
+
MagicResource.assert_restriction(
|
136
|
+
:disable_content_for_helper,
|
137
|
+
"#content_for helper is too much magical...: #{index}"
|
138
|
+
)
|
139
|
+
|
140
|
+
content = if block_given?
|
141
|
+
raise ArgumentError.new("Both content param and block specified: #{index}") if args.present?
|
142
|
+
helper.capture(&block)
|
143
|
+
else
|
144
|
+
raise ArgumentError.new("Need to specify the content by second param or block: #{index}") unless args.present?
|
145
|
+
args.first
|
146
|
+
end
|
147
|
+
|
148
|
+
index = extract_context(index, :content)
|
149
|
+
|
150
|
+
helper.resource_contents[rs] ||= {}
|
151
|
+
helper.resource_contents[rs][index] = content
|
152
|
+
end
|
153
|
+
|
154
|
+
def content(index)
|
155
|
+
MagicResource.assert_restriction(
|
156
|
+
:disable_content_for_helper,
|
157
|
+
"`#content helper is too much magical...: #{index}"
|
158
|
+
)
|
159
|
+
|
160
|
+
index = extract_context(index, :content)
|
161
|
+
|
162
|
+
helper.resource_contents[rs].try(:[], index)
|
163
|
+
end
|
164
|
+
|
165
|
+
def _launch(helper, changed = false)
|
166
|
+
self.helper = helper
|
167
|
+
self.changed = changed
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
def self._launch(helper, *args)
|
172
|
+
args.present? ?
|
173
|
+
self.new(args.first)._launch(helper, true) :
|
174
|
+
# Let's try to keep resource_container to be private..
|
175
|
+
helper.send(:resource_container)._launch(helper)
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
attr_accessor :helper, :changed
|
181
|
+
alias changed? changed
|
182
|
+
attr_reader :rs
|
183
|
+
attr_writer :name, :collection, :item
|
184
|
+
|
185
|
+
def initialize(rs, context = nil)
|
186
|
+
rs = rs.object if rs.respond_to?(:object)
|
187
|
+
self.rs, self.context = rs, context
|
188
|
+
end
|
189
|
+
|
190
|
+
def rs=(new_rs)
|
191
|
+
@rs, self.name = new_rs.kind_of?(Symbol) ?
|
192
|
+
[nil, new_rs] :
|
193
|
+
[new_rs, nil]
|
194
|
+
|
195
|
+
self.collection, self.item = rs.respond_to?(:to_ary) ?
|
196
|
+
[rs, nil] :
|
197
|
+
[nil, rs]
|
198
|
+
|
199
|
+
# Just `||=` not working due to private method
|
200
|
+
self.name = self.name || self.class.resource_name(rs)
|
201
|
+
end
|
202
|
+
|
203
|
+
def context=(new_context)
|
204
|
+
@context = new_context || MagicResource.default_context
|
205
|
+
end
|
206
|
+
|
207
|
+
def extract_context(pattern, context_type)
|
208
|
+
parse_params = CONTEXT_PATTERNS[context_type]
|
209
|
+
|
210
|
+
pattern = pattern.to_s
|
211
|
+
pattern.prepend(parse_params[:separator]) unless pattern.include?(parse_params[:separator])
|
212
|
+
pattern_parts = pattern.match(parse_params[:regexp])
|
213
|
+
raise("Wrong resource target pattern: #{pattern}") unless pattern_parts
|
214
|
+
|
215
|
+
target_name = pattern_parts[:name].try(:to_sym)
|
216
|
+
MagicResource.assert_restriction(
|
217
|
+
:force_resource_name_definition,
|
218
|
+
"Need to specify resource name when switching resource: #{pattern}"
|
219
|
+
) if changed? && target_name.nil?
|
220
|
+
|
221
|
+
if target_name
|
222
|
+
raise("Wrong resource name, expected #{name.inspect} but got #{target_name.inspect}") unless name == target_name
|
223
|
+
end
|
224
|
+
|
225
|
+
#Damn, string keys...
|
226
|
+
pattern_parts.names.include?('context') ?
|
227
|
+
[pattern_parts[:target].to_sym, pattern_parts[:context].try(:to_sym) || context] :
|
228
|
+
pattern_parts[:target].to_sym
|
229
|
+
end
|
230
|
+
|
231
|
+
def extract_route_context(action)
|
232
|
+
action, route_args = action.kind_of?(Hash) ? action.first : [action, {}]
|
233
|
+
|
234
|
+
extract_context(action, :route) << route_args
|
235
|
+
end
|
236
|
+
|
237
|
+
def with_context(context)
|
238
|
+
same_context = self.context == context
|
239
|
+
old_context, self.context = self.context, context unless same_context
|
240
|
+
|
241
|
+
yield
|
242
|
+
ensure
|
243
|
+
self.context = old_context unless same_context
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.resource_name(duck)
|
247
|
+
raise 'Undefined resource name' if duck.nil?
|
248
|
+
return duck if duck.kind_of?(Symbol)
|
249
|
+
return duck.to_sym if duck.kind_of?(String)
|
250
|
+
return duck.model_name.route_key.to_sym if duck.respond_to?(:model_name)
|
251
|
+
return resource_name(duck.klass) if duck.respond_to?(:klass)
|
252
|
+
return resource_name(duck.class) unless duck.kind_of?(Class)
|
253
|
+
duck.name.underscore.pluralize.to_sym
|
254
|
+
end
|
255
|
+
|
256
|
+
def route_to(action, route_args = {}, only_path = true)
|
257
|
+
action = (item.try(:persisted?) ? :update : :create) if action == :save
|
258
|
+
|
259
|
+
options = helper.url_options.deep_merge(
|
260
|
+
action: action,
|
261
|
+
only_path: only_path,
|
262
|
+
_recall: {
|
263
|
+
controller: [name, context].compact.join('/'),
|
264
|
+
# Need to confuse Rails..
|
265
|
+
action: 'hope_not_existing_action'
|
266
|
+
}
|
267
|
+
).deep_merge(route_args)
|
268
|
+
|
269
|
+
resource.try(:persisted?) ?
|
270
|
+
options[:_recall][:id] = resource :
|
271
|
+
options[:_recall].delete(:id)
|
272
|
+
|
273
|
+
url = helper._routes.url_for(options)
|
274
|
+
[url, url.http_verb]
|
275
|
+
end
|
276
|
+
|
277
|
+
def label_for_link(action, label, &block)
|
278
|
+
if block_given?
|
279
|
+
raise 'Both label and block are specified for link_to' if label
|
280
|
+
return helper.capture(&block)
|
281
|
+
end
|
282
|
+
|
283
|
+
label ||= :"button_#{action}"
|
284
|
+
label = t(label) if label.kind_of?(Symbol)
|
285
|
+
label
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module MagicResource
|
2
|
+
module Controller
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
helper MagicResource::Helper
|
7
|
+
helper_method :resource_container, :with_resource_container, :resource_contents
|
8
|
+
|
9
|
+
prepend_before_action :prepare_resource_controller
|
10
|
+
|
11
|
+
protected
|
12
|
+
attr_reader :resources, :resource
|
13
|
+
|
14
|
+
private
|
15
|
+
attr_accessor :resource_name, :resource_context, :resource_container
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def resources=(resources)
|
21
|
+
@resources, @resource = resources, nil
|
22
|
+
prepare_resource_container(resources)
|
23
|
+
end
|
24
|
+
|
25
|
+
def resource=(resource)
|
26
|
+
@resources, @resource = nil, resource
|
27
|
+
prepare_resource_container(resource)
|
28
|
+
end
|
29
|
+
|
30
|
+
def r(*args)
|
31
|
+
MagicResource::Container._launch(self, *args)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def prepare_resource_controller
|
37
|
+
resource_config = self.class.name.match(/^(?<name>\w+?)::(?<context>\w+)Controller$/)
|
38
|
+
raise "Wrong resource controller name: #{self.class.name}" unless resource_config
|
39
|
+
|
40
|
+
self.resource_name = resource_config[:name].underscore.to_sym
|
41
|
+
self.resource_context = resource_config[:context].underscore.to_sym
|
42
|
+
|
43
|
+
prepare_resource_container
|
44
|
+
end
|
45
|
+
|
46
|
+
def prepare_resource_container(rs = nil)
|
47
|
+
_prepare_resource_container(rs)
|
48
|
+
|
49
|
+
MagicResource.assert_restriction(
|
50
|
+
:assert_resource_name_in_controller,
|
51
|
+
"Wrong resource name in controller, expected #{resource_name.inspect} but got #{resource_container.name.inspect}"
|
52
|
+
) unless resource_container.name == resource_name
|
53
|
+
end
|
54
|
+
|
55
|
+
def _prepare_resource_container(rs)
|
56
|
+
self.resource_container = MagicResource::Container.new(rs || resource_name, resource_context)
|
57
|
+
end
|
58
|
+
|
59
|
+
def with_resource_container(new_resource_container)
|
60
|
+
same_resource_container = resource_container == new_resource_container
|
61
|
+
old_resource_container, self.resource_container = resource_container, new_resource_container unless same_resource_container
|
62
|
+
|
63
|
+
yield
|
64
|
+
ensure
|
65
|
+
self.resource_container = old_resource_container unless same_resource_container
|
66
|
+
end
|
67
|
+
|
68
|
+
def resource_contents
|
69
|
+
@_resource_contents ||= {}
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
RubyFeatures.define('magic_resource') do
|
2
|
+
dependency 'activerecord_devkit/association_soft_build'
|
3
|
+
|
4
|
+
apply_to 'ActionDispatch::Http::URL' do
|
5
|
+
applied do |variable|
|
6
|
+
class << self
|
7
|
+
alias_method_chain :url_for, :http_verb
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class_methods do
|
12
|
+
def url_for_with_http_verb(options, *args)
|
13
|
+
path = url_for_without_http_verb(options, *args)
|
14
|
+
options[:path].kind_of?(MagicResource::PathWithHttpVerb) ?
|
15
|
+
MagicResource::PathWithHttpVerb.new(options[:path], path) :
|
16
|
+
path
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
apply_to 'ActionDispatch::Journey::Route' do
|
22
|
+
applied do
|
23
|
+
alias_method_chain :format, :http_verb
|
24
|
+
end
|
25
|
+
|
26
|
+
instance_methods do
|
27
|
+
def format_with_http_verb(*args)
|
28
|
+
MagicResource::PathWithHttpVerb.new(self.verb, format_without_http_verb(*args))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end.apply
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MagicResource
|
2
|
+
class PathWithHttpVerb < String
|
3
|
+
attr_reader :http_verb
|
4
|
+
|
5
|
+
def initialize(http_verb, path)
|
6
|
+
@http_verb = http_verb.kind_of?(self.class) ?
|
7
|
+
http_verb.http_verb :
|
8
|
+
http_verb.source.scan(/\w+/).first.try(:downcase).try(:to_sym)
|
9
|
+
|
10
|
+
super(path)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'magic-resource/feature'
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: magic-resource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Tokarenko
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-features
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activerecord-devkit
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Rails plugin which introduces the resource-based ideology for WEB applications
|
84
|
+
development.
|
85
|
+
email: private.tokarenko.sergey@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- LICENSE
|
91
|
+
- README.md
|
92
|
+
- lib/generators/magic_resource/install_generator.rb
|
93
|
+
- lib/generators/magic_resource/templates/config/locales/en/resources.yml
|
94
|
+
- lib/generators/magic_resource/templates/magic-resource.rb
|
95
|
+
- lib/magic-resource.rb
|
96
|
+
- lib/magic-resource/container.rb
|
97
|
+
- lib/magic-resource/controller.rb
|
98
|
+
- lib/magic-resource/feature.rb
|
99
|
+
- lib/magic-resource/helper.rb
|
100
|
+
- lib/magic-resource/path_with_http_verb.rb
|
101
|
+
- lib/magic-resource/railtie.rb
|
102
|
+
- lib/magic-resource/version.rb
|
103
|
+
homepage: https://github.com/stokarenko/magic-resource
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.4.5
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Rails plugin which introduces the resource-based ideology for WEB applications
|
127
|
+
development.
|
128
|
+
test_files: []
|
129
|
+
has_rdoc:
|