resourcerer 0.2.2 → 2.0.4

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