adequate_exposure 0.0.4 → 0.0.5

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: 08518a93835d972d60b024e436f478f894b811c4
4
- data.tar.gz: 7d0c3042b2d662a44e1d22ec6415fc46c1957078
3
+ metadata.gz: 331663a8f718896cab6be3800c0af9fc98379e8b
4
+ data.tar.gz: e942a3866cdd940adf9dd9e08e979b68b8456446
5
5
  SHA512:
6
- metadata.gz: 3925a14260bd04bf95bba1e8785595ccb2771526b056f42bcc63e88978ab218520bc911462a61b124cef4df0c8103f08e1a2314c63e5623007552387d5786f8a
7
- data.tar.gz: 027d750fbafb02bcee26be876daa877e04c033d5656a035c8df117f0289b8ffa5cc3f4becb93563e952f8b1a40ed95c5ac57efd797136eaebaa204af7389ccdd
6
+ metadata.gz: 4ce222e1500bce49ee95bc6eec47bc7f4956591070fba3b9acf5e5a3e3585296da02c79bb82c6532e65acf7049e39833709e8efe8032a8de58b18a6da26eaf59
7
+ data.tar.gz: 9e292f24fbf0242ee15b8bb41483a2aa73def2316373612645d94c99592430c31ba41bfc3be9d243326e29f5ee62084ec7e408b3da4b12a5201dd07dcf7645d2
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.9
3
+ - 2.0
4
+ - 2.1
data/README.md CHANGED
@@ -1,22 +1,23 @@
1
1
  # Adequate Exposure
2
-
3
- This is WIP. Please don't send pull requests yet, I'm still actively rewriting things.
2
+ [![Gem Version](https://img.shields.io/gem/v/adequate_exposure.svg)](https://rubygems.org/gems/adequate_exposure)
3
+ [![Build Status](https://img.shields.io/travis/rwz/adequate_exposure.svg)](http://travis-ci.org/rwz/adequate_exposure)
4
+ [![Code Climate](https://img.shields.io/codeclimate/github/rwz/adequate_exposure.svg)](https://codeclimate.com/github/rwz/adequate_exposure)
4
5
 
5
6
  Exposing things, adequately.
6
7
 
7
8
  Adequate exposure is a lightweight alternative to [Decent
8
- Exposure](https://github.com/voxdolo/decent_exposure). With it's narrowly
9
+ Exposure](https://github.com/voxdolo/decent_exposure). With its narrowly
9
10
  focused api you can get exactly what you need without all the extra dressing.
10
11
 
11
12
  *Note: It is not the intent of the author to imply that Decent Exposure is
12
- inadequate.)*
13
+ inadequate.*
13
14
 
14
15
  Installation is as simple as: `$ gem install adequate_exposure`. Once you have
15
16
  that down we can start talking about the API.
16
17
 
17
18
  ## API
18
19
 
19
- The whole API consists of one `expose` method.
20
+ The whole API consists of two methods so far: `expose` and `expose!`.
20
21
 
21
22
  In the simplest scenario you'll just use it to expose a model in the
22
23
  controller:
@@ -28,28 +29,29 @@ end
28
29
  ```
29
30
 
30
31
  Now every time you call `thing` in your controller or view, it'll look for an
31
- id and try to perform `Thing.find(id)` or `Thing.new` if the id is not found.
32
- It'll also memoize the result in `@exposed_thing` instance variable.
32
+ ID and try to perform `Thing.find(id)`. If the ID isn't found, it'll call
33
+ `Thing.new(things_params)`. The result will be memoized in an `@exposed_thing`
34
+ instance variable.
33
35
 
34
36
  The default resolving workflow if pretty powerful and customizable. It could be
35
37
  expressed with the following pseudocode:
36
38
 
37
39
  ```ruby
38
40
  def fetch(scope, id)
39
- instance = id ? find(id, scope) : build(scope)
41
+ instance = id ? find(id, scope) : build(build_params, scope)
40
42
  decorate(instance)
41
43
  end
42
44
 
43
45
  def id
44
- params[:id] || params[:thing_id]
46
+ params[:thing_id] || params[:id]
45
47
  end
46
48
 
47
49
  def find(id, scope)
48
- scope.find(id) # Thing.find(id)
50
+ scope.find(id)
49
51
  end
50
52
 
51
- def build(scope)
52
- scope.new(build_params) # Thing.new(thing_params)
53
+ def build(params, scope)
54
+ scope.new(params) # Thing.new(params)
53
55
  end
54
56
 
55
57
  def scope
@@ -73,75 +75,176 @@ def decorate(thing)
73
75
  end
74
76
  ```
75
77
 
78
+ The exposure is also lazy, which means that it won't do anything until you call
79
+ the method. To eliminate this lazyness you can use `expose!` macro instead,
80
+ which will try to resolve the exposure in a before filter.
81
+
76
82
  Each step could be overrided with options. The acceptable options to the
77
83
  `expose` macro are:
78
84
 
79
- **fetch**
85
+ ### `fetch`
80
86
 
81
- This is the entry point. Fetch proc defines how to resolve your exposure in the
82
- first place.
87
+ This is the entry point. The `fetch` proc defines how to resolve your exposure
88
+ in the first place.
83
89
 
84
90
  ```ruby
85
91
  expose :thing, fetch: ->{ get_thing_some_way_or_another }
86
92
  ```
87
93
 
88
- **find**
94
+ Because the above behavior overrides the normal workflow, all other options
95
+ would be ignored. However, Adequate Exposure is decent enough to actually blow
96
+ up with an error so you don't accidentally do this.
89
97
 
90
- Defines how to perform the finding. Could be useful if you don't want to use standard
91
- Rails finder.
98
+ There are other less verbose ways to pass the `fetch` block, since you'll
99
+ probably be using it often:
92
100
 
93
101
  ```ruby
94
- expose :thing, find: ->(id, scope){ scope.find_by(slug: id) }
102
+ expose(:thing){ get_thing_some_way_or_another }
95
103
  ```
96
104
 
97
- **build**
105
+ Or if you (like me) absolutely hate parens in side-effect methods:
98
106
 
99
- Allows to override the build process that takes place when id is not provided.
107
+ ```ruby
108
+ expose :thing, ->{ get_thing_some_way_or_another }
109
+ ```
110
+
111
+ There is another shortcut that allows you to redefine entire fetch block with
112
+ less code:
100
113
 
101
114
  ```ruby
102
- expose :thing, build: ->(scope){ Thing.build_with_defaults }
115
+ expose :comments, from: :post
116
+ # equivalent to
117
+ expose :comments, ->{ post.comments }
103
118
  ```
104
119
 
105
- **id**
120
+ ### `id`
121
+
122
+ The default fetch logic relies on the presence of an ID. And of course Adequate
123
+ Exposure allows to to specify how exactly you want the ID to be extracted.
106
124
 
107
- Specifies how to extract id from parameters hash.
125
+ Default behavior could be expressed using following code:
108
126
 
109
127
  ```ruby
110
- # default
111
128
  expose :thing, id: ->{ params[:thing_id] || params[:id] }
129
+ ```
112
130
 
113
- # id is always goona be 42
131
+ But nothing is stopping you from throwing in any arbitrary code:
132
+
133
+ ```ruby
134
+ # id is always gonna be the answer to ultimate question of life, the universe,
135
+ # and everyting
114
136
  expose :thing, id: ->{ 42 }
137
+ ```
115
138
 
139
+ Passing lambdas might not always be fun, so here are couple shortcuts that could
140
+ help making life easier.
141
+
142
+ ```ruby
116
143
  # equivalent to id: ->{ params[:custom_thing_id] }
117
144
  expose :thing, id: :custom_thing_id
118
145
 
119
146
  # equivalent to id: ->{ params[:try_this_id] || params[:or_maybe_that_id] }
120
- expose :thing, id: %i[try_this_id or_maybe_that_id]
147
+ expose :thing, id: [:try_this_id, :or_maybe_that_id]
148
+ ```
149
+
150
+ ### `find`
151
+
152
+ If an ID was provided, Adequate Exposure will try to find the model using it.
153
+ Default behavior could be expressed with this configuration:
154
+
155
+ ```ruby
156
+ expose :thing, find: ->(id, scope){ scope.find(id) }
157
+ ```
158
+
159
+ Where `scope` is a model scope, like `Thing` or `User.active` or
160
+ `Post.published`.
161
+
162
+ Now, if you're using FriendlyId or Stringex or something similar, you'd have to
163
+ customize your finding logic. You code might look somewhat like this:
164
+
165
+ ```ruby
166
+ expose :thing, find: ->(id, scope){ scope.find_by!(slug: id) }
167
+ ```
168
+
169
+ Again, because this is likely to happen a lot, Adequate Exposure gives you a
170
+ decent shortcut so you can get more done by typing less.
171
+
172
+ ```ruby
173
+ expose :thing, find_by: :slug
121
174
  ```
122
175
 
123
- **scope**
176
+ ### `build`
177
+
178
+ When an ID is not present, Adequate Exposure tries to build an object for you. By
179
+ default, it behaves like this:
180
+
181
+ ```ruby
182
+ expose :thing, build: ->(thing_params, scope){ scope.new(thing_params) }
183
+ ```
184
+
185
+ ### `build_params`
186
+
187
+ This options is responsible for calulating params before passing it to the
188
+ build step. The default behavior was modeled with Strong Parameters in mind and
189
+ is somewhat smart: it calls `thing_params` controller method if it's available
190
+ and request method it not `GET`. In all other cases it produces an empty hash.
191
+
192
+ You can easily specify which controller method you want it to call instead of
193
+ `thing_params`, or just provide your own logic:
194
+
195
+ ```ruby
196
+ expose :thing, build_params: :custom_thing_params
197
+ expose :other_thing, build_params: ->{ { foo: "bar" } }
198
+
199
+ private
200
+
201
+ def custom_thing_params
202
+ # strong parameters stuff goes here
203
+ end
204
+ ```
205
+
206
+ ### `scope`
124
207
 
125
208
  Defines the scope that's used in `find` and `build` steps.
126
209
 
127
210
  ```ruby
128
211
  expose :thing, scope: ->{ current_user.things }
212
+ expose :user, scope: ->{ User.active }
213
+ expose :post, scope: ->{ Post.published }
214
+ ```
215
+
216
+ Like before, shortcuts are there to make you happier:
217
+
218
+ ```ruby
219
+ expose :post, scope: :published
220
+ # fully equivalent to
221
+ expose :post, scope: ->{ Post.published }
222
+ ```
223
+
224
+ and
225
+
226
+ ```ruby
227
+ expose :thing, parent: :current_user
228
+ # fully equivalent to:
229
+ expose :thing, scope: ->{ current_user.things }
129
230
  ```
130
231
 
131
- **model**
232
+ ### `model`
132
233
 
133
- Specify the model class to use.
234
+ Allows to specify the model class to use. Pretty straightforward.
134
235
 
135
236
  ```ruby
136
237
  expose :thing, model: ->{ AnotherThing }
137
238
  expose :thing, model: AnotherThing
239
+ expose :thing, model: "AnotherThing"
138
240
  expose :thing, model: :another_thing
139
241
  ```
140
242
 
243
+ ### `decorate`
141
244
 
142
- **decorate**
143
-
144
- Allows to define a block that wraps an instance before it's returned. Useful for decorators.
245
+ Before returning the thing, Adequate Exposure will run it through the
246
+ decoration process. Initially, this does nothing, but you can obviously change
247
+ that:
145
248
 
146
249
  ```ruby
147
250
  expose :thing, decorate: ->(thing){ ThingDecorator.new(thing) }
@@ -1,6 +1,4 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
4
2
  require "adequate_exposure/version"
5
3
 
6
4
  Gem::Specification.new do |spec|
@@ -15,7 +13,7 @@ Gem::Specification.new do |spec|
15
13
  spec.test_files = spec.files.grep(/\Aspec\//)
16
14
  spec.require_path = "lib"
17
15
 
18
- spec.required_ruby_version = "~> 2.0"
16
+ spec.required_ruby_version = ">= 1.9.3"
19
17
 
20
18
  spec.add_dependency "railties", "~> 4.0"
21
19
  spec.add_dependency "activesupport", "~> 4.0"
@@ -2,8 +2,10 @@ module AdequateExposure
2
2
  class Attribute
3
3
  attr_reader :name, :fetch, :ivar_name
4
4
 
5
- def initialize(name:, fetch:, ivar_name:)
6
- @name, @fetch, @ivar_name = name, fetch, ivar_name
5
+ def initialize(options)
6
+ @name = options.fetch(:name)
7
+ @fetch = options.fetch(:fetch)
8
+ @ivar_name = options.fetch(:ivar_name)
7
9
  end
8
10
 
9
11
  def getter_method_name
@@ -6,8 +6,10 @@ module AdequateExposure
6
6
  new(*args, &block).expose!
7
7
  end
8
8
 
9
- def initialize(controller, name, fetch_block=nil, **options, &block)
9
+ def initialize(controller, name, *args, &block)
10
10
  @controller = controller
11
+ options = args.extract_options!
12
+ fetch_block = args.pop
11
13
  @options = options.with_indifferent_access.merge(name: name)
12
14
 
13
15
  merge_lambda_option :fetch, fetch_block if fetch_block
@@ -11,7 +11,7 @@ module AdequateExposure
11
11
  options.fetch(:name)
12
12
  end
13
13
 
14
- %i[fetch find build build_params scope model id decorate].each do |method_name|
14
+ %w[fetch find build build_params scope model id decorate].each do |method_name|
15
15
  define_method method_name do |*args|
16
16
  ivar_name = "@#{method_name}"
17
17
  return instance_variable_get(ivar_name) if instance_variable_defined?(ivar_name)
@@ -23,7 +23,7 @@ module AdequateExposure
23
23
 
24
24
  def default_fetch
25
25
  computed_scope = scope(model)
26
- instance = id ? find(id, computed_scope) : build(computed_scope)
26
+ instance = id ? find(id, computed_scope) : build(build_params, computed_scope)
27
27
  decorate(instance)
28
28
  end
29
29
 
@@ -43,8 +43,8 @@ module AdequateExposure
43
43
  scope.find(id)
44
44
  end
45
45
 
46
- def default_build(scope)
47
- scope.new(build_params)
46
+ def default_build(params, scope)
47
+ scope.new(params)
48
48
  end
49
49
 
50
50
  def default_decorate(instance)
@@ -1,3 +1,3 @@
1
1
  module AdequateExposure
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -22,7 +22,7 @@ describe AdequateExposure::Controller do
22
22
  let(:controller){ controller_klass.new }
23
23
  before{ allow(controller).to receive(:request){ request } }
24
24
 
25
- %i[expose expose!].each do |method_name|
25
+ %w[expose expose!].each do |method_name|
26
26
  define_method method_name do |*args, &block|
27
27
  controller_klass.send method_name, *args, &block
28
28
  end
@@ -274,7 +274,7 @@ describe AdequateExposure::Controller do
274
274
  end
275
275
 
276
276
  it "allows overriding id with an array of symbols" do
277
- expose :thing, id: %i[non-existent-id lolwut another_id_param]
277
+ expose :thing, id: %w[non-existent-id lolwut another_id_param]
278
278
  controller.params.merge! another_id_param: 42
279
279
  end
280
280
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adequate_exposure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pavel Pravosud
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-07 00:00:00.000000000 Z
11
+ date: 2014-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -46,6 +46,7 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - ".gitignore"
49
+ - ".travis.yml"
49
50
  - Gemfile
50
51
  - LICENSE.txt
51
52
  - README.md
@@ -72,9 +73,9 @@ require_paths:
72
73
  - lib
73
74
  required_ruby_version: !ruby/object:Gem::Requirement
74
75
  requirements:
75
- - - "~>"
76
+ - - ">="
76
77
  - !ruby/object:Gem::Version
77
- version: '2.0'
78
+ version: 1.9.3
78
79
  required_rubygems_version: !ruby/object:Gem::Requirement
79
80
  requirements:
80
81
  - - ">="