reorm 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +219 -0
  7. data/Rakefile +2 -0
  8. data/config/database.yml +11 -0
  9. data/lib/reorm/configuration.rb +12 -0
  10. data/lib/reorm/cursor.rb +162 -0
  11. data/lib/reorm/exceptions.rb +13 -0
  12. data/lib/reorm/field_path.rb +53 -0
  13. data/lib/reorm/model.rb +132 -0
  14. data/lib/reorm/modules/database_modules.rb +67 -0
  15. data/lib/reorm/modules/event_modules.rb +82 -0
  16. data/lib/reorm/modules/validation_modules.rb +29 -0
  17. data/lib/reorm/modules.rb +7 -0
  18. data/lib/reorm/property_errors.rb +53 -0
  19. data/lib/reorm/validators/exclusion_validator.rb +18 -0
  20. data/lib/reorm/validators/inclusion_validator.rb +18 -0
  21. data/lib/reorm/validators/maximum_length_validator.rb +19 -0
  22. data/lib/reorm/validators/minimum_length_validator.rb +19 -0
  23. data/lib/reorm/validators/presence_validator.rb +17 -0
  24. data/lib/reorm/validators/validator.rb +13 -0
  25. data/lib/reorm/validators.rb +10 -0
  26. data/lib/reorm/version.rb +6 -0
  27. data/lib/reorm.rb +47 -0
  28. data/reorm.gemspec +30 -0
  29. data/spec/catwalk/modules/timestamped_spec.rb +17 -0
  30. data/spec/reorm/cursor_spec.rb +214 -0
  31. data/spec/reorm/field_path_spec.rb +65 -0
  32. data/spec/reorm/model_spec.rb +268 -0
  33. data/spec/reorm/modules/event_source_spec.rb +49 -0
  34. data/spec/reorm/modules/table_backed_spec.rb +46 -0
  35. data/spec/reorm/modules/timestamped_spec.rb +28 -0
  36. data/spec/reorm/modules/validation_modules_spec.rb +157 -0
  37. data/spec/reorm/property_errors_spec.rb +120 -0
  38. data/spec/reorm/validators/exclusion_validator_spec.rb +34 -0
  39. data/spec/reorm/validators/inclusion_validator_spec.rb +36 -0
  40. data/spec/reorm/validators/maximum_length_validator_spec.rb +37 -0
  41. data/spec/reorm/validators/minimum_length_validator_spec.rb +39 -0
  42. data/spec/reorm/validators/presence_validator_spec.rb +44 -0
  43. data/spec/reorm/validators/validator_spec.rb +23 -0
  44. data/spec/spec_helper.rb +118 -0
  45. metadata +216 -0
@@ -0,0 +1,65 @@
1
+ require "spec_helper"
2
+
3
+ describe Reorm::FieldPath do
4
+ describe "#name()" do
5
+ subject {
6
+ Reorm::FieldPath.new(:first, :second, :third)
7
+ }
8
+
9
+ it "returns the last element of the field path as the name" do
10
+ expect(subject.name).to eq(:third)
11
+ end
12
+ end
13
+
14
+ describe "#value()" do
15
+ it "fetches the value from the input" do
16
+ field = Reorm::FieldPath.new(:first)
17
+ expect(field.value({first: 1})).to eq(1)
18
+ end
19
+
20
+ it "traverses a hierarchy if one has been specified" do
21
+ field = Reorm::FieldPath.new(:first, :second, :third)
22
+ expect(field.value({first: {second: {third: 3}}})).to eq(3)
23
+ end
24
+
25
+ it "returns nil of the field does not exist" do
26
+ field = Reorm::FieldPath.new(:first, :second, :third)
27
+ expect(field.value({first: 1})).to be_nil
28
+ end
29
+ end
30
+
31
+ describe "#value!()" do
32
+ it "raises an exception if the value does not exist" do
33
+ field = Reorm::FieldPath.new(:first, :second, :third)
34
+ expect {
35
+ field.value!({first: 1})
36
+ }.to raise_exception(Reorm::Error, "Unable to locate the #{field.name} (full path: #{field}) field for an instance of the Hash class.")
37
+ end
38
+ end
39
+
40
+ describe "#exists?()" do
41
+ subject {
42
+ Reorm::FieldPath.new(:first, :second, :third)
43
+ }
44
+
45
+ it "returns true if the field exists within the specified document" do
46
+ expect(subject.exists?({first: {second: {third: 3}}})).to eq(true)
47
+ end
48
+
49
+ it "returns false if the field does not exist within the specified document" do
50
+ expect(subject.exists?({first: 1})).to eq(false)
51
+ end
52
+ end
53
+
54
+ describe "#to_s" do
55
+ it "returns the correct value for a simple field path" do
56
+ field = Reorm::FieldPath.new(:first)
57
+ expect(field.to_s).to eq("first")
58
+ end
59
+
60
+ it "returns the correct value for a more complicated field path" do
61
+ field = Reorm::FieldPath.new(:first, :second, :third)
62
+ expect(field.to_s).to eq("first -> second -> third")
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,268 @@
1
+ require "spec_helper"
2
+
3
+ class ValidationTestModel < Reorm::Model
4
+ before_validate :called
5
+ after_validate :called
6
+
7
+ def initialize(setting=nil)
8
+ super(blah: setting)
9
+ @calls = 0
10
+ end
11
+ attr_reader :calls
12
+
13
+ def called
14
+ @calls += 1
15
+ end
16
+
17
+ def validate
18
+ super
19
+ errors.add(:blah, "has not been set") if [nil, ""].include?(blah)
20
+ end
21
+ end
22
+
23
+ class SaveTestModel < Reorm::Model
24
+ after_create :on_after_create
25
+ after_save :on_after_save
26
+ after_update :on_after_update
27
+ before_create :on_before_create
28
+ before_save :on_before_save
29
+ before_update :on_before_update
30
+
31
+ def initialize(properties={})
32
+ super(properties)
33
+ @events = []
34
+ end
35
+ attr_reader :events
36
+
37
+ def reset_events
38
+ @events = []
39
+ end
40
+
41
+ def on_after_create
42
+ @events << :after_create
43
+ end
44
+
45
+ def on_after_save
46
+ @events << :after_save
47
+ end
48
+
49
+ def on_after_update
50
+ @events << :after_update
51
+ end
52
+
53
+ def on_before_create
54
+ @events << :before_create
55
+ end
56
+
57
+ def on_before_save
58
+ @events << :before_save
59
+ end
60
+
61
+ def on_before_update
62
+ @events << :before_update
63
+ end
64
+ end
65
+
66
+ describe Reorm::Model do
67
+ describe "#valid?()" do
68
+ subject {
69
+ ValidationTestModel.new
70
+ }
71
+
72
+ it "returns true if the an object does not fail validation" do
73
+ subject.blah = 1
74
+ expect(subject.valid?).to eq(true)
75
+ end
76
+
77
+ it "returns false if an object fails validation" do
78
+ expect(subject.valid?).to eq(false)
79
+ end
80
+ end
81
+
82
+ describe "#validate" do
83
+ subject {
84
+ ValidationTestModel.new
85
+ }
86
+
87
+ it "resets the list of errors for an object" do
88
+ expect(subject.valid?).to eq(false)
89
+ expect(subject.errors.clear?).to eq(false)
90
+ subject.blah = 123
91
+ subject.validate
92
+ expect(subject.errors.clear?).to eq(true)
93
+ end
94
+
95
+ it "fires a before and after validation events when called" do
96
+ subject.validate
97
+ expect(subject.calls).to eq(2)
98
+ end
99
+ end
100
+
101
+ describe "#save()" do
102
+ describe "for a previously unsaved object" do
103
+ subject {
104
+ SaveTestModel.new(one: 1, two: 2, three: 3)
105
+ }
106
+
107
+ it "creates a record when called on a valid object" do
108
+ subject.save
109
+ expect(subject.id).not_to be_nil
110
+ record = nil
111
+ Reorm.connection do |connection|
112
+ record = r.table(subject.table_name).get(subject.id).run(connection)
113
+ end
114
+ expect(record).not_to be_nil
115
+ expect(record["id"]).to eq(subject.id)
116
+ expect(record["one"]).to eq(subject.one)
117
+ expect(record["two"]).to eq(subject.two)
118
+ expect(record["three"]).to eq(subject.three)
119
+ end
120
+
121
+ it "fires the before create, before save, after save and after create events" do
122
+ subject.save
123
+ expect(subject.events).to eq([:before_create, :before_save, :after_save, :after_create])
124
+ end
125
+
126
+ it "raises an exception if the object is invalid and validation is required" do
127
+ expect {
128
+ ValidationTestModel.new(nil).save
129
+ }.to raise_exception(Reorm::Error, "Validation error encountered saving an instance of the ValidationTestModel class.")
130
+ end
131
+
132
+ it "does not raises an exception if the object is invalid but validation is off" do
133
+ expect {
134
+ ValidationTestModel.new(nil).save(false)
135
+ }.not_to raise_exception
136
+ end
137
+ end
138
+
139
+ describe "for a previously saved object" do
140
+ subject {
141
+ SaveTestModel.new(one: 1, two: 2, three: 3)
142
+ }
143
+
144
+ before do
145
+ subject.save
146
+ subject.reset_events
147
+ end
148
+
149
+ it "updates the objects record when called on a valid object" do
150
+ subject.two = "TWO"
151
+ subject.save
152
+ record = nil
153
+ Reorm.connection do |connection|
154
+ record = r.table(subject.table_name).get(subject.id).run(connection)
155
+ end
156
+ expect(record).not_to be_nil
157
+ expect(record["id"]).to eq(subject.id)
158
+ expect(record["one"]).to eq(subject.one)
159
+ expect(record["two"]).to eq(subject.two)
160
+ expect(record["three"]).to eq(subject.three)
161
+ end
162
+
163
+ it "fires the before update, before save, after save and after update events" do
164
+ subject.save
165
+ expect(subject.events).to eq([:before_update, :before_save, :after_save, :after_update])
166
+ end
167
+
168
+ it "raises an exception if the object is invalid and validation is required" do
169
+ model = ValidationTestModel.new(1)
170
+ model.save
171
+ model.blah = nil
172
+ expect {
173
+ model.save
174
+ }.to raise_exception(Reorm::Error, "Validation error encountered saving an instance of the ValidationTestModel class.")
175
+ end
176
+
177
+ it "does not raises an exception if the object is invalid but validation is off" do
178
+ model = ValidationTestModel.new(1)
179
+ model.save
180
+ model.blah = nil
181
+ expect {
182
+ model.save(false)
183
+ }.not_to raise_exception
184
+ end
185
+ end
186
+ end
187
+
188
+ describe "#[]()" do
189
+ subject {
190
+ SaveTestModel.new(one: 1, two: "Two")
191
+ }
192
+
193
+ it "returns the value of the named property if it exists" do
194
+ expect(subject[:one]).to eq(1)
195
+ expect(subject[:two]).to eq("Two")
196
+ end
197
+
198
+ it "returns nil if the named property does not exist" do
199
+ expect(subject[:blah]).to be_nil
200
+ end
201
+ end
202
+
203
+ describe "#[]=()" do
204
+ subject {
205
+ SaveTestModel.new(one: 1, two: "Two")
206
+ }
207
+
208
+ it "assigns the property value if it does not exist" do
209
+ subject[:three] = 3
210
+ expect(subject.three).to eq(3)
211
+ end
212
+
213
+ it "updates the property value if it does exist" do
214
+ subject[:one] = "One"
215
+ expect(subject[:one]).to eq("One")
216
+ end
217
+ end
218
+
219
+ describe "#create()" do
220
+ let(:standin) {
221
+ SaveTestModel.new
222
+ }
223
+
224
+ it "creates and saves an instance of the specified class" do
225
+ expect(standin).to receive(:save)
226
+ expect(SaveTestModel).to receive(:new).and_return(standin)
227
+ expect(SaveTestModel.create).to eq(standin)
228
+ end
229
+ end
230
+
231
+ describe "#all()" do
232
+ before do
233
+ SaveTestModel.create(one: 1, two: 2)
234
+ SaveTestModel.create(one: 3, two: 4)
235
+ SaveTestModel.create(one: 5, two: 6)
236
+ end
237
+
238
+ subject {
239
+ SaveTestModel
240
+ }
241
+
242
+ it "returns a cursor for the model records" do
243
+ cursor = subject.all
244
+ expect(cursor).not_to be_nil
245
+ expect(cursor.class).to eq(Reorm::Cursor)
246
+ expect(cursor.inject([]) {|list, entry| list << entry.one}.sort).to eq([1,3,5])
247
+ end
248
+ end
249
+
250
+ describe "#filter()" do
251
+ before do
252
+ SaveTestModel.create(one: 1, two: 2)
253
+ SaveTestModel.create(one: 3, two: 4)
254
+ SaveTestModel.create(one: 5, two: 6)
255
+ end
256
+
257
+ subject {
258
+ SaveTestModel
259
+ }
260
+
261
+ it "returns a cursor for the model records" do
262
+ cursor = subject.filter(two: 4)
263
+ expect(cursor).not_to be_nil
264
+ expect(cursor.class).to eq(Reorm::Cursor)
265
+ expect(cursor.inject([]) {|list, entry| list << entry.one}.sort).to eq([3])
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ class EventSourceTest
4
+ include Reorm::EventHandler
5
+ include Reorm::EventSource
6
+ extend Reorm::EventHandler
7
+ extend Reorm::EventSource
8
+
9
+ def initialize
10
+ @count = 0
11
+ end
12
+ attr_reader :count
13
+
14
+ def reset
15
+ @count = 0
16
+ end
17
+
18
+ def event_handler
19
+ @count += 1
20
+ end
21
+ end
22
+
23
+ describe EventSourceTest do
24
+ subject {
25
+ EventSourceTest.new
26
+ }
27
+
28
+ before do
29
+ subject.reset
30
+ end
31
+
32
+ describe "#fire_events()" do
33
+ describe "called at the instance level" do
34
+ it "calls methods assigned to events when those events are fired" do
35
+ subject.before_create :event_handler
36
+ subject.fire_events(events: [:before_create])
37
+ expect(subject.count).to eq(1)
38
+ end
39
+ end
40
+
41
+ describe "called at the class level" do
42
+ it "calls methods assigned to events when those events are fired" do
43
+ EventSourceTest.before_create :event_handler
44
+ EventSourceTest.fire_events({target: subject, events: [:before_create]})
45
+ expect(subject.count).to eq(1)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ class TableBackedClass
4
+ include Reorm::TableBacked
5
+ end
6
+
7
+ describe TableBackedClass do
8
+ describe "class level methods" do
9
+ class TableBackedClass1
10
+ include Reorm::TableBacked
11
+ table_name "my_table_1"
12
+ primary_key :other
13
+ end
14
+
15
+ subject {
16
+ TableBackedClass1
17
+ }
18
+
19
+ it "allows the table name to be set and retrieved at the class level" do
20
+ expect(subject.table_name).to eq("my_table_1")
21
+ end
22
+
23
+ it "allows the primary key to be set and retrieved at the class leve" do
24
+ expect(subject.primary_key).to eq(:other)
25
+ end
26
+ end
27
+
28
+ describe "instance level methods" do
29
+ class TableBackedClass2
30
+ include Reorm::TableBacked
31
+ end
32
+
33
+ subject {
34
+ TableBackedClass2.new
35
+ }
36
+
37
+ it "allows the table name to be retrieved at the instance level" do
38
+ expect(subject.table_name).to eq("table_backed_class2s")
39
+ end
40
+
41
+ it "allows the primary key to be set and retrieved at the instance level" do
42
+ instance = TableBackedClass.new
43
+ expect(subject.primary_key).to eq(:id)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ class TimestampedTestClass < Reorm::Model
4
+ include Reorm::Timestamped
5
+ end
6
+
7
+ describe Reorm::Timestamped do
8
+ subject {
9
+ TimestampedTestClass
10
+ }
11
+
12
+ it "set the timestamp fields when a record is created" do
13
+ record = subject.create(label: "First")
14
+ expect(record.created_at).not_to be_nil
15
+ expect(record.updated_at).to be_nil
16
+ end
17
+
18
+ it "set the update_at field when a record is updated" do
19
+ record = subject.create(label: "Second")
20
+ created = record.created_at.to_i
21
+ sleep(0.2)
22
+
23
+ record.label = "Updated"
24
+ record.save
25
+ expect(record.created_at.to_i).to eq(created)
26
+ expect(record.updated_at).not_to be_nil
27
+ end
28
+ end
@@ -0,0 +1,157 @@
1
+ require "spec_helper"
2
+
3
+ class ValidationsTestClass < Reorm::Model
4
+ include Reorm::Validations
5
+
6
+ def initialize(properties={})
7
+ super(properties)
8
+ @errors = Reorm::PropertyErrors.new
9
+ end
10
+ attr_reader :errors
11
+
12
+ def include?(property)
13
+ to_h.include?(property)
14
+ end
15
+ end
16
+
17
+ describe Reorm::Validations do
18
+ subject {
19
+ ValidationsTestClass.new
20
+ }
21
+ let(:path) {
22
+ "one -> two -> three".to_sym
23
+ }
24
+
25
+ describe "#validate_presence_of()" do
26
+ it "does not set an error if the field value is set" do
27
+ subject.one = {two: {three: 3}}
28
+ subject.validate_presence_of([:one, :two, :three])
29
+ expect(subject.errors.clear?).to eq(true)
30
+ end
31
+
32
+ it "sets an error if the object does not possess the field value" do
33
+ subject.validate_presence_of([:one, :two, :three])
34
+ expect(subject.errors.clear?).to eq(false)
35
+ expect(subject.errors).to include(path)
36
+ expect(subject.errors.messages(path)).to include("cannot be blank.")
37
+ end
38
+
39
+ it "sets an error if the object does possess the field value but its a blank string" do
40
+ subject.one = {two: {three: ""}}
41
+ subject.validate_presence_of([:one, :two, :three])
42
+ expect(subject.errors.clear?).to eq(false)
43
+ expect(subject.errors).to include(path)
44
+ expect(subject.errors.messages(path)).to include("cannot be blank.")
45
+ end
46
+
47
+ it "sets an error if the object does possess the field value but its nil" do
48
+ subject.one = {two: {three: nil}}
49
+ subject.validate_presence_of([:one, :two, :three])
50
+ expect(subject.errors.clear?).to eq(false)
51
+ expect(subject.errors).to include(path)
52
+ expect(subject.errors.messages(path)).to include("cannot be blank.")
53
+ end
54
+ end
55
+
56
+ describe "#validate_length_of()" do
57
+ describe "with a minimum setting" do
58
+ it "does not set an error if the object as a field value that exceeds the minimum length" do
59
+ subject.one = {two: {three: "12345"}}
60
+ subject.validate_length_of([:one, :two, :three], minimum: 3)
61
+ expect(subject.errors.clear?).to eq(true)
62
+ end
63
+
64
+ it "does not set an error if the object as a field value that equals the minimum length" do
65
+ subject.one = {two: {three: "123"}}
66
+ subject.validate_length_of([:one, :two, :three], minimum: 3)
67
+ expect(subject.errors.clear?).to eq(true)
68
+ end
69
+
70
+ it "does not set an error if the object does not possess the field value" do
71
+ subject.validate_length_of([:one, :two, :three], minimum: 3)
72
+ expect(subject.errors.clear?).to eq(false)
73
+ expect(subject.errors).to include(path)
74
+ expect(subject.errors.messages(path)).to include("is too short (minimum length is 3 characters).")
75
+ end
76
+
77
+ it "does set an error if the object has a field value that is less than the minimum length" do
78
+ subject.one = {two: {three: "12"}}
79
+ subject.validate_length_of([:one, :two, :three], minimum: 3)
80
+ expect(subject.errors.clear?).to eq(false)
81
+ expect(subject.errors).to include(path)
82
+ expect(subject.errors.messages(path)).to include("is too short (minimum length is 3 characters).")
83
+ end
84
+ end
85
+
86
+ describe "with a maximum setting" do
87
+ it "does not set an error if the object as a field value that is less than the maximum length" do
88
+ subject.one = {two: {three: "12"}}
89
+ subject.validate_length_of([:one, :two, :three], maximum: 3)
90
+ expect(subject.errors.clear?).to eq(true)
91
+ end
92
+
93
+ it "does not set an error if the object as a field value that equals the maximum length" do
94
+ subject.one = {two: {three: "123"}}
95
+ subject.validate_length_of([:one, :two, :three], maximum: 3)
96
+ expect(subject.errors.clear?).to eq(true)
97
+ end
98
+
99
+ it "does not set an error if the object does not possess the field value" do
100
+ subject.validate_length_of([:one, :two, :three], maximum: 3)
101
+ expect(subject.errors.clear?).to eq(true)
102
+ end
103
+
104
+ it "does set an error if the object has a field value that is greater than the maximum length" do
105
+ subject.one = {two: {three: "1234"}}
106
+ subject.validate_length_of([:one, :two, :three], maximum: 3)
107
+ expect(subject.errors.clear?).to eq(false)
108
+ expect(subject.errors).to include(path)
109
+ expect(subject.errors.messages(path)).to include("is too long (maximum length is 3 characters).")
110
+ end
111
+ end
112
+ end
113
+
114
+ describe "#validate_inclusion_of()" do
115
+ it "does not set an error if the object field value is one of the permitted set" do
116
+ subject.one = {two: {three: 3}}
117
+ subject.validate_inclusion_of([:one, :two, :three], 1, 2, 3, 4, 5)
118
+ expect(subject.errors.clear?).to eq(true)
119
+ end
120
+
121
+ it "does set an error if the object does not possess the field value" do
122
+ subject.validate_inclusion_of([:one, :two, :three], 1, 2, 3, 4, 5)
123
+ expect(subject.errors.clear?).to eq(false)
124
+ expect(subject.errors).to include(path)
125
+ expect(subject.errors.messages(path)).to include("is not set to one of its permitted values.")
126
+ end
127
+
128
+ it "does set an error if the object field value is not one of the permitted set" do
129
+ subject.one = {two: {three: 17}}
130
+ subject.validate_inclusion_of([:one, :two, :three], 1, 2, 3, 4, 5)
131
+ expect(subject.errors.clear?).to eq(false)
132
+ expect(subject.errors).to include(path)
133
+ expect(subject.errors.messages(path)).to include("is not set to one of its permitted values.")
134
+ end
135
+ end
136
+
137
+ describe "#validate_exclusion_of()" do
138
+ it "does not set an error if the object field value is not one of the excluded set" do
139
+ subject.one = {two: {three: 10}}
140
+ subject.validate_exclusion_of([:one, :two, :three], 1, 2, 3, 4, 5)
141
+ expect(subject.errors.clear?).to eq(true)
142
+ end
143
+
144
+ it "does not set an error if the object does not possess the field value" do
145
+ subject.validate_exclusion_of([:one, :two, :three], 1, 2, 3, 4, 5)
146
+ expect(subject.errors.clear?).to eq(true)
147
+ end
148
+
149
+ it "it does set an error if the object field value is one of the excluded set" do
150
+ subject.one = {two: {three: 4}}
151
+ subject.validate_exclusion_of([:one, :two, :three], 1, 2, 3, 4, 5)
152
+ expect(subject.errors.clear?).to eq(false)
153
+ expect(subject.errors).to include(path)
154
+ expect(subject.errors.messages(path)).to include("is set to an unpermitted value.")
155
+ end
156
+ end
157
+ end