assembler 1.1.0 → 1.2.0
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 +4 -4
- data/.rspec +1 -2
- data/CHANGES.md +11 -0
- data/CONTRIBUTING.md +19 -0
- data/README.md +153 -30
- data/RELEASING.md +15 -0
- data/lib/assembler.rb +48 -16
- data/lib/assembler/builder.rb +17 -14
- data/lib/assembler/initializer.rb +18 -21
- data/lib/assembler/parameter.rb +79 -0
- data/lib/assembler/version.rb +1 -1
- data/spec/after_assembly_spec.rb +91 -0
- data/spec/assemble_from_options_spec.rb +384 -0
- data/spec/{assembler_spec.rb → assemble_from_spec.rb} +19 -3
- data/spec/before_assembly_spec.rb +80 -0
- data/spec/spec_helper.rb +1 -0
- metadata +13 -5
- data/lib/assembler/parameters.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1d3eb4e375c75519e76037b6ac633be9901d78d
|
4
|
+
data.tar.gz: 0fd01f2626ac0d88104703fa5ea2545d7c581f85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ff23dd635deec43014f704eabbc1282de5f5a7cd1fa6bd584da667eba048ecb39e2b895989e321dfb70c7acb515d219402e15ed983dc8650f262e166bb46842
|
7
|
+
data.tar.gz: d833e8aba1907888013f186e4afc2f22a42edea19d81ff6893791f6dc05f60ca8781396c27a97f8b8e6cc8931081383f8dddf6b509dc5df7b576fab5b8f1b25d
|
data/.rspec
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
--
|
2
|
-
--format documentation
|
1
|
+
--require spec_helper
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 1.2.0
|
4
|
+
|
5
|
+
* Add ability to access (optionally) coerced values on the builder object, for
|
6
|
+
example Foo.new(some: 'value') {|b| puts b.some}. (Ryan Michael)
|
7
|
+
* Add `assemble_from_options` DSL method for defining coercions and aliases. (Ryan Michael)
|
8
|
+
* Add `before_assembly` and `after_assembly` hooks. (Ben Hamill)
|
9
|
+
* Make the `before_assembly` and `after_assembly` hooks additive, in the case
|
10
|
+
that those methods are called more than once. (Ben Hamill)
|
11
|
+
* Change up spec set up and move project documentation around a little. (Ben
|
12
|
+
Hamill)
|
13
|
+
|
3
14
|
## 1.1.0
|
4
15
|
|
5
16
|
* Make Ruby 1.9-compatible. (Ryan Michael)
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
Help is gladly welcomed. If you have a feature you'd like to add, it's much more
|
4
|
+
likely to get in (or get in faster) the closer you stick to these steps:
|
5
|
+
|
6
|
+
1. Open an Issue to talk about it. We can discuss whether it's the right
|
7
|
+
direction or maybe help track down a bug, etc.
|
8
|
+
1. Fork the project, and make a branch to work on your feature/fix. Master is
|
9
|
+
where you'll want to start from.
|
10
|
+
1. Turn the Issue into a Pull Request. There are several ways to do this, but
|
11
|
+
[hub](https://github.com/defunkt/hub) is probably the easiest.
|
12
|
+
1. Make sure your Pull Request includes tests.
|
13
|
+
1. Bonus points if your Pull Request updates `CHANGES.md` to include a summary
|
14
|
+
of your changes and your name like the other entries. If the last entry is
|
15
|
+
the last release, add a new `## Unreleased` heading.
|
16
|
+
1. *Do not* rev the version number in your pull request.
|
17
|
+
|
18
|
+
If you don't know how to fix something, even just a Pull Request that includes a
|
19
|
+
failing test can be helpful. If in doubt, make an Issue to discuss.
|
data/README.md
CHANGED
@@ -7,14 +7,26 @@ bunch of nanomachines that can build [almost anything](http://en.wikipedia.org/w
|
|
7
7
|
|
8
8
|
Assembler is a library that gives you a DSL to describe a super-handy
|
9
9
|
initializer pattern. You specify the parameters your object should take and
|
10
|
-
Assembler
|
11
|
-
a [builder object](http://c2.com/cgi/wiki?BuilderPattern) to a block.
|
12
|
-
care of storing the parameters and gives you private accessors, too.
|
10
|
+
Assembler gives you an initializer that takes an options hash as well as
|
11
|
+
yielding a [builder object](http://c2.com/cgi/wiki?BuilderPattern) to a block.
|
12
|
+
It takes care of storing the parameters and gives you private accessors, too.
|
13
|
+
|
14
|
+
## Contents
|
15
|
+
|
16
|
+
* [Usage](#usage)
|
17
|
+
* [assemble_from](#assemble_from)
|
18
|
+
* [assemble_from_options](#assemble_from_options)
|
19
|
+
* [Before and After Hooks](#before-and-after-hooks)
|
20
|
+
* [Contributing](#contributing)
|
13
21
|
|
14
22
|
|
15
23
|
## Usage
|
16
24
|
|
17
|
-
|
25
|
+
### `assemble_from`
|
26
|
+
|
27
|
+
The `assemble_from` method is the core of Assembler. It's also aliased as
|
28
|
+
`assemble_with`. The basic use case is to pass in required parameters followed
|
29
|
+
by optional parameters (and their defaults). Take this simple example:
|
18
30
|
|
19
31
|
```ruby
|
20
32
|
class IMAPConnection
|
@@ -26,8 +38,8 @@ class IMAPConnection
|
|
26
38
|
end
|
27
39
|
```
|
28
40
|
|
29
|
-
|
30
|
-
For example:
|
41
|
+
This enables you to instantiate your object with either an options hash or via a
|
42
|
+
block. For example:
|
31
43
|
|
32
44
|
```ruby
|
33
45
|
# These two are equivalent:
|
@@ -42,9 +54,14 @@ IMAPConnection.new do |aw|
|
|
42
54
|
aw.hostname = 'imap.example.com'
|
43
55
|
aw.use_ssl = false
|
44
56
|
end
|
57
|
+
|
58
|
+
# Or you can do a combination, if you need to:
|
59
|
+
IMAPConnection.new(hostname: 'imap.example.com') do |aw|
|
60
|
+
aw.use_ssl = false
|
61
|
+
end
|
45
62
|
```
|
46
63
|
|
47
|
-
Note that when
|
64
|
+
Note that when you set `use_ssl` to `false`, the code respects that, rather than
|
48
65
|
over-writing anything falsey with the default. If you don't want that, override
|
49
66
|
it like with `port`, below.
|
50
67
|
|
@@ -69,12 +86,14 @@ end
|
|
69
86
|
These various syntaxes enable some trickery when you're dealing with a world of
|
70
87
|
uncertainty. Let's look at a more complicated example.
|
71
88
|
|
72
|
-
Say
|
89
|
+
Say you want a class that lets us describe an Elastic Load Balancer for Amazon
|
73
90
|
Web Services. There's a lot of complexity in what each of these arguments might
|
74
|
-
be, but the key thing for
|
91
|
+
be, but the key thing for this example is this: If you have `subnets`, you
|
75
92
|
shouldn't have `availability_zones` and if you have `availability_zones`, you
|
76
93
|
shouldn't have `subnets`. And, importantly, you shouldn't send in extraneous
|
77
|
-
keys
|
94
|
+
keys; you need to be able to differentiate callers sending `nil` explicitly from
|
95
|
+
not sending in anything when you make whatever API calls you're going to make to
|
96
|
+
Amazon.
|
78
97
|
|
79
98
|
```ruby
|
80
99
|
class AmazonELB
|
@@ -95,9 +114,9 @@ end
|
|
95
114
|
```
|
96
115
|
|
97
116
|
Now, since there's a lot of complexity in what each of these arguments might be,
|
98
|
-
say
|
99
|
-
|
100
|
-
"standard" ELB.
|
117
|
+
say you've developed some best-practices about what each of them should be. And
|
118
|
+
you want to make it easy to pop off slight variations on what you consider to be
|
119
|
+
a "standard" ELB.
|
101
120
|
|
102
121
|
``` ruby
|
103
122
|
module ELBFactory
|
@@ -108,7 +127,7 @@ module ELBFactory
|
|
108
127
|
elb.security_groups = security_groups
|
109
128
|
elb.instance_ids = instance_ids
|
110
129
|
|
111
|
-
elb.health_check =
|
130
|
+
elb.health_check = HealthCheck.new(
|
112
131
|
target: 'HTTP:8000/',
|
113
132
|
healthy_threshold: '3',
|
114
133
|
unhealthy_threshold: '5',
|
@@ -143,21 +162,125 @@ would look similar to the above, but require you to assign an intermediate
|
|
143
162
|
variable for no semantic benefit.
|
144
163
|
|
145
164
|
|
165
|
+
### `assemble_from_options`
|
166
|
+
|
167
|
+
If you need to do something more complicated than what's provided by
|
168
|
+
`assemble_from`, you can specify per-argument options using
|
169
|
+
`assemble_from_options`. Like `assemble_from`, it's also aliased as
|
170
|
+
`assemble_with_options`.
|
171
|
+
|
172
|
+
Default values can be specified using the `:default` option, and work the same
|
173
|
+
as using hash-syntax with `assemble_from`.
|
174
|
+
|
175
|
+
If you would like to do some type of value coercion you can specify either a
|
176
|
+
symbol or a callable using the `:coerce` option. Symbols will be passed as
|
177
|
+
messages to the input object, and anything that responds to `#call` will be
|
178
|
+
called with the input object as an argument.
|
179
|
+
|
180
|
+
If you need to accept aliased key names you can use the `:aliases` option to
|
181
|
+
specify a list of keys. Aliases only apply to input processing; instance
|
182
|
+
variables aren't set and accessors aren't be provided.
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
class IMAPConnection
|
186
|
+
extend Assembler
|
187
|
+
|
188
|
+
# Here we want to assign an IP address so we only do DNS lookup once.
|
189
|
+
assemble_from_options :hostname, coerce: ->(h) { Resolv.getaddress(h) }
|
190
|
+
|
191
|
+
# Defaults must be specified explicitly; arguments with no default are required.
|
192
|
+
assemble_from_options :use_ssl, default: false
|
193
|
+
|
194
|
+
# We'll accept values named 'port' or 'host_port' (but we'll only assign '@port').
|
195
|
+
# Symbols can also be passed for coercions.
|
196
|
+
assemble_from_options :port, default: nil, coerce: :to_i, aliases: [:host_port]
|
197
|
+
end
|
198
|
+
|
199
|
+
instance = IMAPConnection.new(hostname: 'localhost') do |b|
|
200
|
+
puts b.hostname # => '127.0.0.0' - i.e. the accessor returns the coerced value.
|
201
|
+
puts b.use_ssl # => false - i.e. the accessor returns the default value if none is specified.
|
202
|
+
b.port = '100' # Will be coerced to the integer 100.
|
203
|
+
end
|
204
|
+
|
205
|
+
instance.host_port # => MethodMissing error - accessors aren't defined for aliases.
|
206
|
+
```
|
207
|
+
|
208
|
+
### Before and After Hooks
|
209
|
+
|
210
|
+
In some cases, you might need to take care of some extra things during object
|
211
|
+
initialization. One simple case would be if you're inheriting from another class
|
212
|
+
and need to call `super` to make sure it initializes correctly. Enter
|
213
|
+
`before_assembly` and `after_assembly`.
|
214
|
+
|
215
|
+
They both take a block and that block gets evaluated in the scope of your
|
216
|
+
instance before or after the rest of Assembler's initializer runs. This means
|
217
|
+
instance variables and private methods are available to you and that `self` is
|
218
|
+
the object being created. Nothing is `yield`ed to the blocks.
|
219
|
+
|
220
|
+
If you don't need to pass arguments or always pass the same arguments, you could
|
221
|
+
do something like this:
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
class Professor < Employee
|
225
|
+
extend Assembler
|
226
|
+
|
227
|
+
before_assembly do
|
228
|
+
super('teaching')
|
229
|
+
end
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
If, however, you need to react to or interact with options that are passed in,
|
234
|
+
you can do something like this:
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
class Professor < Employee
|
238
|
+
extend Assembler
|
239
|
+
|
240
|
+
attr_reader :title
|
241
|
+
|
242
|
+
assemble_with :department_name, :degree_subject
|
243
|
+
after_assembly do
|
244
|
+
@title = "PhD of #{degree_subject}, #{department_name}"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
If you call these methods more than once, each block will be run in the order
|
250
|
+
declared. The most common case would be a child class adding more before or
|
251
|
+
after functionality to that declared by a parent.
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
class Employee
|
255
|
+
extend Assembler
|
256
|
+
|
257
|
+
assemble_with :manager_id, department_name: nil
|
258
|
+
|
259
|
+
attr_reader :manager
|
260
|
+
|
261
|
+
after_assembly do
|
262
|
+
@manager = Manager.find(manager_id)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
class Professor < Employee
|
267
|
+
assemble_with :department_chair
|
268
|
+
|
269
|
+
attr_reader :people_answerable_to
|
270
|
+
|
271
|
+
after_assembly do
|
272
|
+
@people_answerable_to = [manager, department_name]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
```
|
276
|
+
|
277
|
+
|
146
278
|
## Contributing
|
147
279
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
1. Turn the Issue into a Pull Request. There are several ways to do this, but
|
156
|
-
[hub](https://github.com/defunkt/hub) is probably the easiest.
|
157
|
-
1. Make sure your Pull Request includes tests.
|
158
|
-
1. Bonus points if your Pull Request updates `CHANGES.md` to include a summary
|
159
|
-
of your changes and your name like the other entries. If the last entry is
|
160
|
-
the last release, add a new `## Unreleased` heading at the top.
|
161
|
-
|
162
|
-
If you don't know how to fix something, even just a Pull Request that includes a
|
163
|
-
failing test can be helpful. If in doubt, make an Issue to discuss.
|
280
|
+
If you'd like to contribute, please see the [contribution guidelines](CONTRIBUTING.md).
|
281
|
+
|
282
|
+
|
283
|
+
## Releasing
|
284
|
+
|
285
|
+
Maintainers: Please make sure to follow the [release steps](RELEASING.md) when
|
286
|
+
it's time to cut a new release.
|
data/RELEASING.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Releasing
|
2
|
+
|
3
|
+
If you want to push a new version of this gem, do this:
|
4
|
+
|
5
|
+
1. Ideally, every Pull Request should already have included an addition to the
|
6
|
+
`CHANGES.md` file summarizing the changes and crediting the author(s). It
|
7
|
+
doesn't hurt to review this to see if anything needs adding.
|
8
|
+
1. Commit any changes you make.
|
9
|
+
1. Go into version.rb and bump the version number
|
10
|
+
[as appopriate](http://semver.org/).
|
11
|
+
1. Go into CHANGES.md and change the "Unlreleased" heading to match the new
|
12
|
+
version number.
|
13
|
+
1. Commit these changes with a message like, "Minor version bump," or similar.
|
14
|
+
1. Run `rake release`.
|
15
|
+
1. High five someone nearby.
|
data/lib/assembler.rb
CHANGED
@@ -2,19 +2,27 @@ require "assembler/version"
|
|
2
2
|
require "assembler/initializer"
|
3
3
|
|
4
4
|
module Assembler
|
5
|
-
|
5
|
+
def assemble_from_options(*args)
|
6
|
+
assembly_setup do
|
7
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
8
|
+
param_names = args
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
param_names.each do |param_name|
|
11
|
+
param = Parameter.new(param_name, options)
|
12
|
+
assembly_parameters_hash[param.name] = param
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :assemble_with_options, :assemble_from_options
|
12
17
|
|
13
|
-
|
14
|
-
|
18
|
+
def assemble_from(*args)
|
19
|
+
assembly_setup do
|
20
|
+
optional = args.last.is_a?(Hash) ? args.pop : {}
|
21
|
+
required = args
|
15
22
|
|
16
|
-
|
17
|
-
|
23
|
+
optional.each { |k,v| assemble_from_options(k, default: v) }
|
24
|
+
required.each { |k| assemble_from_options(k) }
|
25
|
+
end
|
18
26
|
end
|
19
27
|
alias_method :assemble_with, :assemble_from
|
20
28
|
|
@@ -24,15 +32,39 @@ module Assembler
|
|
24
32
|
assemble_from(*args)
|
25
33
|
end
|
26
34
|
|
27
|
-
def
|
28
|
-
|
35
|
+
def before_assembly(&block)
|
36
|
+
assembly_setup do
|
37
|
+
before_assembly_blocks << block
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def before_assembly_blocks
|
42
|
+
@before_assembly_blocks ||= []
|
29
43
|
end
|
30
44
|
|
31
|
-
def
|
32
|
-
|
45
|
+
def after_assembly(&block)
|
46
|
+
assembly_setup do
|
47
|
+
after_assembly_blocks << block
|
48
|
+
end
|
33
49
|
end
|
34
50
|
|
35
|
-
def
|
36
|
-
|
51
|
+
def after_assembly_blocks
|
52
|
+
@after_assembly_blocks ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
def assembly_parameters
|
56
|
+
assembly_parameters_hash.values
|
57
|
+
end
|
58
|
+
|
59
|
+
def assembly_parameters_hash
|
60
|
+
@assembly_parameters_hash ||= {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def assembly_setup
|
64
|
+
yield
|
65
|
+
ensure
|
66
|
+
include Assembler::Initializer
|
67
|
+
attr_reader *assembly_parameters.map(&:name)
|
68
|
+
private *assembly_parameters.map(&:name)
|
37
69
|
end
|
38
70
|
end
|
data/lib/assembler/builder.rb
CHANGED
@@ -1,27 +1,30 @@
|
|
1
1
|
module Assembler
|
2
2
|
class Builder
|
3
|
-
def initialize(
|
4
|
-
@
|
3
|
+
def initialize(parameters_hash, options = {})
|
4
|
+
@options = options
|
5
|
+
@parameters_hash = parameters_hash
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
parameters_hash.each do |parameter_name, parameter|
|
8
|
+
parameter.name_and_aliases.each do |name_or_alias|
|
9
|
+
self.singleton_class.class_eval(<<-RUBY)
|
10
|
+
def #{name_or_alias}=(value)
|
11
|
+
options[:#{parameter_name.to_sym}] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def #{name_or_alias}
|
15
|
+
parameters_hash[:#{parameter_name}].value_from(options)
|
16
|
+
end
|
17
|
+
RUBY
|
18
|
+
end
|
12
19
|
end
|
13
20
|
end
|
14
21
|
|
15
22
|
def to_h
|
16
|
-
|
23
|
+
options
|
17
24
|
end
|
18
25
|
|
19
26
|
private
|
20
27
|
|
21
|
-
attr_reader :
|
22
|
-
|
23
|
-
def parameters
|
24
|
-
@parameters ||= {}
|
25
|
-
end
|
28
|
+
attr_reader :parameters_hash, :options
|
26
29
|
end
|
27
30
|
end
|
@@ -1,39 +1,36 @@
|
|
1
1
|
require "assembler/builder"
|
2
|
-
require "assembler/
|
2
|
+
require "assembler/parameter"
|
3
3
|
|
4
4
|
module Assembler
|
5
5
|
module Initializer
|
6
6
|
def initialize(options={})
|
7
|
-
|
7
|
+
if self.class.before_assembly_blocks.any?
|
8
|
+
self.class.before_assembly_blocks.each do |block|
|
9
|
+
instance_eval(&block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
builder = Assembler::Builder.new(self.class.assembly_parameters_hash, options)
|
8
14
|
|
9
15
|
yield builder if block_given?
|
10
16
|
|
11
|
-
|
17
|
+
missing_required_parameters = []
|
12
18
|
|
13
|
-
|
19
|
+
self.class.assembly_parameters.each do |param|
|
20
|
+
if_required_and_missing = -> { missing_required_parameters << param.name }
|
14
21
|
|
15
|
-
|
16
|
-
remember_value_or(param_name) { missing_required_params << param_name }
|
17
|
-
end
|
22
|
+
value = param.value_from(builder.to_h, &if_required_and_missing)
|
18
23
|
|
19
|
-
|
20
|
-
remember_value_or(param_name) { default_value }
|
24
|
+
instance_variable_set(:"@#{param.name}", value)
|
21
25
|
end
|
22
26
|
|
23
|
-
raise(ArgumentError, "missing keywords: #{
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
+
raise(ArgumentError, "missing keywords: #{missing_required_parameters.join(', ')}") if missing_required_parameters.any?
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
instance_variable_set(
|
32
|
-
:"@#{param_name}",
|
33
|
-
full_options.fetch(param_name) do
|
34
|
-
block.call
|
29
|
+
if self.class.after_assembly_blocks.any?
|
30
|
+
self.class.after_assembly_blocks.each do |block|
|
31
|
+
instance_eval(&block)
|
35
32
|
end
|
36
|
-
|
33
|
+
end
|
37
34
|
end
|
38
35
|
end
|
39
36
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Assembler
|
2
|
+
class Parameter
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize(name, options = {})
|
6
|
+
@name = name.to_sym
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def name_and_aliases
|
11
|
+
@name_and_aliases ||= [name] + aliases.map(&:to_sym)
|
12
|
+
end
|
13
|
+
|
14
|
+
def value_from(hash, &if_required_and_missing)
|
15
|
+
@memoized_value_from ||= {}
|
16
|
+
|
17
|
+
# NOTE: Jruby's Hash#hash implementation is BS:
|
18
|
+
# {:foo => :foo}.hash => 1
|
19
|
+
# {:bar => :bar}.hash => 1
|
20
|
+
# {:foo => :foo}.to_a.hash => 806614226
|
21
|
+
# {:bar => :bar}.to_a.hash => 3120054328
|
22
|
+
# Go figure...
|
23
|
+
memoization_key = hash.to_a.hash
|
24
|
+
|
25
|
+
return @memoized_value_from[memoization_key] if @memoized_value_from[memoization_key]
|
26
|
+
|
27
|
+
first_key = key_names.find { |name_or_alias| hash.has_key?(name_or_alias) }
|
28
|
+
|
29
|
+
raw_value = hash.fetch(first_key) do
|
30
|
+
options.fetch(:default) do
|
31
|
+
if_required_and_missing.call unless if_required_and_missing.nil?
|
32
|
+
|
33
|
+
# Returning here so we don't call coerce_value(nil)
|
34
|
+
return
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
@memoized_value_from[memoization_key] = coerce_value(raw_value)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :options
|
44
|
+
|
45
|
+
def key_names
|
46
|
+
name_and_aliases.flat_map do |name_or_alias|
|
47
|
+
[name_or_alias.to_sym, name_or_alias.to_s]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_default?
|
52
|
+
options.has_key?(:default)
|
53
|
+
end
|
54
|
+
|
55
|
+
def default
|
56
|
+
options[:default]
|
57
|
+
end
|
58
|
+
|
59
|
+
def coercion
|
60
|
+
options[:coerce]
|
61
|
+
end
|
62
|
+
|
63
|
+
def coerce_value(value)
|
64
|
+
if !coercion
|
65
|
+
value
|
66
|
+
elsif coercion.kind_of?(Symbol)
|
67
|
+
value.send(coercion)
|
68
|
+
elsif coercion.respond_to?(:call)
|
69
|
+
coercion.call(value)
|
70
|
+
else
|
71
|
+
raise ArgumentError, "don't know how to handle coerce value #{coercion}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def aliases
|
76
|
+
@aliases ||= Array(options[:aliases]) + Array(options[:alias])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/assembler/version.rb
CHANGED
@@ -0,0 +1,91 @@
|
|
1
|
+
describe Assembler do
|
2
|
+
describe "#after_assembly" do
|
3
|
+
context "with no other assembler helpers called" do
|
4
|
+
let(:klass) do
|
5
|
+
Class.new do
|
6
|
+
extend Assembler
|
7
|
+
|
8
|
+
after_assembly do
|
9
|
+
@after = true
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :after
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { klass.new }
|
17
|
+
|
18
|
+
it "calls the after block" do
|
19
|
+
expect(subject.after).to be_true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with a more complex declaration" do
|
24
|
+
let(:klass) do
|
25
|
+
Class.new do
|
26
|
+
extend Assembler
|
27
|
+
|
28
|
+
assemble_with middle: 'middle'
|
29
|
+
|
30
|
+
after_assembly do
|
31
|
+
a_private_method
|
32
|
+
@after = true
|
33
|
+
@middle = 'after'
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :after, :middle
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def a_private_method
|
41
|
+
"Shhhhhhh! it's a secret!"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
subject do
|
47
|
+
klass.new
|
48
|
+
end
|
49
|
+
|
50
|
+
it "calls the after block" do
|
51
|
+
expect(subject.after).to be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "calls the block after running the rest of the initializer" do
|
55
|
+
expect(subject.middle).to eq('after')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "allows access to private methods" do
|
59
|
+
expect { klass.new }.to_not raise_error
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "with two after hooks defined" do
|
64
|
+
let(:klass) do
|
65
|
+
Class.new do
|
66
|
+
extend Assembler
|
67
|
+
|
68
|
+
after_assembly do
|
69
|
+
hooks << :first
|
70
|
+
end
|
71
|
+
|
72
|
+
after_assembly do
|
73
|
+
hooks << :second
|
74
|
+
end
|
75
|
+
|
76
|
+
def hooks
|
77
|
+
@hooks ||= []
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
subject do
|
83
|
+
klass.new
|
84
|
+
end
|
85
|
+
|
86
|
+
it "calls the after hooks in order of declaration" do
|
87
|
+
expect(subject.hooks).to eq([:first, :second])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,384 @@
|
|
1
|
+
describe Assembler do
|
2
|
+
describe "#assemble_from_options" do
|
3
|
+
context "without parameters" do
|
4
|
+
subject do
|
5
|
+
Class.new do
|
6
|
+
extend Assembler
|
7
|
+
|
8
|
+
assemble_from_options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "throws away all input parameters (method arguments)" do
|
13
|
+
built_object = subject.new(foo: 'foo', bar: 'bar')
|
14
|
+
|
15
|
+
expect(built_object.instance_variables).to_not include(:@foo, :@bar)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "doesn't have methods on the builder object" do
|
19
|
+
subject.new do |builder|
|
20
|
+
expect(builder).to_not respond_to(:foo=)
|
21
|
+
expect(builder).to_not respond_to(:foo)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with no default parameter" do
|
27
|
+
subject do
|
28
|
+
Class.new do
|
29
|
+
extend Assembler
|
30
|
+
|
31
|
+
assemble_from_options :foo, :bar
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "barfs from missing required parameters" do
|
36
|
+
expect { subject.new }.to raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "holds onto the parameters (method arguments)" do
|
40
|
+
built_object = subject.new(foo: 'baz', bar: 'qux')
|
41
|
+
|
42
|
+
expect(built_object.instance_variable_get(:@foo)).to eq('baz')
|
43
|
+
expect(built_object.instance_variable_get(:@bar)).to eq('qux')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "holds onto the parameters (block)" do
|
47
|
+
built_object = subject.new do |builder|
|
48
|
+
builder.foo = 'baz'
|
49
|
+
builder.bar = 'qux'
|
50
|
+
end
|
51
|
+
|
52
|
+
expect(built_object.instance_variable_get(:@foo)).to eq('baz')
|
53
|
+
expect(built_object.instance_variable_get(:@bar)).to eq('qux')
|
54
|
+
end
|
55
|
+
|
56
|
+
it "ignores un-named parameters in method arguments" do
|
57
|
+
built_object = subject.new(foo: 'foo', bar: 'bar', baz: 'baz')
|
58
|
+
|
59
|
+
expect(built_object.instance_variables).to_not include(:@baz)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't create builder methods for un-named parameters" do
|
63
|
+
expect {
|
64
|
+
subject.new do |builder|
|
65
|
+
builder.baz = 'baz'
|
66
|
+
end
|
67
|
+
}.to raise_error(NoMethodError)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "provides accessors in the builder object" do
|
71
|
+
subject.new do |builder|
|
72
|
+
builder.foo = :new_foo
|
73
|
+
builder.bar = :new_bar
|
74
|
+
|
75
|
+
expect(builder.foo).to eq(:new_foo)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns nil for un-assigned parameters" do
|
80
|
+
subject.new do |builder|
|
81
|
+
expect(builder.foo).to be_nil
|
82
|
+
|
83
|
+
builder.foo = :new_foo
|
84
|
+
builder.bar = :new_bar
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "incorporates constructor args in the builder accessors" do
|
89
|
+
subject.new(foo: 'new_foo', bar: 'new_bar') do |builder|
|
90
|
+
expect(builder.foo).to eq('new_foo')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "with default parameter" do
|
96
|
+
subject do
|
97
|
+
Class.new do
|
98
|
+
extend Assembler
|
99
|
+
|
100
|
+
assemble_from_options :foo, :bar, default: 'default'
|
101
|
+
assemble_from_options :baz, :qux, default: nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "uses default values for missing parameters (method arguments)" do
|
106
|
+
built_object = subject.new
|
107
|
+
|
108
|
+
expect(built_object.instance_variable_get(:@foo)).to eq('default')
|
109
|
+
expect(built_object.instance_variable_get(:@bar)).to eq('default')
|
110
|
+
expect(built_object.instance_variable_get(:@baz)).to eq(nil)
|
111
|
+
expect(built_object.instance_variable_get(:@qux)).to eq(nil)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "uses default values for missing parameters (block)" do
|
115
|
+
built_object = subject.new do |builder|
|
116
|
+
end
|
117
|
+
|
118
|
+
expect(built_object.instance_variable_get(:@foo)).to eq('default')
|
119
|
+
expect(built_object.instance_variable_get(:@bar)).to eq('default')
|
120
|
+
expect(built_object.instance_variable_get(:@baz)).to eq(nil)
|
121
|
+
expect(built_object.instance_variable_get(:@qux)).to eq(nil)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "holds onto the parameters (method arguments)" do
|
125
|
+
built_object = subject.new(foo: 'foo', bar: 'bar', baz: 'baz', qux: 'qux')
|
126
|
+
|
127
|
+
expect(built_object.instance_variable_get(:@foo)).to eq('foo')
|
128
|
+
expect(built_object.instance_variable_get(:@bar)).to eq('bar')
|
129
|
+
expect(built_object.instance_variable_get(:@baz)).to eq('baz')
|
130
|
+
expect(built_object.instance_variable_get(:@qux)).to eq('qux')
|
131
|
+
end
|
132
|
+
|
133
|
+
it "holds onto the parameters (block)" do
|
134
|
+
built_object = subject.new do |builder|
|
135
|
+
builder.foo = 'foo'
|
136
|
+
builder.bar = 'bar'
|
137
|
+
builder.baz = 'baz'
|
138
|
+
builder.qux = 'qux'
|
139
|
+
end
|
140
|
+
|
141
|
+
expect(built_object.instance_variable_get(:@foo)).to eq('foo')
|
142
|
+
expect(built_object.instance_variable_get(:@bar)).to eq('bar')
|
143
|
+
expect(built_object.instance_variable_get(:@baz)).to eq('baz')
|
144
|
+
expect(built_object.instance_variable_get(:@qux)).to eq('qux')
|
145
|
+
end
|
146
|
+
|
147
|
+
it "ignores un-named parameters in method arguments" do
|
148
|
+
built_object = subject.new(nope: 'nope')
|
149
|
+
|
150
|
+
expect(built_object.instance_variables).to_not include(:@nope)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "doesn't create builder methods for un-named parameters" do
|
154
|
+
expect {
|
155
|
+
subject.new do |builder|
|
156
|
+
builder.nope = 'nope'
|
157
|
+
end
|
158
|
+
}.to raise_error(NoMethodError)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "provides accessors in the builder object" do
|
162
|
+
subject.new do |builder|
|
163
|
+
builder.foo = :new_foo
|
164
|
+
expect(builder.foo).to eq(:new_foo)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it "pre-fills default values in the builder accessors" do
|
169
|
+
subject.new do |builder|
|
170
|
+
expect(builder.foo).to eq('default')
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it "incorporates constructor args in the builder accessors" do
|
175
|
+
subject.new(foo: 'new_foo') do |builder|
|
176
|
+
expect(builder.foo).to eq('new_foo')
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "with symbol coerce parameter" do
|
182
|
+
subject do
|
183
|
+
Class.new do
|
184
|
+
extend Assembler
|
185
|
+
|
186
|
+
assemble_from_options :foo, coerce: :to_set
|
187
|
+
assemble_from_options :bar, coerce: :to_set, default: [:coerced]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
let(:argument) { double('argument', to_set: Set.new([:coerced])) }
|
192
|
+
|
193
|
+
it "sends parameter value to constructor argument (method arguments)" do
|
194
|
+
expect(argument).to receive(:to_set).and_return(Set.new([:coerced]))
|
195
|
+
subject.new(foo: argument)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "assigns the output of the coercion (method arguments)" do
|
199
|
+
built_object = subject.new(foo: argument)
|
200
|
+
|
201
|
+
expect(built_object.instance_variable_get(:@foo)).to eq(Set.new([:coerced]))
|
202
|
+
end
|
203
|
+
|
204
|
+
it "sends parameter value to constructor argument (block)" do
|
205
|
+
expect(argument).to receive(:to_set).and_return(Set.new([:coerced]))
|
206
|
+
subject.new { |b| b.foo = argument }
|
207
|
+
end
|
208
|
+
|
209
|
+
it "assigns the output of the coercion (block)" do
|
210
|
+
built_object = subject.new { |b| b.foo = argument }
|
211
|
+
|
212
|
+
expect(built_object.instance_variable_get(:@foo)).to eq(Set.new([:coerced]))
|
213
|
+
end
|
214
|
+
|
215
|
+
it "coerces default values in the builder accessor" do
|
216
|
+
subject.new(foo: [:foo]) do |builder|
|
217
|
+
expect(builder.bar).to eq(Set.new([:coerced]))
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it "coerces constructor args in the builder accessors" do
|
222
|
+
subject.new(foo: [:not_default]) do |builder|
|
223
|
+
expect(builder.foo).to eq(Set.new([:not_default]))
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it "coerces assigned values in the buidler accessors" do
|
228
|
+
subject.new do |builder|
|
229
|
+
builder.foo = [:not_default]
|
230
|
+
expect(builder.foo).to eq(Set.new([:not_default]))
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context "with callable coerce parameter" do
|
236
|
+
subject do
|
237
|
+
callable = double('callable')
|
238
|
+
allow(callable).to receive(:call) do |s|
|
239
|
+
[s, :called]
|
240
|
+
end
|
241
|
+
|
242
|
+
Class.new do
|
243
|
+
extend Assembler
|
244
|
+
|
245
|
+
assemble_from_options :lambda, default: nil, coerce: ->(s) { [s, :called] }
|
246
|
+
assemble_from_options :proc, default: nil, coerce: Proc.new { |s| [s, :called] }
|
247
|
+
assemble_from_options :callable, default: nil, coerce: callable
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
let(:argument) { double('argument') }
|
252
|
+
|
253
|
+
it "assigns the output of the coercion (method arguments)" do
|
254
|
+
expect(subject.new(:lambda => :original).instance_variable_get(:@lambda)).to eq([:original, :called])
|
255
|
+
expect(subject.new(:proc => :original).instance_variable_get(:@proc)).to eq([:original, :called])
|
256
|
+
expect(subject.new(:callable => :original).instance_variable_get(:@callable)).to eq([:original, :called])
|
257
|
+
end
|
258
|
+
|
259
|
+
it "assigns the output of the coercion (block)" do
|
260
|
+
expect(subject.new {|b| b.lambda = :original}.instance_variable_get(:@lambda)).to eq([:original, :called])
|
261
|
+
expect(subject.new {|b| b.proc = :original}.instance_variable_get(:@proc)).to eq([:original, :called])
|
262
|
+
expect(subject.new {|b| b.callable = :original}.instance_variable_get(:@callable)).to eq([:original, :called])
|
263
|
+
end
|
264
|
+
|
265
|
+
it "coerces default values in the builder accessor" do
|
266
|
+
subject.new do |builder|
|
267
|
+
expect(builder.lambda).to eq([nil, :called])
|
268
|
+
expect(builder.proc).to eq([nil, :called])
|
269
|
+
expect(builder.callable).to eq([nil, :called])
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
it "coerces constructor args in the builder accessors" do
|
274
|
+
subject.new(:lambda => :original, :proc => :original, :callable => :original) do |builder|
|
275
|
+
expect(builder.lambda).to eq([:original, :called])
|
276
|
+
expect(builder.proc).to eq([:original, :called])
|
277
|
+
expect(builder.callable).to eq([:original, :called])
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
it "coerces assigned values in the buidler accessors" do
|
282
|
+
subject.new do |builder|
|
283
|
+
builder.lambda = :original
|
284
|
+
builder.proc = :original
|
285
|
+
builder.callable = :original
|
286
|
+
|
287
|
+
expect(builder.lambda).to eq([:original, :called])
|
288
|
+
expect(builder.proc).to eq([:original, :called])
|
289
|
+
expect(builder.callable).to eq([:original, :called])
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context "with singular alias parameter" do
|
295
|
+
subject do
|
296
|
+
Class.new do
|
297
|
+
extend Assembler
|
298
|
+
|
299
|
+
assemble_from_options :foo, :alias => :bar
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
it "creates an alias" do
|
304
|
+
expect(subject.new(bar: :bar).instance_variable_get(:@foo)).to eq(:bar)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
context "with enumerable alias parameter" do
|
309
|
+
subject do
|
310
|
+
Class.new do
|
311
|
+
extend Assembler
|
312
|
+
|
313
|
+
assemble_from_options :foo, :alias => [:bar, :baz]
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
it "creates aliases" do
|
318
|
+
expect(subject.new(bar: :bar).instance_variable_get(:@foo)).to eq(:bar)
|
319
|
+
expect(subject.new(baz: :baz).instance_variable_get(:@foo)).to eq(:baz)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "with singular aliases parameter" do
|
324
|
+
subject do
|
325
|
+
Class.new do
|
326
|
+
extend Assembler
|
327
|
+
|
328
|
+
assemble_from_options :foo, aliases: :bar
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
it "creates an alias" do
|
333
|
+
expect(subject.new(bar: :bar).instance_variable_get(:@foo)).to eq(:bar)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context "with enumerable aliases parameter" do
|
338
|
+
subject do
|
339
|
+
Class.new do
|
340
|
+
extend Assembler
|
341
|
+
|
342
|
+
assemble_from_options :foo, aliases: [:bar, :baz]
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
it "creates aliases" do
|
347
|
+
expect(subject.new(bar: :bar).instance_variable_get(:@foo)).to eq(:bar)
|
348
|
+
expect(subject.new(baz: :baz).instance_variable_get(:@foo)).to eq(:baz)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
context "when called more than once for the same key" do
|
353
|
+
subject do
|
354
|
+
Class.new do
|
355
|
+
extend Assembler
|
356
|
+
|
357
|
+
assemble_from_options :foo
|
358
|
+
assemble_from_options :foo, default: :foo, coerce: :to_sym, aliases: [:bar]
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
it "re-writes default" do
|
363
|
+
expect(subject.new.instance_variable_get(:@foo)).to eq(:foo)
|
364
|
+
end
|
365
|
+
|
366
|
+
it "re-writes aliases (method)" do
|
367
|
+
expect(subject.new(bar: :bar).instance_variable_get(:@foo)).to eq(:bar)
|
368
|
+
end
|
369
|
+
|
370
|
+
it "re-writes aliases (block)" do
|
371
|
+
expect(subject.new { |s| s.bar = :bar}.instance_variable_get(:@foo)).to eq(:bar)
|
372
|
+
end
|
373
|
+
|
374
|
+
it "re-writes coercions (method)" do
|
375
|
+
expect(subject.new(foo: 'foo').instance_variable_get(:@foo)).to eq(:foo)
|
376
|
+
end
|
377
|
+
|
378
|
+
it "re-writes coercions (block)" do
|
379
|
+
expect(subject.new { |s| s.foo = 'foo'}.instance_variable_get(:@foo)).to eq(:foo)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'assembler'
|
3
|
-
|
4
1
|
describe Assembler do
|
5
2
|
describe "#assemble_from" do
|
6
3
|
context "without parameters" do
|
@@ -82,6 +79,25 @@ describe Assembler do
|
|
82
79
|
end
|
83
80
|
}.to raise_error(NoMethodError)
|
84
81
|
end
|
82
|
+
|
83
|
+
it "provides accessors in the builder object" do
|
84
|
+
subject.new do |builder|
|
85
|
+
builder.foo = :new_foo
|
86
|
+
expect(builder.foo).to eq(:new_foo)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it "pre-fills default values in the builder accessors" do
|
91
|
+
subject.new(foo: 'foo') do |builder|
|
92
|
+
expect(builder.bar).to eq('bar')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "incorporates contructor args in the builder accessors" do
|
97
|
+
subject.new(foo: 'foo', bar: 'new_bar') do |builder|
|
98
|
+
expect(builder.bar).to eq('new_bar')
|
99
|
+
end
|
100
|
+
end
|
85
101
|
end
|
86
102
|
|
87
103
|
context "when called more than once" do
|
@@ -0,0 +1,80 @@
|
|
1
|
+
describe Assembler do
|
2
|
+
describe "#before_assembly" do
|
3
|
+
context "with no other assembler helpers called" do
|
4
|
+
let(:klass) do
|
5
|
+
Class.new do
|
6
|
+
extend Assembler
|
7
|
+
|
8
|
+
before_assembly do
|
9
|
+
@before = true
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :before
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { klass.new }
|
17
|
+
|
18
|
+
it "calls the before block" do
|
19
|
+
expect(subject.before).to be_true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with a more complex declaration" do
|
24
|
+
let(:klass) do
|
25
|
+
Class.new do
|
26
|
+
extend Assembler
|
27
|
+
|
28
|
+
assemble_with :middle
|
29
|
+
|
30
|
+
before_assembly do
|
31
|
+
@before = true
|
32
|
+
@middle = 'before'
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :before, :middle
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
subject do
|
40
|
+
klass.new(middle: 'middle')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "calls the before block" do
|
44
|
+
expect(subject.before).to be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "calls the block before running the rest of the initializer" do
|
48
|
+
expect(subject.middle).to eq('middle')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with two before hooks defined" do
|
53
|
+
let(:klass) do
|
54
|
+
Class.new do
|
55
|
+
extend Assembler
|
56
|
+
|
57
|
+
before_assembly do
|
58
|
+
hooks << :first
|
59
|
+
end
|
60
|
+
|
61
|
+
before_assembly do
|
62
|
+
hooks << :second
|
63
|
+
end
|
64
|
+
|
65
|
+
def hooks
|
66
|
+
@hooks ||= []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
subject do
|
72
|
+
klass.new
|
73
|
+
end
|
74
|
+
|
75
|
+
it "calls the before hooks in order of declaration" do
|
76
|
+
expect(subject.hooks).to eq([:first, :second])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: assembler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Hamill
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -79,17 +79,22 @@ files:
|
|
79
79
|
- ".rspec"
|
80
80
|
- ".travis.yml"
|
81
81
|
- CHANGES.md
|
82
|
+
- CONTRIBUTING.md
|
82
83
|
- Gemfile
|
83
84
|
- LICENSE.txt
|
84
85
|
- README.md
|
86
|
+
- RELEASING.md
|
85
87
|
- Rakefile
|
86
88
|
- assembler.gemspec
|
87
89
|
- lib/assembler.rb
|
88
90
|
- lib/assembler/builder.rb
|
89
91
|
- lib/assembler/initializer.rb
|
90
|
-
- lib/assembler/
|
92
|
+
- lib/assembler/parameter.rb
|
91
93
|
- lib/assembler/version.rb
|
92
|
-
- spec/
|
94
|
+
- spec/after_assembly_spec.rb
|
95
|
+
- spec/assemble_from_options_spec.rb
|
96
|
+
- spec/assemble_from_spec.rb
|
97
|
+
- spec/before_assembly_spec.rb
|
93
98
|
- spec/spec_helper.rb
|
94
99
|
homepage: https://github.com/benhamill/assembler#readme
|
95
100
|
licenses:
|
@@ -116,5 +121,8 @@ signing_key:
|
|
116
121
|
specification_version: 4
|
117
122
|
summary: Block-based initializers for your objects.
|
118
123
|
test_files:
|
119
|
-
- spec/
|
124
|
+
- spec/after_assembly_spec.rb
|
125
|
+
- spec/assemble_from_options_spec.rb
|
126
|
+
- spec/assemble_from_spec.rb
|
127
|
+
- spec/before_assembly_spec.rb
|
120
128
|
- spec/spec_helper.rb
|
data/lib/assembler/parameters.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
module Assembler
|
2
|
-
class Parameters
|
3
|
-
def initialize(params={})
|
4
|
-
@params = params
|
5
|
-
end
|
6
|
-
|
7
|
-
def fetch(key, &block)
|
8
|
-
params.fetch(key.to_sym) do
|
9
|
-
params.fetch(key.to_s) do
|
10
|
-
block.call
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
attr_reader :params
|
18
|
-
end
|
19
|
-
end
|