decent_exposure 2.3.3 → 3.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.