attr_extras 4.0.0 → 4.1.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 +5 -13
- data/README.md +108 -60
- data/attr_extras.gemspec +2 -4
- data/lib/attr_extras/attr_initialize.rb +3 -3
- data/lib/attr_extras/version.rb +1 -1
- data/spec/attr_initialize_spec.rb +7 -2
- metadata +23 -9
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
MDZkNDRkNzc2MDMyOGUwMzVjODE1YmE5NGNkODNhMjY4YTM5NzZjYw==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 08a7329a2b945733d0baf670533dd8801c1880a9
|
4
|
+
data.tar.gz: 7f01ba7e4aadab65bb51ec5cdca127f97be8461e
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
MmZkM2U3NzNiOTBjMWVmYzQyODA4MzBhMWU3ODIyYmZkNjFjODRhYTAzOTlm
|
11
|
-
MGY0NjEzYWIxZjU5MDhlNjRmNDkzYmZlMWVjMmY0ODQ5MWY0YWI=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MWNiMmYwMDI5MDYzZjBjYWY2OGFhNTMyZWIyZjJjZDkzNDRlOWUxY2Q3NzNm
|
14
|
-
ZWJhYWViN2I5NmU1ZWY1OWFhODQ3NGEwOTY1YmE3MGZjNWY5YTU0NTY5YWU2
|
15
|
-
ODgwNjZmOGE2MjQzZmQ0MzE5ZDY5OTYyMzhjNjVlODk2NzMyMGQ=
|
6
|
+
metadata.gz: c3df45d212321d6d1e1046c7439fc9825e047b8b1d769f2b5e2b453f028449dec6bd07034560f821b1e43f3cb6446a13ac5a18edbb1e2725e49c6d44b895863d
|
7
|
+
data.tar.gz: ba8800473f4c53b5b83118bd125fa4212728610f2a65475c92af65ef5611094810c343cdf20ab493b675adfd1b5f9cdbe10bfaaae600a7572c5416c546fc186e
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Takes some boilerplate out of Ruby, lowering the barrier to extracting small foc
|
|
6
6
|
|
7
7
|
Instead of
|
8
8
|
|
9
|
-
```
|
9
|
+
``` ruby
|
10
10
|
class InvoiceBuilder
|
11
11
|
def initialize(invoice, employee)
|
12
12
|
@invoice, @employee = invoice, employee
|
@@ -20,7 +20,7 @@ end
|
|
20
20
|
|
21
21
|
you can just do
|
22
22
|
|
23
|
-
```
|
23
|
+
``` ruby
|
24
24
|
class InvoiceBuilder
|
25
25
|
pattr_initialize :invoice, :employee
|
26
26
|
end
|
@@ -35,102 +35,146 @@ Also provides conveniences for creating value objects, method objects, query met
|
|
35
35
|
|
36
36
|
## Usage
|
37
37
|
|
38
|
+
* [`pattr_initialize`](#pattr_initialize)
|
39
|
+
* [`vattr_initialize`](#vattr_initialize)
|
40
|
+
* [`attr_initialize`](#attr_initialize)
|
41
|
+
* [`attr_private`](#attr_private)
|
42
|
+
* [`attr_value`](#attr_value)
|
43
|
+
* [`static_facade`](#static_facade)
|
44
|
+
* [`method_object`](#method_object)
|
45
|
+
* [`attr_implement`](#attr_implement)
|
46
|
+
* [`attr_query`](#attr_query)
|
47
|
+
* [`attr_id_query`](#attr_id_query)
|
38
48
|
|
39
|
-
### `attr_initialize :foo, :bar`
|
40
49
|
|
41
|
-
Defines an initializer that takes two arguments and assigns `@foo` and `@bar`.
|
42
50
|
|
43
|
-
|
51
|
+
### `pattr_initialize`
|
44
52
|
|
45
|
-
`
|
53
|
+
`pattr_initialize :foo, :bar` defines both initializer and private readers: shortcut for
|
46
54
|
|
55
|
+
``` ruby
|
56
|
+
attr_initialize :foo, :bar
|
57
|
+
attr_private :foo, :bar
|
58
|
+
```
|
47
59
|
|
48
|
-
|
60
|
+
Example:
|
49
61
|
|
50
|
-
|
62
|
+
``` ruby
|
63
|
+
class Item
|
64
|
+
pattr_initalize :name, :price
|
51
65
|
|
52
|
-
|
66
|
+
def price_with_vat
|
67
|
+
price * 1.25
|
68
|
+
end
|
69
|
+
end
|
53
70
|
|
71
|
+
Item.new("Pug", 100).price_with_vat # => 125.0
|
72
|
+
```
|
54
73
|
|
55
|
-
|
74
|
+
[The `attr_initialize` notation](#attr_initialize) for hash arguments is also supported: `pattr_initialize :foo, [:bar, :baz!]`
|
56
75
|
|
57
|
-
Defines public readers. Does not define writers, as [value objects](http://en.wikipedia.org/wiki/Value_object) are typically immutable.
|
58
76
|
|
59
|
-
|
77
|
+
### `vattr_initialize`
|
60
78
|
|
79
|
+
`vattr_initialize :foo, :bar` defines initializer, public readers and [value object identity](#attr_value): shortcut for
|
61
80
|
|
62
|
-
|
81
|
+
``` ruby
|
82
|
+
attr_initialize :foo, :bar
|
83
|
+
attr_value :foo, :bar
|
84
|
+
```
|
63
85
|
|
64
|
-
|
86
|
+
Example:
|
65
87
|
|
66
|
-
```
|
67
|
-
|
68
|
-
|
88
|
+
``` ruby
|
89
|
+
class Country
|
90
|
+
vattr_initialize :code
|
91
|
+
end
|
92
|
+
|
93
|
+
Country.new("SE") == Country.new("SE") # => true
|
94
|
+
Country.new("SE").code # => "SE"
|
69
95
|
```
|
70
96
|
|
71
|
-
The `attr_initialize` notation for hash arguments is also supported: `
|
97
|
+
[The `attr_initialize` notation](#attr_initialize) for hash arguments is also supported: `vattr_initialize :foo, [:bar, :baz!]`
|
72
98
|
|
73
99
|
|
74
|
-
### `
|
100
|
+
### `attr_initialize`
|
75
101
|
|
76
|
-
|
102
|
+
`attr_initialize :foo, :bar` defines an initializer that takes two arguments and assigns `@foo` and `@bar`.
|
77
103
|
|
78
|
-
|
79
|
-
attr_initialize :foo, :bar
|
80
|
-
attr_value :foo, :bar
|
81
|
-
```
|
104
|
+
`attr_initialize :foo, [:bar, :baz!]` defines an initializer that takes one regular argument, assigning `@foo`, and one hash argument, assigning `@bar` (optional) and `@baz` (required).
|
82
105
|
|
83
|
-
|
106
|
+
`attr_initialize [:bar, :baz!]` defines an initializer that takes one hash argument, assigning `@bar` (optional) and `@baz` (required).
|
84
107
|
|
108
|
+
`attr_initialize` can also accept a block which will be invoked after initialization. This is useful for calling `super` appropriately in subclasses or initializing private data as necessary.
|
109
|
+
|
110
|
+
|
111
|
+
### `attr_private`
|
112
|
+
|
113
|
+
`attr_private :foo, :bar` defines private readers for `@foo` and `@bar`.
|
114
|
+
|
115
|
+
|
116
|
+
### `attr_value`
|
117
|
+
|
118
|
+
`attr_value :foo, :bar` defines public readers for `@foo` and `@bar` and also defines object equality: two value objects of the same class with the same values will be considered equal (with `==` and `eql?`, in `Set`s, as `Hash` keys etc).
|
119
|
+
|
120
|
+
It does not define writers, because [value objects](http://en.wikipedia.org/wiki/Value_object) are typically immutable.
|
85
121
|
|
86
|
-
### `static_facade :fooable?, :foo`<br>
|
87
122
|
|
88
|
-
|
123
|
+
### `static_facade`
|
124
|
+
|
125
|
+
`static_facade :allow?, :user` defines an `.allow?` class method that delegates to an instance method by the same name, having first provided `user` as a private reader.
|
89
126
|
|
90
127
|
This is handy when a class-method API makes sense but you still want [the refactorability of instance methods](http://blog.codeclimate.com/blog/2012/11/14/why-ruby-class-methods-resist-refactoring/).
|
91
128
|
|
129
|
+
Example:
|
130
|
+
|
92
131
|
``` ruby
|
93
132
|
class PublishingPolicy
|
94
133
|
static_facade :allow?, :user
|
95
|
-
static_facade :disallow?, :user
|
96
134
|
|
97
135
|
def allow?
|
98
|
-
user.admin? &&
|
136
|
+
user.admin? && complicated_extracted_method
|
99
137
|
end
|
100
138
|
|
101
|
-
|
102
|
-
|
139
|
+
private
|
140
|
+
|
141
|
+
def complicated_extracted_method
|
142
|
+
# …
|
103
143
|
end
|
104
144
|
end
|
105
145
|
|
106
146
|
PublishingPolicy.allow?(user)
|
107
147
|
```
|
108
148
|
|
109
|
-
`static_facade :
|
149
|
+
`static_facade :allow?, :user` is a shortcut for
|
110
150
|
|
111
151
|
``` ruby
|
112
|
-
pattr_initialize :
|
152
|
+
pattr_initialize :user
|
113
153
|
|
114
|
-
def self.
|
115
|
-
new(
|
154
|
+
def self.allow?(user)
|
155
|
+
new(user).allow?
|
116
156
|
end
|
117
157
|
```
|
118
158
|
|
119
|
-
The `attr_initialize` notation for hash arguments is also supported: `static_facade :
|
159
|
+
[The `attr_initialize` notation](#attr_initialize) for hash arguments is also supported: `static_facade :allow?, :user, [:user_agent, :ip!]`
|
160
|
+
|
161
|
+
You don't have to specify arguments/readers if you don't want them: just `static_facade :tuesday?` is also valid.
|
162
|
+
|
163
|
+
"Static façade" is the least bad name for this pattern we've come up with. Suggestions are welcome.
|
120
164
|
|
121
|
-
You don't have to specify arguments/readers if you don't want them: just `static_facade :fooable?` is also valid.
|
122
165
|
|
166
|
+
### `method_object`
|
123
167
|
|
124
|
-
|
168
|
+
*NOTE: v4.0.0 made a breaking change! [`static_facade`](#static_facade) does exactly what `method_object` used to do; the new `method_object` no longer accepts a method name argument.*
|
125
169
|
|
126
|
-
|
170
|
+
`method_object :foo` defines a `.call` class method that delegates to an instance method by the same name, having first provided `foo` as a private reader.
|
127
171
|
|
128
|
-
|
172
|
+
This is a special case of [`static_facade`](#static_facade) for when you want a [Method Object](http://refactoring.com/catalog/replaceMethodWithMethodObject.html), and the class name itself will communicate the action it performs.
|
129
173
|
|
130
|
-
|
174
|
+
Example:
|
131
175
|
|
132
176
|
``` ruby
|
133
|
-
class
|
177
|
+
class CalculatePrice
|
134
178
|
method_object :order
|
135
179
|
|
136
180
|
def call
|
@@ -150,11 +194,13 @@ end
|
|
150
194
|
|
151
195
|
class Order
|
152
196
|
def price
|
153
|
-
|
197
|
+
CalculatePrice.call(self)
|
154
198
|
end
|
155
199
|
end
|
156
200
|
```
|
157
201
|
|
202
|
+
You could even do `CalculatePrice.(self)` if you like, since we're using the [`call` convention](http://www.ruby-doc.org/core-2.2.0/Proc.html#method-i-call).
|
203
|
+
|
158
204
|
`method_object :foo` is a shortcut for
|
159
205
|
|
160
206
|
``` ruby
|
@@ -171,23 +217,14 @@ def self.call(foo)
|
|
171
217
|
end
|
172
218
|
```
|
173
219
|
|
174
|
-
The `attr_initialize` notation for hash arguments is also supported: `method_object :foo, [:bar, :baz!]`
|
220
|
+
[The `attr_initialize` notation](#attr_initialize) for hash arguments is also supported: `method_object :foo, [:bar, :baz!]`
|
175
221
|
|
176
222
|
You don't have to specify arguments/readers if you don't want them: just `method_object` is also valid.
|
177
223
|
|
178
224
|
|
179
|
-
### `
|
180
|
-
|
181
|
-
Defines query methods like `foo?`, which is true if (and only if) `foo_id` is truthy. Goes well with Active Record.
|
182
|
-
|
183
|
-
|
184
|
-
### `attr_query :foo?, :bar?`<br>
|
225
|
+
### `attr_implement`
|
185
226
|
|
186
|
-
|
187
|
-
|
188
|
-
### `attr_implement :foo, :bar`<br>
|
189
|
-
|
190
|
-
Defines nullary (0-argument) methods `foo` and `bar` that raise e.g. `"Implement a 'foo()' method"`.
|
227
|
+
`attr_implement :foo, :bar` defines nullary (0-argument) methods `foo` and `bar` that raise e.g. `"Implement a 'foo()' method"`.
|
191
228
|
|
192
229
|
`attr_implement :foo, [:name, :age]` will define a binary (2-argument) method `foo` that raises `"Implement a 'foo(name, age)' method"`.
|
193
230
|
|
@@ -204,6 +241,16 @@ end
|
|
204
241
|
though it is shorter, more declarative, gives you a clear message and handles edge cases you might not have thought about (see tests).
|
205
242
|
|
206
243
|
|
244
|
+
### `attr_query`
|
245
|
+
|
246
|
+
`attr_query :foo?, :bar?` defines query methods like `foo?`, which is true if (and only if) `foo` is truthy.
|
247
|
+
|
248
|
+
|
249
|
+
### `attr_id_query`
|
250
|
+
|
251
|
+
`attr_id_query :foo?, :bar?` defines query methods like `foo?`, which is true if (and only if) `foo_id` is truthy. Goes well with Active Record.
|
252
|
+
|
253
|
+
|
207
254
|
## Philosophy
|
208
255
|
|
209
256
|
Findability is a core value.
|
@@ -211,14 +258,15 @@ Hence the long name `attr_initialize`, so you see it when scanning for the initi
|
|
211
258
|
and the enforced questionmarks with `attr_id_query :foo?`, so you can search for that method.
|
212
259
|
|
213
260
|
|
214
|
-
##
|
261
|
+
## Q & A
|
215
262
|
|
216
|
-
See: ["Struct inheritance is overused"](http://thepugautomatic.com/2013/08/struct-inheritance-is-overused/)
|
217
263
|
|
264
|
+
### Why not use `Struct` instead of `pattr_initialize`?
|
265
|
+
|
266
|
+
See: ["Struct inheritance is overused"](http://thepugautomatic.com/2013/08/struct-inheritance-is-overused/)
|
218
267
|
|
219
|
-
## Why not use `private; attr_reader :foo`?
|
220
268
|
|
221
|
-
|
269
|
+
### Why not use `private; attr_reader :foo` instead of `attr_private :foo`?
|
222
270
|
|
223
271
|
Other than being more to type, declaring `attr_reader` after `private` will actually give you a warning (deserved or not) if you run Ruby with warnings turned on.
|
224
272
|
|
@@ -242,7 +290,7 @@ Or install it yourself as:
|
|
242
290
|
|
243
291
|
## Running the tests
|
244
292
|
|
245
|
-
Run
|
293
|
+
Run them with:
|
246
294
|
|
247
295
|
`rake`
|
248
296
|
|
data/attr_extras.gemspec
CHANGED
@@ -15,10 +15,8 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.license = "MIT"
|
16
16
|
gem.version = AttrExtras::VERSION
|
17
17
|
|
18
|
+
gem.add_development_dependency "minitest", ">= 5"
|
19
|
+
|
18
20
|
# For Travis CI.
|
19
21
|
gem.add_development_dependency "rake"
|
20
|
-
|
21
|
-
if RUBY_VERSION < "1.9.3"
|
22
|
-
gem.add_development_dependency "minitest"
|
23
|
-
end
|
24
22
|
end
|
@@ -15,7 +15,7 @@ class AttrExtras::AttrInitialize
|
|
15
15
|
set_ivar_from_hash = method(:set_ivar_from_hash)
|
16
16
|
|
17
17
|
klass.send(:define_method, :initialize) do |*values|
|
18
|
-
validate_arity.call(values.length)
|
18
|
+
validate_arity.call(values.length, self.class)
|
19
19
|
|
20
20
|
names.zip(values).each do |name_or_names, value|
|
21
21
|
if name_or_names.is_a?(Array)
|
@@ -38,13 +38,13 @@ class AttrExtras::AttrInitialize
|
|
38
38
|
|
39
39
|
private
|
40
40
|
|
41
|
-
def validate_arity(provided_arity)
|
41
|
+
def validate_arity(provided_arity, klass)
|
42
42
|
arity_without_hashes = names.count { |name| not name.is_a?(Array) }
|
43
43
|
arity_with_hashes = names.length
|
44
44
|
|
45
45
|
unless (arity_without_hashes..arity_with_hashes).include?(provided_arity)
|
46
46
|
arity_range = [ arity_without_hashes, arity_with_hashes ].uniq.join("..")
|
47
|
-
raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{arity_range})"
|
47
|
+
raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{arity_range}) for #{klass.name} initializer"
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
data/lib/attr_extras/version.rb
CHANGED
@@ -4,6 +4,10 @@ describe Object, ".attr_initialize" do
|
|
4
4
|
let(:klass) do
|
5
5
|
Class.new do
|
6
6
|
attr_initialize :foo, :bar
|
7
|
+
|
8
|
+
def self.name
|
9
|
+
"ExampleClass"
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
9
13
|
|
@@ -14,7 +18,8 @@ describe Object, ".attr_initialize" do
|
|
14
18
|
end
|
15
19
|
|
16
20
|
it "requires all arguments" do
|
17
|
-
lambda { klass.new("Foo") }.must_raise ArgumentError
|
21
|
+
exception = lambda { klass.new("Foo") }.must_raise ArgumentError
|
22
|
+
exception.message.must_equal "wrong number of arguments (1 for 2) for ExampleClass initializer"
|
18
23
|
end
|
19
24
|
|
20
25
|
it "can set ivars from a hash" do
|
@@ -51,7 +56,7 @@ describe Object, ".attr_initialize" do
|
|
51
56
|
lambda { klass.new(:optional => "X") }.must_raise KeyError
|
52
57
|
end
|
53
58
|
|
54
|
-
it "
|
59
|
+
it "accepts a block for initialization" do
|
55
60
|
klass = Class.new do
|
56
61
|
attr_initialize :value do
|
57
62
|
@copy = @value
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attr_extras
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henrik Nyh
|
@@ -10,20 +10,34 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-
|
13
|
+
date: 2015-02-09 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: minitest
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '5'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '5'
|
15
29
|
- !ruby/object:Gem::Dependency
|
16
30
|
name: rake
|
17
31
|
requirement: !ruby/object:Gem::Requirement
|
18
32
|
requirements:
|
19
|
-
- -
|
33
|
+
- - ">="
|
20
34
|
- !ruby/object:Gem::Version
|
21
35
|
version: '0'
|
22
36
|
type: :development
|
23
37
|
prerelease: false
|
24
38
|
version_requirements: !ruby/object:Gem::Requirement
|
25
39
|
requirements:
|
26
|
-
- -
|
40
|
+
- - ">="
|
27
41
|
- !ruby/object:Gem::Version
|
28
42
|
version: '0'
|
29
43
|
description:
|
@@ -33,9 +47,9 @@ executables: []
|
|
33
47
|
extensions: []
|
34
48
|
extra_rdoc_files: []
|
35
49
|
files:
|
36
|
-
- .gitignore
|
37
|
-
- .rvmrc
|
38
|
-
- .travis.yml
|
50
|
+
- ".gitignore"
|
51
|
+
- ".rvmrc"
|
52
|
+
- ".travis.yml"
|
39
53
|
- Gemfile
|
40
54
|
- LICENSE.txt
|
41
55
|
- README.md
|
@@ -69,12 +83,12 @@ require_paths:
|
|
69
83
|
- lib
|
70
84
|
required_ruby_version: !ruby/object:Gem::Requirement
|
71
85
|
requirements:
|
72
|
-
- -
|
86
|
+
- - ">="
|
73
87
|
- !ruby/object:Gem::Version
|
74
88
|
version: '0'
|
75
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
90
|
requirements:
|
77
|
-
- -
|
91
|
+
- - ">="
|
78
92
|
- !ruby/object:Gem::Version
|
79
93
|
version: '0'
|
80
94
|
requirements: []
|