son_jay 0.4.1 → 0.5.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 +8 -8
- data/CHANGELOG.md +13 -1
- data/lib/son_jay/acts_as_model.rb +6 -1
- data/lib/son_jay/model_array.rb +43 -2
- data/lib/son_jay/object_model/{properties → content}/abstract.rb +44 -8
- data/lib/son_jay/object_model/content/content_with_extra.rb +63 -0
- data/lib/son_jay/object_model/{properties/properties_without_extra.rb → content/content_without_extra.rb} +12 -2
- data/lib/son_jay/object_model/content.rb +20 -0
- data/lib/son_jay/object_model/content_data.rb +74 -0
- data/lib/son_jay/object_model/property_definitions.rb +12 -0
- data/lib/son_jay/object_model.rb +33 -13
- data/lib/son_jay/value_array.rb +1 -1
- data/lib/son_jay/version.rb +1 -1
- data/spec/acts_as_model_spec.rb +4 -4
- data/spec/model_array_spec.rb +86 -25
- data/spec/object_model/content/content_with_extra_spec.rb +201 -0
- data/spec/object_model/content/content_without_extra_spec.rb +180 -0
- data/spec/object_model/content_data_spec.rb +81 -0
- data/spec/object_model_spec.rb +106 -92
- data/spec/son_jay_spec.rb +1 -1
- data/spec/value_array_spec.rb +2 -2
- metadata +13 -9
- data/lib/son_jay/object_model/extra_data.rb +0 -36
- data/lib/son_jay/object_model/properties/properties_with_extra.rb +0 -27
- data/lib/son_jay/object_model/properties.rb +0 -20
- data/spec/object_model/extra_data_spec.rb +0 -37
data/spec/object_model_spec.rb
CHANGED
@@ -62,97 +62,21 @@ describe SonJay::ObjectModel do
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
it "has number of entries equal to number of defined properties" do
|
69
|
-
expect( sonj_content.length ).to eq( 4 )
|
70
|
-
end
|
71
|
-
|
72
|
-
it "has name-indexed settable/gettable value properties by string or symbol" do
|
73
|
-
sonj_content[ :aaa ] = 1
|
74
|
-
sonj_content[ 'bbb' ] = 'XYZ'
|
75
|
-
|
76
|
-
expect( sonj_content[ 'aaa' ] ).to eq( 1 )
|
77
|
-
expect( sonj_content[ :bbb ] ).to eq( 'XYZ' )
|
78
|
-
end
|
79
|
-
|
80
|
-
it "has nil defaults for value properties" do
|
81
|
-
expect( sonj_content[ 'aaa' ] ).to be_nil
|
82
|
-
expect( sonj_content[ :bbb ] ).to be_nil
|
83
|
-
end
|
84
|
-
|
85
|
-
it "has name-indexed gettable values for defined modeled-object properties by string or symbol" do
|
86
|
-
expect( sonj_content['detail_xy'] ).
|
87
|
-
to be_kind_of( subject_module::DetailXY )
|
88
|
-
expect( sonj_content[:detail_z] ).
|
89
|
-
to be_kind_of( subject_module::DetailZ )
|
90
|
-
end
|
91
|
-
|
92
|
-
it "rejects assignment of an undefined property" do
|
93
|
-
expect{ sonj_content['qq'] = 0 }.to raise_exception(
|
94
|
-
SonJay::PropertyNameError
|
95
|
-
)
|
96
|
-
end
|
97
|
-
|
98
|
-
it "returns nil for name-indexed access to a non-existent property" do
|
99
|
-
expect( sonj_content[ 'qq' ] ).to be_nil
|
100
|
-
expect( sonj_content[ :rr ] ).to be_nil
|
101
|
-
end
|
102
|
-
|
103
|
-
it "has fetchable value properties by name string or symbol" do
|
104
|
-
sonj_content[ :aaa ] = 1
|
105
|
-
sonj_content[ 'bbb' ] = 'XYZ'
|
106
|
-
|
107
|
-
expect( sonj_content.fetch( 'aaa' ) ).to eq( 1 )
|
108
|
-
expect( sonj_content.fetch( :bbb ) ).to eq( 'XYZ' )
|
109
|
-
end
|
110
|
-
|
111
|
-
it "has nil defaults for value property fetches" do
|
112
|
-
expect( sonj_content.fetch( 'aaa' ) ).to be_nil
|
113
|
-
expect( sonj_content.fetch( :bbb ) ).to be_nil
|
114
|
-
end
|
115
|
-
|
116
|
-
it "has name-indexed fetchable values for defined modeled-object properties by string or symbol" do
|
117
|
-
expect( sonj_content.fetch('detail_xy') ).
|
118
|
-
to be_kind_of( subject_module::DetailXY )
|
119
|
-
expect( sonj_content.fetch(:detail_z) ).
|
120
|
-
to be_kind_of( subject_module::DetailZ )
|
121
|
-
end
|
122
|
-
|
123
|
-
it "rejects fetch of an undefined property by name" do
|
124
|
-
expect{ sonj_content.fetch('qq') }.to raise_exception(
|
125
|
-
SonJay::PropertyNameError
|
126
|
-
)
|
127
|
-
expect{ sonj_content.fetch(:rr) }.to raise_exception(
|
128
|
-
SonJay::PropertyNameError
|
129
|
-
)
|
130
|
-
end
|
131
|
-
|
132
|
-
context "without extras allowed" do
|
133
|
-
it "rejects access to extra properties object" do
|
134
|
-
expect{ sonj_content.extra }.
|
135
|
-
to raise_exception( SonJay::DisabledMethodError )
|
136
|
-
end
|
137
|
-
end
|
65
|
+
it "has direct property accessor methods for each property" do
|
66
|
+
model_instance.aaa, model_instance.bbb = 11, 22
|
67
|
+
content = model_instance.model_content
|
138
68
|
|
139
|
-
|
140
|
-
|
141
|
-
model_class.class_eval do
|
142
|
-
allow_extras
|
143
|
-
end
|
144
|
-
end
|
69
|
+
expect( [content['aaa'], content['bbb']] ).
|
70
|
+
to eq( [11, 22] )
|
145
71
|
|
146
|
-
|
147
|
-
|
148
|
-
end
|
149
|
-
end
|
72
|
+
expect( [model_instance.aaa, model_instance.bbb] ).
|
73
|
+
to eq( [11, 22] )
|
150
74
|
|
151
|
-
|
75
|
+
expect( model_instance.detail_xy ).
|
76
|
+
to equal( content['detail_xy'] )
|
77
|
+
expect( model_instance.detail_z ).
|
78
|
+
to equal( content['detail_z'] )
|
152
79
|
|
153
|
-
it "has direct property accessor methods for each property" do
|
154
|
-
model_instance.aaa, model_instance.bbb = 11, 22
|
155
|
-
expect( [model_instance.aaa, model_instance.bbb] ).to eq( [11, 22] )
|
156
80
|
expect( model_instance.detail_xy ).
|
157
81
|
to be_kind_of( subject_module::DetailXY )
|
158
82
|
expect( model_instance.detail_z ).
|
@@ -242,7 +166,7 @@ describe SonJay::ObjectModel do
|
|
242
166
|
end
|
243
167
|
|
244
168
|
it "has number of entries equal to total number of defined properties" do
|
245
|
-
expect( model_instance.
|
169
|
+
expect( model_instance.model_content.length ).to eq( 6 )
|
246
170
|
end
|
247
171
|
|
248
172
|
end
|
@@ -257,7 +181,7 @@ describe SonJay::ObjectModel do
|
|
257
181
|
)
|
258
182
|
end
|
259
183
|
|
260
|
-
it "parses from JSON with extra properties to an instance with defined properties filled in" do
|
184
|
+
it "parses from JSON with extra properties to an instance with only defined properties filled in" do
|
261
185
|
json = <<-JSON
|
262
186
|
{
|
263
187
|
"aaa": 123 ,
|
@@ -277,6 +201,21 @@ describe SonJay::ObjectModel do
|
|
277
201
|
expect( instance.detail_xy.yyy ).to eq('y')
|
278
202
|
expect( instance.detail_z.zzz ).to eq('z')
|
279
203
|
end
|
204
|
+
|
205
|
+
it "can be explicitly, shallowly converted to an isolated hash" do
|
206
|
+
model_instance.aaa = 111
|
207
|
+
model_instance.bbb = 222
|
208
|
+
|
209
|
+
actual_hash = model_instance.to_h
|
210
|
+
|
211
|
+
model_instance.bbb = 999
|
212
|
+
|
213
|
+
expect( actual_hash.length ).to eq( 4 )
|
214
|
+
expect( actual_hash[ 'aaa' ] ).to eq( 111 )
|
215
|
+
expect( actual_hash[ 'bbb' ] ).to eq( 222 )
|
216
|
+
expect( actual_hash[ 'detail_xy' ] ).to equal( model_instance.detail_xy )
|
217
|
+
expect( actual_hash[ 'detail_z' ] ).to equal( model_instance.detail_z )
|
218
|
+
end
|
280
219
|
end
|
281
220
|
|
282
221
|
context "with extras allowed" do
|
@@ -298,15 +237,15 @@ describe SonJay::ObjectModel do
|
|
298
237
|
expect( model_instance.aaa ).to eq( 111 )
|
299
238
|
expect( model_instance.bbb ).to eq( 222 )
|
300
239
|
|
301
|
-
expect( model_instance.
|
240
|
+
expect( model_instance.model_content.extra.to_h ).
|
302
241
|
to eq( 'qqq' => 333, 'rrr' => 444 )
|
303
242
|
end
|
304
243
|
|
305
244
|
it "allows name-index reading of both defined and arbitrary, extra properties" do
|
306
245
|
model_instance.aaa = 111
|
307
246
|
model_instance.bbb = 222
|
308
|
-
model_instance.
|
309
|
-
model_instance.
|
247
|
+
model_instance.model_content.extra[ 'qqq' ] = 333
|
248
|
+
model_instance.model_content.extra[ :rrr ] = 444
|
310
249
|
expect( model_instance[ :aaa ] ).to eq( 111 )
|
311
250
|
expect( model_instance[ 'bbb' ] ).to eq( 222 )
|
312
251
|
expect( model_instance[ :qqq ] ).to eq( 333 )
|
@@ -355,6 +294,80 @@ describe SonJay::ObjectModel do
|
|
355
294
|
end
|
356
295
|
end
|
357
296
|
|
297
|
+
context "with subclasses" do
|
298
|
+
before do
|
299
|
+
module subject_module::M
|
300
|
+
class SubA < RootModel
|
301
|
+
properties do
|
302
|
+
property :ccc
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class SubB < RootModel
|
307
|
+
allow_extras
|
308
|
+
|
309
|
+
properties do
|
310
|
+
property :detail_z2, model: DetailZ
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
let( :sub_a_class ) { subject_module::SubA }
|
317
|
+
let( :sub_b_class ) { subject_module::SubB }
|
318
|
+
let!( :sub_a_instance ) { sub_a_class.new }
|
319
|
+
let!( :sub_b_instance ) { sub_b_class.new }
|
320
|
+
|
321
|
+
it "allows a subclass to inherit property definitions from its parent" do
|
322
|
+
missing_property_names_a =
|
323
|
+
model_class.property_definitions.map( &:name ) -
|
324
|
+
sub_a_class.property_definitions.map( &:name )
|
325
|
+
expect( missing_property_names_a ).to eq( [] )
|
326
|
+
|
327
|
+
missing_property_names_b =
|
328
|
+
model_class.property_definitions.map( &:name ) -
|
329
|
+
sub_b_class.property_definitions.map( &:name )
|
330
|
+
expect( missing_property_names_b ).to eq( [] )
|
331
|
+
end
|
332
|
+
|
333
|
+
it "does not leak subclass property definitions to its parent" do
|
334
|
+
property_names = model_class.property_definitions.map(&:name)
|
335
|
+
expect( property_names ).not_to include( 'ccc' )
|
336
|
+
expect( property_names ).not_to include( 'detail_z2' )
|
337
|
+
end
|
338
|
+
|
339
|
+
it "allows a subclass to inherit property access behavior from its parent" do
|
340
|
+
sub_a_instance.aaa = 123
|
341
|
+
expect( sub_a_instance.aaa ).to eq( 123 )
|
342
|
+
end
|
343
|
+
|
344
|
+
it "allows adding value properties to the subclass" do
|
345
|
+
expect( sub_a_instance ).to respond_to( :ccc )
|
346
|
+
end
|
347
|
+
|
348
|
+
it "does not leak subclass value property definitions to its parent class" do
|
349
|
+
expect( model_instance ).not_to respond_to( :ccc )
|
350
|
+
end
|
351
|
+
|
352
|
+
it "allows adding model properties to the subclass" do
|
353
|
+
expect( sub_b_instance.detail_z2 ).to respond_to( :zzz )
|
354
|
+
end
|
355
|
+
|
356
|
+
it "does not leak subclass model property definitions to its parent class" do
|
357
|
+
expect( model_instance ).not_to respond_to( :detail_z2 )
|
358
|
+
end
|
359
|
+
|
360
|
+
it "supports subclass allowing extras when the parent class does not" do
|
361
|
+
sub_b_instance['xyz'] = 123
|
362
|
+
expect( sub_b_instance['xyz'] ).to eq( 123 )
|
363
|
+
end
|
364
|
+
|
365
|
+
it "does not leak allowing of extras to its parent class" do
|
366
|
+
expect{
|
367
|
+
model_instance['xyz'] = 123
|
368
|
+
}.to raise_exception( SonJay::PropertyNameError )
|
369
|
+
end
|
370
|
+
end
|
358
371
|
end
|
359
372
|
|
360
373
|
describe "a subclass with a directly self-referential property specification" do
|
@@ -400,6 +413,7 @@ describe SonJay::ObjectModel do
|
|
400
413
|
expect{ subclass.property_definitions }.
|
401
414
|
to raise_exception( SonJay::InfiniteRegressError )
|
402
415
|
end
|
416
|
+
|
403
417
|
end
|
404
418
|
|
405
419
|
end
|
data/spec/son_jay_spec.rb
CHANGED
data/spec/value_array_spec.rb
CHANGED
@@ -6,9 +6,9 @@ describe SonJay::ValueArray do
|
|
6
6
|
expect( subject << 1 << 2 ).to eq( [1, 2] )
|
7
7
|
end
|
8
8
|
|
9
|
-
describe '#
|
9
|
+
describe '#model_content' do
|
10
10
|
it "returns the instance itself" do
|
11
|
-
expect( subject.
|
11
|
+
expect( subject.model_content ).to eq( subject )
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: son_jay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Jorgensen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -124,11 +124,11 @@ files:
|
|
124
124
|
- lib/son_jay/acts_as_model.rb
|
125
125
|
- lib/son_jay/model_array.rb
|
126
126
|
- lib/son_jay/object_model.rb
|
127
|
-
- lib/son_jay/object_model/
|
128
|
-
- lib/son_jay/object_model/
|
129
|
-
- lib/son_jay/object_model/
|
130
|
-
- lib/son_jay/object_model/
|
131
|
-
- lib/son_jay/object_model/
|
127
|
+
- lib/son_jay/object_model/content.rb
|
128
|
+
- lib/son_jay/object_model/content/abstract.rb
|
129
|
+
- lib/son_jay/object_model/content/content_with_extra.rb
|
130
|
+
- lib/son_jay/object_model/content/content_without_extra.rb
|
131
|
+
- lib/son_jay/object_model/content_data.rb
|
132
132
|
- lib/son_jay/object_model/properties_definer.rb
|
133
133
|
- lib/son_jay/object_model/property_definition.rb
|
134
134
|
- lib/son_jay/object_model/property_definitions.rb
|
@@ -137,7 +137,9 @@ files:
|
|
137
137
|
- son_jay.gemspec
|
138
138
|
- spec/acts_as_model_spec.rb
|
139
139
|
- spec/model_array_spec.rb
|
140
|
-
- spec/object_model/
|
140
|
+
- spec/object_model/content/content_with_extra_spec.rb
|
141
|
+
- spec/object_model/content/content_without_extra_spec.rb
|
142
|
+
- spec/object_model/content_data_spec.rb
|
141
143
|
- spec/object_model/property_definition_spec.rb
|
142
144
|
- spec/object_model_spec.rb
|
143
145
|
- spec/son_jay_spec.rb
|
@@ -179,7 +181,9 @@ test_files:
|
|
179
181
|
- features/support/env.rb
|
180
182
|
- spec/acts_as_model_spec.rb
|
181
183
|
- spec/model_array_spec.rb
|
182
|
-
- spec/object_model/
|
184
|
+
- spec/object_model/content/content_with_extra_spec.rb
|
185
|
+
- spec/object_model/content/content_without_extra_spec.rb
|
186
|
+
- spec/object_model/content_data_spec.rb
|
183
187
|
- spec/object_model/property_definition_spec.rb
|
184
188
|
- spec/object_model_spec.rb
|
185
189
|
- spec/son_jay_spec.rb
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module SonJay
|
4
|
-
class ObjectModel
|
5
|
-
|
6
|
-
class ExtraData
|
7
|
-
extend Forwardable
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@data = {}
|
11
|
-
end
|
12
|
-
|
13
|
-
def []=(name, value)
|
14
|
-
name = "#{name}" unless String === name
|
15
|
-
@data[name] = value
|
16
|
-
end
|
17
|
-
|
18
|
-
def [](name)
|
19
|
-
name = "#{name}" unless String === name
|
20
|
-
@data[name]
|
21
|
-
end
|
22
|
-
|
23
|
-
def hash_merge(other)
|
24
|
-
@data.merge( other )
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_h
|
28
|
-
@data.dup
|
29
|
-
end
|
30
|
-
|
31
|
-
def_delegator :@data, :empty?
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module SonJay
|
2
|
-
class ObjectModel
|
3
|
-
module Properties
|
4
|
-
|
5
|
-
class PropertiesWithExtra < Abstract
|
6
|
-
|
7
|
-
def extra
|
8
|
-
@extra ||= ObjectModel::ExtraData.new
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def load_extra_property(name_string, value)
|
14
|
-
extra[ name_string ] = value
|
15
|
-
end
|
16
|
-
|
17
|
-
def hash_for_json
|
18
|
-
extra.empty? ?
|
19
|
-
@data :
|
20
|
-
extra.hash_merge( @data )
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'son_jay/object_model/properties/abstract'
|
2
|
-
require 'son_jay/object_model/properties/properties_without_extra'
|
3
|
-
require 'son_jay/object_model/properties/properties_with_extra'
|
4
|
-
|
5
|
-
module SonJay
|
6
|
-
class ObjectModel
|
7
|
-
|
8
|
-
module Properties
|
9
|
-
|
10
|
-
def self.new(property_definitions, allow_extra)
|
11
|
-
klass = allow_extra ?
|
12
|
-
self::PropertiesWithExtra :
|
13
|
-
self::PropertiesWithoutExtra
|
14
|
-
klass.new( property_definitions )
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe SonJay::ObjectModel::ExtraData do
|
4
|
-
|
5
|
-
it "provides value access by name symbol or string" do
|
6
|
-
subject[ :aaa ] = 1
|
7
|
-
subject[ 'bbb' ] = 2
|
8
|
-
expect( subject[ 'aaa' ] ).to eq( 1 )
|
9
|
-
expect( subject[ :bbb ] ).to eq( 2 )
|
10
|
-
end
|
11
|
-
|
12
|
-
it "merges with a hash, returning a hash" do
|
13
|
-
subject[ :aa ] = 1
|
14
|
-
subject[ :bb ] = 2
|
15
|
-
|
16
|
-
actual = subject.hash_merge(
|
17
|
-
"bb" => 22,
|
18
|
-
"cc" => 33
|
19
|
-
)
|
20
|
-
|
21
|
-
expect( actual ).to eq(
|
22
|
-
'aa' => 1,
|
23
|
-
'bb' => 22,
|
24
|
-
'cc' => 33
|
25
|
-
)
|
26
|
-
end
|
27
|
-
|
28
|
-
it "indicates when it is empty" do
|
29
|
-
expect( subject ).to be_empty
|
30
|
-
end
|
31
|
-
|
32
|
-
it "indicates when it is not empty" do
|
33
|
-
subject[:a] = 'a'
|
34
|
-
expect( subject ).not_to be_empty
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|