attributes_dsl 0.0.1 → 0.0.2

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 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