modis 1.4.1-java

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,18 @@
1
+ require 'spec_helper'
2
+
3
+ module ErrorsSpec
4
+ class MockModel
5
+ include Modis::Model
6
+
7
+ attribute :name, :string
8
+ end
9
+ end
10
+
11
+ describe Modis::Errors do
12
+ let(:model) { ErrorsSpec::MockModel.new }
13
+
14
+ it 'adds errors' do
15
+ model.errors.add(:name, 'is not valid')
16
+ expect(model.errors[:name]).to eq(['is not valid'])
17
+ end
18
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ module FindersSpec
4
+ class User
5
+ include Modis::Model
6
+ self.namespace = 'users'
7
+
8
+ attribute :name, :string
9
+ attribute :age, :integer
10
+ attribute :parent_default, :string, default: 'omg'
11
+ end
12
+
13
+ class Consumer < User
14
+ attribute :consumed, :boolean
15
+ end
16
+
17
+ class Producer < User
18
+ attribute :child_default, :string, default: 'derp'
19
+ end
20
+ end
21
+
22
+ describe Modis::Finder do
23
+ let!(:model) { FindersSpec::User.create!(name: 'Ian', age: 28) }
24
+ let(:found) { FindersSpec::User.find(model.id) }
25
+
26
+ it 'finds by ID' do
27
+ expect(found.id).to eq(model.id)
28
+ expect(found.name).to eq(model.name)
29
+ expect(found.age).to eq(model.age)
30
+ end
31
+
32
+ it 'finds multiple by ID' do
33
+ model1 = FindersSpec::User.create!(name: 'Ian', age: 28)
34
+ model2 = FindersSpec::User.create!(name: 'Tanya', age: 32)
35
+ model3 = FindersSpec::User.create!(name: 'Kyle', age: 35)
36
+ models = FindersSpec::User.find(model1.id, model2.id, model3.id)
37
+ expect(models).to eq([model1, model2, model3])
38
+ end
39
+
40
+ it 'raises an error a record could not be found' do
41
+ expect do
42
+ FindersSpec::User.find(model.id + 1)
43
+ end.to raise_error(Modis::RecordNotFound, "Couldn't find FindersSpec::User with id=#{model.id + 1}")
44
+
45
+ expect do
46
+ FindersSpec::User.find(model.id, model.id + 1)
47
+ end.to raise_error(Modis::RecordNotFound, "Couldn't find FindersSpec::User with id=#{model.id + 1}")
48
+ end
49
+
50
+ it 'does not flag an attribute as dirty on a found instance' do
51
+ expect(found.id_changed?).to be false
52
+ end
53
+
54
+ describe 'all' do
55
+ it 'returns all records' do
56
+ m2 = FindersSpec::User.create!(name: 'Tanya', age: 30)
57
+ m3 = FindersSpec::User.create!(name: 'Kyle', age: 32)
58
+ expect(FindersSpec::User.all).to eq([model, m2, m3])
59
+ end
60
+
61
+ it 'does not return a destroyed record' do
62
+ model.destroy
63
+ expect(FindersSpec::User.all).to eq([])
64
+ end
65
+ end
66
+
67
+ it 'identifies a found record as not being new' do
68
+ expect(found.new_record?).to be false
69
+ end
70
+
71
+ describe 'Single Table Inheritance' do
72
+ it 'returns the correct namespace' do
73
+ expect(FindersSpec::Consumer.namespace).to eq('users')
74
+ expect(FindersSpec::Consumer.absolute_namespace).to eq('modis:users')
75
+ expect(FindersSpec::Producer.namespace).to eq('users')
76
+ expect(FindersSpec::Producer.absolute_namespace).to eq('modis:users')
77
+ end
78
+
79
+ it 'returns instances of the correct class' do
80
+ FindersSpec::Consumer.create!(name: 'Kyle')
81
+ FindersSpec::Producer.create!(name: 'Tanya')
82
+
83
+ models = FindersSpec::User.all
84
+
85
+ ian = models.find { |model| model.name == 'Ian' }
86
+ kyle = models.find { |model| model.name == 'Kyle' }
87
+ tanya = models.find { |model| model.name == 'Tanya' }
88
+
89
+ expect(ian).to be_kind_of(FindersSpec::User)
90
+ expect(kyle).to be_kind_of(FindersSpec::Consumer)
91
+ expect(tanya).to be_kind_of(FindersSpec::Producer)
92
+
93
+ expect(FindersSpec::User.find(ian.id)).to be_kind_of(FindersSpec::User)
94
+ expect(FindersSpec::User.find(kyle.id)).to be_kind_of(FindersSpec::Consumer)
95
+ expect(FindersSpec::User.find(tanya.id)).to be_kind_of(FindersSpec::Producer)
96
+ end
97
+
98
+ it 'inherits attributes from the parent' do
99
+ consumer = FindersSpec::Consumer.create!(name: 'Kyle', consumed: true)
100
+ expect(consumer.attributes.keys.sort).to eq(%w(age consumed id name parent_default type))
101
+ end
102
+
103
+ it 'inherits default attribute values from the parent' do
104
+ producer = FindersSpec::Producer.create!(name: 'Kyle')
105
+ expect(producer.parent_default).to eq('omg')
106
+ expect(producer.child_default).to eq('derp')
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ module IndexSpec
4
+ class MockModel
5
+ include Modis::Model
6
+
7
+ attribute :name, :string
8
+ index :name
9
+ end
10
+ end
11
+
12
+ describe Modis::Index do
13
+ let!(:model) { IndexSpec::MockModel.create!(name: 'Ian') }
14
+
15
+ describe 'create' do
16
+ it 'adds a new model to the index' do
17
+ index = IndexSpec::MockModel.index_for(:name, 'Ian')
18
+ expect(index).to include(model.id)
19
+ end
20
+ end
21
+
22
+ describe 'update' do
23
+ before do
24
+ model.name = 'Kyle'
25
+ model.save!
26
+ end
27
+
28
+ it 'adds the model to the new index' do
29
+ index = IndexSpec::MockModel.index_for(:name, 'Kyle')
30
+ expect(index).to include(model.id)
31
+ end
32
+
33
+ it 'removes the model from the old index' do
34
+ index = IndexSpec::MockModel.index_for(:name, 'Ian')
35
+ expect(index).to_not include(model.id)
36
+ end
37
+ end
38
+
39
+ describe 'destroy' do
40
+ it 'removes a destroyed model id from the index' do
41
+ model.destroy
42
+ index = IndexSpec::MockModel.index_for(:name, 'Ian')
43
+ expect(index).to_not include(model.id)
44
+ end
45
+
46
+ it 'does not find a destroyed model' do
47
+ model.destroy
48
+ models = IndexSpec::MockModel.where(name: 'Ian')
49
+ expect(models).to be_empty
50
+ end
51
+ end
52
+
53
+ it 'finds by index' do
54
+ models = IndexSpec::MockModel.where(name: 'Ian')
55
+ expect(models.first.name).to eq('Ian')
56
+ end
57
+
58
+ it 'finds multiple matches' do
59
+ IndexSpec::MockModel.create!(name: 'Ian')
60
+ models = IndexSpec::MockModel.where(name: 'Ian')
61
+ expect(models.count).to eq(2)
62
+ end
63
+
64
+ it 'returns an empty array if there are no results' do
65
+ expect(IndexSpec::MockModel.where(name: 'Foo')).to be_empty
66
+ end
67
+
68
+ it 'raises an error when trying to query against multiple indexes' do
69
+ expect { IndexSpec::MockModel.where(name: 'Ian', age: 29) }.to raise_error(Modis::IndexError, 'Queries using multiple indexes is not currently supported.')
70
+ end
71
+
72
+ it 'indexes a nil value' do
73
+ model.name = nil
74
+ model.save!
75
+ expect(IndexSpec::MockModel.where(name: nil)).to include(model)
76
+ end
77
+
78
+ it 'distinguishes between nil and blank string values' do
79
+ model1 = IndexSpec::MockModel.create!(name: nil)
80
+ model2 = IndexSpec::MockModel.create!(name: "")
81
+ expect(IndexSpec::MockModel.where(name: nil)).to eq([model1])
82
+ expect(IndexSpec::MockModel.where(name: "")).to eq([model2])
83
+ end
84
+ end
@@ -0,0 +1,319 @@
1
+ require 'spec_helper'
2
+
3
+ module PersistenceSpec
4
+ class MockModel
5
+ include Modis::Model
6
+
7
+ attribute :name, :string, default: 'Ian'
8
+ attribute :age, :integer
9
+ validates :name, presence: true
10
+
11
+ before_create :test_before_create
12
+ after_create :test_after_create
13
+
14
+ before_update :test_before_update
15
+ after_update :test_after_update
16
+
17
+ before_save :test_before_save
18
+ after_save :test_after_save
19
+
20
+ def called_callbacks
21
+ @called_callbacks ||= []
22
+ end
23
+
24
+ def test_after_create
25
+ called_callbacks << :test_after_create
26
+ end
27
+
28
+ def test_before_create
29
+ called_callbacks << :test_before_create
30
+ end
31
+
32
+ def test_after_update
33
+ called_callbacks << :test_after_update
34
+ end
35
+
36
+ def test_before_update
37
+ called_callbacks << :test_before_update
38
+ end
39
+
40
+ def test_after_save
41
+ called_callbacks << :test_after_save
42
+ end
43
+
44
+ def test_before_save
45
+ called_callbacks << :test_before_save
46
+ end
47
+ end
48
+ end
49
+
50
+ describe Modis::Persistence do
51
+ let(:model) { PersistenceSpec::MockModel.new }
52
+
53
+ describe 'namespaces' do
54
+ it 'returns the namespace' do
55
+ expect(PersistenceSpec::MockModel.namespace).to eq('persistence_spec:mock_model')
56
+ end
57
+
58
+ it 'returns the absolute namespace' do
59
+ expect(PersistenceSpec::MockModel.absolute_namespace).to eq('modis:persistence_spec:mock_model')
60
+ end
61
+
62
+ it 'allows the namespace to be set explicitly' do
63
+ PersistenceSpec::MockModel.namespace = 'other'
64
+ expect(PersistenceSpec::MockModel.absolute_namespace).to eq('modis:other')
65
+ end
66
+
67
+ after { PersistenceSpec::MockModel.namespace = nil }
68
+ end
69
+
70
+ it 'returns a key' do
71
+ model.save!
72
+ expect(model.key).to eq('modis:persistence_spec:mock_model:1')
73
+ end
74
+
75
+ it 'returns a nil key if not saved' do
76
+ expect(model.key).to be_nil
77
+ end
78
+
79
+ it 'works with ActiveModel dirty tracking' do
80
+ expect { model.name = 'Kyle' }.to change(model, :changed).to(['name'])
81
+ expect(model.name_changed?).to be true
82
+ end
83
+
84
+ it 'resets dirty tracking when saved' do
85
+ model.name = 'Kyle'
86
+ expect(model.name_changed?).to be true
87
+ model.save!
88
+ expect(model.name_changed?).to be false
89
+ end
90
+
91
+ it 'resets dirty tracking when created' do
92
+ model = PersistenceSpec::MockModel.create!(name: 'Ian')
93
+ expect(model.name_changed?).to be false
94
+ end
95
+
96
+ it 'does not identify an attribute as changed if the value is the default' do
97
+ expect(model.class.attributes_with_defaults['name']).to eq('Ian')
98
+ expect(model.name).to eq('Ian')
99
+ expect(model.name_changed?).to be false
100
+ end
101
+
102
+ it 'is persisted' do
103
+ expect(model.persisted?).to be true
104
+ end
105
+
106
+ it 'does not track the ID if the underlying Redis command failed' do
107
+ redis = double(hmset: double(value: nil), sadd: nil)
108
+ expect(model.class).to receive(:transaction).and_yield(redis)
109
+ expect(redis).to receive(:pipelined).and_yield
110
+ model.save
111
+ expect { model.class.find(model.id) }.to raise_error(Modis::RecordNotFound)
112
+ end
113
+
114
+ it 'does not perform validation if validate: false' do
115
+ model.name = nil
116
+ expect(model.valid?).to be false
117
+ expect { model.save!(validate: false) }.to_not raise_error
118
+ model.reload
119
+ expect(model.name).to be_nil
120
+
121
+ expect(model.save(validate: false)).to be true
122
+ end
123
+
124
+ describe 'an existing record' do
125
+ it 'only updates dirty attributes' do
126
+ model.name = 'Ian'
127
+ model.age = 10
128
+ model.save!
129
+ model.age = 11
130
+ redis = double
131
+ expect(redis).to receive(:hmset).with("modis:persistence_spec:mock_model:1", ["age", "\v"]).and_return(double(value: 'OK'))
132
+ expect(model.class).to receive(:transaction).and_yield(redis)
133
+ expect(redis).to receive(:pipelined).and_yield
134
+ model.save!
135
+ expect(model.age).to eq(11)
136
+ end
137
+ end
138
+
139
+ describe 'reload' do
140
+ it 'reloads attributes' do
141
+ model.save!
142
+ model2 = model.class.find(model.id)
143
+ model2.name = 'Changed'
144
+ model2.save!
145
+ expect { model.reload }.to change(model, :name).to('Changed')
146
+ end
147
+
148
+ it 'resets dirty tracking' do
149
+ model.save!
150
+ model.name = 'Foo'
151
+ expect(model.name_changed?).to be true
152
+ model.reload
153
+ expect(model.name_changed?).to be false
154
+ end
155
+
156
+ it 'raises an error if the record has not been saved' do
157
+ expect { model.reload }.to raise_error(Modis::RecordNotFound, "Couldn't find PersistenceSpec::MockModel without an ID")
158
+ end
159
+ end
160
+
161
+ describe 'callbacks' do
162
+ it 'preserves dirty state for the duration of the callback life cycle'
163
+ it 'halts the chain if a callback returns false'
164
+
165
+ describe 'a new record' do
166
+ it 'calls the before_create callback' do
167
+ model.save!
168
+ expect(model.called_callbacks).to include(:test_before_create)
169
+ end
170
+
171
+ it 'calls the after create callback' do
172
+ model.save!
173
+ expect(model.called_callbacks).to include(:test_after_create)
174
+ end
175
+ end
176
+
177
+ describe 'an existing record' do
178
+ before { model.save! }
179
+
180
+ it 'calls the before_update callback' do
181
+ model.save!
182
+ expect(model.called_callbacks).to include(:test_before_update)
183
+ end
184
+
185
+ it 'calls the after update callback' do
186
+ model.save!
187
+ expect(model.called_callbacks).to include(:test_after_update)
188
+ end
189
+ end
190
+
191
+ it 'calls the before_save callback' do
192
+ model.save!
193
+ expect(model.called_callbacks).to include(:test_before_save)
194
+ end
195
+
196
+ it 'calls the after save callback' do
197
+ model.save!
198
+ expect(model.called_callbacks).to include(:test_after_save)
199
+ end
200
+ end
201
+
202
+ describe 'create' do
203
+ it 'resets dirty tracking' do
204
+ model = PersistenceSpec::MockModel.create(name: 'Ian')
205
+ expect(model.name_changed?).to be false
206
+ end
207
+
208
+ describe 'a valid model' do
209
+ it 'returns the created model' do
210
+ model = PersistenceSpec::MockModel.create(name: 'Ian')
211
+ expect(model.valid?).to be true
212
+ expect(model.new_record?).to be false
213
+ end
214
+ end
215
+
216
+ describe 'an invalid model' do
217
+ it 'returns the unsaved model' do
218
+ model = PersistenceSpec::MockModel.create(name: nil)
219
+ expect(model.valid?).to be false
220
+ expect(model.new_record?).to be true
221
+ end
222
+ end
223
+ end
224
+
225
+ describe 'update_attribute' do
226
+ it 'does not perform validation' do
227
+ model.name = nil
228
+ expect(model.valid?).to be false
229
+ model.name = 'Test'
230
+ model.update_attribute(:name, nil)
231
+ end
232
+
233
+ it 'invokes callbacks' do
234
+ model.update_attribute(:name, 'Derp')
235
+ expect(model.called_callbacks).to_not be_empty
236
+ end
237
+
238
+ it 'updates all dirty attributes' do
239
+ model.age = 29
240
+ model.update_attribute(:name, 'Derp')
241
+ model.reload
242
+ expect(model.age).to eq 29
243
+ end
244
+ end
245
+
246
+ describe 'update_attributes!' do
247
+ it 'updates the given attributes' do
248
+ model.update_attributes!(name: 'Derp', age: 29)
249
+ model.reload
250
+ expect(model.name).to eq 'Derp'
251
+ expect(model.age).to eq 29
252
+ end
253
+
254
+ it 'invokes callbacks' do
255
+ model.update_attributes!(name: 'Derp')
256
+ expect(model.called_callbacks).to_not be_empty
257
+ end
258
+
259
+ it 'updates all dirty attributes' do
260
+ model.age = 29
261
+ model.update_attributes!(name: 'Derp')
262
+ model.reload
263
+ expect(model.age).to eq 29
264
+ end
265
+
266
+ it 'raises an error if the model is invalid' do
267
+ expect do
268
+ model.update_attributes!(name: nil).to be false
269
+ end.to raise_error(Modis::RecordInvalid)
270
+ end
271
+ end
272
+
273
+ describe 'update_attributes' do
274
+ it 'updates the given attributes' do
275
+ model.update_attributes(name: 'Derp', age: 29)
276
+ model.reload
277
+ expect(model.name).to eq('Derp')
278
+ expect(model.age).to eq(29)
279
+ end
280
+
281
+ it 'invokes callbacks' do
282
+ model.update_attributes(name: 'Derp')
283
+ expect(model.called_callbacks).to_not be_empty
284
+ end
285
+
286
+ it 'updates all dirty attributes' do
287
+ model.age = 29
288
+ model.update_attributes(name: 'Derp')
289
+ model.reload
290
+ expect(model.age).to eq(29)
291
+ end
292
+
293
+ it 'returns false if the model is invalid' do
294
+ expect(model.update_attributes(name: nil)).to be false
295
+ end
296
+ end
297
+
298
+ describe 'YAML backward compatability' do
299
+ it 'loads a YAML serialized value' do
300
+ Modis.with_connection do |redis|
301
+ model.save!
302
+ key = model.class.key_for(model.id)
303
+ record = redis.hgetall(key)
304
+ record['age'] = YAML.dump(30)
305
+ redis.hmset(key, *record.to_a)
306
+ record = redis.hgetall(key)
307
+
308
+ expect(record['age']).to eq("--- 30\n...\n")
309
+
310
+ model.reload
311
+ expect(model.age).to eq(30)
312
+
313
+ model.save!(yaml_sucks: true)
314
+ record = redis.hgetall(key)
315
+ expect(record['age']).to eq("\x1E")
316
+ end
317
+ end
318
+ end
319
+ end