modis 0.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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +15 -0
- data/lib/modis/attributes.rb +136 -0
- data/lib/modis/configuration.rb +12 -0
- data/lib/modis/errors.rb +14 -0
- data/lib/modis/finders.rb +43 -0
- data/lib/modis/model.rb +50 -0
- data/lib/modis/persistence.rb +162 -0
- data/lib/modis/transaction.rb +13 -0
- data/lib/modis/version.rb +3 -0
- data/lib/modis.rb +27 -0
- data/lib/tasks/spec.rake +18 -0
- data/redis_model.gemspec +23 -0
- data/spec/attributes_spec.rb +179 -0
- data/spec/errors_spec.rb +18 -0
- data/spec/finders_spec.rb +84 -0
- data/spec/persistence_spec.rb +266 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/simplecov_helper.rb +19 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/transaction_spec.rb +14 -0
- data/spec/validations_spec.rb +49 -0
- metadata +123 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module AttributesSpec
|
4
|
+
class MockModel
|
5
|
+
include Modis::Model
|
6
|
+
|
7
|
+
attribute :name, :string, default: 'Janet'
|
8
|
+
attribute :age, :integer, default: 60
|
9
|
+
attribute :percentage, :float
|
10
|
+
attribute :created_at, :timestamp
|
11
|
+
attribute :flag, :boolean
|
12
|
+
attribute :array, :array
|
13
|
+
attribute :hash, :hash
|
14
|
+
attribute :hash_strict, :hash, strict: true
|
15
|
+
attribute :hash_not_strict, :hash, strict: false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Modis::Attributes do
|
20
|
+
let(:model) { AttributesSpec::MockModel.new }
|
21
|
+
|
22
|
+
it 'defines attributes' do
|
23
|
+
model.name = 'bar'
|
24
|
+
model.name.should == 'bar'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'applies an default value' do
|
28
|
+
model.name.should eq 'Janet'
|
29
|
+
model.age.should eq 60
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does not mark an attribute with a default as dirty' do
|
33
|
+
model.name_changed?.should be_false
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises an error for an unsupported attribute type' do
|
37
|
+
expect do
|
38
|
+
class AttributesSpec::MockModel
|
39
|
+
attribute :unsupported, :symbol
|
40
|
+
end
|
41
|
+
end.to raise_error(Modis::UnsupportedAttributeType)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'assigns attributes' do
|
45
|
+
model.assign_attributes(name: 'bar')
|
46
|
+
model.name.should eq 'bar'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'does not attempt to assign attributes that are not defined on the model' do
|
50
|
+
model.assign_attributes(missing_attr: 'derp')
|
51
|
+
model.respond_to?(:missing_attr).should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'allows an attribute to be nilled' do
|
55
|
+
model.name = nil
|
56
|
+
model.save!
|
57
|
+
model.class.find(model.id).name.should be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ':string type' do
|
61
|
+
it 'is coerced' do
|
62
|
+
model.name = 'Ian'
|
63
|
+
model.save!
|
64
|
+
found = AttributesSpec::MockModel.find(model.id)
|
65
|
+
found.name.should eq 'Ian'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ':integer type' do
|
70
|
+
it 'is coerced' do
|
71
|
+
model.age = 18
|
72
|
+
model.save!
|
73
|
+
found = AttributesSpec::MockModel.find(model.id)
|
74
|
+
found.age.should eq 18
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe ':float type' do
|
79
|
+
it 'is coerced' do
|
80
|
+
model.percentage = 18.6
|
81
|
+
model.save!
|
82
|
+
found = AttributesSpec::MockModel.find(model.id)
|
83
|
+
found.percentage.should eq 18.6
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'coerces a string representation to Float' do
|
87
|
+
model.percentage = '18.6'
|
88
|
+
model.save!
|
89
|
+
found = AttributesSpec::MockModel.find(model.id)
|
90
|
+
found.percentage.should eq 18.6
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe ':timestamp type' do
|
95
|
+
it 'is coerced' do
|
96
|
+
now = Time.now
|
97
|
+
model.created_at = now
|
98
|
+
model.save!
|
99
|
+
found = AttributesSpec::MockModel.find(model.id)
|
100
|
+
found.created_at.should be_kind_of(Time)
|
101
|
+
found.created_at.to_s.should eq now.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'coerces a string representation to Time' do
|
105
|
+
now = Time.now
|
106
|
+
model.created_at = now.to_s
|
107
|
+
model.save!
|
108
|
+
found = AttributesSpec::MockModel.find(model.id)
|
109
|
+
found.created_at.should be_kind_of(Time)
|
110
|
+
found.created_at.to_s.should eq now.to_s
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe ':boolean type' do
|
115
|
+
it 'is coerced' do
|
116
|
+
model.flag = 'true'
|
117
|
+
model.save!
|
118
|
+
found = AttributesSpec::MockModel.find(model.id)
|
119
|
+
found.flag.should eq true
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'raises an error if assigned a non-boolean value' do
|
123
|
+
expect { model.flag = 'unf!' }.to raise_error(Modis::AttributeCoercionError)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe ':array type' do
|
128
|
+
it 'is coerced' do
|
129
|
+
model.array = [1, 2, 3]
|
130
|
+
model.save!
|
131
|
+
found = AttributesSpec::MockModel.find(model.id)
|
132
|
+
found.array.should eq [1, 2, 3]
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'raises an error when assigned another type' do
|
136
|
+
expect { model.array = {foo: :bar} }.to raise_error(Modis::AttributeCoercionError)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'does not raise an error when assigned a JSON array string' do
|
140
|
+
expect { model.array = "[1,2,3]" }.to_not raise_error
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'does not raise an error when a JSON string does not deserialize to an Array' do
|
144
|
+
expect { model.array = "{\"foo\":\"bar\"}" }.to raise_error(Modis::AttributeCoercionError)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe ':hash type' do
|
149
|
+
it 'is coerced' do
|
150
|
+
model.hash = {foo: :bar}
|
151
|
+
model.save!
|
152
|
+
found = AttributesSpec::MockModel.find(model.id)
|
153
|
+
found.hash.should eq({'foo' => 'bar'})
|
154
|
+
end
|
155
|
+
|
156
|
+
describe 'strict: true' do
|
157
|
+
it 'raises an error if the value cannot be decoded on assignment' do
|
158
|
+
expect { model.hash_strict = "test" }.to raise_error(MultiJson::ParseError)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'does not raise an error if the value can be decoded on assignment' do
|
162
|
+
expect { model.hash_strict = "{\"foo\":\"bar\"}" }.to_not raise_error
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'strict: false' do
|
167
|
+
it 'returns the value if it cannot be decoded' do
|
168
|
+
model.write_attribute(:hash_not_strict, "test")
|
169
|
+
model.save!
|
170
|
+
found = AttributesSpec::MockModel.find(model.id)
|
171
|
+
found.hash_not_strict.should eq "test"
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'does not raise an error when assigned another type' do
|
175
|
+
expect { model.hash_not_strict = "test" }.to_not raise_error
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/spec/errors_spec.rb
ADDED
@@ -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
|
+
model.errors[:name].should eq ['is not valid']
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,84 @@
|
|
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
|
+
end
|
11
|
+
|
12
|
+
class Consumer < User
|
13
|
+
end
|
14
|
+
|
15
|
+
class Producer < User
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Modis::Finders do
|
20
|
+
let!(:model) { FindersSpec::User.create!(name: 'Ian', age: 28) }
|
21
|
+
let(:found) { FindersSpec::User.find(model.id) }
|
22
|
+
|
23
|
+
it 'finds by ID' do
|
24
|
+
found.id.should eq model.id
|
25
|
+
found.name.should eq model.name
|
26
|
+
found.age.should eq model.age
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'raises an error if the record could not be found' do
|
30
|
+
expect do
|
31
|
+
FindersSpec::User.find(model.id + 1)
|
32
|
+
end.to raise_error(Modis::RecordNotFound, "Couldn't find FindersSpec::User with id=#{model.id + 1}")
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'does not flag an attribute as dirty on a found instance' do
|
36
|
+
found.id_changed?.should be_false
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'all' do
|
40
|
+
it 'returns all records' do
|
41
|
+
m2 = FindersSpec::User.create!(name: 'Tanya', age: 30)
|
42
|
+
m3 = FindersSpec::User.create!(name: 'Kyle', age: 32)
|
43
|
+
|
44
|
+
FindersSpec::User.all.should == [model, m2, m3]
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not return a destroyed record' do
|
48
|
+
model.destroy
|
49
|
+
FindersSpec::User.all.should == []
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'identifies a found record as not being new' do
|
54
|
+
found.new_record?.should be_false
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'Single Table Inheritance' do
|
58
|
+
it 'returns the correct namespace' do
|
59
|
+
FindersSpec::Consumer.namespace.should eq 'users'
|
60
|
+
FindersSpec::Consumer.absolute_namespace.should eq 'modis:users'
|
61
|
+
FindersSpec::Producer.namespace.should eq 'users'
|
62
|
+
FindersSpec::Producer.absolute_namespace.should eq 'modis:users'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns instances of the correct class' do
|
66
|
+
FindersSpec::Consumer.create!(name: 'Kyle')
|
67
|
+
FindersSpec::Producer.create!(name: 'Tanya')
|
68
|
+
|
69
|
+
models = FindersSpec::User.all
|
70
|
+
|
71
|
+
ian = models.find { |model| model.name == 'Ian' }
|
72
|
+
kyle = models.find { |model| model.name == 'Kyle' }
|
73
|
+
tanya = models.find { |model| model.name == 'Tanya' }
|
74
|
+
|
75
|
+
ian.should be_kind_of(FindersSpec::User)
|
76
|
+
kyle.should be_kind_of(FindersSpec::Consumer)
|
77
|
+
tanya.should be_kind_of(FindersSpec::Producer)
|
78
|
+
|
79
|
+
FindersSpec::User.find(ian.id).should be_kind_of(FindersSpec::User)
|
80
|
+
FindersSpec::User.find(kyle.id).should be_kind_of(FindersSpec::Consumer)
|
81
|
+
FindersSpec::User.find(tanya.id).should be_kind_of(FindersSpec::Producer)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,266 @@
|
|
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
|
+
PersistenceSpec::MockModel.namespace.should eq 'persistence_spec:mock_model'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns the absolute namespace' do
|
59
|
+
PersistenceSpec::MockModel.absolute_namespace.should 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
|
+
PersistenceSpec::MockModel.absolute_namespace.should 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
|
+
model.key.should eq 'modis:persistence_spec:mock_model:1'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns a nil key if not saved' do
|
76
|
+
model.key.should 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
|
+
model.name_changed?.should be_true
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'resets dirty tracking when saved' do
|
85
|
+
model.name = 'Kyle'
|
86
|
+
model.name_changed?.should be_true
|
87
|
+
model.save!
|
88
|
+
model.name_changed?.should be_false
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'resets dirty tracking when created' do
|
92
|
+
model = PersistenceSpec::MockModel.create!(name: 'Ian')
|
93
|
+
model.name_changed?.should be_false
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'is persisted' do
|
97
|
+
model.persisted?.should be_true
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'does not track the ID if the underlying Redis command failed'
|
101
|
+
|
102
|
+
it 'does not perform validation if validate: false' do
|
103
|
+
model.name = nil
|
104
|
+
model.valid?.should be_false
|
105
|
+
expect { model.save!(validate: false) }.to_not raise_error
|
106
|
+
model.reload
|
107
|
+
model.name.should be_nil
|
108
|
+
|
109
|
+
model.save(validate: false).should be_true
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'an existing record' do
|
113
|
+
it 'only updates dirty attributes'
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'reload' do
|
117
|
+
it 'reloads attributes' do
|
118
|
+
model.save!
|
119
|
+
model2 = model.class.find(model.id)
|
120
|
+
model2.name = 'Changed'
|
121
|
+
model2.save!
|
122
|
+
expect { model.reload }.to change(model, :name).to('Changed')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'resets dirty tracking' do
|
126
|
+
model.save!
|
127
|
+
model.name = 'Foo'
|
128
|
+
model.name_changed?.should be_true
|
129
|
+
model.reload
|
130
|
+
model.name_changed?.should be_false
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'raises an error if the record has not been saved' do
|
134
|
+
expect { model.reload }.to raise_error(Modis::RecordNotFound, "Couldn't find PersistenceSpec::MockModel without an ID")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe 'callbacks' do
|
139
|
+
it 'preserves dirty state for the duration of the callback life cycle'
|
140
|
+
it 'halts the chain if a callback returns false'
|
141
|
+
|
142
|
+
describe 'a new record' do
|
143
|
+
it 'calls the before_create callback' do
|
144
|
+
model.save!
|
145
|
+
model.called_callbacks.should include(:test_before_create)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'calls the after create callback' do
|
149
|
+
model.save!
|
150
|
+
model.called_callbacks.should include(:test_after_create)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe 'an existing record' do
|
155
|
+
before { model.save! }
|
156
|
+
|
157
|
+
it 'calls the before_update callback' do
|
158
|
+
model.save!
|
159
|
+
model.called_callbacks.should include(:test_before_update)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'calls the after update callback' do
|
163
|
+
model.save!
|
164
|
+
model.called_callbacks.should include(:test_after_update)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'calls the before_save callback' do
|
169
|
+
model.save!
|
170
|
+
model.called_callbacks.should include(:test_before_save)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'calls the after save callback' do
|
174
|
+
model.save!
|
175
|
+
model.called_callbacks.should include(:test_after_save)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'create' do
|
180
|
+
it 'resets dirty tracking' do
|
181
|
+
model = PersistenceSpec::MockModel.create(name: 'Ian')
|
182
|
+
model.name_changed?.should be_false
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'a valid model' do
|
186
|
+
it 'returns the created model'
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'an invalid model' do
|
190
|
+
it 'returns the unsaved model'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe 'update_attribute' do
|
195
|
+
it 'does not perform validation' do
|
196
|
+
model.name = nil
|
197
|
+
model.valid?.should be_false
|
198
|
+
model.name = 'Test'
|
199
|
+
model.update_attribute(:name, nil)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'invokes callbacks' do
|
203
|
+
model.update_attribute(:name, 'Derp')
|
204
|
+
model.called_callbacks.should_not be_empty
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'updates all dirty attributes' do
|
208
|
+
model.age = 29
|
209
|
+
model.update_attribute(:name, 'Derp')
|
210
|
+
model.reload
|
211
|
+
model.age.should eq 29
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe 'update_attributes!' do
|
216
|
+
it 'updates the given attributes' do
|
217
|
+
model.update_attributes!(name: 'Derp', age: 29)
|
218
|
+
model.reload
|
219
|
+
model.name.should eq 'Derp'
|
220
|
+
model.age.should eq 29
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'invokes callbacks' do
|
224
|
+
model.update_attributes!(name: 'Derp')
|
225
|
+
model.called_callbacks.should_not be_empty
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'updates all dirty attributes' do
|
229
|
+
model.age = 29
|
230
|
+
model.update_attributes!(name: 'Derp')
|
231
|
+
model.reload
|
232
|
+
model.age.should eq 29
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'raises an error if the model is invalid' do
|
236
|
+
expect do
|
237
|
+
model.update_attributes!(name: nil).should be_false
|
238
|
+
end.to raise_error(Modis::RecordInvalid)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe 'update_attributes' do
|
243
|
+
it 'updates the given attributes' do
|
244
|
+
model.update_attributes(name: 'Derp', age: 29)
|
245
|
+
model.reload
|
246
|
+
model.name.should eq 'Derp'
|
247
|
+
model.age.should eq 29
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'invokes callbacks' do
|
251
|
+
model.update_attributes(name: 'Derp')
|
252
|
+
model.called_callbacks.should_not be_empty
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'updates all dirty attributes' do
|
256
|
+
model.age = 29
|
257
|
+
model.update_attributes(name: 'Derp')
|
258
|
+
model.reload
|
259
|
+
model.age.should eq 29
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'returns false if the model is invalid' do
|
263
|
+
model.update_attributes(name: nil).should be_false
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
begin
|
2
|
+
require './spec/support/simplecov_helper'
|
3
|
+
include SimpleCovHelper
|
4
|
+
start_simple_cov('unit')
|
5
|
+
rescue LoadError
|
6
|
+
puts "Coverage disabled."
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'modis'
|
10
|
+
|
11
|
+
Modis.configure do |config|
|
12
|
+
config.namespace = 'modis'
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.after :each do
|
17
|
+
keys = Modis.redis.keys "#{Modis.config.namespace}:*"
|
18
|
+
Modis.redis.del *keys unless keys.empty?
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require './spec/support/simplecov_quality_formatter'
|
3
|
+
|
4
|
+
module SimpleCovHelper
|
5
|
+
def start_simple_cov(name)
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter '/spec/'
|
8
|
+
command_name name
|
9
|
+
|
10
|
+
if ENV['TRAVIS']
|
11
|
+
require 'coveralls'
|
12
|
+
formatter SimpleCov::Formatter::MultiFormatter[SimpleCov::Formatter::QualityFormatter,
|
13
|
+
Coveralls::SimpleCov::Formatter]
|
14
|
+
else
|
15
|
+
formatter SimpleCov::Formatter::QualityFormatter
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module TransactionSpec
|
4
|
+
class MockModel
|
5
|
+
include Modis::Model
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Modis::Transaction do
|
10
|
+
it 'yields the block in a transaction' do
|
11
|
+
Modis.redis.should_receive(:multi).and_yield
|
12
|
+
TransactionSpec::MockModel.transaction {}
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'validations' do
|
4
|
+
class TestModel
|
5
|
+
include Modis::Model
|
6
|
+
attribute :name, :string
|
7
|
+
validates :name, presence: true
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:model) { TestModel.new }
|
11
|
+
|
12
|
+
it 'responds to valid?' do
|
13
|
+
model.name = nil
|
14
|
+
model.valid?.should be_false
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets errors on the model' do
|
18
|
+
model.name = nil
|
19
|
+
model.valid?
|
20
|
+
model.errors[:name].should eq ["can't be blank"]
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'save' do
|
24
|
+
it 'returns true if the model is valid' do
|
25
|
+
model.name = "Ian"
|
26
|
+
model.save.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns false if the model is invalid' do
|
30
|
+
model.name = nil
|
31
|
+
model.save.should be_false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'save!' do
|
36
|
+
it 'raises an error if the model is invalid' do
|
37
|
+
model.name = nil
|
38
|
+
expect do
|
39
|
+
model.save!.should be_false
|
40
|
+
end.to raise_error(Modis::RecordInvalid)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'create!' do
|
45
|
+
it 'raises an error if the record is not valid' do
|
46
|
+
expect { TestModel.create!(name: nil) }.to raise_error(Modis::RecordInvalid)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|