decent_exposure 2.3.3 → 3.0.0.beta1

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: e9fff0c90cb010d9b35e02bc714102c23bd75bbe
4
- data.tar.gz: 94da8ffea402d21cd7cfc9b5f34e1e4a9412c25e
3
+ metadata.gz: 2ec6d62cc52928970902ade2a9998e8f58758f7c
4
+ data.tar.gz: 0a3aff2440f88ed56a20197d270402c826c6b5e3
5
5
  SHA512:
6
- metadata.gz: 3dcca3f45da8a0af81eca8aadefc5c399c4e8f7cbddc20522c3b1902849a06a19f4a50e89f013f96d2934d57cf846e9f6fafd6b51e65c36907a0a76963fdd613
7
- data.tar.gz: 0748f7dac7ac669e937719541b189f8c2337aada64a69124f784edfd3a387c6f11cbe11bf7fe23713c912a5e84dcfcc51f8c5fbb1f3f6fa63c9b983a0dc42354
6
+ metadata.gz: c085171f6369dda3d9ae3f459aed654dcdc5a0934f28b61b605ffa1cbff9951cc5b44b214179d3969764064c748c7cddac8dc58655c7972f09dcaa6c5bec0bff
7
+ data.tar.gz: 692f0b40e7d1435ebaec5ac4d96422b8cb69592afcb3b5010c597f71f65f7c3f88588ee26dffd17ae38cd83ad269f5fa1581030bbf132ea3c4a63a8b38d1bf81
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 2.2.2
3
+ - 2.3.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Hashrocket
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,501 +1,338 @@
1
- [![Gem Version](https://badge.fury.io/rb/decent_exposure.svg)](https://badge.fury.io/rb/decent_exposure)
2
- [![Build Status](https://api.travis-ci.org/hashrocket/decent_exposure.svg?branch=master)](https://travis-ci.org/hashrocket/decent_exposure)
3
- [![Code Climate](https://codeclimate.com/github/hashrocket/decent_exposure/badges/gpa.svg)](https://codeclimate.com/github/hashrocket/decent_exposure)
1
+ ![Decent Exposure](decent_exposure.png)
4
2
 
5
- **Note**: Version 2.3.x will be the last series of releases that support
6
- Rails 3.x and Ruby 1.8/1.9. Starting with version 3.0, Decent Exposure
7
- will only support Rails 4.0 and above, and Ruby 2.0 and above.
3
+ [![Gem Version](https://img.shields.io/gem/v/decent_exposure.svg)](https://rubygems.org/gems/decent_exposure)
4
+ [![Build Status](https://img.shields.io/travis/hashrocket/decent_exposure.svg)](http://travis-ci.org/hashrocket/decent_exposure)
5
+ [![Code Climate](https://img.shields.io/codeclimate/github/hashrocket/decent_exposure.svg)](https://codeclimate.com/github/hashrocket/decent_exposure)
6
+ [![Inline docs](http://inch-ci.org/github/hashrocket/decent_exposure.svg?branch=master&style=shields)](http://inch-ci.org/github/hashrocket/decent_exposure)
8
7
 
9
- ## Mad Decent
8
+ ### Notice
10
9
 
11
- Rails controllers are the sweaty armpit of every rails app. This is due, in
12
- large part, to the fact that they expose their instance variables directly to
13
- their views. This means that your instance variables are your interface... and
14
- that you've broken encapsulation. Instance variables are meant to be private,
15
- for Science's sake!
10
+ DecentExposure `3.0` is a major change from `< 3.0`. If you are using `rails <= 4.2.x` please look at `decent_exposure < 3.0`.
16
11
 
17
- What `decent_exposure` proposes is that you go from this:
12
+ **Be aware... mild API changes ahead.**
13
+
14
+ ### Installation
15
+
16
+ Add this line to your application's Gemfile:
18
17
 
19
18
  ```ruby
20
- class Controller
21
- def new
22
- @person = Person.new(params[:person])
23
- end
19
+ gem 'decent_exposure'
20
+ ```
24
21
 
25
- def create
26
- @person = Person.new(params[:person])
27
- if @person.save
28
- redirect_to(@person)
29
- else
30
- render :new
31
- end
32
- end
22
+ And then execute:
33
23
 
34
- def edit
35
- @person = Person.find(params[:id])
36
- end
24
+ $ bundle
37
25
 
38
- def update
39
- @person = Person.find(params[:id])
40
- if @person.update_attributes(params[:person])
41
- redirect_to(@person)
42
- else
43
- render :edit
44
- end
45
- end
26
+ Or install it yourself as:
27
+
28
+ $ gem install decent_exposure
29
+
30
+ ### API
31
+
32
+ The whole API consists of three methods so far: `expose`, `expose!`, and
33
+ `exposure_config`.
34
+
35
+ In the simplest scenario you'll just use it to expose a model in the
36
+ controller:
37
+
38
+ ```ruby
39
+ class ThingsController < ApplicationController
40
+ expose :thing
46
41
  end
47
42
  ```
48
43
 
49
- To something like this:
44
+ Now every time you call `thing` in your controller or view, it will look for an
45
+ ID and try to perform `Thing.find(id)`. If the ID isn't found, it will call
46
+ `Thing.new(things_params)`. The result will be memoized in an `@exposed_thing`
47
+ instance variable.
48
+
49
+ #### Example Controller
50
+
51
+ Here's what a standard Rails 4 CRUD controller using Decent Exposure might look like:
50
52
 
51
53
  ```ruby
52
- class Controller
53
- expose(:person)
54
+ class ThingsController < ApplicationController
55
+ expose :things, ->{ Thing.all }
56
+ expose :thing
54
57
 
55
58
  def create
56
- if person.save
57
- redirect_to(person)
59
+ if thing.save
60
+ redirect_to thing_path(thing)
58
61
  else
59
62
  render :new
60
63
  end
61
64
  end
62
65
 
63
66
  def update
64
- if person.save
65
- redirect_to(person)
67
+ if thing.update(thing_params)
68
+ redirect_to thing_path(thing)
66
69
  else
67
70
  render :edit
68
71
  end
69
72
  end
70
- end
71
- ```
72
-
73
- And your views from this:
74
73
 
75
- ```ruby
76
- @person.email
77
- ```
74
+ def destroy
75
+ thing.destroy
76
+ redirect_to things_path
77
+ end
78
78
 
79
- To simply this:
79
+ private
80
80
 
81
- ```ruby
82
- person.email
81
+ def thing_params
82
+ params.require(:thing).permit(:foo, :bar)
83
+ end
84
+ end
83
85
  ```
84
86
 
85
- In your forms, instead of this:
87
+ ### Under the Hood
86
88
 
87
- ```ruby
88
- = form_for @person do |f|
89
- ...
90
- ```
91
-
92
- To this:
89
+ The default resolving workflow is pretty powerful and customizable. It could be
90
+ expressed with the following pseudocode:
93
91
 
94
92
  ```ruby
95
- = form_for person do |f|
96
- ...
97
- ```
98
-
99
- `decent_exposure` makes it easy to define named methods that are made available
100
- to your views and which memoize the resultant values. It also tucks away the
101
- details of the common fetching, initializing and updating of resources and
102
- their parameters.
103
-
104
- That's neat and all, but the real advantage comes when it's time to refactor
105
- (because you've encapsulated now). What happens when you need to scope your
106
- `Person` resource from a `Company`? Which implementation isolates those changes
107
- better? In that particular example, `decent_exposure` goes one step farther and
108
- will handle the scoping for you (with a smidge of configuration) while still
109
- handling all that repetitive initialization, as we'll see next.
110
-
111
- Even if you decide not to use `decent_exposure`, do yourself a favor and stop
112
- using instance variables in your views. Your code will be cleaner and easier to
113
- refactor as a result. If you want to learn more about this approach, I've
114
- expanded on my thoughts in the article [A Diatribe on Maintaining State][1].
93
+ def fetch(scope, id)
94
+ instance = id ? find(id, scope) : build(build_params, scope)
95
+ decorate(instance)
96
+ end
115
97
 
116
- ## Environmental Awareness
98
+ def id
99
+ params[:thing_id] || params[:id]
100
+ end
117
101
 
118
- Well, no it won't lessen your carbon footprint, but it does take a lot of
119
- cues from what's going on around it...
102
+ def find(id, scope)
103
+ scope.find(id)
104
+ end
120
105
 
121
- `decent_exposure` will build the requested object in one of a couple of ways
122
- depending on what the `params` make available to it. At its simplest, when an
123
- `id` is present in the `params` hash, `decent_exposure` will attempt to find a
124
- record. In absence of `params[:id]` `decent_exposure` will try to build a new
125
- record.
106
+ def build(params, scope)
107
+ scope.new(params) # Thing.new(params)
108
+ end
126
109
 
127
- Once the object has been obtained, it attempts to set the attributes of the
128
- resulting object. Thus, a newly minted `person` instance will get any
129
- attributes set that've been passed along in `params[:person]`. When you
130
- interact with `person` in your create action, just call save on it and handle
131
- the valid/invalid branch. Let's revisit our previous example:
110
+ def scope
111
+ model # Thing
112
+ end
132
113
 
133
- ```ruby
134
- class Controller
135
- expose(:person)
114
+ def model
115
+ exposure_name.classify.constantize # :thing -> Thing
116
+ end
136
117
 
137
- def create
138
- if person.save
139
- redirect_to(person)
140
- else
141
- render :new
142
- end
118
+ def build_params
119
+ if respond_to?(:thing_params, true) && !request.get?
120
+ thing_params
121
+ else
122
+ {}
143
123
  end
144
124
  end
145
- ```
146
125
 
147
- Behind the scenes, `decent_exposure` has essentially done this:
148
-
149
- ```ruby
150
- person.attributes = params[:person]
126
+ def decorate(thing)
127
+ thing
128
+ end
151
129
  ```
152
130
 
153
- In Rails, this assignment is actually a merge with the current attributes and
154
- it marks attributes as dirty as you would expect. This is why you're simply
155
- able to call `save` on the `person` instance in the create action, rather than
156
- the typical `update_attributes(params[:person])`.
157
-
158
- **An Aside**
131
+ The exposure is also lazy, which means that it won't do anything until you call
132
+ the method. To eliminate this laziness you can use the `expose!` macro instead,
133
+ which will try to resolve the exposure in a before filter.
159
134
 
160
- Did you notice there's no `new` action? Yeah, that's because we don't need it.
161
- More often than not actions that respond to `GET` requests are just setting up
162
- state. Since we've declared an interface to our state and made it available to
163
- the view (a.k.a. the place where we actually want to access it), we just let
164
- Rails do it's magic and render the `new` view, lazily evaluating `person` when
165
- we actually need it.
135
+ It is possible to override each step with options. The acceptable options to the
136
+ `expose` macro are:
166
137
 
167
- **A Caveat**
138
+ ### `fetch`
168
139
 
169
- Rails conveniently responds with a 404 if you get a record not found in the
170
- controller. Since we don't find the object until we're in the view in this
171
- paradigm, we get an ugly `ActionView::TemplateError` instead. If this is
172
- problematic for you, consider using the `expose!` method to circumvent lazy
173
- evaluation and eagerly evaluate whilst still in the controller.
140
+ This is the entry point. The `fetch` proc defines how to resolve your exposure
141
+ in the first place.
174
142
 
175
- ## Usage
143
+ ```ruby
144
+ expose :thing, fetch: ->{ get_thing_some_way_or_another }
145
+ ```
176
146
 
177
- In an effort to make the examples below a bit less magical, we'll offer a
178
- simplified explanation for how the exposed resource would be queried for
179
- (assuming you are using `ActiveRecord`).
147
+ Because the above behavior overrides the normal workflow, all other options
148
+ would be ignored. However, Decent Exposure is decent enough to actually blow
149
+ up with an error so you don't accidentally do this.
180
150
 
181
- ### Obtaining an instance of an object:
151
+ There are other less verbose ways to pass the `fetch` block, since you'll
152
+ probably be using it often:
182
153
 
183
154
  ```ruby
184
- expose(:person)
155
+ expose(:thing){ get_thing_some_way_or_another }
185
156
  ```
186
157
 
187
- **Query Explanation**
188
-
189
- <table>
190
- <tr>
191
- <td><code>id</code> present?</td>
192
- <td>Query</td>
193
- </tr>
194
- <tr>
195
- <td><code>true</code></td>
196
- <td><code>Person.find(params[:id])</code></td>
197
- </tr>
198
- <tr>
199
- <td><code>false</code></td>
200
- <td><code>Person.new(params[:person])</code></td>
201
- </tr>
202
- </table>
203
-
204
- ### Obtaining a collection of objects
158
+ Or
205
159
 
206
160
  ```ruby
207
- expose(:people)
161
+ expose :thing, ->{ get_thing_some_way_or_another }
208
162
  ```
209
163
 
210
- **Query Explanation**
211
-
212
- <table>
213
- <tr>
214
- <td>Query</td>
215
- </tr>
216
- <tr>
217
- <td><code>Person.scoped</code></td>
218
- </tr>
219
- </table>
220
-
221
- ### Scoping your object queries
222
-
223
- Want to scope your queries to ensure object hierarchy? `decent_exposure`
224
- automatically scopes singular forms of a resource from a plural form where
225
- they're defined:
164
+ Or even shorter
226
165
 
227
166
  ```ruby
228
- expose(:people)
229
- expose(:person)
167
+ expose :thing, :get_thing_some_way_or_another
230
168
  ```
231
169
 
232
- **Query Explanation**
233
-
234
- <table>
235
- <tr>
236
- <td><code>id</code> present?</td>
237
- <td>Query</td>
238
- </tr>
239
- <tr>
240
- <td><code>true</code></td>
241
- <td><code>Person.scoped.find(params[:id])</code></td>
242
- </tr>
243
- <tr>
244
- <td><code>false</code></td>
245
- <td><code>Person.scoped.new(params[:person])</code></td>
246
- </tr>
247
- </table>
248
-
249
- How about a more realistic scenario where the object hierarchy specifies
250
- something useful, like only finding people in a given company:
170
+ There is another shortcut that allows you to redefine the entire fetch block
171
+ with less code:
251
172
 
252
173
  ```ruby
253
- expose(:company)
254
- expose(:people, ancestor: :company)
255
- expose(:person)
174
+ expose :comments, from: :post
175
+ # equivalent to
176
+ expose :comments, ->{ post.comments }
256
177
  ```
257
178
 
258
- **Query Explanation**
259
-
260
- <table>
261
- <tr>
262
- <td>person <code>id</code> present?</td>
263
- <td>Query</td>
264
- </tr>
265
- <tr>
266
- <td><code>true</code></td>
267
- <td><code>Company.find(params[:company_id]).people.find(params[:id])</code></td>
268
- </tr>
269
- <tr>
270
- <td><code>false</code></td>
271
- <td><code>Company.find(params[:company_id]).people.new(params[:person])</code></td>
272
- </tr>
273
- </table>
179
+ ### `id`
274
180
 
275
- ### Further configuration
181
+ The default fetch logic relies on the presence of an ID. And of course Decent
182
+ Exposure allows you to specify how exactly you want the ID to be extracted.
276
183
 
277
- `decent_exposure` is a configurable beast. Let's take a look at some of the
278
- things you can do:
279
-
280
- **Specify the model name:**
184
+ Default behavior could be expressed using following code:
281
185
 
282
186
  ```ruby
283
- expose(:company, model: :enterprisey_company)
187
+ expose :thing, id: ->{ params[:thing_id] || params[:id] }
284
188
  ```
285
189
 
286
- **Specify the parameter accessor method:**
190
+ But nothing is stopping you from throwing in any arbitrary code:
287
191
 
288
192
  ```ruby
289
- expose(:company, params: :company_params)
193
+ expose :thing, id: ->{ 42 }
290
194
  ```
291
195
 
292
- **Specify the finder method:**
196
+ Passing lambdas might not always be fun, so here are a couple of shortcuts that
197
+ could help make life easier.
293
198
 
294
199
  ```ruby
295
- expose(:article, finder: :find_by_slug)
200
+ expose :thing, id: :custom_thing_id
201
+ # equivalent to
202
+ expose :thing, id: ->{ params[:custom_thing_id] }
203
+
204
+ expose :thing, id: [:try_this_id, :or_maybe_that_id]
205
+ # equivalent to
206
+ expose :thing, id: ->{ params[:try_this_id] || params[:or_maybe_that_id] }
296
207
  ```
297
208
 
298
- **Specify the parameter key to use to fetch the object:**
209
+ ### `find`
210
+
211
+ If an ID was provided, Decent Exposure will try to find the model using it.
212
+ Default behavior could be expressed with this configuration:
299
213
 
300
214
  ```ruby
301
- expose(:article, finder_parameter: :slug)
215
+ expose :thing, find: ->(id, scope){ scope.find(id) }
302
216
  ```
303
217
 
304
- ### Setting a distinct object for a single action
218
+ Where `scope` is a model scope, like `Thing` or `User.active` or
219
+ `Post.published`.
305
220
 
306
- There are times when one action in a controller is different from the
307
- rest of the actions. Rather than putting conditional logic in your
308
- exposure block, a better approach is the use the controller's setter
309
- methods:
221
+ Now, if you're using FriendlyId or Stringex or something similar, you'd have to
222
+ customize your finding logic. Your code might look somewhat like this:
310
223
 
311
224
  ```ruby
312
- expose(:articles) { current_user.articles }
313
- expose(:article)
314
-
315
- def index
316
- self.articles = Article.all
317
- end
225
+ expose :thing, find: ->(id, scope){ scope.find_by!(slug: id) }
318
226
  ```
319
227
 
320
- ### Getting your hands dirty
321
-
322
- While we try to make things as easy for you as possible, sometimes you just
323
- need to go off the beaten path. For those times, `expose` takes a block which
324
- it lazily evaluates and returns the result of when called. So for instance:
228
+ Again, because this is likely to happen a lot, Decent Exposure gives you a
229
+ decent shortcut so you can get more done by typing less.
325
230
 
326
231
  ```ruby
327
- expose(:environment) { Rails.env }
232
+ expose :thing, find_by: :slug
328
233
  ```
329
234
 
330
- This block is evaluated and the memoized result is returned whenever you call
331
- `environment`.
332
-
333
- #### Using the Default decent_exposure Goodness
235
+ ### `build`
334
236
 
335
- If you don't want to go too far off the beaten path, the value of the default
336
- exposure can be easily obtained inside of your custom block. The block will
337
- receive a proxy object that you can use to lazily evaluate
338
- the default decent_exposure logic. For example:
237
+ When an ID is not present, Decent Exposure tries to build an object for you.
238
+ By default, it behaves like this:
339
239
 
340
240
  ```ruby
341
- expose(:articles) {|default| default.limit(10) }
241
+ expose :thing, build: ->(thing_params, scope){ scope.new(thing_params) }
342
242
  ```
343
243
 
344
- This allows you to customize your exposures, without having to redo all of
345
- the built-in logic decent_exposure gives you out of the box.
244
+ ### `build_params`
346
245
 
347
- ### Custom strategies
246
+ These options are responsible for calulating params before passing them to the
247
+ build step. The default behavior was modeled with Strong Parameters in mind and
248
+ is somewhat smart: it calls the `thing_params` controller method if it's
249
+ available and the request method is not `GET`. In all other cases it produces
250
+ an empty hash.
348
251
 
349
- For the times when custom behavior is needed for resource finding,
350
- `decent_exposure` provides a base class for extending. For example, if
351
- scoping a resource from `current_user` is not an option, but you'd like
352
- to verify a resource's relationship to the `current_user`, you can use a
353
- custom strategy like the following:
252
+ You can easily specify which controller method you want it to call instead of
253
+ `thing_params`, or just provide your own logic:
354
254
 
355
255
  ```ruby
356
- class VerifiableStrategy < DecentExposure::Strategy
357
- delegate :current_user, :to => :controller
256
+ expose :thing, build_params: :custom_thing_params
257
+ expose :other_thing, build_params: ->{ { foo: "bar" } }
358
258
 
359
- def resource
360
- instance = model.find(params[:id])
361
- if current_user != instance.user
362
- raise ActiveRecord::RecordNotFound
363
- end
364
- instance
365
- end
366
- end
367
- ```
368
-
369
- You would then use your custom strategy in your controller:
259
+ private
370
260
 
371
- ```ruby
372
- expose(:post, strategy: VerifiableStrategy)
261
+ def custom_thing_params
262
+ # strong parameters stuff goes here
263
+ end
373
264
  ```
374
265
 
375
- The API only necessitates you to define `resource`, but provides some
376
- common helpers to access common things, such as the `params` hash. For
377
- everything else, you can delegate to `controller`, which is the same as
378
- `self` in the context of a normal controller action.
379
-
380
- ### Customizing your exposures
266
+ ### `scope`
381
267
 
382
- For most things, you'll be able to pass a few configuration options and get
383
- the desired behavior. For changes you want to affect every call to `expose` in
384
- a controller or controllers inheriting from it (e.g. `ApplicationController`,
385
- if you need to change the behavior for all your controllers), you can define
386
- an `decent_configuration` block:
268
+ Defines the scope that's used in `find` and `build` steps.
387
269
 
388
270
  ```ruby
389
- class ApplicationController < ActionController::Base
390
- decent_configuration do
391
- strategy MongoidStrategy
392
- end
393
- end
271
+ expose :thing, scope: ->{ current_user.things }
272
+ expose :user, scope: ->{ User.active }
273
+ expose :post, scope: ->{ Post.published }
394
274
  ```
395
275
 
396
- A `decent_configuration` block without a `:name` argument is considered the
397
- "default" configuration for that controller (and it's ancestors). All things
398
- considered, you probably only want to change the strategy in a default.
399
- Nonetheless, you can pass any configuration option you can to an individual
400
- exposure to the `decent_configuration` block.
401
-
402
- If you don't want a specific configuration to affect every exposure in the
403
- given controller, you can give it a name like so:
276
+ Like before, shortcuts are there to make you happier:
404
277
 
405
278
  ```ruby
406
- class ArticleController < ApplicationController
407
- decent_configuration(:sluggable) do
408
- finder :find_by_slug
409
- finder_parameter :slug
410
- end
411
- end
279
+ expose :post, scope: :published
280
+ # equivalent to
281
+ expose :post, scope: ->{ Post.published }
412
282
  ```
413
283
 
414
- And opt into it like so:
284
+ and
415
285
 
416
286
  ```ruby
417
- expose(:article, config: :sluggable)
287
+ expose :thing, parent: :current_user
288
+ # equivalent to:
289
+ expose :thing, scope: ->{ current_user.things }
418
290
  ```
419
291
 
420
- ## Usage with Rails 4 (or strong\_parameters plugin)
292
+ ### `model`
421
293
 
422
- If you're using Rails 4 or
423
- [strong\_parameters](https://github.com/rails/strong_parameters), add the
424
- following to your ApplicationController:
294
+ Allows you to specify the model class to use. Pretty straightforward.
425
295
 
426
-
427
- ``` ruby
428
- class ApplicationController < ActionController::Base
429
- decent_configuration do
430
- strategy DecentExposure::StrongParametersStrategy
431
- end
432
- end
296
+ ```ruby
297
+ expose :thing, model: ->{ AnotherThing }
298
+ expose :thing, model: AnotherThing
299
+ expose :thing, model: "AnotherThing"
300
+ expose :thing, model: :another_thing
433
301
  ```
434
302
 
435
- Then, when you'd like parameters to be assigned to a model, add the
436
- `attributes` option to your exposure:
303
+ ### `decorate`
437
304
 
438
- ```ruby
439
- class FooController < ApplicationController
440
- expose(:foo, attributes: :foo_params)
305
+ Before returning the thing, Decent Exposure will run it through the
306
+ decoration process. Initially, this does nothing, but you can obviously change
307
+ that:
441
308
 
442
- private
443
- def foo_params
444
- params.require(:foo).permit(:bar, :baz)
445
- end
446
- end
309
+ ```ruby
310
+ expose :thing, decorate: ->(thing){ ThingDecorator.new(thing) }
447
311
  ```
448
312
 
449
- In the example above, `foo_params` will only be called on a PUT, POST or
450
- PATCH request.
313
+ ## `exposure_config`
451
314
 
452
-
453
- ## Testing
454
-
455
- Controller testing remains trivially easy. The shift is that you now set expectations on methods rather than instance variables. With RSpec, this mostly means avoiding `assign` and `assigns`.
315
+ You can pre-save some configuration with `exposure_config` method to reuse it
316
+ later.
456
317
 
457
318
  ```ruby
458
- describe CompaniesController do
459
- describe "GET index" do
460
-
461
- # this...
462
- it "assigns @companies" do
463
- company = Company.create
464
- get :index
465
- assigns(:companies).should eq([company])
466
- end
319
+ exposure_config :cool_find, find: ->{ very_cool_find_code }
320
+ exposure_config :cool_build, build: ->{ very_cool_build_code }
467
321
 
468
- # becomes this
469
- it "exposes companies" do
470
- company = Company.create
471
- get :index
472
- controller.companies.should eq([company])
473
- end
474
- end
475
- end
322
+ expose :thing, with: [:cool_find, :cool_build]
323
+ expose :another_thing, with: :cool_build
476
324
  ```
477
325
 
478
- View specs follow a similar pattern:
326
+ ## Contributing
479
327
 
480
- ```ruby
481
- describe "people/index.html.erb" do
328
+ 1. Fork it (https://github.com/hashrocket/decent_exposure/fork)
329
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
330
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
331
+ 4. Push to the branch (`git push origin my-new-feature`)
332
+ 5. Create a new Pull Request
482
333
 
483
- # this...
484
- it "lists people" do
485
- assign(:people, [ mock_model(Person, name: 'John Doe') ])
486
- render
487
- rendered.should have_content('John Doe')
488
- end
489
-
490
- # becomes this
491
- it "lists people" do
492
- view.stub(people: [ mock_model(Person, name: 'John Doe') ])
493
- render
494
- rendered.should have_content('John Doe')
495
- end
496
-
497
- end
498
- ```
334
+ ## About
499
335
 
336
+ [![Hashrocket logo](hashrocket_logo.png)](https://hashrocket.com)
500
337
 
501
- [1]: http://stephencaudill.com/blog/2010/a-diatribe-on-maintaining-state.html
338
+ Decent Exposure is supported by the team at [Hashrocket](https://hashrocket.com), a multidisciplinary design & development consultancy. If you'd like to [work with us](https://hashrocket.com/contact-us/hire-us) or [join our team](https://hashrocket.com/contact-us/jobs), don't hesitate to get in touch.