embedson 0.0.2 → 1.0.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +13 -0
- data/README.md +125 -23
- data/Rakefile +4 -0
- data/embedson.gemspec +4 -1
- data/lib/embedson.rb +4 -0
- data/lib/embedson/exceptions.rb +21 -0
- data/lib/embedson/model.rb +8 -73
- data/lib/embedson/model/embedded_builder.rb +100 -0
- data/lib/embedson/model/embeds_builder.rb +68 -0
- data/lib/embedson/model/method_builder.rb +81 -0
- data/lib/embedson/version.rb +1 -1
- data/spec/embedson/model_spec.rb +421 -60
- data/spec/spec_helper.rb +17 -47
- metadata +25 -5
@@ -0,0 +1,68 @@
|
|
1
|
+
module Embedson
|
2
|
+
module Model
|
3
|
+
class EmbedsBuilder
|
4
|
+
attr_reader :builder, :klass
|
5
|
+
|
6
|
+
def initialize(builder)
|
7
|
+
@builder = builder
|
8
|
+
@klass = builder.klass
|
9
|
+
end
|
10
|
+
|
11
|
+
def define
|
12
|
+
methods_embeds.each do |meth|
|
13
|
+
klass.class_exec builder, &send(meth)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def methods_embeds
|
20
|
+
[:writer, :reader, :related_model, :build_related_model]
|
21
|
+
end
|
22
|
+
|
23
|
+
def writer
|
24
|
+
proc do |builder|
|
25
|
+
define_method("#{builder.field_name}=") do |arg|
|
26
|
+
verify_arg_klass(arg)
|
27
|
+
send_self_to_related(arg)
|
28
|
+
|
29
|
+
instance_variable_set(builder.instance_var_name, arg)
|
30
|
+
write_attribute(builder.column_name, arg.nil? ? arg : arg.to_h)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def reader
|
36
|
+
proc do |builder|
|
37
|
+
define_method(builder.field_name) do
|
38
|
+
return if read_attribute(builder.column_name).nil?
|
39
|
+
|
40
|
+
build_related_model if instance_variable_get(builder.instance_var_name).nil?
|
41
|
+
instance_variable_get(builder.instance_var_name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def related_model
|
47
|
+
proc do |builder|
|
48
|
+
private
|
49
|
+
|
50
|
+
define_method('related_model') do
|
51
|
+
builder.related_klass_name.constantize.new(read_attribute(builder.column_name))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_related_model
|
57
|
+
proc do |builder|
|
58
|
+
private
|
59
|
+
|
60
|
+
define_method('build_related_model') do
|
61
|
+
instance_variable_set(builder.instance_var_name, related_model)
|
62
|
+
related_model.public_send(builder.inverse_set, self) if related_model.respond_to?(builder.inverse_set)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Embedson
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class MethodBuilder
|
5
|
+
|
6
|
+
attr_reader :klass, :field_name, :options
|
7
|
+
|
8
|
+
def initialize(klass, name, options)
|
9
|
+
@klass = klass
|
10
|
+
@field_name = name
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def embeds
|
15
|
+
EmbedsBuilder.new(self).define
|
16
|
+
generate_common
|
17
|
+
end
|
18
|
+
|
19
|
+
def embedded
|
20
|
+
EmbeddedBuilder.new(self).define
|
21
|
+
generate_common
|
22
|
+
end
|
23
|
+
|
24
|
+
def column_name
|
25
|
+
@column_name ||= options.fetch(:column_name, nil) || field_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def related_klass_name
|
29
|
+
@related_klass_name ||= (options.fetch(:class_name, nil) || field_name).to_s.classify
|
30
|
+
end
|
31
|
+
|
32
|
+
def instance_var_name
|
33
|
+
@instance_var_name ||= "@#{field_name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def inverse_get
|
37
|
+
@inverse_get ||= options.fetch(:inverse_of, nil) || klass.name.demodulize.tableize.singularize
|
38
|
+
end
|
39
|
+
|
40
|
+
def inverse_set
|
41
|
+
@inverse_set ||= "#{inverse_get}="
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def generate_common
|
47
|
+
methods_for_both.each do |meth|
|
48
|
+
klass.class_exec self, &send(meth)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def methods_for_both
|
53
|
+
[:send_self_to_related, :verify_arg_klass]
|
54
|
+
end
|
55
|
+
|
56
|
+
def verify_arg_klass
|
57
|
+
proc do |builder|
|
58
|
+
private
|
59
|
+
|
60
|
+
define_method('verify_arg_klass') do |arg|
|
61
|
+
unless arg.nil? || arg.is_a?(builder.related_klass_name.constantize)
|
62
|
+
raise ClassTypeError.new(arg.class.name, builder.related_klass_name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def send_self_to_related
|
69
|
+
proc do |builder|
|
70
|
+
private
|
71
|
+
|
72
|
+
define_method('send_self_to_related') do |arg|
|
73
|
+
if arg.respond_to?(builder.inverse_set) && arg.public_send(builder.inverse_get) != self
|
74
|
+
arg.public_send(builder.inverse_set, self)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/embedson/version.rb
CHANGED
data/spec/embedson/model_spec.rb
CHANGED
@@ -7,61 +7,60 @@ describe Embedson::Model do
|
|
7
7
|
|
8
8
|
with_model :Parent do
|
9
9
|
table do |t|
|
10
|
-
t.json :
|
10
|
+
t.json :embeddeda
|
11
11
|
end
|
12
12
|
|
13
13
|
model do
|
14
|
-
|
15
|
-
|
16
|
-
embeds_one :embedded
|
14
|
+
embeds_one :embeddeda
|
17
15
|
end
|
18
16
|
end
|
19
17
|
|
20
|
-
class
|
18
|
+
class Embeddeda
|
21
19
|
|
22
20
|
def to_h
|
23
21
|
{ defined: 'in', embedded: true }
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
|
-
let(:parent) { Parent.new
|
28
|
-
let(:embedded) {
|
25
|
+
let(:parent) { Parent.new }
|
26
|
+
let(:embedded) { Embeddeda.new }
|
29
27
|
|
30
28
|
it 'adds "embeds_one" method' do
|
31
29
|
expect(Parent).to respond_to(:embeds_one)
|
32
30
|
end
|
33
31
|
|
34
|
-
it 'adds "
|
35
|
-
expect(
|
32
|
+
it 'adds "embeddeda" method' do
|
33
|
+
expect(parent).to respond_to(:embeddeda)
|
36
34
|
end
|
37
35
|
|
38
|
-
it 'adds "
|
39
|
-
expect(
|
36
|
+
it 'adds "embeddeda=" method' do
|
37
|
+
expect(parent).to respond_to("embeddeda=")
|
40
38
|
end
|
41
39
|
|
42
|
-
describe 'defined .
|
43
|
-
it 'assigns value of
|
40
|
+
describe 'defined .embeddeda= method' do
|
41
|
+
it 'assigns value of Embeddeda class' do
|
42
|
+
|
44
43
|
expect {
|
45
|
-
parent.
|
46
|
-
}.to change{ parent.
|
44
|
+
parent.embeddeda = embedded
|
45
|
+
}.to change{ parent.embeddeda }.from(nil).to(embedded)
|
47
46
|
end
|
48
47
|
|
49
48
|
context 'when assigning nil' do
|
50
|
-
let(:parent) { Parent.new(
|
49
|
+
let(:parent) { Parent.new(embeddeda: embedded) }
|
51
50
|
|
52
51
|
it 'removes assignmnent' do
|
53
52
|
parent
|
54
53
|
expect {
|
55
|
-
parent.
|
56
|
-
}.to change { parent.
|
54
|
+
parent.embeddeda = nil
|
55
|
+
}.to change { parent.embeddeda }.from(embedded).to(nil)
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
60
59
|
context 'when assigning wrong class' do
|
61
|
-
it 'raises
|
60
|
+
it 'raises ClassTypeError' do
|
62
61
|
expect{
|
63
|
-
parent.
|
64
|
-
}.to raise_error(
|
62
|
+
parent.embeddeda = 'something'
|
63
|
+
}.to raise_error(Embedson::ClassTypeError)
|
65
64
|
end
|
66
65
|
end
|
67
66
|
|
@@ -72,41 +71,41 @@ describe Embedson::Model do
|
|
72
71
|
|
73
72
|
it 'marks parent as changed' do
|
74
73
|
expect {
|
75
|
-
parent.
|
74
|
+
parent.embeddeda = embedded
|
76
75
|
}.to change{ parent.changed? }.from(false).to(true)
|
77
76
|
end
|
78
77
|
|
79
78
|
context 'and saved after change' do
|
80
|
-
it 'saves
|
79
|
+
it 'saves Embeddeda class in column' do
|
81
80
|
expect{
|
82
|
-
parent.
|
81
|
+
parent.embeddeda = embedded
|
83
82
|
parent.save!
|
84
|
-
}.to change { parent.reload.read_attribute(:
|
83
|
+
}.to change { parent.reload.read_attribute(:embeddeda) }.from(nil).to(embedded.to_h.stringify_keys)
|
85
84
|
end
|
86
85
|
end
|
87
86
|
end
|
88
87
|
end
|
89
88
|
|
90
|
-
describe 'defined embedded method' do
|
89
|
+
describe 'defined .embedded method' do
|
91
90
|
context 'when value column is null' do
|
92
91
|
it 'returns nil' do
|
93
|
-
expect(parent.
|
92
|
+
expect(parent.embeddeda).to be nil
|
94
93
|
end
|
95
94
|
end
|
96
95
|
|
97
|
-
it 'returns
|
98
|
-
parent.
|
99
|
-
expect(parent.
|
96
|
+
it 'returns Embeddeda class' do
|
97
|
+
parent.embeddeda = embedded
|
98
|
+
expect(parent.embeddeda).to be_a Embeddeda
|
100
99
|
end
|
101
100
|
|
102
101
|
context 'when there is defined value in column' do
|
103
102
|
before do
|
104
|
-
parent.
|
103
|
+
parent.embeddeda = embedded
|
105
104
|
parent.save!
|
106
105
|
end
|
107
106
|
|
108
|
-
it 'returns
|
109
|
-
expect(parent.reload.
|
107
|
+
it 'returns Embeddeda class initialized with value from column' do
|
108
|
+
expect(parent.reload.embeddeda).to eq embedded
|
110
109
|
end
|
111
110
|
end
|
112
111
|
end
|
@@ -120,13 +119,11 @@ describe Embedson::Model do
|
|
120
119
|
end
|
121
120
|
|
122
121
|
model do
|
123
|
-
|
124
|
-
|
125
|
-
embeds_one :embedded, column_name: :data
|
122
|
+
embeds_one :embeddedb, column_name: :data
|
126
123
|
end
|
127
124
|
end
|
128
125
|
|
129
|
-
class
|
126
|
+
class Embeddedb < OpenStruct
|
130
127
|
|
131
128
|
def to_h
|
132
129
|
{ defined: 'in', embedded: true }
|
@@ -134,23 +131,23 @@ describe Embedson::Model do
|
|
134
131
|
end
|
135
132
|
|
136
133
|
let(:parent) { Parent.new() }
|
137
|
-
let(:embedded) {
|
134
|
+
let(:embedded) { Embeddedb.new() }
|
138
135
|
|
139
136
|
it 'adds "embeds_one" method' do
|
140
137
|
expect(Parent).to respond_to(:embeds_one)
|
141
138
|
end
|
142
139
|
|
143
140
|
it 'adds "embedded" method' do
|
144
|
-
expect(
|
141
|
+
expect(parent).to respond_to(:embeddedb)
|
145
142
|
end
|
146
143
|
|
147
144
|
it 'adds "embedded=" method' do
|
148
|
-
expect(
|
145
|
+
expect(parent).to respond_to("embeddedb=")
|
149
146
|
end
|
150
147
|
|
151
148
|
it 'saves embedded class to data column' do
|
152
149
|
expect {
|
153
|
-
parent.
|
150
|
+
parent.embeddedb = embedded
|
154
151
|
parent.save!
|
155
152
|
}.to change{ parent.read_attribute(:data) }.from(nil).to(embedded.to_h.stringify_keys)
|
156
153
|
end
|
@@ -160,17 +157,15 @@ describe Embedson::Model do
|
|
160
157
|
|
161
158
|
with_model :Parent do
|
162
159
|
table do |t|
|
163
|
-
t.json :
|
160
|
+
t.json :embeddedc
|
164
161
|
end
|
165
162
|
|
166
163
|
model do
|
167
|
-
|
168
|
-
|
169
|
-
embeds_one :embedded, inverse_of: :parent_m
|
164
|
+
embeds_one :embeddedc, inverse_of: :parent_m
|
170
165
|
end
|
171
166
|
end
|
172
167
|
|
173
|
-
class
|
168
|
+
class Embeddedc < OpenStruct
|
174
169
|
extend Embedson::Model
|
175
170
|
|
176
171
|
embedded_in :parent_m, class_name: 'Parent'
|
@@ -180,24 +175,24 @@ describe Embedson::Model do
|
|
180
175
|
end
|
181
176
|
end
|
182
177
|
|
183
|
-
let(:parent) { Parent.new
|
184
|
-
let(:embedded) {
|
178
|
+
let(:parent) { Parent.new }
|
179
|
+
let(:embedded) { Embeddedc.new }
|
185
180
|
|
186
181
|
it 'adds "embeds_one" method' do
|
187
182
|
expect(Parent).to respond_to(:embeds_one)
|
188
183
|
end
|
189
184
|
|
190
185
|
it 'adds "embedded" method' do
|
191
|
-
expect(
|
186
|
+
expect(parent).to respond_to(:embeddedc)
|
192
187
|
end
|
193
188
|
|
194
189
|
it 'adds "embedded=" method' do
|
195
|
-
expect(
|
190
|
+
expect(parent).to respond_to("embeddedc=")
|
196
191
|
end
|
197
192
|
|
198
193
|
it 'assigns self to parent_m in embedded class' do
|
199
|
-
parent.
|
200
|
-
expect(parent.
|
194
|
+
parent.embeddedc = embedded
|
195
|
+
expect(parent.embeddedc.parent_m).to eq parent
|
201
196
|
end
|
202
197
|
end
|
203
198
|
|
@@ -209,13 +204,11 @@ describe Embedson::Model do
|
|
209
204
|
end
|
210
205
|
|
211
206
|
model do
|
212
|
-
|
213
|
-
|
214
|
-
embeds_one :emb, class_name: 'Embedded', column_name: :embedded
|
207
|
+
embeds_one :emb, class_name: 'Embeddedd', column_name: :embedded
|
215
208
|
end
|
216
209
|
end
|
217
210
|
|
218
|
-
class
|
211
|
+
class Embeddedd < OpenStruct
|
219
212
|
extend Embedson::Model
|
220
213
|
|
221
214
|
embedded_in :parent
|
@@ -226,23 +219,391 @@ describe Embedson::Model do
|
|
226
219
|
end
|
227
220
|
|
228
221
|
let(:parent) { Parent.new(embedded: { some: 'thing' }) }
|
229
|
-
let(:embedded) {
|
222
|
+
let(:embedded) { Embeddedd.new() }
|
230
223
|
|
231
224
|
it 'adds "embeds_one" method' do
|
232
225
|
expect(Parent).to respond_to(:embeds_one)
|
233
226
|
end
|
234
227
|
|
235
228
|
it 'adds "emb" method' do
|
236
|
-
expect(
|
229
|
+
expect(parent).to respond_to(:emb)
|
237
230
|
end
|
238
231
|
|
239
232
|
it 'adds "emb=" method' do
|
240
|
-
expect(
|
233
|
+
expect(parent).to respond_to("emb=")
|
241
234
|
end
|
242
235
|
|
243
|
-
it '
|
236
|
+
it 'returns emb as Embedded' do
|
237
|
+
expect(parent.emb).to be_a Embeddedd
|
244
238
|
expect(parent.emb.some).to eq 'thing'
|
245
239
|
end
|
246
240
|
end
|
247
241
|
end
|
242
|
+
|
243
|
+
describe '#embedded_in' do
|
244
|
+
context 'when only relation name is specified' do
|
245
|
+
|
246
|
+
with_model :Parent do
|
247
|
+
table do |t|
|
248
|
+
t.json :embeddede
|
249
|
+
end
|
250
|
+
|
251
|
+
model do
|
252
|
+
embeds_one :embeddede
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class Embeddede < OpenStruct
|
257
|
+
extend Embedson::Model
|
258
|
+
|
259
|
+
embedded_in :parent
|
260
|
+
|
261
|
+
def change=(arg)
|
262
|
+
@change = arg
|
263
|
+
end
|
264
|
+
|
265
|
+
def to_h
|
266
|
+
h = { defined: 'in', embedded: true }
|
267
|
+
h.merge!(change: @change) if @change.present?
|
268
|
+
h
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
let(:parent) { Parent.new() }
|
273
|
+
let(:embedded) { Embeddede.new() }
|
274
|
+
|
275
|
+
it 'adds "embeds_one" method' do
|
276
|
+
expect(Embeddede).to respond_to(:embedded_in)
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'adds "parent" method' do
|
280
|
+
expect(embedded).to respond_to(:parent)
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'adds "parent=" method' do
|
284
|
+
expect(embedded).to respond_to("parent=")
|
285
|
+
end
|
286
|
+
|
287
|
+
describe 'defined .parent= method' do
|
288
|
+
it 'assigns value of embeding class' do
|
289
|
+
expect {
|
290
|
+
embedded.parent = parent
|
291
|
+
}.to change{ embedded.parent }.from(nil).to(parent)
|
292
|
+
end
|
293
|
+
|
294
|
+
context 'when assigning nil' do
|
295
|
+
let(:embedded) { Embeddede.new(parent: parent) }
|
296
|
+
|
297
|
+
it 'removes assignmnent' do
|
298
|
+
parent
|
299
|
+
expect {
|
300
|
+
embedded.parent = nil
|
301
|
+
}.to change { embedded.parent }.from(parent).to(nil)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'when assigning wrong class' do
|
306
|
+
it 'raises ClassTypeError' do
|
307
|
+
expect{
|
308
|
+
embedded.parent = 'something'
|
309
|
+
}.to raise_error(Embedson::ClassTypeError)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context 'when parent is persisted' do
|
314
|
+
before do
|
315
|
+
parent.save!
|
316
|
+
end
|
317
|
+
|
318
|
+
it 'marks parent as changed' do
|
319
|
+
expect {
|
320
|
+
embedded.parent = parent
|
321
|
+
}.to change{ parent.changed? }.from(false).to(true)
|
322
|
+
end
|
323
|
+
|
324
|
+
context 'and saved after change' do
|
325
|
+
it 'saves Embedded class in column' do
|
326
|
+
expect{
|
327
|
+
embedded.parent = parent
|
328
|
+
parent.save!
|
329
|
+
}.to change { parent.reload.read_attribute(:embeddede) }.from(nil).to(embedded.to_h.stringify_keys)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe 'defined .save method' do
|
336
|
+
|
337
|
+
context 'when there is parent' do
|
338
|
+
let(:embedded) { Embeddede.new(parent: parent) }
|
339
|
+
|
340
|
+
it 'calls .save on parent' do
|
341
|
+
expect(parent).to receive(:save)
|
342
|
+
embedded.save
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'saves to_h result in parent column' do
|
346
|
+
parent.save!
|
347
|
+
expect {
|
348
|
+
embedded.save
|
349
|
+
}.to change { parent.reload.read_attribute(:embeddede) }.from(nil).to({ "defined" => "in", "embedded" => true })
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
context 'when there is no parent' do
|
354
|
+
let(:embedded) { Embeddede.new }
|
355
|
+
|
356
|
+
it 'returns false' do
|
357
|
+
expect(embedded.save).to eq(false)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe 'defined .save! method' do
|
363
|
+
|
364
|
+
context 'when there is parent' do
|
365
|
+
let(:embedded) { Embeddede.new(parent: parent) }
|
366
|
+
|
367
|
+
it 'calls .save on parent' do
|
368
|
+
expect(parent).to receive(:save!)
|
369
|
+
embedded.save!
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'saves to_h result in parent column' do
|
373
|
+
parent.save!
|
374
|
+
expect {
|
375
|
+
embedded.save!
|
376
|
+
}.to change { parent.reload.read_attribute(:embeddede) }.from(nil).to({ "defined" => "in", "embedded" => true })
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
context 'when there is no parent' do
|
381
|
+
it 'raises argument error' do
|
382
|
+
expect { embedded.save! }.to raise_error(Embedson::NoParentError)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
describe 'defined .destroy method' do
|
388
|
+
|
389
|
+
context 'when there is parent' do
|
390
|
+
let(:embedded) { Embeddede.new(parent: parent) }
|
391
|
+
|
392
|
+
it 'calls .save! on parent' do
|
393
|
+
expect(parent).to receive(:save!)
|
394
|
+
embedded.destroy
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'saves nil in parent column' do
|
398
|
+
embedded.save!
|
399
|
+
expect {
|
400
|
+
embedded.destroy
|
401
|
+
}.to change { parent.reload.read_attribute(:embeddede) }.from({ "defined" => "in", "embedded" => true }).to(nil)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
context 'when there is no parent' do
|
406
|
+
let(:embedded) { Embeddede.new }
|
407
|
+
|
408
|
+
it 'returns false' do
|
409
|
+
expect(embedded.destroy).to eq(false)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
describe 'defined .embedson_model_changed! method' do
|
415
|
+
|
416
|
+
context 'when there is parent' do
|
417
|
+
let(:embedded) { Embeddede.new(parent: parent) }
|
418
|
+
|
419
|
+
it 'returns true' do
|
420
|
+
expect(embedded.embedson_model_changed!).to eq(true)
|
421
|
+
end
|
422
|
+
|
423
|
+
it 'assigns new value to parent' do
|
424
|
+
embedded.change = 'registered'
|
425
|
+
expect {
|
426
|
+
embedded.embedson_model_changed!
|
427
|
+
}.to change { parent.read_attribute(:embeddede) }.from({ "defined" => "in", "embedded" => true }).to({ "defined" => "in", "embedded" => true, 'change' => 'registered' })
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
context 'when there is no parent' do
|
432
|
+
it 'raises argument error' do
|
433
|
+
expect { embedded.embedson_model_changed! }.to raise_error(Embedson::NoParentError)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
describe 'defined .initialize method' do
|
439
|
+
|
440
|
+
|
441
|
+
class Embeddedf
|
442
|
+
extend Embedson::Model
|
443
|
+
|
444
|
+
attr_reader :one, :two
|
445
|
+
|
446
|
+
def initialize(attributes = {})
|
447
|
+
@one = attributes[:one]
|
448
|
+
@two = attributes[:two]
|
449
|
+
end
|
450
|
+
|
451
|
+
# when you put it before initialize
|
452
|
+
# Embeddedf.new(parent: parent)
|
453
|
+
# will not owkr
|
454
|
+
embedded_in :parent
|
455
|
+
|
456
|
+
def to_h
|
457
|
+
{ one: one, two: two }
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
let(:embedded) { Embeddedf.new(one: '1', two: '2', parent: parent)}
|
462
|
+
|
463
|
+
it 'allows to initialize with parent' do
|
464
|
+
expect(embedded.parent).to eq parent
|
465
|
+
end
|
466
|
+
|
467
|
+
it 'keeps initialize working' do
|
468
|
+
expect(embedded.to_h).to eq({ one: '1', two: '2' })
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
context 'when options include inverse_of' do
|
474
|
+
|
475
|
+
with_model :Parent do
|
476
|
+
table do |t|
|
477
|
+
t.json :emb
|
478
|
+
end
|
479
|
+
|
480
|
+
model do
|
481
|
+
embeds_one :emb, class_name: Embeddedg
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
class Embeddedg < OpenStruct
|
486
|
+
extend Embedson::Model
|
487
|
+
|
488
|
+
embedded_in :parent, inverse_of: :emb
|
489
|
+
|
490
|
+
def to_h
|
491
|
+
{ defined: 'in', embedded: true }
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
let(:parent) { Parent.new() }
|
496
|
+
let(:embedded) { Embeddedg.new() }
|
497
|
+
|
498
|
+
it 'adds "embeds_one" method' do
|
499
|
+
expect(Embeddedg).to respond_to(:embedded_in)
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'adds "parent" method' do
|
503
|
+
expect(embedded).to respond_to(:parent)
|
504
|
+
end
|
505
|
+
|
506
|
+
it 'adds "parent=" method' do
|
507
|
+
expect(embedded).to respond_to("parent=")
|
508
|
+
end
|
509
|
+
|
510
|
+
it 'assigns self to inverse_of value in parent class' do
|
511
|
+
embedded.parent = parent
|
512
|
+
expect(embedded.parent.emb).to eq embedded
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
context 'when options include class_name' do
|
517
|
+
|
518
|
+
with_model :Parented do
|
519
|
+
table do |t|
|
520
|
+
t.json :embeddedh
|
521
|
+
end
|
522
|
+
|
523
|
+
model do
|
524
|
+
embeds_one :embeddedh
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
class Embeddedh < OpenStruct
|
529
|
+
extend Embedson::Model
|
530
|
+
|
531
|
+
embedded_in :parent, class_name: 'Parented'
|
532
|
+
|
533
|
+
def to_h
|
534
|
+
{ defined: 'in', embedded: true }
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
let(:parent) { Parented.new }
|
539
|
+
let(:embedded) { Embeddedh.new }
|
540
|
+
|
541
|
+
it 'adds "embeds_one" method' do
|
542
|
+
expect(Embeddedh).to respond_to(:embedded_in)
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'adds "parent" method' do
|
546
|
+
expect(embedded).to respond_to(:parent)
|
547
|
+
end
|
548
|
+
|
549
|
+
it 'adds "parent=" method' do
|
550
|
+
expect(embedded).to respond_to("parent=")
|
551
|
+
end
|
552
|
+
|
553
|
+
describe 'defined .parent method' do
|
554
|
+
let(:embedded) { Embeddedh.new(parent: parent) }
|
555
|
+
|
556
|
+
it 'returns object of Parented class' do
|
557
|
+
expect(embedded.parent).to be_a(Parented)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
context 'when parent model does not have defined embeds_one' do
|
563
|
+
|
564
|
+
with_model :Parent do
|
565
|
+
table do |t|
|
566
|
+
t.json :embeddedi
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
class Embeddedi < OpenStruct
|
571
|
+
extend Embedson::Model
|
572
|
+
|
573
|
+
embedded_in :parent
|
574
|
+
|
575
|
+
def to_h
|
576
|
+
{ defined: 'in', embedded: true }
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
let(:parent) { Parent.new() }
|
581
|
+
let(:embedded) { Embeddedi.new() }
|
582
|
+
|
583
|
+
it 'adds "embeds_one" method' do
|
584
|
+
expect(Embeddedi).to respond_to(:embedded_in)
|
585
|
+
end
|
586
|
+
|
587
|
+
it 'adds "parent" method' do
|
588
|
+
expect(embedded).to respond_to(:parent)
|
589
|
+
end
|
590
|
+
|
591
|
+
it 'adds "parent=" method' do
|
592
|
+
expect(embedded).to respond_to("parent=")
|
593
|
+
end
|
594
|
+
|
595
|
+
describe 'defined .save method' do
|
596
|
+
let(:embedded) { Embeddedi.new(parent: parent) }
|
597
|
+
|
598
|
+
context 'when there is no to_json method' do
|
599
|
+
it 'raises TypeError' do
|
600
|
+
parent.save!
|
601
|
+
expect {
|
602
|
+
embedded.save!
|
603
|
+
}.to raise_error(TypeError)
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
248
609
|
end
|