resourcerer 1.0.0 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b06994a58d730b98de201b5c3da0223f380e2dd5
4
- data.tar.gz: cef7fd6af87ee0d92761c70c22f7ce0796da21e1
3
+ metadata.gz: ecc554dcf8cff06fd33d74f17763e4be93830c12
4
+ data.tar.gz: 822ece730ec85a39775250b14163549407c3d4fc
5
5
  SHA512:
6
- metadata.gz: fde5d21d084804512b05fa28cfc7b09e59a54c34d97ac8f95502274b4e262a8b25264f832767ba1b2e63ce5c359e9e6c2d9415b50785b36b4141bffb9cfe8f6b
7
- data.tar.gz: 670c8bf4194bf8e3482d1b0cb9c8a9a0acc8e5474a66a63f76bea8d59355826b1621169396ad31b67e134e16bbebc33899790584b036e790e1b222e1291f65af
6
+ metadata.gz: 39261e118675be12572f8a52e1df8de3049e401f2d47f08a6d3bb63efa8f64a54ea71d948ea9526c7bed0bb034f05cefa30a8d4985e7398c709baa1e1a802018
7
+ data.tar.gz: 435fe862ac93948f2f4122555463f73ef079d8ca0172baadef5bb7392374975b17a7e4cbd4228c9ecb20e6c6ba1c5ed87e13445e830016b3a753fab1c4663ff2
data/README.md CHANGED
@@ -1,262 +1,281 @@
1
- Resourcerer [![Gem Version](https://badge.fury.io/rb/resourcerer.svg)](http://badge.fury.io/rb/resourcerer) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ElMassimo/queryable/blob/master/LICENSE.txt)
1
+ Resourcerer [![Gem Version](https://img.shields.io/gem/v/resourcerer.svg?colorB=e9573f)](https://rubygems.org/gems/resourcerer) [![Build Status](https://travis-ci.org/ElMassimo/resourcerer.svg)](https://travis-ci.org/ElMassimo/resourcerer) [![Coverage Status](https://coveralls.io/repos/github/ElMassimo/resourcerer/badge.svg?branch=master)](https://coveralls.io/github/ElMassimo/resourcerer?branch=master) [![Inline docs](http://inch-ci.org/github/ElMassimo/resourcerer.svg)](http://inch-ci.org/github/ElMassimo/resourcerer) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ElMassimo/resourcerer/blob/master/LICENSE.txt)
2
2
  =====================
3
- What `resourcerer` proposes is that you go from this:
3
+
4
+ A small library to help you avoid boilerplate for standard CRUD actions, while improving your controllers' readibility.
5
+
6
+ ### Installation
7
+
8
+ Add this line to your application's Gemfile:
4
9
 
5
10
  ```ruby
6
- class PersonController < ApplicationController
7
- def new
8
- @person = Person.new
9
- end
11
+ gem 'resourcerer'
12
+ ```
10
13
 
11
- def create
12
- @person = Person.new(person_params)
13
- if @person.save
14
- redirect_to(@person)
15
- else
16
- render :new
17
- end
18
- end
14
+ And then execute:
19
15
 
20
- def edit
21
- @person = Person.find(params[:id])
22
- end
16
+ $ bundle
23
17
 
24
- def update
25
- @person = Person.find(params[:id])
26
- if @person.update_attributes(person_params)
27
- redirect_to(@person)
28
- else
29
- render :edit
30
- end
31
- end
18
+ Or install it yourself as:
32
19
 
33
- private
34
- def person_params
35
- params.require(:person).permit(:name)
36
- end
20
+ $ gem install resourcerer
21
+
22
+ ### Usage
23
+
24
+ In the simplest scenario you'll just use it to define a resource in the controller:
25
+
26
+ ```ruby
27
+ class BandsController < ApplicationController
28
+ resource :band
37
29
  end
38
30
  ```
39
31
 
40
- To something like this:
32
+ Now every time you call `band` in your controller or view, it will look for an
33
+ ID and try to perform `Band.find(id)`. If an ID parameter isn't found, it will
34
+ call `Band.new(band_params)`. The result will be memoized in a
35
+ `@resourcerer_band` instance variable.
36
+
37
+ #### Example
38
+
39
+ Here's what a standard Rails CRUD controller using Resourcerer might look like:
41
40
 
42
41
  ```ruby
43
- class PersonController < ApplicationController
44
- resource :person do
45
- permit [:name]
42
+ class BandsController < ApplicationController
43
+ resource :band do
44
+ permit [:name, :genre]
46
45
  end
47
46
 
48
47
  def create
49
- if person.save
50
- redirect_to(person)
48
+ if band.save
49
+ redirect_to band_path(band)
51
50
  else
52
51
  render :new
53
52
  end
54
53
  end
55
54
 
56
55
  def update
57
- if person.save
58
- redirect_to(person)
56
+ if band.save
57
+ redirect_to band_path(band)
59
58
  else
60
59
  render :edit
61
60
  end
62
61
  end
62
+
63
+ def destroy
64
+ band.destroy
65
+ redirect_to bands_path
66
+ end
63
67
  end
64
68
  ```
65
69
 
66
- The idea is that you don't have to write boilerplate for standard CRUD actions, while at the same time improving your controllers readibility.
70
+ That's [way less code than usual!](https://gist.github.com/ElMassimo/18fbdb7108f46f3c975712945f7a3318) :smiley:
67
71
 
68
- ## Usage
72
+ ### Under the Hood
69
73
 
70
- Let's see what Resourcerer is doing behind the curtains :smiley:.
74
+ The default resolving workflow is pretty powerful and customizable. It could be
75
+ expressed with the following pseudocode:
71
76
 
72
- This examples assume that you are using Rails 4 Strong Parameters.
77
+ ```ruby
78
+ def fetch(scope, id)
79
+ instance = id ? find(id, scope) : build(attrs, scope)
80
+ instance.tap { instance.assign_attributes(attrs) if assign? }
81
+ end
73
82
 
74
- ### Obtaining a resource:
83
+ def id
84
+ params[:band_id] || params[:id]
85
+ end
75
86
 
76
- ```ruby
77
- resource :person
78
- ```
87
+ def find(id, scope)
88
+ scope.find(id)
89
+ end
79
90
 
80
- **Query Explanation**
81
-
82
- <table>
83
- <tr>
84
- <td><code>id</code> present?</td>
85
- <td>Query (get/delete)</td>
86
- <td>Query (post/patch/put)</td>
87
- </tr>
88
- <tr>
89
- <td><code>true</code></td>
90
- <td><code>Person.find(params[:id])</code></td>
91
- <td><code>Person.find(params[:id]).attributes = person_params</code></td>
92
- </tr>
93
- <tr>
94
- <td><code>false</code></td>
95
- <td><code>Person.new</code></td>
96
- <td><code>Person.new(person_params)</code></td>
97
- </tr>
98
- </table>
99
-
100
-
101
- ### Configuration
102
-
103
- ### DSL
104
- Resourcer also features a nice DSL, which is helpful when you need more control over the resource
105
- lifecycle.
106
-
107
- You can also access every configuration option available above:
108
- ```ruby
109
- resource(:employee) do
110
- model :person
111
- find_by :name
112
- find {|name| company.find_employee(name) }
113
- build { company.new_employee }
114
- assign { params.require(:employee).permit(:name) }
115
- permit [:name, :description]
91
+ def build(params, scope)
92
+ scope.new(params) # Band.new(params)
93
+ end
94
+
95
+ def scope
96
+ model # Band
97
+ end
98
+
99
+ def model
100
+ :band.classify.constantize # Band
101
+ end
102
+
103
+ def assign?
104
+ action_name == 'update'
105
+ end
106
+
107
+ def attrs
108
+ if respond_to?(:band_params, true) && !request.get?
109
+ band_params
110
+ else
111
+ {}
112
+ end
116
113
  end
117
- # is the same as:
118
- resource(:employee, model: :person, finder_attribute: :name, finder: ->(name){ company.find_employee(name) }, builder: ->{ company.new_employee }, attributes: ->{ params.require(:employee).permit(:name) })
119
114
  ```
120
- The DSL is more convenient when you have an object oriented design and want to allow an object to handle its collections, or as a quick way to set the StrongParameters method.
115
+ The resource is lazy, so it won't do anyband until the method is called.
121
116
 
122
- Configuration options play well together, and the defaults try to make intelligent use of them. For example,
123
- setting the `finder_attribute` in the example above changes the `finder_param` to `person_name` instead of `person_id`, and the value of that parameter is provided to the finder block.
117
+ ## Configuration
124
118
 
125
- Let's take a look at some of the things you can do:
119
+ It is possible to override each step with options. The acceptable options to the
120
+ `resource` macro are:
126
121
 
127
- **Specify the model name:**
122
+ ### `id`
123
+
124
+ In order to fetch a resource Resourcerer relies on the presence of an ID:
128
125
 
129
126
  ```ruby
130
- resource(:company, model: :enterprise)
127
+ # Default Behavior
128
+ resource :band, id: ->{ params[:band_id] || params[:id] }
131
129
  ```
132
130
 
133
- **Specify the parameter key to use to fetch the object:**
131
+ You can override any option's default behavior by passing in a `Proc`:
134
132
 
135
133
  ```ruby
136
- resource(:enterprise, finder_param: :company_id)
134
+ resource :band, id: ->{ 42 }
137
135
  ```
138
136
 
139
- **Specify the model attribute to use to perform the search:**
137
+ Passing lambdas might not always be fun, so most options provide shortcuts that
138
+ might help make life easier:
140
139
 
141
140
  ```ruby
142
- resource(:enterprise, find_by: :name)
141
+ resource :band, id: :custom_band_id
142
+ # same as
143
+ resource :band, id: ->{ params[:custom_band_id] }
144
+
145
+ resource :band, id: [:try_this_id, :or_maybe_that_id]
146
+ # same as
147
+ resource :band, id: ->{ params[:try_this_id] || params[:or_maybe_that_id] }
143
148
  ```
144
149
 
145
- **Specify how to obtain the object attributes:**
150
+ ### `find`
151
+
152
+ If an ID was provided, Resourcerer will try to find the model:
146
153
 
147
154
  ```ruby
148
- # Specify the strong parameters method's name when using the default `StrongParametersStrategy`
149
- resource(:employee, attributes_method: :person_params)
155
+ # Default Behavior
156
+ resource :band, find: -> (id, scope) { scope.find(id) }
157
+ ```
158
+
159
+ Where `scope` is a model scope, like `Band` or `User.active` or
160
+ `Post.published`. There's even a convenient shortcut for cases where the ID is
161
+ actually something else:
150
162
 
151
- # Specify the parameter key that holds the attributes when using the `EagerAttributesStrategy`
152
- resource(:person, param_key: :employee)
163
+ ```ruby
164
+ resource :band, find_by: :slug
165
+ # same as
166
+ resource :band, find: ->(slug, scope){ scope.find_by!(slug: slug) }
153
167
  ```
154
168
 
155
- ### Setting a distinct object for a single action
169
+ ### `build`
156
170
 
157
- There are times when one action in a controller is different from the
158
- rest of the actions. A nice approach to circumvent this is to use the
159
- controller's setter methods. This example uses [presenter_rails](https://github.com/ElMassimo/presenter_rails).
171
+ When an ID is not present, Resourcerer tries to build an object for you:
160
172
 
161
173
  ```ruby
162
- resource(:article)
163
-
164
- def oldest
165
- self.article = Article.find_oldest
166
- render :show
167
- end
174
+ # Default Behavior
175
+ resource :band, build: ->(attrs, scope){ scope.new(band_params) }
168
176
  ```
169
177
 
170
- ### Custom strategies
178
+ ### `attrs`
171
179
 
172
- For times when you need custom behavior for resource finding, you can
173
- create your own strategy by extending `Resourcerer::Strategy`:
180
+ This option is responsible for calulating params before passing them to the
181
+ build step. The default behavior was modeled with Strong Parameters in mind and
182
+ is somewhat smart: it calls the `band_params` controller method if it's
183
+ available and the request method is not `GET`. In all other cases it produces
184
+ an empty hash.
185
+
186
+ You can easily specify which controller method you want it to call instead of
187
+ `band_params`, or just provide your own logic:
174
188
 
175
189
  ```ruby
176
- class VerifiableStrategy < Resourcerer::Strategy
177
- delegate :current_user, :to => :controller
190
+ resource :band, attrs: :custom_band_params
191
+ resource :other_band, attrs: ->{ { foo: "bar" } }
178
192
 
179
- def resource
180
- instance = model.find(params[:id])
181
- if current_user != instance.user
182
- raise ActiveRecord::RecordNotFound
183
- end
184
- instance
185
- end
193
+ private
194
+
195
+ def custom_band_params
196
+ params.require(:band).permit(:name, :genre)
186
197
  end
187
198
  ```
188
199
 
189
- You would then use your custom strategy in your controller:
200
+ Using the default model name conventions? `permit` can do that for you:
190
201
 
191
202
  ```ruby
192
- resource(:post, strategy: VerifiableStrategy)
203
+ resource :band, permit: [:name, :genre]
193
204
  ```
194
205
 
195
- ## Using decorators or presenters
196
- ### With [draper](http://github.com/drapergem/draper)
206
+ ### `collection`
197
207
 
198
- If you use decorators, you can go from something like this:
208
+ Defines the scope that's used in `find` and `build` steps:
199
209
 
200
210
  ```ruby
201
- class PersonController < ApplicationController
202
- def new
203
- @person = Person.new.decorate
204
- end
211
+ resource :band, collection: ->{ current_user.bands }
212
+ ```
205
213
 
206
- def create
207
- @person = Person.new(person_params)
208
- if @person.save
209
- redirect_to(@person)
210
- else
211
- @person = @person.decorate
212
- render :new
213
- end
214
- end
214
+ ### `model`
215
215
 
216
- def edit
217
- @person = Person.find(params[:id]).decorate
218
- end
216
+ Allows you to specify the model class to use:
219
217
 
220
- def update
221
- @person = Person.find(params[:id])
222
- if @person.update_attributes(person_params)
223
- redirect_to(@person)
224
- else
225
- @person = @person.decorate
226
- render :edit
227
- end
228
- end
218
+ ```ruby
219
+ resource :band, model: ->{ AnotherBand }
220
+ resource :band, model: AnotherBand
221
+ resource :band, model: "AnotherBand"
222
+ resource :band, model: :another_band
223
+ ```
229
224
 
230
- private
231
- def person_params
232
- params.require(:person).permit(:name)
233
- end
234
- end
225
+ ### `assign` and `assign?`
226
+
227
+ Allows you to specify whether the attributes should be assigned:
228
+
229
+ ```ruby
230
+ resource :band, assign?: false
231
+ resource :band, assign?: [:edit, :update]
232
+ resource :band, assign?: ->{ current_user.admin? }
235
233
  ```
236
234
 
237
- To something like this by adding [presenter_rails](https://github.com/ElMassimo/presenter_rails) to the mix:
235
+ and also how to assign them:
238
236
 
239
237
  ```ruby
240
- class PersonController < ApplicationController
241
- resource :person do
242
- permit :name
243
- end
238
+ resource :band, assign: ->(band, attrs) { band.set_info(attrs) }
244
239
 
245
- present :person do
246
- person.decorate
247
- end
240
+ ```
241
+
242
+
243
+ ## Advanced Configuration with `resource_config`
244
+
245
+ You can define configuration presets with the `resource_config` method to reuse
246
+ them later in different resource definitions.
247
+
248
+ ```ruby
249
+ resource_config :cool_find, find: ->{ very_cool_find_code }
250
+ resource_config :cool_build, build: ->{ very_cool_build_code }
251
+
252
+ resource :band, using: [:cool_find, :cool_build]
253
+ resource :another_band, using: :cool_build
254
+ ```
255
+
256
+ Options that are passed to `resource` will take precedence over the presets.
257
+
258
+
259
+ ## Decorators or Presenters (like [draper](http://github.com/drapergem/draper))
260
+
261
+ If you use decorators, you'll be able to avoid [even more boilerplate](https://gist.github.com/ElMassimo/6775148c0d7364be111531a254b41ba9) if you throw [presenter_rails](https://github.com/ElMassimo/presenter_rails) in the mix:
262
+
263
+ ```ruby
264
+ class BandController < ApplicationController
265
+ resource(:band, permit: :name)
266
+ present(:band) { band.decorate }
248
267
 
249
268
  def create
250
- if person.save
251
- redirect_to(person)
269
+ if band.save
270
+ redirect_to(band)
252
271
  else
253
272
  render :new
254
273
  end
255
274
  end
256
275
 
257
276
  def update
258
- if person.save
259
- redirect_to(person)
277
+ if band.save
278
+ redirect_to(band)
260
279
  else
261
280
  render :edit
262
281
  end
@@ -264,23 +283,15 @@ class PersonController < ApplicationController
264
283
  end
265
284
  ```
266
285
 
267
- ### Comparison with [decent_exposure](https://github.com/voxdolo/decent_exposure).
286
+ ### Comparison with [Decent Exposure](https://github.com/hashrocket/decent_exposure).
268
287
 
269
- Resourcerer is heavily inspired on [decent exposure](https://github.com/voxdolo/decent_exposure), it attempts to be more predictable by focusing on finding a resource and assigning attributes, and discarding completely the view exposure part.
288
+ Resourcerer is heavily inspired on [Decent Exposure](https://github.com/hashrocket/decent_exposure), but it attempts to be simpler and more flexible by not focusing on exposing variables to the view context.
270
289
 
271
290
  #### Similarities
272
- Both allow you to find or initialize a resource and assign attributes, removing the boilerplate from most CRUD actions.
291
+ Both allow you to find or initialize a model and assign attributes, removing the boilerplate from most CRUD actions.
273
292
 
274
293
  #### Differences
275
- Resourcerer does not expose an object to the view in any way, scope the query to a collection method if defined, nor deal with collections. It also has better support for strong parameters.
276
-
277
-
278
- ### Caveats
279
- #### When using StrongParametersStrategy
280
- Since attributes are assigned on every POST, PUT, and PATCH request, sometimes when using Strong Parameters it's not desirable that the attributes method is called. For that reason, the presence of `params[param_key]` is checked before assigning attributes.
281
- ##### Troubleshooting
282
- - _The attributes are not being assigned_: Check that the resource name matches the param used in the attributes method, and set the `param_key` configuration if they are different.
283
- - _Need an error to be thrown if the params are not present_: Use the `EagerStrongParametersStrategy`, available in the sample strategies in this repository, you can set it using the `strategy` configuration option.
294
+ Resourcerer does not expose an object to the view in any way, nor deal with decoratation. It also provides better support for strong parameters.
284
295
 
285
296
  ### Special Thanks
286
- Resourcerer was inspired by [decent_exposure](https://github.com/voxdolo/decent_exposure).
297
+ Resourcerer is based on [DecentExposure](https://github.com/hashrocket/decent_exposure).