attributes_dsl 0.0.2 → 0.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 +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
|