reorm 0.1.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.
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