attributes_dsl 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -4
- data/CHANGELOG.md +23 -0
- data/README.md +29 -55
- data/attributes_dsl.gemspec +6 -7
- data/benchmark/run.rb +4 -4
- data/lib/attributes_dsl.rb +11 -24
- data/lib/attributes_dsl/attribute.rb +49 -34
- data/lib/attributes_dsl/attributes.rb +34 -34
- data/lib/attributes_dsl/transprocs.rb +141 -0
- data/lib/attributes_dsl/version.rb +1 -1
- data/spec/integration/blacklisted_attribute_spec.rb +49 -0
- data/spec/integration/coerced_attribute_spec.rb +27 -0
- data/spec/integration/default_attribute_spec.rb +20 -0
- data/spec/integration/required_attribute_spec.rb +17 -0
- data/spec/integration/simple_attribute_spec.rb +29 -0
- data/spec/integration/unknown_attribute_spec.rb +10 -0
- data/spec/integration/unreadable_attribute_spec.rb +18 -0
- data/spec/integration/whitelisted_attribute_spec.rb +49 -0
- data/spec/spec_helper.rb +3 -0
- metadata +40 -27
- data/spec/integration/attributes_dsl_spec.rb +0 -76
- data/spec/unit/attribute_spec.rb +0 -126
- data/spec/unit/attributes_spec.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c399823c440258ebcd951ce049e16877934b630
|
4
|
+
data.tar.gz: 345a83666686089656e22eb5a38f88997baccd41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a25c08685b444464ad501a9a0a5a9d945ea854bef5959241b53918470a7dbda28d0c173aec1c6a57709ad637c490ec8864bafad9f2c1cc5a6d622d56450a667f
|
7
|
+
data.tar.gz: 36a64b86b318bbad9cfbceddefdf9fb49616f2443f259a2024642461fdb03d6eb143216913e24cfd402938c37ef38c6ea8cce2da8b1f42eec67022931cb40aac
|
data/.travis.yml
CHANGED
@@ -4,14 +4,10 @@ bundler_args: --without metrics --without benchmarks
|
|
4
4
|
cache: bundler
|
5
5
|
script: bundle exec rake test:coverage:run
|
6
6
|
rvm:
|
7
|
-
- '1.9.3'
|
8
|
-
- '2.0'
|
9
7
|
- '2.1'
|
10
8
|
- '2.2'
|
11
9
|
- ruby-head
|
12
|
-
- rbx-2 --1.9
|
13
10
|
- rbx-2 --2.1
|
14
|
-
- jruby-1.7-19mode
|
15
11
|
- jruby-9.0.0.0
|
16
12
|
- jruby-head
|
17
13
|
matrix:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
## version 0.1.0 2015-12-08
|
2
|
+
|
3
|
+
Stabilized DSL
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
* Option `:reader` that allows skipping attribute reader (nepalez)
|
8
|
+
* Option `:only` whitelists allowed values (nepalez)
|
9
|
+
* Option `:except` blacklists allowed values (nepalez)
|
10
|
+
* Option `:coercer` is an alternative to block syntax (nepalez)
|
11
|
+
* An instance can be initialized by hash with both symbolic and string keys (nepalez)
|
12
|
+
|
13
|
+
### Deleted
|
14
|
+
|
15
|
+
* Support for rubies < 2.1
|
16
|
+
|
17
|
+
### Internal
|
18
|
+
|
19
|
+
* Removed freezing of values (nepalez)
|
20
|
+
* Slightly (20-25%) improved efficiency due to usage of transpfoc (nepalez)
|
21
|
+
|
22
|
+
[Compare v0.0.2...v0.1.0](https://github.com/nepalez/attributes_dsl/compare/v0.0.2...v0.1.0)
|
23
|
+
|
1
24
|
## version 0.0.2 2015-09-11
|
2
25
|
|
3
26
|
This version is a result of applying v0.0.1 to the existing gems:
|
data/README.md
CHANGED
@@ -21,40 +21,21 @@ require "attributes_dsl"
|
|
21
21
|
class User
|
22
22
|
extend AttributesDSL
|
23
23
|
|
24
|
-
|
25
|
-
attribute :
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# `sex` is optional and set to `:male` by default.
|
30
|
-
# It can be set to either :male or :female
|
31
|
-
attribute :sex, default: :male do |value|
|
32
|
-
(value == :male )? :male : :female
|
33
|
-
end
|
34
|
-
|
35
|
-
# `age` is optional and set to `nil` by default.
|
36
|
-
# Then it is converted to integer
|
37
|
-
attribute :age, &:to_i
|
38
|
-
|
39
|
-
# `position` is optional and set to `nil` by default
|
40
|
-
attribute :position
|
41
|
-
|
42
|
-
# All other attributes are ignored
|
24
|
+
attribute :name, required: true, coerce: -> v { v.to_s }
|
25
|
+
attribute :sex, default: :male, only: /male|female/
|
26
|
+
attribute :age, only: 18..25
|
27
|
+
attribute :city, reader: false, except: %w(Moscow)
|
43
28
|
end
|
44
29
|
|
45
|
-
user = User.new(name:
|
30
|
+
user = User.new(name: :Jane, sex: :female, age: 24, city: "Kiev")
|
46
31
|
user.attributes
|
47
|
-
# => { name: :Jane, sex: :female, age: 26,
|
32
|
+
# => { name: :Jane, sex: :female, age: 26, city: "Kiev" }
|
48
33
|
|
49
34
|
# Aliases for attributes[:some_attribute]
|
50
|
-
user.name
|
51
|
-
user.sex
|
52
|
-
user.age
|
53
|
-
user.
|
54
|
-
|
55
|
-
# Required attributes should be assigned:
|
56
|
-
user = User.new(sex: :women, age: "26", place: "Moscow")
|
57
|
-
# => #<ArgumentError "Undefined attributes: name">
|
35
|
+
user.name # => "Jane"
|
36
|
+
user.sex # => :female
|
37
|
+
user.age # => 26
|
38
|
+
user.city # => #<NoMethodError ...>
|
58
39
|
```
|
59
40
|
|
60
41
|
Additional Details
|
@@ -62,10 +43,14 @@ Additional Details
|
|
62
43
|
|
63
44
|
### Attribute declaration
|
64
45
|
|
65
|
-
The `attribute` class method takes the `name` and
|
46
|
+
The `attribute` class method takes the `name` and 3 options:
|
66
47
|
|
67
|
-
- `:default` for the default value (otherwise `nil`)
|
48
|
+
- `:default` for the default value (otherwise `nil`).
|
68
49
|
- `:required` to declare the attribute as required. It will be ignored if a default value is provided!
|
50
|
+
- `:reader` defines whether the attribute reader should be defined (`true` by default).
|
51
|
+
- `:only` defines allowed values. You can use procs (`-> v { v.to_s }`), ranges (`18..25`), regexps (`/male|female/`), constants (`String`), or arrays (`[:male, :female]`) to define a restriction.
|
52
|
+
- `:except` defines forbidden values.
|
53
|
+
- `:coercer` defines a procedure to convert assigned value.
|
69
54
|
|
70
55
|
It is also takes the block, used to coerce a value. The coercer is applied to the default value too.
|
71
56
|
|
@@ -75,10 +60,9 @@ Instance methods (like `#name`) are just aliases for the corresponding value of
|
|
75
60
|
|
76
61
|
```ruby
|
77
62
|
user = User.new(name: "John")
|
78
|
-
user.attributes # => { name: :John, sex: :male, age:
|
79
|
-
user.name # => :John
|
63
|
+
user.attributes # => { name: :John, sex: :male, age: nil, city: nil }
|
80
64
|
|
81
|
-
#
|
65
|
+
user.name # => :John
|
82
66
|
user.instance_variable_get :@name # => nil
|
83
67
|
```
|
84
68
|
|
@@ -93,7 +77,8 @@ end
|
|
93
77
|
|
94
78
|
user = UserWithRole.new(name: "Sam")
|
95
79
|
user.attributes
|
96
|
-
|
80
|
+
user.attributes
|
81
|
+
# => { name: :John, sex: :male, age: nil, city: nil, role: :user }
|
97
82
|
```
|
98
83
|
|
99
84
|
### Undefining Attributes
|
@@ -160,29 +145,21 @@ The results are following:
|
|
160
145
|
|
161
146
|
```
|
162
147
|
-------------------------------------------------
|
163
|
-
|
164
|
-
|
165
|
-
fast_attributes
|
166
|
-
attributes_dsl
|
167
|
-
active_attr
|
168
|
-
virtus
|
169
|
-
|
170
|
-
Comparison:
|
171
|
-
anima: 211637.9 i/s
|
172
|
-
kwattr: 187276.2 i/s - 1.13x slower
|
173
|
-
fast_attributes: 160916.1 i/s - 1.32x slower
|
174
|
-
attributes_dsl: 71850.0 i/s - 2.95x slower
|
175
|
-
active_attr: 71489.1 i/s - 2.96x slower
|
176
|
-
virtus: 45553.8 i/s - 4.65x slower
|
148
|
+
kwattr: 183416.9 i/s
|
149
|
+
anima: 169647.3 i/s - 1.08x slower
|
150
|
+
fast_attributes: 156036.2 i/s - 1.18x slower
|
151
|
+
attributes_dsl: 74495.9 i/s - 2.46x slower
|
152
|
+
active_attr: 74469.4 i/s - 2.46x slower
|
153
|
+
virtus: 46587.0 i/s - 3.94x slower
|
177
154
|
```
|
178
155
|
|
179
156
|
Results above are pretty reasonable.
|
180
157
|
|
181
158
|
The gem is faster than `virtus` that has many additional features.
|
182
159
|
|
183
|
-
It is as fast as `active_attrs` (but has more
|
160
|
+
It is as fast as `active_attrs` (but has more options).
|
184
161
|
|
185
|
-
It is 2 times slower than `fast_attributes` that has no coercer and default values. And it is
|
162
|
+
It is 2 times slower than `fast_attributes` that has no coercer and default values. And it is 2.5 times slower than `anima` and `kwattr` that provide only simple attribute's declaration.
|
186
163
|
|
187
164
|
Installation
|
188
165
|
------------
|
@@ -209,12 +186,10 @@ gem install attributes_dsl
|
|
209
186
|
Compatibility
|
210
187
|
-------------
|
211
188
|
|
212
|
-
Tested under rubies [compatible to MRI 1
|
189
|
+
Tested under rubies [compatible to MRI 2.1+][versions].
|
213
190
|
|
214
191
|
Uses [RSpec][rspec] 3.0+ for testing and [hexx-suit][hexx-suit] for dev/test tools collection.
|
215
192
|
|
216
|
-
100% [mutant]-proof covered.
|
217
|
-
|
218
193
|
Contributing
|
219
194
|
------------
|
220
195
|
|
@@ -222,7 +197,6 @@ Contributing
|
|
222
197
|
* [Fork the project](https://github.com/nepalez/attributes_dsl)
|
223
198
|
* Create your feature branch (`git checkout -b my-new-feature`)
|
224
199
|
* Add tests for it
|
225
|
-
* Run `rake mutant` or `rake exhort` to ensure 100% [mutant][mutant] coverage
|
226
200
|
* Commit your changes (`git commit -am '[UPDATE] Add some feature'`)
|
227
201
|
* Push to the branch (`git push origin my-new-feature`)
|
228
202
|
* Create a new Pull Request
|
data/attributes_dsl.gemspec
CHANGED
@@ -2,7 +2,6 @@ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
|
2
2
|
require "attributes_dsl/version"
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
|
6
5
|
gem.name = "attributes_dsl"
|
7
6
|
gem.version = AttributesDSL::VERSION.dup
|
8
7
|
gem.author = "Andrew Kozin"
|
@@ -16,11 +15,11 @@ Gem::Specification.new do |gem|
|
|
16
15
|
gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
|
17
16
|
gem.require_paths = ["lib"]
|
18
17
|
|
19
|
-
gem.required_ruby_version = ">= 1
|
20
|
-
|
21
|
-
gem.add_runtime_dependency "ice_nine", "~> 0.11"
|
22
|
-
gem.add_runtime_dependency "equalizer", "~> 0.0", ">= 0.0.11"
|
18
|
+
gem.required_ruby_version = ">= 2.1"
|
23
19
|
|
24
|
-
gem.
|
20
|
+
gem.add_runtime_dependency "equalizer", "~> 0.0.11"
|
21
|
+
gem.add_runtime_dependency "transproc", "~> 0.4.0"
|
25
22
|
|
26
|
-
|
23
|
+
gem.add_development_dependency "hexx-rspec", "~> 0.5.2"
|
24
|
+
gem.add_development_dependency "ice_nine", "~> 0.11.1"
|
25
|
+
end
|
data/benchmark/run.rb
CHANGED
@@ -6,10 +6,10 @@ module AttributesDSLExample
|
|
6
6
|
class User
|
7
7
|
extend AttributesDSL
|
8
8
|
|
9
|
-
attribute :foo, required: true
|
10
|
-
attribute :bar, default: :BAR
|
11
|
-
attribute :baz, default: :BAZ
|
12
|
-
attribute :qux
|
9
|
+
attribute :foo, required: true
|
10
|
+
attribute :bar, default: :BAR
|
11
|
+
attribute :baz, default: :BAZ
|
12
|
+
attribute :qux
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.call
|
data/lib/attributes_dsl.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require "equalizer"
|
4
|
-
require "
|
4
|
+
require "transproc"
|
5
5
|
|
6
6
|
# Simple DSL for PORO attributes
|
7
7
|
#
|
@@ -11,6 +11,7 @@ require "ice_nine"
|
|
11
11
|
#
|
12
12
|
module AttributesDSL
|
13
13
|
|
14
|
+
require_relative "attributes_dsl/transprocs"
|
14
15
|
require_relative "attributes_dsl/attribute"
|
15
16
|
require_relative "attributes_dsl/attributes"
|
16
17
|
|
@@ -31,35 +32,26 @@ module AttributesDSL
|
|
31
32
|
# extend AttributeDSL
|
32
33
|
#
|
33
34
|
# attribute :foo, required: true do |value|
|
34
|
-
# value.to_i % 5
|
35
|
+
# value.to_i % 5 # value coercer
|
35
36
|
# end
|
36
37
|
#
|
37
|
-
# attribute :bar, default: :BAR
|
38
|
+
# attribute :bar, default: :BAR, reader: false
|
38
39
|
# end
|
39
40
|
#
|
40
41
|
# @param [#to_sym] name The unique name of the attribute
|
41
|
-
# @param [Proc] coercer The proc to coerce values (including the default ones)
|
42
42
|
# @param [Hash] options
|
43
|
-
#
|
44
|
-
# @option options [Boolean] :required
|
45
|
-
# Whether the attribute should be required by the +initializer+
|
46
|
-
# This option is ignored (set to +false+) when default value is provided
|
47
|
-
# @option options [Object] :default
|
48
|
-
# The default value for the attribute
|
43
|
+
# @param [Proc] coercer The proc to coerce values (including the default ones)
|
49
44
|
#
|
50
45
|
# @return [undefined]
|
51
46
|
#
|
52
47
|
def attribute(name, options = {}, &coercer)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
define_method(s_name) { attributes.fetch(s_name) }
|
48
|
+
@attributes = attributes.add(name, options, &coercer)
|
49
|
+
define_method(name) { attributes.fetch(name) } if attributes.reader? name
|
57
50
|
end
|
58
51
|
|
59
52
|
# @private
|
60
53
|
def self.extended(klass)
|
61
|
-
|
62
|
-
klass.__send__(:include, InstanceMethods)
|
54
|
+
klass.instance_eval { include InstanceMethods }
|
63
55
|
end
|
64
56
|
|
65
57
|
# @private
|
@@ -69,7 +61,6 @@ module AttributesDSL
|
|
69
61
|
|
70
62
|
# Defines instance methods for the hash of attributes and its initializer
|
71
63
|
module InstanceMethods
|
72
|
-
|
73
64
|
# @!attribute [r] attributes
|
74
65
|
#
|
75
66
|
# @return [Hash] the hash of initialized attributes
|
@@ -80,12 +71,8 @@ module AttributesDSL
|
|
80
71
|
#
|
81
72
|
# @param [Hash] attributes
|
82
73
|
#
|
83
|
-
# @raise [ArgumentError] in case a required attribute is missed
|
84
|
-
#
|
85
74
|
def initialize(attributes = {})
|
86
|
-
@attributes = self.class.attributes.
|
75
|
+
@attributes = self.class.attributes.transformer[attributes]
|
87
76
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end # module AttributesDSL
|
77
|
+
end
|
78
|
+
end
|
@@ -9,7 +9,6 @@ module AttributesDSL
|
|
9
9
|
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
10
10
|
#
|
11
11
|
class Attribute
|
12
|
-
|
13
12
|
include Equalizer.new(:name)
|
14
13
|
|
15
14
|
# @!attribute [r] name
|
@@ -18,52 +17,68 @@ module AttributesDSL
|
|
18
17
|
#
|
19
18
|
attr_reader :name
|
20
19
|
|
21
|
-
# @!attribute [r]
|
22
|
-
#
|
23
|
-
# @return [Object] the default value of the attribute
|
24
|
-
#
|
25
|
-
attr_reader :default
|
26
|
-
|
27
|
-
# @!attribute [r] required
|
28
|
-
#
|
29
|
-
# @return [Boolean] whether the attribute is required
|
30
|
-
#
|
31
|
-
attr_reader :required
|
32
|
-
|
33
|
-
# @!attribute [r] coercer
|
20
|
+
# @!attribute [r] reader
|
34
21
|
#
|
35
|
-
# @return [
|
22
|
+
# @return [Boolean] whether an attribute should be readable
|
36
23
|
#
|
37
|
-
attr_reader :
|
24
|
+
attr_reader :reader
|
38
25
|
|
39
26
|
# Initializes the attribute
|
40
27
|
#
|
41
|
-
# @param [
|
28
|
+
# @param [#to_sym] name
|
42
29
|
# @param [Hash] options
|
43
30
|
# @param [Proc] coercer
|
44
31
|
#
|
45
|
-
# @option options [Object] :default
|
46
|
-
# @option options [Boolean] :required
|
47
|
-
#
|
48
32
|
def initialize(name, options = {}, &coercer)
|
49
|
-
@name
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@coercer = coercer
|
53
|
-
|
54
|
-
IceNine.deep_freeze(self)
|
33
|
+
@name = name.to_sym
|
34
|
+
@options = { coercer: coercer }.merge(options)
|
35
|
+
@reader = @options.fetch(:reader) { true }
|
55
36
|
end
|
56
37
|
|
57
|
-
#
|
38
|
+
# A proc that transform a hash of attributes using current settings
|
58
39
|
#
|
59
|
-
# @
|
40
|
+
# @return [Proc]
|
60
41
|
#
|
61
|
-
|
62
|
-
|
63
|
-
def value(input)
|
64
|
-
coercer ? coercer[input] : input
|
42
|
+
def transformer
|
43
|
+
convert unless @options.empty?
|
65
44
|
end
|
66
45
|
|
67
|
-
|
46
|
+
private
|
47
|
+
|
48
|
+
def convert
|
49
|
+
@convert ||= Transprocs[:convert, name, presence, absence]
|
50
|
+
end
|
51
|
+
|
52
|
+
def presence
|
53
|
+
[whitelist, blacklist, coercer].compact.reduce(:>>) || identity
|
54
|
+
end
|
68
55
|
|
69
|
-
|
56
|
+
def absence
|
57
|
+
[missed, default].compact.reduce(:>>) || identity
|
58
|
+
end
|
59
|
+
|
60
|
+
def identity
|
61
|
+
Transprocs[:identity]
|
62
|
+
end
|
63
|
+
|
64
|
+
def missed
|
65
|
+
Transprocs[:missed, name] if @options[:required]
|
66
|
+
end
|
67
|
+
|
68
|
+
def default
|
69
|
+
Transprocs[:default, name, @options[:default]] if @options[:default]
|
70
|
+
end
|
71
|
+
|
72
|
+
def whitelist
|
73
|
+
Transprocs[:whitelist, name, @options[:only]] if @options[:only]
|
74
|
+
end
|
75
|
+
|
76
|
+
def blacklist
|
77
|
+
Transprocs[:blacklist, name, @options[:except]] if @options[:except]
|
78
|
+
end
|
79
|
+
|
80
|
+
def coercer
|
81
|
+
Transprocs[:coerce, name, @options[:coercer]] if @options[:coercer]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -10,22 +10,14 @@ module AttributesDSL
|
|
10
10
|
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
11
11
|
#
|
12
12
|
class Attributes
|
13
|
-
|
14
13
|
# @!attribute [r] attributes
|
15
14
|
#
|
16
15
|
# Uses the set of attributes to ensure their uniqueness (by name)
|
17
16
|
#
|
18
17
|
# @return [Set] the set of registered attributes
|
19
18
|
#
|
20
|
-
|
21
|
-
|
22
|
-
# Initializes an immutable collection with an initial set of attributes
|
23
|
-
#
|
24
|
-
# @param [Hash] attributes
|
25
|
-
#
|
26
|
-
def initialize(attributes = {})
|
27
|
-
@attributes = attributes
|
28
|
-
IceNine.deep_freeze(self)
|
19
|
+
def attributes
|
20
|
+
@attributes ||= {}
|
29
21
|
end
|
30
22
|
|
31
23
|
# Initializes the attribute from given arguments
|
@@ -35,41 +27,49 @@ module AttributesDSL
|
|
35
27
|
#
|
36
28
|
# @return [AttributesDSL::Attributes]
|
37
29
|
#
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
def add(name, options = {}, &coercer)
|
31
|
+
name = name.to_sym
|
32
|
+
value = Attribute.new(name, options, &coercer)
|
33
|
+
clone_with do
|
34
|
+
@attributes = attributes.merge(name => value)
|
35
|
+
@transformer = nil
|
36
|
+
end
|
42
37
|
end
|
43
38
|
|
44
|
-
#
|
39
|
+
# Returns the proc that converts a hash of attributes using current setting
|
40
|
+
#
|
41
|
+
# @return [Proc]
|
45
42
|
#
|
46
|
-
|
43
|
+
def transformer
|
44
|
+
@transformer ||= transprocs.flatten.compact.reduce(:>>)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Checks whether an attribute reader should be defined
|
47
48
|
#
|
48
|
-
# @param [
|
49
|
+
# @param [#to_sym] name
|
49
50
|
#
|
50
|
-
# @return [
|
51
|
+
# @return [Boolean]
|
51
52
|
#
|
52
|
-
def
|
53
|
-
|
54
|
-
key = e.name
|
55
|
-
value = input.fetch(key) { e.default }
|
56
|
-
a.merge(key => e.value(value))
|
57
|
-
end
|
53
|
+
def reader?(name)
|
54
|
+
attributes[name.to_sym].reader
|
58
55
|
end
|
59
56
|
|
60
57
|
private
|
61
58
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
59
|
+
def transprocs
|
60
|
+
[
|
61
|
+
Transprocs[:filter, keys],
|
62
|
+
attributes.values.map(&:transformer),
|
63
|
+
Transprocs[:update, keys]
|
64
|
+
]
|
67
65
|
end
|
68
66
|
|
69
|
-
def
|
70
|
-
attributes.
|
67
|
+
def keys
|
68
|
+
attributes.keys
|
71
69
|
end
|
72
70
|
|
73
|
-
|
74
|
-
|
75
|
-
end
|
71
|
+
def clone_with(&block)
|
72
|
+
dup.tap { |instance| instance.instance_eval(&block) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|