attr_extras 4.4.0 → 4.5.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/.travis.yml +0 -1
- data/README.md +62 -8
- data/attr_extras.gemspec +1 -0
- data/lib/attr_extras/attr_implement.rb +32 -0
- data/lib/attr_extras/explicit.rb +12 -23
- data/lib/attr_extras/version.rb +1 -1
- data/script/test +2 -5
- data/spec/attr_extras/aattr_initialize_spec.rb +58 -0
- data/spec/attr_extras/pattr_initialize_spec.rb +1 -1
- data/spec/attr_extras/rattr_initialize_spec.rb +1 -1
- data/spec/attr_extras/vattr_initialize_spec.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a8bcd34f935912a3cf97f5a31b39330e357aa61
|
4
|
+
data.tar.gz: f99cb0a632963344367f906285cc572b913932a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 796bdd29576ca722747c2b2508571f6ab60197741e1dbf91532f4f6cdcbcfcc478e81d3ffb41e72345a22d5319eb9e066879144b6eaf7624bd0dd6765223d5aa
|
7
|
+
data.tar.gz: 5b0a456b2657f7a33823b98b864821ab8845296e63d459d661cce67d7d2b2a296208cb7097e34209bd4063510c79dc9f3906fbe8cd7ad7bce6265e1de7faca11
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -42,6 +42,7 @@ Also provides conveniences for creating value objects, method objects, query met
|
|
42
42
|
* [`pattr_initialize`](#pattr_initialize) / [`attr_private_initialize`](#attr_private_initialize)
|
43
43
|
* [`vattr_initialize`](#vattr_initialize) / [`attr_value_initialize`](#attr_value_initialize)
|
44
44
|
* [`rattr_initialize`](#rattr_initialize) / [`attr_reader_initialize`](#attr_reader_initialize)
|
45
|
+
* [`aattr_initialize`](#aattr_initialize) / [`attr_accessor_initialize`](#attr_accessor_initialize)
|
45
46
|
* [`static_facade`](#static_facade)
|
46
47
|
* [`method_object`](#method_object)
|
47
48
|
* [`attr_implement`](#attr_implement)
|
@@ -57,7 +58,7 @@ Also provides conveniences for creating value objects, method objects, query met
|
|
57
58
|
|
58
59
|
`attr_initialize [:bar, :baz!]` defines an initializer that takes one hash argument, assigning `@bar` (optional) and `@baz` (required).
|
59
60
|
|
60
|
-
`attr_initialize` can also accept a block which will be invoked after initialization. This is useful for
|
61
|
+
`attr_initialize` can also accept a block which will be invoked after initialization. This is useful for e.g. initializing private data as necessary.
|
61
62
|
|
62
63
|
|
63
64
|
### `attr_private`
|
@@ -88,7 +89,7 @@ Example:
|
|
88
89
|
|
89
90
|
``` ruby
|
90
91
|
class Item
|
91
|
-
|
92
|
+
pattr_initialize :name, :price
|
92
93
|
|
93
94
|
def price_with_vat
|
94
95
|
price * 1.25
|
@@ -142,19 +143,47 @@ Example:
|
|
142
143
|
|
143
144
|
``` ruby
|
144
145
|
class PublishBook
|
145
|
-
|
146
|
+
rattr_initialize :book_name, :publisher_backend
|
146
147
|
|
147
148
|
def call
|
148
149
|
publisher_backend.publish book_name
|
149
150
|
end
|
150
151
|
end
|
151
152
|
|
152
|
-
service = PublishBook.new("A Novel")
|
153
|
+
service = PublishBook.new("A Novel", publisher)
|
153
154
|
service.book_name # => "A Novel"
|
154
155
|
```
|
155
156
|
|
156
157
|
[The `attr_initialize` notation](#attr_initialize) for hash arguments is also supported: `rattr_initialize :foo, [:bar, :baz!]`
|
157
158
|
|
159
|
+
### `aattr_initialize`
|
160
|
+
### `attr_accessor_initialize`
|
161
|
+
|
162
|
+
`aattr_initialize :foo, :bar` defines an initializer, public readers, and public writers. It's a shortcut for:
|
163
|
+
|
164
|
+
``` ruby
|
165
|
+
attr_initialize :foo, :bar
|
166
|
+
attr_accessor :foo, :bar
|
167
|
+
```
|
168
|
+
|
169
|
+
`aattr_initialize` is aliased as `attr_accessor_initialize`, if you prefer a longer but clearer name.
|
170
|
+
|
171
|
+
Example:
|
172
|
+
|
173
|
+
``` ruby
|
174
|
+
class Client
|
175
|
+
aattr_initialize :username, :access_token
|
176
|
+
end
|
177
|
+
|
178
|
+
client = Client.new("barsoom", "SECRET")
|
179
|
+
client.username # => "barsoom"
|
180
|
+
|
181
|
+
client.access_token = "NEW_SECRET"
|
182
|
+
client.access_token # => "NEW_SECRET"
|
183
|
+
```
|
184
|
+
|
185
|
+
[The `attr_initialize` notation](#attr_initialize) for hash arguments and blocks is also supported.
|
186
|
+
|
158
187
|
### `static_facade`
|
159
188
|
|
160
189
|
`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.
|
@@ -275,6 +304,23 @@ end
|
|
275
304
|
|
276
305
|
though it is shorter, more declarative, gives you a clear message and handles edge cases you might not have thought about (see tests).
|
277
306
|
|
307
|
+
Note that you can also use this with modules, to effectively mix in interfaces:
|
308
|
+
|
309
|
+
``` ruby
|
310
|
+
module Bookable
|
311
|
+
attr_implement :book, [:bookable]
|
312
|
+
attr_implement :booked?
|
313
|
+
end
|
314
|
+
|
315
|
+
class Invoice
|
316
|
+
include Bookable
|
317
|
+
end
|
318
|
+
|
319
|
+
class Payment
|
320
|
+
include Bookable
|
321
|
+
end
|
322
|
+
```
|
323
|
+
|
278
324
|
|
279
325
|
### `attr_query`
|
280
326
|
|
@@ -294,7 +340,7 @@ This is not ideal if you're using attr\_extras in a library: those who depend on
|
|
294
340
|
|
295
341
|
It's also not obvious where the methods come from. You can be more explicit about it, and restrict where the methods are added, like this:
|
296
342
|
|
297
|
-
```
|
343
|
+
``` ruby
|
298
344
|
require "attr_extras/explicit"
|
299
345
|
|
300
346
|
class MyLib
|
@@ -306,7 +352,7 @@ end
|
|
306
352
|
|
307
353
|
Crucially, you need to `require "attr_extras/explicit"` *instead of* `require "attr_extras"`. Some frameworks, like Ruby on Rails, may automatically require everything in your `Gemfile`. You can avoid that with `gem "attr_extras", require: "attr_extras/explicit"`.
|
308
354
|
|
309
|
-
In explicit mode, you need to call `extend AttrExtras.mixin` *in every class* that wants the attr\_extras methods.
|
355
|
+
In explicit mode, you need to call `extend AttrExtras.mixin` *in every class or module* that wants the attr\_extras methods.
|
310
356
|
|
311
357
|
|
312
358
|
## Philosophy
|
@@ -330,6 +376,10 @@ Other than being more to type, declaring `attr_reader` after `private` will actu
|
|
330
376
|
|
331
377
|
If you don't want the dependency on `attr_extras`, you can get rid of the warnings with `attr_reader :foo; private :foo`. Or just define a regular private method.
|
332
378
|
|
379
|
+
### Can I use attr\_extras in `BasicObject`s?
|
380
|
+
|
381
|
+
No, sorry. It depends on various methods that `BasicObject`s don't have. Use a regular `Object` or make do without attr\_extras.
|
382
|
+
|
333
383
|
|
334
384
|
## Installation
|
335
385
|
|
@@ -350,11 +400,15 @@ Or install it yourself as:
|
|
350
400
|
|
351
401
|
Run them with:
|
352
402
|
|
353
|
-
|
403
|
+
rake
|
354
404
|
|
355
405
|
Or to see warnings (try not to have any):
|
356
406
|
|
357
|
-
|
407
|
+
RUBYOPT=-w rake
|
408
|
+
|
409
|
+
You can run an individual test using the [m](https://github.com/qrush/m) gem:
|
410
|
+
|
411
|
+
m spec/attr_extras/attr_initialize_spec.rb:48
|
358
412
|
|
359
413
|
The tests are intentionally split into two test suites for reasons described in `Rakefile`.
|
360
414
|
|
data/attr_extras.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.version = AttrExtras::VERSION
|
17
17
|
|
18
18
|
gem.add_development_dependency "minitest", ">= 5"
|
19
|
+
gem.add_development_dependency "m", "~> 1.3.1" # Running individual tests.
|
19
20
|
|
20
21
|
# For Travis CI.
|
21
22
|
gem.add_development_dependency "rake"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class AttrExtras::AttrImplement
|
2
|
+
def initialize(klass, names)
|
3
|
+
@klass, @names = klass, names.dup
|
4
|
+
end
|
5
|
+
|
6
|
+
def apply
|
7
|
+
arg_names = @names.last.is_a?(Array) ? @names.pop : []
|
8
|
+
expected_arity = arg_names.length
|
9
|
+
|
10
|
+
# Make available within the block.
|
11
|
+
names = @names
|
12
|
+
|
13
|
+
mod = Module.new do
|
14
|
+
define_method :method_missing do |name, *args|
|
15
|
+
if names.include?(name)
|
16
|
+
provided_arity = args.length
|
17
|
+
|
18
|
+
if provided_arity != expected_arity
|
19
|
+
raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{expected_arity})"
|
20
|
+
end
|
21
|
+
|
22
|
+
raise AttrExtras::MethodNotImplementedError, "Implement a '#{name}(#{arg_names.join(", ")})' method"
|
23
|
+
else
|
24
|
+
super(name, *args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# include is private in Ruby 2.0 and earlier.
|
30
|
+
@klass.send(:include, mod)
|
31
|
+
end
|
32
|
+
end
|
data/lib/attr_extras/explicit.rb
CHANGED
@@ -2,6 +2,7 @@ require "attr_extras/version"
|
|
2
2
|
require "attr_extras/attr_initialize"
|
3
3
|
require "attr_extras/attr_value"
|
4
4
|
require "attr_extras/attr_query"
|
5
|
+
require "attr_extras/attr_implement"
|
5
6
|
require "attr_extras/utils"
|
6
7
|
|
7
8
|
module AttrExtras
|
@@ -9,12 +10,12 @@ module AttrExtras
|
|
9
10
|
class MethodNotImplementedError < Exception; end
|
10
11
|
|
11
12
|
def self.mixin
|
12
|
-
self::
|
13
|
+
self::Mixin
|
13
14
|
end
|
14
15
|
|
15
|
-
# Separate module
|
16
|
+
# Separate module so that mixing in the methods doesn't also mix in constants:
|
16
17
|
# http://thepugautomatic.com/2014/02/private-api/
|
17
|
-
module
|
18
|
+
module Mixin
|
18
19
|
def attr_initialize(*names, &block)
|
19
20
|
AttrInitialize.new(self, names, block).apply
|
20
21
|
end
|
@@ -53,6 +54,13 @@ module AttrExtras
|
|
53
54
|
|
54
55
|
alias_method :attr_reader_initialize, :rattr_initialize
|
55
56
|
|
57
|
+
def aattr_initialize(*names, &block)
|
58
|
+
attr_initialize(*names, &block)
|
59
|
+
attr_accessor(*Utils.flat_names(names))
|
60
|
+
end
|
61
|
+
|
62
|
+
alias_method :attr_accessor_initialize, :aattr_initialize
|
63
|
+
|
56
64
|
def static_facade(method_name, *names)
|
57
65
|
define_singleton_method(method_name) do |*values|
|
58
66
|
new(*values).public_send(method_name)
|
@@ -74,26 +82,7 @@ module AttrExtras
|
|
74
82
|
end
|
75
83
|
|
76
84
|
def attr_implement(*names)
|
77
|
-
|
78
|
-
arity = arg_names.length
|
79
|
-
|
80
|
-
mod = Module.new do
|
81
|
-
define_method :method_missing do |name, *args|
|
82
|
-
if names.include?(name)
|
83
|
-
provided_arity = args.length
|
84
|
-
|
85
|
-
if provided_arity != arity
|
86
|
-
raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{arity})"
|
87
|
-
end
|
88
|
-
|
89
|
-
raise MethodNotImplementedError, "Implement a '#{name}(#{arg_names.join(", ")})' method"
|
90
|
-
else
|
91
|
-
super(name, *args)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
include mod
|
85
|
+
AttrImplement.new(self, names).apply
|
97
86
|
end
|
98
87
|
end
|
99
88
|
end
|
data/lib/attr_extras/version.rb
CHANGED
data/script/test
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
#
|
4
|
-
# in this case we need to work around test unit a bit.
|
3
|
+
# This script is per the Auctionet.com developer conventions for running tests in the same way in any project.
|
5
4
|
|
6
|
-
|
7
|
-
file = ARGV.first.split(":").first
|
8
|
-
system("bundle exec ruby #{file}") || exit(1)
|
5
|
+
system("bundle exec m #{ARGV.first}") || exit(1)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Object, ".aattr_initialize" do
|
4
|
+
it "creates an initializer and public readers" do
|
5
|
+
klass = Class.new do
|
6
|
+
aattr_initialize :foo, :bar
|
7
|
+
end
|
8
|
+
|
9
|
+
example = klass.new("Foo", "Bar")
|
10
|
+
|
11
|
+
example.foo.must_equal "Foo"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates public writers" do
|
15
|
+
klass = Class.new do
|
16
|
+
aattr_initialize :foo, :bar
|
17
|
+
end
|
18
|
+
|
19
|
+
example = klass.new("Foo", "Bar")
|
20
|
+
example.foo = "Baz"
|
21
|
+
|
22
|
+
example.foo.must_equal "Baz"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "works with hash ivars" do
|
26
|
+
klass = Class.new do
|
27
|
+
aattr_initialize :foo, [:bar, :baz!]
|
28
|
+
end
|
29
|
+
|
30
|
+
example = klass.new("Foo", :bar => "Bar", :baz => "Baz")
|
31
|
+
|
32
|
+
example.baz.must_equal "Baz"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "accepts a block for initialization" do
|
36
|
+
klass = Class.new do
|
37
|
+
aattr_initialize :value do
|
38
|
+
@copy = @value
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :copy
|
42
|
+
end
|
43
|
+
|
44
|
+
example = klass.new("expected")
|
45
|
+
|
46
|
+
example.copy.must_equal "expected"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "accepts the alias attr_accessor_initialize" do
|
50
|
+
klass = Class.new do
|
51
|
+
attr_accessor_initialize :foo, :bar
|
52
|
+
end
|
53
|
+
|
54
|
+
example = klass.new("Foo", "Bar")
|
55
|
+
|
56
|
+
example.foo.must_equal "Foo"
|
57
|
+
end
|
58
|
+
end
|
@@ -33,7 +33,7 @@ describe Object, ".pattr_initialize" do
|
|
33
33
|
example.copy.must_equal "expected"
|
34
34
|
end
|
35
35
|
|
36
|
-
it "accepts the alias
|
36
|
+
it "accepts the alias attr_private_initialize" do
|
37
37
|
klass = Class.new do
|
38
38
|
attr_private_initialize :foo, :bar
|
39
39
|
end
|
@@ -19,7 +19,7 @@ describe Object, ".rattr_initialize" do
|
|
19
19
|
example.public_send(:baz).must_equal "Baz"
|
20
20
|
end
|
21
21
|
|
22
|
-
it "accepts the alias
|
22
|
+
it "accepts the alias attr_reader_initialize" do
|
23
23
|
klass = Class.new do
|
24
24
|
attr_reader_initialize :foo, :bar
|
25
25
|
end
|
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.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henrik Nyh
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2016-03-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: minitest
|
@@ -26,6 +26,20 @@ dependencies:
|
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '5'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: m
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 1.3.1
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.3.1
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: rake
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,6 +69,7 @@ files:
|
|
55
69
|
- Rakefile
|
56
70
|
- attr_extras.gemspec
|
57
71
|
- lib/attr_extras.rb
|
72
|
+
- lib/attr_extras/attr_implement.rb
|
58
73
|
- lib/attr_extras/attr_initialize.rb
|
59
74
|
- lib/attr_extras/attr_query.rb
|
60
75
|
- lib/attr_extras/attr_value.rb
|
@@ -62,6 +77,7 @@ files:
|
|
62
77
|
- lib/attr_extras/utils.rb
|
63
78
|
- lib/attr_extras/version.rb
|
64
79
|
- script/test
|
80
|
+
- spec/attr_extras/aattr_initialize_spec.rb
|
65
81
|
- spec/attr_extras/attr_id_query_spec.rb
|
66
82
|
- spec/attr_extras/attr_implement_spec.rb
|
67
83
|
- spec/attr_extras/attr_initialize_spec.rb
|
@@ -102,6 +118,7 @@ signing_key:
|
|
102
118
|
specification_version: 4
|
103
119
|
summary: Takes some boilerplate out of Ruby with methods like attr_initialize.
|
104
120
|
test_files:
|
121
|
+
- spec/attr_extras/aattr_initialize_spec.rb
|
105
122
|
- spec/attr_extras/attr_id_query_spec.rb
|
106
123
|
- spec/attr_extras/attr_implement_spec.rb
|
107
124
|
- spec/attr_extras/attr_initialize_spec.rb
|