attr_extras 2.0.0 → 2.0.1
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 +13 -5
- data/README.md +36 -56
- data/lib/attr_extras/attr_initialize.rb +57 -0
- data/lib/attr_extras/attr_query.rb +13 -0
- data/lib/attr_extras/utils.rb +5 -0
- data/lib/attr_extras/version.rb +1 -1
- data/lib/attr_extras.rb +8 -54
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjhmZWFmMjMzZDY4YmFmYWYyZDFjYjIyNzU1ZGJlYmFlMmYxNGEwMQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTI2NzNiMmEwZDRlNjM3ZjM5ODZiMDcyYTU0YWNmZmE0YTEyODNkOQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MzE5YmQ3ZjBmYjY5MzY4OTczY2VkNzNiMDVkNDMxNzUyYmFkMzFlMmJmNTA5
|
10
|
+
MjYxMjJkZDE1YmJkYzdlNjM2NzIwOWZhMjc2OTUxYjBjNmNjN2RiZDEwODIy
|
11
|
+
YWVmZGE0MWZjOGY3NWFmNGNkODg5Mzk0NDllMGMzOWJkN2Q3ZGQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
M2NkMWU1NjAxYTNkMjczY2VkYWEwMDMyODM5OGY0YjcxNDU4YjRlZWRkZmYy
|
14
|
+
MjZlYzM3NjgyOWVhMjg2NjMyNmJiZTk2OTdlNjk4ODFkZTU4Y2E2ZjFkNWY5
|
15
|
+
OTgxY2EwYjk0Nzc4MWNmYTc4MTBiZTM0ODQwNzllZTE5ZGY2OGI=
|
data/README.md
CHANGED
@@ -28,6 +28,10 @@ end
|
|
28
28
|
|
29
29
|
This nicely complements Ruby's built-in `attr_accessor`, `attr_reader` and `attr_writer`.
|
30
30
|
|
31
|
+
Supports positional arguments as well as optional and required hash arguments.
|
32
|
+
|
33
|
+
Also provides conveniences for creating value objects, method objects and query methods.
|
34
|
+
|
31
35
|
|
32
36
|
## Usage
|
33
37
|
|
@@ -79,83 +83,59 @@ The `attr_initialize` notation notation for hash arguments is also supported: `v
|
|
79
83
|
|
80
84
|
### `method_object :fooable?, :foo`<br>
|
81
85
|
|
82
|
-
Defines a `.fooable?` class method that takes
|
83
|
-
|
84
|
-
The `attr_initialize` notation notation for hash arguments is also supported: `method_object :fooable?, :foo, [:bar, :baz!]`
|
85
|
-
|
86
|
-
You don't have to specify readers if you don't want them: `method_object :fooable?` is also valid.
|
87
|
-
|
88
|
-
|
89
|
-
### `attr_id_query :foo?, :bar?`<br>
|
90
|
-
Defines query methods like `foo?`, which is true iff `foo_id` is truthy. Goes well with Active Record.
|
91
|
-
|
86
|
+
Defines a `.fooable?` class method that takes arguments (`foo`) and delegates to an instance method that can access those arguments as private readers.
|
92
87
|
|
93
|
-
|
94
|
-
Defines query methods like `foo?`, which is true iff `foo` is truthy.
|
88
|
+
This is useful for [method objects](http://refactoring.com/catalog/replaceMethodWithMethodObject.html):
|
95
89
|
|
90
|
+
``` ruby
|
91
|
+
class PriceCalculator
|
92
|
+
method_object :calculate,
|
93
|
+
:order
|
96
94
|
|
97
|
-
|
95
|
+
def calculate
|
96
|
+
order.price * factor
|
97
|
+
end
|
98
98
|
|
99
|
-
|
100
|
-
Hence the long name `attr_initialize`, so you see it when scanning for the initializer;
|
101
|
-
and the enforced questionmarks with `attr_id_query :foo?`, so you can search for that method.
|
99
|
+
private
|
102
100
|
|
101
|
+
def factor
|
102
|
+
1 + rand
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
103
106
|
|
104
|
-
|
107
|
+
Shortcut for
|
105
108
|
|
106
109
|
``` ruby
|
107
|
-
|
108
|
-
|
109
|
-
attr_id_query :item?
|
110
|
-
attr_query :oof?
|
111
|
-
|
112
|
-
def oof
|
113
|
-
foo.reverse
|
114
|
-
end
|
110
|
+
attr_initialize :foo
|
111
|
+
attr_private :foo
|
115
112
|
|
116
|
-
|
117
|
-
|
118
|
-
end
|
113
|
+
def self.fooable?(foo)
|
114
|
+
new(foo).fooable?
|
119
115
|
end
|
116
|
+
```
|
120
117
|
|
121
|
-
|
122
|
-
x.oof # => "!ooF"
|
123
|
-
x.foo # NoMethodError: private method `foo' called.
|
124
|
-
x.item? # => true
|
125
|
-
x.oof? # => true
|
118
|
+
The `attr_initialize` notation notation for hash arguments is also supported: `method_object :fooable?, :foo, [:bar, :baz!]`
|
126
119
|
|
120
|
+
You don't have to specify readers if you don't want them: `method_object :fooable?` is also valid.
|
127
121
|
|
128
|
-
class MyMethodObject
|
129
|
-
method_object :fooable?,
|
130
|
-
:foo
|
131
122
|
|
132
|
-
|
133
|
-
foo == :some_value
|
134
|
-
end
|
135
|
-
end
|
123
|
+
### `attr_id_query :foo?, :bar?`<br>
|
136
124
|
|
137
|
-
|
138
|
-
MyMethodObject.fooable?(:another_value) # => false
|
125
|
+
Defines query methods like `foo?`, which is true if (and only if) `foo_id` is truthy. Goes well with Active Record.
|
139
126
|
|
140
127
|
|
141
|
-
|
142
|
-
attr_initialize :foo, [:bar, :baz]
|
143
|
-
attr_reader :bar
|
144
|
-
end
|
128
|
+
### `attr_query :foo?, :bar?`<br>
|
145
129
|
|
146
|
-
|
147
|
-
x.bar # => "Bar!"
|
130
|
+
Defines query methods like `foo?`, which is true if (and only if) `foo` is truthy.
|
148
131
|
|
149
132
|
|
150
|
-
|
151
|
-
|
152
|
-
|
133
|
+
## Philosophy
|
134
|
+
|
135
|
+
Findability is a core value.
|
136
|
+
Hence the long name `attr_initialize`, so you see it when scanning for the initializer;
|
137
|
+
and the enforced questionmarks with `attr_id_query :foo?`, so you can search for that method.
|
153
138
|
|
154
|
-
x = MyValueObject.new(5, 10)
|
155
|
-
x.foo # => 5
|
156
|
-
x.bar # => 10
|
157
|
-
x.foo = 20 # NoMethodError: undefined method `foo=''`
|
158
|
-
```
|
159
139
|
|
160
140
|
## Why not use `Struct`?
|
161
141
|
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class AttrExtras::AttrInitialize
|
2
|
+
def initialize(klass, names)
|
3
|
+
@klass, @names = klass, names
|
4
|
+
end
|
5
|
+
|
6
|
+
attr_reader :klass, :names
|
7
|
+
private :klass, :names
|
8
|
+
|
9
|
+
def apply
|
10
|
+
# The define_method block can't call our methods, so we need to make
|
11
|
+
# things available via local variables.
|
12
|
+
names = @names
|
13
|
+
validate_arity = method(:validate_arity)
|
14
|
+
set_ivar_from_hash = method(:set_ivar_from_hash)
|
15
|
+
|
16
|
+
klass.send(:define_method, :initialize) do |*values|
|
17
|
+
validate_arity.call(values.length)
|
18
|
+
|
19
|
+
names.zip(values).each do |name_or_names, value|
|
20
|
+
if name_or_names.is_a?(Array)
|
21
|
+
hash = value || {}
|
22
|
+
|
23
|
+
name_or_names.each do |name|
|
24
|
+
set_ivar_from_hash.call(self, name, hash)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
name = name_or_names
|
28
|
+
instance_variable_set("@#{name}", value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def validate_arity(provided_arity)
|
37
|
+
arity_without_hashes = names.count { |name| not name.is_a?(Array) }
|
38
|
+
arity_with_hashes = names.length
|
39
|
+
|
40
|
+
unless (arity_without_hashes..arity_with_hashes).include?(provided_arity)
|
41
|
+
arity_range = [arity_without_hashes, arity_with_hashes].uniq.join("..")
|
42
|
+
raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{arity_range})"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_ivar_from_hash(instance, name, hash)
|
47
|
+
if name.to_s.end_with?("!")
|
48
|
+
actual_name = name.to_s.chop.to_sym
|
49
|
+
value = hash.fetch(actual_name)
|
50
|
+
else
|
51
|
+
actual_name = name
|
52
|
+
value = hash[name]
|
53
|
+
end
|
54
|
+
|
55
|
+
instance.instance_variable_set("@#{actual_name}", value)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AttrExtras::AttrQuery
|
2
|
+
def self.define_with_suffix(klass, suffix, *names)
|
3
|
+
names.each do |name|
|
4
|
+
name = name.to_s
|
5
|
+
|
6
|
+
raise "#{__method__} wants `#{name}?`, not `#{name}`." unless name.end_with?("?")
|
7
|
+
|
8
|
+
klass.send(:define_method, name) do # def foo?
|
9
|
+
!!send("#{name.chop}#{suffix}") # !!send("foo_id")
|
10
|
+
end # end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/attr_extras/version.rb
CHANGED
data/lib/attr_extras.rb
CHANGED
@@ -1,40 +1,12 @@
|
|
1
1
|
require "attr_extras/version"
|
2
|
+
require "attr_extras/attr_initialize"
|
3
|
+
require "attr_extras/attr_query"
|
4
|
+
require "attr_extras/utils"
|
2
5
|
|
3
6
|
module AttrExtras
|
4
7
|
module ClassMethods
|
5
8
|
def attr_initialize(*names)
|
6
|
-
|
7
|
-
max_arity = names.length
|
8
|
-
|
9
|
-
define_method(:initialize) do |*values|
|
10
|
-
provided_arity = values.length
|
11
|
-
|
12
|
-
unless (min_arity..max_arity).include?(provided_arity)
|
13
|
-
arity_range = [min_arity, max_arity].uniq.join("..")
|
14
|
-
raise ArgumentError, "wrong number of arguments (#{provided_arity} for #{arity_range})"
|
15
|
-
end
|
16
|
-
|
17
|
-
names.zip(values).each do |name_or_names, value|
|
18
|
-
if name_or_names.is_a?(Array)
|
19
|
-
value ||= {}
|
20
|
-
|
21
|
-
name_or_names.each do |name|
|
22
|
-
if name.to_s.end_with?("!")
|
23
|
-
actual_name = name.to_s.chop.to_sym
|
24
|
-
actual_value = value.fetch(actual_name)
|
25
|
-
else
|
26
|
-
actual_name = name
|
27
|
-
actual_value = value[name]
|
28
|
-
end
|
29
|
-
|
30
|
-
instance_variable_set("@#{actual_name}", actual_value)
|
31
|
-
end
|
32
|
-
else
|
33
|
-
name = name_or_names
|
34
|
-
instance_variable_set("@#{name}", value)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
9
|
+
AttrInitialize.new(self, names).apply
|
38
10
|
end
|
39
11
|
|
40
12
|
def attr_private(*names)
|
@@ -44,12 +16,12 @@ module AttrExtras
|
|
44
16
|
|
45
17
|
def pattr_initialize(*names)
|
46
18
|
attr_initialize(*names)
|
47
|
-
attr_private *
|
19
|
+
attr_private *Utils.flat_names(names)
|
48
20
|
end
|
49
21
|
|
50
22
|
def vattr_initialize(*names)
|
51
23
|
attr_initialize(*names)
|
52
|
-
attr_value *
|
24
|
+
attr_value *Utils.flat_names(names)
|
53
25
|
end
|
54
26
|
|
55
27
|
def attr_value(*names)
|
@@ -71,29 +43,11 @@ module AttrExtras
|
|
71
43
|
end
|
72
44
|
|
73
45
|
def attr_query(*names)
|
74
|
-
|
46
|
+
AttrQuery.define_with_suffix(self, "", *names)
|
75
47
|
end
|
76
48
|
|
77
49
|
def attr_id_query(*names)
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def attr_flat_names(names)
|
84
|
-
names.flatten.map { |x| x.to_s.sub(/!\z/, "") }
|
85
|
-
end
|
86
|
-
|
87
|
-
def attr_query_with_suffix(*names, suffix)
|
88
|
-
names.each do |name|
|
89
|
-
name = name.to_s
|
90
|
-
|
91
|
-
raise "#{__method__} wants `#{name}?`, not `#{name}`." unless name.end_with?("?")
|
92
|
-
|
93
|
-
define_method(name) do # def foo?
|
94
|
-
!!send("#{name.chop}#{suffix}") # !!send("foo_id")
|
95
|
-
end # end
|
96
|
-
end
|
50
|
+
AttrQuery.define_with_suffix(self, "_id", *names)
|
97
51
|
end
|
98
52
|
end
|
99
53
|
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: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Henrik Nyh
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
name: rake
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - '>='
|
18
|
+
- - ! '>='
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '0'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - '>='
|
25
|
+
- - ! '>='
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
28
|
description:
|
@@ -40,6 +40,9 @@ files:
|
|
40
40
|
- Rakefile
|
41
41
|
- attr_extras.gemspec
|
42
42
|
- lib/attr_extras.rb
|
43
|
+
- lib/attr_extras/attr_initialize.rb
|
44
|
+
- lib/attr_extras/attr_query.rb
|
45
|
+
- lib/attr_extras/utils.rb
|
43
46
|
- lib/attr_extras/version.rb
|
44
47
|
- script/test
|
45
48
|
- spec/attr_extras_spec.rb
|
@@ -53,17 +56,17 @@ require_paths:
|
|
53
56
|
- lib
|
54
57
|
required_ruby_version: !ruby/object:Gem::Requirement
|
55
58
|
requirements:
|
56
|
-
- - '>='
|
59
|
+
- - ! '>='
|
57
60
|
- !ruby/object:Gem::Version
|
58
61
|
version: '0'
|
59
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
63
|
requirements:
|
61
|
-
- - '>='
|
64
|
+
- - ! '>='
|
62
65
|
- !ruby/object:Gem::Version
|
63
66
|
version: '0'
|
64
67
|
requirements: []
|
65
68
|
rubyforge_project:
|
66
|
-
rubygems_version: 2.2.
|
69
|
+
rubygems_version: 2.2.1
|
67
70
|
signing_key:
|
68
71
|
specification_version: 4
|
69
72
|
summary: Takes some boilerplate out of Ruby with methods like attr_initialize.
|