attributed_object 0.2.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.
@@ -0,0 +1,129 @@
1
+ require 'attributed_object'
2
+
3
+ describe AttributedObject::Strict do
4
+ class SimpleFoo
5
+ include AttributedObject::Strict
6
+ attribute :bar
7
+ end
8
+
9
+ class TypedFoo
10
+ include AttributedObject::Strict
11
+ attribute :a_string, :string, default: 'its a string'
12
+ attribute :a_boolean, :boolean, default: false
13
+ attribute :a_integer, :integer, default: 77
14
+ attribute :a_float, :float, default: 98.12
15
+ attribute :a_numeric, :numeric, default: 12.12
16
+ attribute :a_symbol, :symbol, default: :some_default_symbol
17
+ attribute :a_string_by_class, String, default: 'some default string'
18
+ attribute :another_class, SimpleFoo, default: nil
19
+ attribute :a_array, :array, default: nil
20
+ attribute :a_array_of, ArrayOf(:integer), default: nil
21
+ attribute :a_hash, :hash, default: nil
22
+ attribute :a_hash_of, HashOf(:string, :integer), default: nil
23
+ end
24
+
25
+ class TypedFooWithOption
26
+ include AttributedObject::Strict
27
+ attributed_object type_check: :strict
28
+ attribute :a_string, :string, default: 'its a string'
29
+ end
30
+
31
+ it 'can be set with option' do
32
+ expect { TypedFooWithOption.new(a_string: :its_a_symbol) }.to raise_error(AttributedObject::TypeError)
33
+ end
34
+
35
+ it 'can handle primitive ruby types' do
36
+ f = TypedFoo.new(
37
+ a_boolean: true,
38
+ a_integer: 12,
39
+ a_float: 42.7,
40
+ a_numeric: 35.9,
41
+ a_symbol: :my_symbol,
42
+ a_string_by_class: 'my class string check',
43
+ another_class: SimpleFoo.new(bar: 'hi'),
44
+ a_array: ['1'],
45
+ a_array_of: [1],
46
+ a_hash: {foo: 'bar'},
47
+ a_hash_of: { 'hello' => 1 },
48
+ )
49
+
50
+ expect(f.a_string).to eq('its a string')
51
+ expect(f.a_boolean).to eq(true)
52
+ expect(f.a_integer).to eq(12)
53
+ expect(f.a_float).to eq(42.7)
54
+ expect(f.a_numeric).to eq(35.9)
55
+ expect(f.a_symbol).to eq(:my_symbol)
56
+ expect(f.a_string_by_class).to eq('my class string check')
57
+ expect(f.another_class).to eq(SimpleFoo.new(bar: 'hi'))
58
+ expect(f.a_array).to eq(['1'])
59
+ expect(f.a_array_of).to eq([1])
60
+ expect(f.a_hash).to eq({foo: 'bar'})
61
+ expect(f.a_hash_of).to eq({'hello' => 1})
62
+ end
63
+
64
+ it 'raises error on non-string' do
65
+ expect { TypedFoo.new(a_string: :its_a_symbol) }.to raise_error(AttributedObject::TypeError)
66
+ end
67
+
68
+ it 'raises error on non-bool' do
69
+ expect { TypedFoo.new(a_boolean: 42) }.to raise_error(AttributedObject::TypeError)
70
+ end
71
+
72
+ it 'raises error on non-integer' do
73
+ expect { TypedFoo.new(a_integer: '42') }.to raise_error(AttributedObject::TypeError)
74
+ end
75
+
76
+ it 'raises error on non-float' do
77
+ expect { TypedFoo.new(a_float: 42) }.to raise_error(AttributedObject::TypeError)
78
+ end
79
+
80
+ it 'raises error on non-symbol' do
81
+ expect { TypedFoo.new(a_symbol: 'its a string') }.to raise_error(AttributedObject::TypeError)
82
+ end
83
+
84
+ it 'raises error on non-numeric' do
85
+ expect { TypedFoo.new(a_numeric: 'its a string') }.to raise_error(AttributedObject::TypeError)
86
+ end
87
+
88
+ it 'raises error on non-strings when string is defined by class' do
89
+ expect { TypedFoo.new(a_string_by_class: 42) }.to raise_error(AttributedObject::TypeError)
90
+ end
91
+
92
+ it 'raises error on non SimpleFoos' do
93
+ expect { TypedFoo.new(another_class: 42) }.to raise_error(AttributedObject::TypeError)
94
+ end
95
+
96
+ it 'raises error on non-array' do
97
+ expect { TypedFoo.new(a_array: 'its a string') }.to raise_error(AttributedObject::TypeError)
98
+ end
99
+
100
+ it 'raises error on array with wrong contents' do
101
+ expect { TypedFoo.new(a_array_of: 'its a string') }.to raise_error(AttributedObject::TypeError)
102
+ expect { TypedFoo.new(a_array_of: ['its a string']) }.to raise_error(AttributedObject::TypeError)
103
+ expect { TypedFoo.new(a_array_of: [1]) }.not_to raise_error
104
+ end
105
+
106
+ it 'raises error on non-hash' do
107
+ expect { TypedFoo.new(a_hash: 'its a string') }.to raise_error(AttributedObject::TypeError)
108
+ end
109
+
110
+ it 'raises error on hash with wrong contents' do
111
+ expect { TypedFoo.new(a_hash_of: 'its a string') }.to raise_error(AttributedObject::TypeError)
112
+ expect { TypedFoo.new(a_hash_of: { 'foo' => '1' }) }.to raise_error(AttributedObject::TypeError)
113
+ expect { TypedFoo.new(a_hash_of: { 1 => 1 }) }.to raise_error(AttributedObject::TypeError)
114
+ expect { TypedFoo.new(a_hash_of: { 'foo' => 1 }) }.not_to raise_error
115
+ end
116
+
117
+ it 'raises no errors for nil values' do
118
+ expect { TypedFoo.new(a_string: nil) }.not_to raise_error
119
+ end
120
+
121
+ it 'raises on unknown type' do
122
+ expect do
123
+ class Miau
124
+ include AttributedObject::Strict
125
+ attribute :something, :does_not_exist
126
+ end
127
+ end.to raise_error(AttributedObject::ConfigurationError)
128
+ end
129
+ end
@@ -0,0 +1,228 @@
1
+ require 'attributed_object'
2
+
3
+ describe AttributedObject do
4
+ class SimpleFoo
5
+ include AttributedObject::Strict
6
+ attribute :bar
7
+ end
8
+
9
+ class DisallowingNil
10
+ include AttributedObject::Strict
11
+ attribute :bar, disallow: nil
12
+ end
13
+
14
+ class DefaultFoo
15
+ include AttributedObject::Strict
16
+ attribute :bar, default: "my default"
17
+ attribute :dynamic, default: -> { count }
18
+
19
+ def self.reset
20
+ @count = 0
21
+ end
22
+
23
+ def self.count
24
+ @count ||= 0
25
+ @count += 1
26
+ end
27
+ end
28
+
29
+ class ChildFoo < DefaultFoo
30
+ attribute :lollipop, default: "lecker"
31
+ end
32
+
33
+ it 'requires attributes by default' do
34
+ expect { SimpleFoo.new }.to raise_error(AttributedObject::MissingAttributeError)
35
+ expect(SimpleFoo.new(bar: 1).bar).to eq(1)
36
+ end
37
+
38
+ describe 'nil control' do
39
+ it 'allows explicit nil values' do
40
+ expect(SimpleFoo.new(bar: nil).bar).to eq(nil)
41
+ end
42
+
43
+ it 'can be controlled to not allow explicit nil' do
44
+ expect { DisallowingNil.new(bar: nil).bar }.to raise_error(AttributedObject::DisallowedValueError)
45
+ end
46
+ end
47
+
48
+ describe 'default value' do
49
+ before { DefaultFoo.reset }
50
+
51
+ it 'can specify a default value' do
52
+ expect(DefaultFoo.new.bar).to eq("my default")
53
+ expect(DefaultFoo.new(bar: 'other').bar).to eq("other")
54
+ end
55
+
56
+ it 'can specify a lambda as default value' do
57
+ expect(DefaultFoo.new.dynamic).to eq(1)
58
+ expect(DefaultFoo.new.dynamic).to eq(2)
59
+ end
60
+ end
61
+
62
+ describe 'extra_options' do
63
+ context 'inheritance' do
64
+ it 'is passed to the children' do
65
+ class Papa
66
+ include AttributedObject::Strict
67
+ attributed_object default_to: nil
68
+
69
+ attribute :foo
70
+ attribute :bar, default: 'hi'
71
+ end
72
+
73
+ class Sohn < Papa
74
+ attribute :something_else
75
+ end
76
+
77
+ expect(Sohn.new.something_else).to eq(nil)
78
+ expect(Sohn.new.bar).to eq('hi')
79
+ end
80
+ end
81
+
82
+ describe 'default_to' do
83
+ it 'allows changing default for all fields' do
84
+ class Defaulting
85
+ include AttributedObject::Strict
86
+ attributed_object default_to: nil
87
+
88
+ attribute :foo
89
+ attribute :bar, default: 'hi'
90
+ end
91
+
92
+ expect(Defaulting.new.foo).to eq(nil)
93
+ expect(Defaulting.new.bar).to eq('hi')
94
+ end
95
+
96
+ it 'allows type defaulting' do
97
+ class TypeDefaulting
98
+ include AttributedObject::Strict
99
+ attributed_object default_to: AttributedObject::TypeDefaults.new
100
+
101
+ attribute :a_string, :string
102
+ attribute :a_boolean, :boolean
103
+ attribute :a_integer, :integer
104
+ attribute :a_float, :float
105
+ attribute :a_numeric, :numeric
106
+ attribute :a_symbol, :symbol
107
+ attribute :a_string_by_class, String
108
+ attribute :another_class, SimpleFoo
109
+ attribute :a_array, :array
110
+ attribute :a_hash, :hash
111
+ attribute :something_with_default, :string, default: 'foobar'
112
+ attribute :something_without_type
113
+ end
114
+
115
+ expect(TypeDefaulting.new.a_string).to eq('')
116
+ expect(TypeDefaulting.new.a_boolean).to eq(false)
117
+ expect(TypeDefaulting.new.a_integer).to eq(0)
118
+ expect(TypeDefaulting.new.a_float).to eq(0.0)
119
+ expect(TypeDefaulting.new.a_numeric).to eq(0)
120
+ expect(TypeDefaulting.new.a_symbol).to eq(nil)
121
+ expect(TypeDefaulting.new.a_string_by_class).to eq(nil)
122
+ expect(TypeDefaulting.new.another_class).to eq(nil)
123
+ expect(TypeDefaulting.new.a_array).to eq([])
124
+ expect(TypeDefaulting.new.a_hash).to eq({})
125
+ expect(TypeDefaulting.new.something_with_default).to eq('foobar')
126
+ expect(TypeDefaulting.new.something_without_type).to eq(nil)
127
+ end
128
+
129
+ it 'is possible to overwrite and add type defaults' do
130
+ class TypeDefaultingOverwrites
131
+ include AttributedObject::Strict
132
+ attributed_object default_to: AttributedObject::TypeDefaults.new(
133
+ :string => 'my_default_string',
134
+ SimpleFoo => SimpleFoo.new(bar: 'kekse')
135
+ )
136
+
137
+ attribute :a_string, :string
138
+ attribute :a_integer, :integer
139
+ attribute :foo, SimpleFoo
140
+ end
141
+
142
+ expect(TypeDefaultingOverwrites.new.a_string).to eq('my_default_string')
143
+ expect(TypeDefaulting.new.a_integer).to eq(0)
144
+ expect(TypeDefaultingOverwrites.new.foo).to eq(SimpleFoo.new(bar: 'kekse'))
145
+ end
146
+ end
147
+
148
+ describe 'ignore_extra_keys' do
149
+ it 'allows extra_keys' do
150
+ class FooWithExtra
151
+ include AttributedObject::Strict
152
+ attributed_object ignore_extra_keys: true
153
+ attribute :bar, :integer
154
+ end
155
+
156
+ expect { FooWithExtra.new(bar: 12, not_defined: 'asd') }.not_to raise_error
157
+ expect(FooWithExtra.new(bar: 12, not_defined: 'asd').attributes).to eq(bar: 12)
158
+ end
159
+ end
160
+ end
161
+
162
+ it 'throws an error for unknown attributes' do
163
+ expect { SimpleFoo.new(whatever: 'xxx') }.to raise_error(AttributedObject::UnknownAttributeError)
164
+ end
165
+
166
+ it 'inherits the attributes from its superclass' do
167
+ f = ChildFoo.new
168
+ expect(f.bar).to eq("my default")
169
+ expect(f.lollipop).to eq("lecker")
170
+ end
171
+
172
+ it 'does not modify the args' do
173
+ args = {bar: "asd"}
174
+ f = SimpleFoo.new(args)
175
+ f.bar = 'different'
176
+ expect(f.bar).to eq('different')
177
+ expect(args[:bar]).to eq('asd')
178
+ end
179
+
180
+ describe '#==' do
181
+ it 'is equals for same attributes' do
182
+ expect(SimpleFoo.new(bar: 12)).to eq(SimpleFoo.new(bar: 12))
183
+ end
184
+
185
+ it 'is not equal for different attributes' do
186
+ expect(SimpleFoo.new(bar: 77)).to_not eq(SimpleFoo.new(bar: 12))
187
+ end
188
+ end
189
+
190
+ describe 'attribute storage' do
191
+ class InnerStructureFoo
192
+ include AttributedObject::Coerce
193
+ attribute :bar, :string, default: 'default'
194
+ attribute :foo, :string, default: 'default'
195
+ attribute :number, :integer, default: 0
196
+
197
+ def foo=(f)
198
+ @foo = "prefix-#{f}-suffix"
199
+ end
200
+
201
+ def number=(n)
202
+ @number = n+1
203
+ end
204
+ end
205
+
206
+ describe '#attributes' do
207
+ it 'returns the attributes as hash' do
208
+ expect(InnerStructureFoo.new(bar: 'hi').attributes).to eq(
209
+ bar: 'hi',
210
+ foo: 'prefix-default-suffix',
211
+ number: 1
212
+ )
213
+ end
214
+ end
215
+
216
+ it 'stores the data in instance vars' do
217
+ expect(InnerStructureFoo.new(bar: 'hi').instance_variable_get('@bar')).to eq('hi')
218
+ end
219
+
220
+ it 'uses setters' do
221
+ expect(InnerStructureFoo.new(foo: 'middel').foo).to eq('prefix-middel-suffix')
222
+ end
223
+
224
+ it 'uses setters after coercion' do
225
+ expect(InnerStructureFoo.new(number: '42').number).to eq(43)
226
+ end
227
+ end
228
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attributed_object
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Jaap Groeneveld
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Simple and fast module for named arguments in model initializers
56
+ email:
57
+ - jgroeneveld@me.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - attributed_object.gemspec
68
+ - benchmark_attributed_object.rb
69
+ - lib/attributed_object.rb
70
+ - lib/attributed_object/base.rb
71
+ - lib/attributed_object/coerce.rb
72
+ - lib/attributed_object/strict.rb
73
+ - lib/attributed_object/type.rb
74
+ - lib/attributed_object/types/array_of.rb
75
+ - lib/attributed_object/types/hash_of.rb
76
+ - lib/attributed_object/version.rb
77
+ - lib/attributed_object_helpers/hash_util.rb
78
+ - lib/attributed_object_helpers/type_check.rb
79
+ - lib/attributed_object_helpers/type_coerce.rb
80
+ - spec/attributed_object/coerce_spec.rb
81
+ - spec/attributed_object/strict_spec.rb
82
+ - spec/attributed_object_spec.rb
83
+ homepage: ''
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.5.1
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Simple and fast module for named arguments in model initializers
107
+ test_files:
108
+ - spec/attributed_object/coerce_spec.rb
109
+ - spec/attributed_object/strict_spec.rb
110
+ - spec/attributed_object_spec.rb