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.
@@ -25,7 +25,7 @@ describe SonJay::ModelArray do
25
25
  end
26
26
 
27
27
  attr_accessor :loaded_data
28
- def sonj_content ; Content.new(self) ; end
28
+ def model_content ; Content.new(self) ; end
29
29
  end }
30
30
 
31
31
  describe '::array_class' do
@@ -36,40 +36,101 @@ describe SonJay::ModelArray do
36
36
  end
37
37
  end
38
38
 
39
- it "produces instances that are initially empty" do
40
- instance = subclass.new
41
- expect( instance ).to be_empty
42
- expect( instance.length ).to eq( 0 )
43
- end
39
+ describe "an instance" do
40
+ subject { subclass.new }
41
+
42
+ it "is initially empty" do
43
+ expect( subject ).to be_empty
44
+ expect( subject.length ).to eq( 0 )
45
+ end
44
46
 
45
- describe "#additional" do
46
- it "adds a new entry of the modeled type, and returns the entry" do
47
- instance = subclass.new
47
+ describe "#additional" do
48
+ it "adds a new entry of the modeled type, and returns the entry" do
49
+ entry_0 = subject.additional
50
+ entry_1 = subject.additional
48
51
 
49
- entry_0 = instance.additional
50
- entry_1 = instance.additional
52
+ expect( entry_0 ).to be_kind_of( entry_class )
51
53
 
52
- expect( entry_0 ).to be_kind_of( entry_class )
53
- expect( instance.entries ).to eq( [entry_0, entry_1] )
54
+ expect( subject.entries ).to eq( [
55
+ entry_0,
56
+ entry_1
57
+ ] )
58
+ end
54
59
  end
55
- end
56
60
 
57
- describe '#load_data' do
58
- it "loads entries in the given enumerable into its own model instance entries" do
59
- instance = subclass.new
61
+ describe '#load_data' do
62
+ it "loads entries in the given enumerable into its own model instance entries" do
63
+ subject.load_data( ['entry 0 data', 'entry 1 data'] )
60
64
 
61
- instance.load_data( ['entry 0 data', 'entry 1 data'] )
65
+ expect( subject.length ).to eq( 2 )
66
+ expect( subject[0].loaded_data ).to eq( 'entry 0 data' )
67
+ expect( subject[1].loaded_data ).to eq( 'entry 1 data' )
68
+ end
69
+ end
62
70
 
63
- expect( instance.length ).to eq( 2 )
64
- expect( instance[0].loaded_data ).to eq( 'entry 0 data' )
65
- expect( instance[1].loaded_data ).to eq( 'entry 1 data' )
71
+ describe "#to_a" do
72
+ it "returns an isolated array of the entries" do
73
+ entry_0 = subject.additional
74
+ entry_1 = subject.additional
75
+
76
+ actual_array = subject.to_a
77
+
78
+ entry_2 = subject.additional
79
+
80
+ expect( actual_array ).to eq( [
81
+ entry_0,
82
+ entry_1
83
+ ] )
84
+ end
66
85
  end
67
- end
68
86
 
69
- describe '#sonj_content' do
70
- it "returns the model array" do
71
- expect( subject.sonj_content ).to equal( subject )
87
+ describe '#model_content' do
88
+ it "returns the model array" do
89
+ expect( subject.model_content ).to equal( subject )
90
+ end
72
91
  end
92
+
93
+ describe '#freeze' do
94
+ it "causes the instance to behave as frozen" do
95
+ subject.freeze
96
+ expect{ subject.additional }.to raise_exception( RuntimeError )
97
+ end
98
+ end
99
+
100
+ describe '#dup' do
101
+ it "makes a shallow copy" do
102
+ subject.additional
103
+ actual_dup = subject.dup
104
+ expect( actual_dup[0] ).to eq( subject[0] )
105
+ actual_dup.additional
106
+ expect( subject.length ).to eq( 1 )
107
+ end
108
+
109
+ it "returns a thawed copy of a frozen instance" do
110
+ subject.freeze
111
+ actual_dup = subject.dup
112
+ expect( actual_dup ).not_to be_frozen
113
+ expect{ actual_dup.additional }.not_to raise_exception
114
+ end
115
+ end
116
+
117
+ describe '#clone' do
118
+ it "makes a shallow copy" do
119
+ subject.additional
120
+ actual_clone = subject.clone
121
+ expect( actual_clone[0] ).to eq( subject[0] )
122
+ actual_clone.additional
123
+ expect( subject.length ).to eq( 1 )
124
+ end
125
+
126
+ it "returns a frozen copy of a frozen instance" do
127
+ subject.freeze
128
+ actual_clone = subject.clone
129
+ expect( actual_clone ).to be_frozen
130
+ expect{ actual_clone.additional }.to raise_exception( RuntimeError )
131
+ end
132
+ end
133
+
73
134
  end
74
135
  end
75
136
  end
@@ -0,0 +1,201 @@
1
+ require 'spec_helper'
2
+
3
+ module SonJay
4
+
5
+ describe ObjectModel::Content::ContentWithExtra do
6
+ let( :detail_xy_class ) { models_module::DetailXY }
7
+ let( :detail_z_class ) { models_module::DetailZ }
8
+
9
+ let( :models_module ) {
10
+ mod = Module.new
11
+ mod::M = mod
12
+
13
+ module mod::M
14
+ class DetailXY < SonJay::ObjectModel
15
+ properties do
16
+ property :xxx
17
+ property :yyy
18
+ end
19
+ end
20
+
21
+ class DetailZ < SonJay::ObjectModel
22
+ properties do
23
+ property :zzz
24
+ end
25
+ end
26
+ end
27
+
28
+ mod
29
+ }
30
+
31
+ let( :value_property_initializations ) {
32
+ ->(*) {
33
+ property :aaa
34
+ property :bbb
35
+ }
36
+ }
37
+ let( :object_model_property_initializations ) {
38
+ _xy_class = detail_xy_class
39
+ _z_class = detail_z_class
40
+ ->(*) {
41
+ property :detail_xy , model: _xy_class
42
+ property :detail_z , model: [_z_class]
43
+ }
44
+ }
45
+
46
+ context "with various properties defined" do
47
+ subject {
48
+ described_class.new( property_defs )
49
+ }
50
+ let( :property_defs ) {
51
+ ObjectModel::PropertyDefinitions.from_initializations( [
52
+ value_property_initializations,
53
+ object_model_property_initializations
54
+ ] )
55
+ }
56
+
57
+ it "provides access to an extra-properties object" do
58
+ expect( subject.extra.to_h ).to eq( {} )
59
+ end
60
+
61
+ it "rejects assignment of an undefined property" do
62
+ expect{
63
+ subject['qq'] = 0
64
+ }.to raise_exception(
65
+ SonJay::PropertyNameError
66
+ )
67
+ end
68
+
69
+ it "implements enumerable behaviors" do
70
+ expect( subject ).to respond_to( :entries )
71
+ end
72
+
73
+ it "enumerates entries for all defined properties and extra assigned property values" do
74
+ subject['bbb'] = 'B'
75
+ subject['detail_xy'].xxx = 'X'
76
+ subject.extra['qqq'] = 'Q'
77
+ subject.extra['rrr'] = nil
78
+ actual_pairs = []
79
+ subject.each do |*pair|
80
+ actual_pairs << pair
81
+ end
82
+ expect( actual_pairs ).to match_array( [
83
+ [ 'aaa', nil ],
84
+ [ 'bbb', 'B' ],
85
+ [ 'qqq', 'Q' ],
86
+ [ 'rrr', nil ],
87
+ [ 'detail_xy', subject['detail_xy'] ],
88
+ [ 'detail_z', subject['detail_z' ] ],
89
+ ] )
90
+ end
91
+
92
+ describe '#freeze' do
93
+ it "causes the instance to behave as frozen" do
94
+ subject.freeze
95
+ expect{ subject['aaa'] = 1 }.to raise_exception( RuntimeError )
96
+ expect{ subject.extra['qq'] = 2 }.to raise_exception( RuntimeError )
97
+ end
98
+ end
99
+
100
+ describe '#dup' do
101
+ it "makes a shallow copy" do
102
+ subject[ 'aaa' ] = 'A'
103
+ subject[ 'bbb' ] = 'B'
104
+ subject.extra['qq'] = 'Q'
105
+ actual_dup = subject.dup
106
+ expect( actual_dup[ 'aaa' ] ).to eq( 'A' )
107
+ expect( actual_dup[ 'bbb' ] ).to eq( 'B' )
108
+ expect( actual_dup[ 'detail_xy' ] ).to equal( subject[ 'detail_xy' ] )
109
+ expect( actual_dup[ 'detail_z' ] ).to equal( subject[ 'detail_z' ] )
110
+ actual_dup[ 'bbb' ] = 'BB'
111
+ actual_dup.extra['qq'] = 'QQ'
112
+ expect( subject['bbb'] ).to eq( 'B' )
113
+ expect( subject.extra['qq'] ).to eq( 'Q' )
114
+ end
115
+
116
+ it "returns a thawed copy of a frozen instance" do
117
+ subject.extra['qq'] = 11
118
+ subject.freeze
119
+ actual_dup = subject.dup
120
+ expect( actual_dup ).not_to be_frozen
121
+ actual_dup['aaa'] = 98
122
+ actual_dup.extra['qq'] = 99
123
+ expect( actual_dup['aaa'] ).to eq( 98 )
124
+ expect( actual_dup.extra['qq'] ).to eq( 99 )
125
+ end
126
+ end
127
+
128
+ describe '#clone' do
129
+ it "makes a shallow copy" do
130
+ subject[ 'aaa' ] = 'A'
131
+ subject[ 'bbb' ] = 'B'
132
+ subject.extra['qq'] = 'Q'
133
+ actual_clone = subject.clone
134
+ expect( actual_clone[ 'aaa' ] ).to eq( 'A' )
135
+ expect( actual_clone[ 'bbb' ] ).to eq( 'B' )
136
+ expect( actual_clone[ 'detail_xy' ] ).to equal( subject[ 'detail_xy' ] )
137
+ expect( actual_clone[ 'detail_z' ] ).to equal( subject[ 'detail_z' ] )
138
+ expect( actual_clone.extra['qq'] ).to eq('Q')
139
+ actual_clone['bbb'] = 'BB'
140
+ actual_clone.extra['qq'] = 'QQ'
141
+ expect( subject['bbb'] ).to eq( 'B' )
142
+ expect( subject.extra['qq'] ).to eq( 'Q' )
143
+ end
144
+
145
+ it "returns a frozen copy of a frozen instance" do
146
+ subject.extra['qq'] = 1
147
+ subject.freeze
148
+ actual_clone = subject.clone
149
+ expect( actual_clone ).to be_frozen
150
+ expect{ subject['aaa'] = 9 }.to raise_exception( RuntimeError )
151
+ expect{ subject.extra['qq'] = 9 }.to raise_exception( RuntimeError )
152
+ end
153
+ end
154
+ end
155
+
156
+ context "with value properties defined" do
157
+ subject {
158
+ described_class.new( property_defs )
159
+ }
160
+ let( :property_defs ) {
161
+ ObjectModel::PropertyDefinitions.from_initializations( [
162
+ value_property_initializations
163
+ ] )
164
+ }
165
+
166
+ it "has an initial number of entries equal to its number of defined properties" do
167
+ expect( subject.length ).to eq( 2 )
168
+ end
169
+
170
+ it "has name-indexed settable/gettable value properties by string or symbol" do
171
+ subject[ :aaa ] = 1
172
+ subject[ 'bbb' ] = 'XYZ'
173
+
174
+ expect( subject[ 'aaa' ] ).to eq( 1 )
175
+ expect( subject[ :bbb ] ).to eq( 'XYZ' )
176
+ end
177
+
178
+ it "has all property values nil by default" do
179
+ expect( subject[ 'aaa' ] ).to be_nil
180
+ expect( subject[ :bbb ] ).to be_nil
181
+ end
182
+ end
183
+
184
+ context "with model properties defined" do
185
+ subject {
186
+ described_class.new( property_defs )
187
+ }
188
+ let( :property_defs ) {
189
+ ObjectModel::PropertyDefinitions.from_initializations( [
190
+ object_model_property_initializations
191
+ ] )
192
+ }
193
+
194
+ it "has name-indexed gettable values for defined properties by string or symbol" do
195
+ expect( subject[ 'detail_xy' ] ).to be_kind_of( detail_xy_class )
196
+ expect( subject[ :detail_z ] ).to be_kind_of( detail_z_class::Array )
197
+ end
198
+ end
199
+ end
200
+
201
+ end
@@ -0,0 +1,180 @@
1
+ require 'spec_helper'
2
+
3
+ module SonJay
4
+
5
+ describe ObjectModel::Content::ContentWithoutExtra do
6
+ let( :detail_xy_class ) { models_module::DetailXY }
7
+ let( :detail_z_class ) { models_module::DetailZ }
8
+
9
+ let( :models_module ) {
10
+ mod = Module.new
11
+ mod::M = mod
12
+
13
+ module mod::M
14
+ class DetailXY < SonJay::ObjectModel
15
+ properties do
16
+ property :xxx
17
+ property :yyy
18
+ end
19
+ end
20
+
21
+ class DetailZ < SonJay::ObjectModel
22
+ properties do
23
+ property :zzz
24
+ end
25
+ end
26
+ end
27
+
28
+ mod
29
+ }
30
+
31
+ let( :value_property_initializations ) {
32
+ ->(*) {
33
+ property :aaa
34
+ property :bbb
35
+ }
36
+ }
37
+ let( :object_model_property_initializations ) {
38
+ _xy_class = detail_xy_class
39
+ _z_class = detail_z_class
40
+ ->(*) {
41
+ property :detail_xy , model: _xy_class
42
+ property :detail_z , model: [_z_class]
43
+ }
44
+ }
45
+
46
+ context "with various properties defined" do
47
+ subject {
48
+ described_class.new( property_defs )
49
+ }
50
+ let( :property_defs ) {
51
+ ObjectModel::PropertyDefinitions.from_initializations( [
52
+ value_property_initializations,
53
+ object_model_property_initializations
54
+ ] )
55
+ }
56
+
57
+ it "rejects assignment of an undefined property" do
58
+ expect{
59
+ subject['qq'] = 0
60
+ }.to raise_exception(
61
+ SonJay::PropertyNameError
62
+ )
63
+ end
64
+
65
+ it "implements enumerable behaviors" do
66
+ expect( subject ).to respond_to( :entries )
67
+ end
68
+
69
+ it "enumerates entries for all defined properties" do
70
+ subject['bbb'] = 'B'
71
+ subject['detail_xy'].xxx = 'X'
72
+ actual_pairs = []
73
+ subject.each do |*pair|
74
+ actual_pairs << pair
75
+ end
76
+ expect( actual_pairs ).to match_array( [
77
+ [ 'aaa', nil ],
78
+ [ 'bbb', 'B' ],
79
+ [ 'detail_xy', subject['detail_xy'] ],
80
+ [ 'detail_z', subject['detail_z' ] ],
81
+ ] )
82
+ end
83
+
84
+ describe '#freeze' do
85
+ it "causes the instance to behave as frozen" do
86
+ subject.freeze
87
+ expect{ subject['aaa'] = 1 }.to raise_exception( RuntimeError )
88
+ end
89
+ end
90
+
91
+ describe '#dup' do
92
+ it "makes a shallow copy" do
93
+ subject[ 'aaa' ] = 'A'
94
+ subject[ 'bbb' ] = 'B'
95
+ actual_dup = subject.dup
96
+ expect( actual_dup[ 'aaa' ] ).to eq( 'A' )
97
+ expect( actual_dup[ 'bbb' ] ).to eq( 'B' )
98
+ expect( actual_dup[ 'detail_xy' ] ).to equal( subject[ 'detail_xy' ] )
99
+ expect( actual_dup[ 'detail_z' ] ).to equal( subject[ 'detail_z' ] )
100
+ actual_dup[ 'bbb' ] = 'BB'
101
+ expect( subject['bbb'] ).to eq( 'B' )
102
+ end
103
+
104
+ it "returns a thawed copy of a frozen instance" do
105
+ subject.freeze
106
+ actual_dup = subject.dup
107
+ expect( actual_dup ).not_to be_frozen
108
+ actual_dup['aaa'] = 99
109
+ expect( actual_dup['aaa'] ).to eq( 99 )
110
+ end
111
+ end
112
+
113
+ describe '#clone' do
114
+ it "makes a shallow copy" do
115
+ subject[ 'aaa' ] = 'A'
116
+ subject[ 'bbb' ] = 'B'
117
+ actual_clone = subject.clone
118
+ expect( actual_clone[ 'aaa' ] ).to eq( 'A' )
119
+ expect( actual_clone[ 'bbb' ] ).to eq( 'B' )
120
+ expect( actual_clone[ 'detail_xy' ] ).to equal( subject[ 'detail_xy' ] )
121
+ expect( actual_clone[ 'detail_z' ] ).to equal( subject[ 'detail_z' ] )
122
+ actual_clone[ 'bbb' ] = 'BB'
123
+ expect( subject['bbb'] ).to eq( 'B' )
124
+ end
125
+
126
+ it "returns a frozen copy of a frozen instance" do
127
+ subject.freeze
128
+ actual_clone = subject.clone
129
+ expect( actual_clone ).to be_frozen
130
+ expect{ subject['aaa'] = 1 }.to raise_exception( RuntimeError )
131
+ end
132
+ end
133
+ end
134
+
135
+ context "with value properties defined" do
136
+ subject {
137
+ described_class.new( property_defs )
138
+ }
139
+ let( :property_defs ) {
140
+ ObjectModel::PropertyDefinitions.from_initializations( [
141
+ value_property_initializations
142
+ ] )
143
+ }
144
+
145
+ it "has an initial number of entries equal to its number of defined properties" do
146
+ expect( subject.length ).to eq( 2 )
147
+ end
148
+
149
+ it "has name-indexed settable/gettable value properties by string or symbol" do
150
+ subject[ :aaa ] = 1
151
+ subject[ 'bbb' ] = 'XYZ'
152
+
153
+ expect( subject[ 'aaa' ] ).to eq( 1 )
154
+ expect( subject[ :bbb ] ).to eq( 'XYZ' )
155
+ end
156
+
157
+ it "has all property values nil by default" do
158
+ expect( subject[ 'aaa' ] ).to be_nil
159
+ expect( subject[ :bbb ] ).to be_nil
160
+ end
161
+ end
162
+
163
+ context "with model properties defined" do
164
+ subject {
165
+ described_class.new( property_defs )
166
+ }
167
+ let( :property_defs ) {
168
+ ObjectModel::PropertyDefinitions.from_initializations( [
169
+ object_model_property_initializations
170
+ ] )
171
+ }
172
+
173
+ it "has name-indexed gettable values for defined properties by string or symbol" do
174
+ expect( subject[ 'detail_xy' ] ).to be_kind_of( detail_xy_class )
175
+ expect( subject[ :detail_z ] ).to be_kind_of( detail_z_class::Array )
176
+ end
177
+ end
178
+ end
179
+
180
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe SonJay::ObjectModel::ContentData 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
+ describe '#freeze' do
38
+ it "causes the instance to behave as frozen" do
39
+ subject.freeze
40
+ expect{ subject['a'] = 1 }.to raise_exception( RuntimeError )
41
+ end
42
+ end
43
+
44
+ describe '#dup' do
45
+ it "makes a shallow copy" do
46
+ subject[ 'aa' ] = 'A'
47
+ subject[ 'bb' ] = 'B'
48
+ actual_dup = subject.dup
49
+ expect( actual_dup.keys ).to match_array( ['aa', 'bb'] )
50
+ expect( actual_dup['aa'] ).to equal( subject['aa'] )
51
+ actual_dup['bb'] = 'BBB'
52
+ expect( subject['bb'] ).to eq( 'B' )
53
+ end
54
+
55
+ it "returns a thawed copy of a frozen instance" do
56
+ subject.freeze
57
+ actual_dup = subject.dup
58
+ expect( actual_dup ).not_to be_frozen
59
+ actual_dup['aa'] = 1
60
+ expect( actual_dup['aa'] ).to eq( 1 )
61
+ end
62
+ end
63
+
64
+ describe '#clone' do
65
+ it "makes a shallow copy" do
66
+ subject[ 'aa' ] = 'A'
67
+ subject[ 'bb' ] = 'B'
68
+ actual_clone = subject.clone
69
+ expect( actual_clone.keys ).to match_array( ['aa', 'bb'] )
70
+ expect( actual_clone['aa'] ).to equal( subject['aa'] )
71
+ actual_clone['bb'] = 'BBB'
72
+ expect( subject['bb'] ).to eq( 'B' )
73
+ end
74
+
75
+ it "returns a frozen copy of a frozen instance" do
76
+ subject.freeze
77
+ expect( subject.clone ).to be_frozen
78
+ end
79
+ end
80
+
81
+ end