attributes_dsl 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da436a8b36ce35a3c4b4b87b33ed84a03a7dd910
4
- data.tar.gz: 8d7edd3acf452eff9b4255a15a16cfee6f1d00d1
3
+ metadata.gz: a0a8b2957544474ff4116fd2b6d1703de977ba74
4
+ data.tar.gz: b61d244f9ea674fdff84404e269dfa083fca6f64
5
5
  SHA512:
6
- metadata.gz: 63ea9addb5f796e1009f0140cc3523b4be461c280811d19b878edb3247ae5e1abe9f1cda60a05c703dfe7c28558bbf2868e2432c0b5957bce0d6ab15b0a8b408
7
- data.tar.gz: e2555ba15ad33fbadbded4f29c0936687e23273ca534de57a9af3767a82f0a3372561d39c1145ec0031ca44f6afe4b0168d871173573c1b41b2f27ecc722fe74
6
+ metadata.gz: de35390efe7b4feb77a049d040884615e11c5de61e9d8cb3bb595fbf2b71a0b0e3d53b5dd78425c6e775774366bb39cd538b5b6fa3e1af6c67e61ec03863669d
7
+ data.tar.gz: 213cac8c92f35d396ce1272b53baace3a03ee635521e573cf039c85fd72bf4f43f384f46ae7d958188f9ca1d6c3424f82bc992e82c94b7fecd5221c6f10fbff4
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## version 0.0.2 2015-09-11
2
+
3
+ This version is a result of applying v0.0.1 to the existing gems:
4
+
5
+ * [abstract_mapper](https://github.com/nepalez/abstract_mapper)
6
+ * [query_builder](https://github.com/nepalez/query_builder)
7
+ * [rom-kafka](https://github.com/nepalez/rom-kafka) (WIP)
8
+
9
+ ### Added
10
+
11
+ * Support for inheritance of attributes (nepalez)
12
+
13
+ ### Bugs fixed
14
+
15
+ * Allow initializer to be used without arguments (nepalez)
16
+ * Allow attributes to be redefined (nepalez)
17
+ * Remove `AttributesDSL#new` and move its logics to the initializer (nepalez)
18
+
19
+ [Compare v0.0.1...v0.0.2](https://github.com/nepalez/attributes_dsl/compare/v0.0.1...v0.0.2)
20
+
1
21
  ## version 0.0.1 2015-09-10
2
22
 
3
23
  This is the first published version
data/README.md CHANGED
@@ -60,13 +60,18 @@ user = User.new(sex: :women, age: "26", place: "Moscow")
60
60
  Additional Details
61
61
  ------------------
62
62
 
63
+ ### Attribute declaration
64
+
63
65
  The `attribute` class method takes the `name` and 2 options:
66
+
64
67
  - `:default` for the default value (otherwise `nil`);
65
68
  - `:required` to declare the attribute as required. It will be ignored if a default value is provided!
66
69
 
67
70
  It is also takes the block, used to coerce a value. The coercer is applied to the default value too.
68
71
 
69
- Also notice, that instance methods (like `#name`) are just aliases for the corresponding value of the `#attributes` hash. Instance variables aren't defined for them (to ensure syncronization between `#name` and `#attributes[:name]`):
72
+ ### Instance methods
73
+
74
+ Instance methods (like `#name`) are just aliases for the corresponding value of the `#attributes` hash. Instance variables aren't defined for them (to ensure syncronization between `#name` and `#attributes[:name]`):
70
75
 
71
76
  ```ruby
72
77
  user = User.new(name: "John")
@@ -77,6 +82,39 @@ user.name # => :John
77
82
  user.instance_variable_get :@name # => nil
78
83
  ```
79
84
 
85
+ ### Inheritance
86
+
87
+ Subclasses inherits attributes of the superclass:
88
+
89
+ ```ruby
90
+ class UserWithRole < User
91
+ attribute :role, default: :user
92
+ end
93
+
94
+ user = UserWithRole.new(name: "Sam")
95
+ user.attributes
96
+ # => { name: :Sam, sex: :male, age: 0, position: nil, role: :user }
97
+ ```
98
+
99
+ ### Undefining Attributes
100
+
101
+ This feature is not available (and it won't be).
102
+
103
+ The reason is that a subclass should satisfy a contract of its superclass, including the existence of attributes, declared by the superclass.
104
+ All you can do is reload attribute definition in a subclass:
105
+
106
+ ```ruby
107
+ class Person < User
108
+ attribute :name, &:to_s
109
+ end
110
+
111
+ user = Person.new(name: :Sam)
112
+ user.attributes
113
+ # => { name: "Sam", sex: :male, age: 0, position: nil }
114
+ ```
115
+
116
+ ### Freezing
117
+
80
118
  You're free to redefine attributes (class settings are used by the initializer only):
81
119
 
82
120
  ```ruby
@@ -122,20 +160,20 @@ The results are following:
122
160
 
123
161
  ```
124
162
  -------------------------------------------------
125
- anima 202.863k5.1%) i/s - 1.023M
126
- kwattr 171.461k5.3%) i/s - 855.680k
127
- fast_attributes 156.596k (± 2.5%) i/s - 785.792k
128
- attributes_dsl 58.966k4.6%) i/s - 296.514k
129
- active_attr 58.322k2.8%) i/s - 293.205k
130
- virtus 45.734k2.7%) i/s - 228.960k
163
+ anima 211.638k3.7%) i/s - 1.071M
164
+ kwattr 187.276k3.6%) i/s - 947.484k
165
+ fast_attributes 160.916k (± 2.4%) i/s - 816.726k
166
+ attributes_dsl 71.850k3.0%) i/s - 365.365k
167
+ active_attr 71.489k3.6%) i/s - 357.995k
168
+ virtus 45.554k7.1%) i/s - 229.338k
131
169
 
132
170
  Comparison:
133
- anima: 202862.9 i/s
134
- kwattr: 171460.9 i/s - 1.18x slower
135
- fast_attributes: 156596.3 i/s - 1.30x slower
136
- attributes_dsl: 58966.5 i/s - 3.44x slower
137
- active_attr: 58321.6 i/s - 3.48x slower
138
- virtus: 45734.3 i/s - 4.44x slower
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
139
177
  ```
140
178
 
141
179
  Results above are pretty reasonable.
@@ -144,7 +182,7 @@ The gem is faster than `virtus` that has many additional features.
144
182
 
145
183
  It is as fast as `active_attrs` (but has more customizable coercers).
146
184
 
147
- It is 2 times slower than `fast_attributes` that has no coercer and default values. And it is 3-3.5 times slower than `anima` and `kwattr` that provides only the base settings.
185
+ It is 2 times slower than `fast_attributes` that has no coercer and default values. And it is 3 times slower than `anima` and `kwattr` that provides only the base settings.
148
186
 
149
187
  Installation
150
188
  ------------
data/benchmark/run.rb CHANGED
@@ -94,28 +94,28 @@ end
94
94
  Benchmark.ips do |x|
95
95
  x.config time: 5, warmup: 5
96
96
 
97
- x.report("attributes_dsl") do
98
- AttributesDSLExample.call
99
- end
100
-
101
- x.report("virtus") do
102
- VirtusExample.call
97
+ x.report("anima") do
98
+ AnimaExample.call
103
99
  end
104
100
 
105
- x.report("active_attr") do
106
- AttributesDSLExample.call
101
+ x.report("kwattr") do
102
+ KwattrExample.call
107
103
  end
108
104
 
109
105
  x.report("fast_attributes") do
110
106
  FastAttributesExample.call
111
107
  end
112
108
 
113
- x.report("anima") do
114
- AnimaExample.call
109
+ x.report("attributes_dsl") do
110
+ AttributesDSLExample.call
115
111
  end
116
112
 
117
- x.report("kwattr") do
118
- KwattrExample.call
113
+ x.report("active_attr") do
114
+ AttributesDSLExample.call
115
+ end
116
+
117
+ x.report("virtus") do
118
+ VirtusExample.call
119
119
  end
120
120
 
121
121
  x.compare!
@@ -56,24 +56,17 @@ module AttributesDSL
56
56
  define_method(s_name) { attributes.fetch(s_name) }
57
57
  end
58
58
 
59
- # Object contstructor that filters hash of attributes
60
- #
61
- # @param [Hash] hash The hash of attributes to be assinged
62
- #
63
- # @return [Object]
64
- #
65
- # @raise [ArgumentError] in case a required attribute is missed
66
- #
67
- def new(hash)
68
- super attributes.extract(hash)
69
- end
70
-
71
59
  # @private
72
60
  def self.extended(klass)
73
61
  # use __send__ for compatibility to 1.9.3 (where `.include` was private)
74
62
  klass.__send__(:include, InstanceMethods)
75
63
  end
76
64
 
65
+ # @private
66
+ def inherited(klass)
67
+ klass.instance_variable_set(:@attributes, attributes)
68
+ end
69
+
77
70
  # Defines instance methods for the hash of attributes and its initializer
78
71
  module InstanceMethods
79
72
 
@@ -85,12 +78,12 @@ module AttributesDSL
85
78
 
86
79
  # Initializes the object and sets the hash of its [#attributes]
87
80
  #
88
- # Uses attributes prepared by [.new]
89
- #
90
81
  # @param [Hash] attributes
91
82
  #
92
- def initialize(attributes)
93
- @attributes = attributes
83
+ # @raise [ArgumentError] in case a required attribute is missed
84
+ #
85
+ def initialize(attributes = {})
86
+ @attributes = self.class.attributes.extract(attributes)
94
87
  end
95
88
 
96
89
  end # module InstanceMethods
@@ -21,10 +21,10 @@ module AttributesDSL
21
21
 
22
22
  # Initializes an immutable collection with an initial set of attributes
23
23
  #
24
- # @param [Set] attributes
24
+ # @param [Hash] attributes
25
25
  #
26
- def initialize(attributes = nil)
27
- @attributes = Set.new attributes
26
+ def initialize(attributes = {})
27
+ @attributes = attributes
28
28
  IceNine.deep_freeze(self)
29
29
  end
30
30
 
@@ -36,7 +36,9 @@ module AttributesDSL
36
36
  # @return [AttributesDSL::Attributes]
37
37
  #
38
38
  def register(name, options = {}, &coercer)
39
- self.class.new(attributes.to_a << Attribute.new(name, options, &coercer))
39
+ self.class.new(
40
+ attributes.merge(name => Attribute.new(name, options, &coercer))
41
+ )
40
42
  end
41
43
 
42
44
  # Extracts instance attributes from the input hash
@@ -59,13 +61,13 @@ module AttributesDSL
59
61
 
60
62
  def validate(input)
61
63
  undefined = required - input.keys
62
- return attributes if undefined.empty?
64
+ return attributes.values if undefined.empty?
63
65
 
64
66
  fail ArgumentError.new "Undefined attributes: #{undefined.join(", ")}"
65
67
  end
66
68
 
67
69
  def required
68
- attributes.select(&:required).map(&:name)
70
+ attributes.values.select(&:required).map(&:name)
69
71
  end
70
72
 
71
73
  end # class Attributes
@@ -4,6 +4,6 @@ module AttributesDSL
4
4
 
5
5
  # The semantic version of the module.
6
6
  # @see http://semver.org/ Semantic versioning 2.0
7
- VERSION = "0.0.1".freeze
7
+ VERSION = "0.0.2".freeze
8
8
 
9
9
  end # module AttributesDSL
@@ -3,49 +3,74 @@
3
3
  describe AttributesDSL do
4
4
 
5
5
  let(:coercer) { -> value { value.to_s } }
6
- let(:klass) do
7
- Class.new do
8
- extend AttributesDSL
9
-
10
- def initialize(attributes)
11
- super
12
- IceNine.deep_freeze(self)
13
- end
14
- end
15
- end
6
+ let(:klass) { Class.new { extend AttributesDSL } }
16
7
 
17
8
  before do
18
- klass.attribute(:foo, default: :FOO, &:to_s)
19
- klass.attribute("bar", required: true)
9
+ klass.attribute(:foo, &:to_s)
10
+ klass.attribute("bar")
20
11
  klass.attribute(:baz, &:to_i)
21
12
  end
22
13
 
23
14
  subject { klass.new(arguments) }
24
15
 
25
- context "when all required attributes are set" do
26
- let(:arguments) { { bar: :BAR, baz: "42" } }
16
+ describe "instance" do
17
+
18
+ context "without arguments" do
19
+ subject { klass.new }
27
20
 
28
- it "sets the attributes" do
29
- expect(subject.attributes).to eql(foo: "FOO", bar: :BAR, baz: 42)
21
+ it "initializes default attributes" do
22
+ expect(subject.attributes).to eql(foo: "", bar: nil, baz: 0)
23
+ end
30
24
  end
31
25
 
32
- it "defines methods for every attribute" do
33
- expect(subject.foo).to eql "FOO"
34
- expect(subject.bar).to eql :BAR
35
- expect(subject.baz).to eql 42
26
+ context "when all required attributes are set" do
27
+ let(:arguments) { { bar: :BAR, baz: "42" } }
28
+ let(:klass) do
29
+ Class.new do
30
+ extend AttributesDSL
31
+
32
+ def initialize(attributes = {})
33
+ super
34
+ IceNine.deep_freeze(self)
35
+ end
36
+ end
37
+ end
38
+
39
+ it "initializes attributes" do
40
+ expect(subject.attributes).to eql(foo: "", bar: :BAR, baz: 42)
41
+ end
42
+
43
+ it "defines methods for every attribute" do
44
+ expect(subject.foo).to eql ""
45
+ expect(subject.bar).to eql :BAR
46
+ expect(subject.baz).to eql 42
47
+ end
48
+
49
+ it "doesn't freeze argument" do
50
+ expect { subject }.not_to change { arguments.frozen? }
51
+ end
36
52
  end
37
53
 
38
- it "doesn't freeze argument" do
39
- expect { subject }.not_to change { arguments.frozen? }
54
+ context "when a required attribute is missed" do
55
+ let(:arguments) { { foo: :FOO, baz: "42" } }
56
+
57
+ before { klass.attribute("bar", required: true) }
58
+
59
+ it "fails" do
60
+ expect { subject }.to raise_error ArgumentError
61
+ end
40
62
  end
41
- end # context
42
63
 
43
- context "when a required attribute is missed" do
44
- let(:arguments) { { foo: :FOO, baz: "42" } }
64
+ end # describe instance
65
+
66
+ describe "subclass" do
45
67
 
46
- it "fails" do
47
- expect { subject }.to raise_error ArgumentError
68
+ subject { Class.new(klass) }
69
+
70
+ it "inherits parent attributes" do
71
+ expect(subject.new.attributes).to eql(klass.new.attributes)
48
72
  end
49
- end # context
73
+
74
+ end # describe subclass
50
75
 
51
76
  end # describe AttributesDSL
@@ -14,19 +14,25 @@ describe AttributesDSL::Attributes do
14
14
  describe "#attributes" do
15
15
  subject { attributes.attributes }
16
16
 
17
- it { is_expected.to be_kind_of Set }
17
+ it { is_expected.to be_kind_of Hash }
18
18
  it { is_expected.to be_empty }
19
19
  end # describe #attributes
20
20
 
21
21
  describe "#register" do
22
- subject { attributes.register(:foo, default: :FOO, &coercer) }
22
+ subject do
23
+ attributes.register(:bar).register(:foo, default: :FOO, &coercer)
24
+ end
23
25
 
24
26
  it "returns new collection" do
25
27
  expect(subject).to be_kind_of described_class
26
28
  end
27
29
 
28
- it "registers the attribute" do
29
- attribute = subject.attributes.first
30
+ it "adds attributes" do
31
+ expect(subject.attributes.keys).to contain_exactly(:bar, :foo)
32
+ end
33
+
34
+ it "uses arguments" do
35
+ attribute = subject.attributes[:foo]
30
36
 
31
37
  expect(attribute).to be_kind_of AttributesDSL::Attribute
32
38
  expect(attribute.name).to eql :foo
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attributes_dsl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kozin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-10 00:00:00.000000000 Z
11
+ date: 2015-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ice_nine