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,67 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ module ClassDatabaseSettings
7
+ @@class_db_settings = {}
8
+
9
+ def assign_table_defaults
10
+ @@class_db_settings[self] = {primary_key: :id,
11
+ table_name: self.name.split("::").last.tableize}
12
+ end
13
+
14
+ def table_settings
15
+ assign_table_defaults if !@@class_db_settings.include?(self)
16
+ @@class_db_settings[self]
17
+ end
18
+ end
19
+
20
+ module PrimaryKeyedClassMethods
21
+ def primary_key(field=nil)
22
+ table_settings[:primary_key] = field if !field.nil?
23
+ table_settings[:primary_key]
24
+ end
25
+
26
+ def table_name(name=nil)
27
+ table_settings[:table_name] = name if !name.nil?
28
+ table_settings[:table_name]
29
+ end
30
+ end
31
+
32
+ module PrimaryKeyedInstanceMethods
33
+ def primary_key
34
+ self.class.primary_key
35
+ end
36
+
37
+ def table_name
38
+ self.class.table_name
39
+ end
40
+ end
41
+
42
+ # A module that defines the data and methods for a class that is backed on to
43
+ # a database table.
44
+ module TableBacked
45
+ def TableBacked.included(target)
46
+ target.extend(ClassDatabaseSettings)
47
+ target.extend(PrimaryKeyedClassMethods)
48
+ target.include(PrimaryKeyedInstanceMethods)
49
+ end
50
+ end
51
+
52
+ module Timestamped
53
+ def set_created_at
54
+ self.created_at = Time.now
55
+ self.updated_at = nil
56
+ end
57
+
58
+ def set_updated_at
59
+ self.updated_at = Time.now
60
+ end
61
+
62
+ def Timestamped.included(target)
63
+ target.before_create :set_created_at
64
+ target.before_update :set_updated_at
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,82 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ # A module that defines the class level data used for events.
7
+ module EventData
8
+ @@class_events = {}
9
+ end
10
+
11
+ # A module that defines the class level methods for setting event handlers.
12
+ module EventHandler
13
+ include EventData
14
+
15
+ def after_create(*methods)
16
+ store_event_handlers(:after_create, *methods)
17
+ end
18
+
19
+ def after_save(*methods)
20
+ store_event_handlers(:after_save, *methods)
21
+ end
22
+
23
+ def after_update(*methods)
24
+ store_event_handlers(:after_update, *methods)
25
+ end
26
+
27
+ def after_validate(*methods)
28
+ store_event_handlers(:after_validate, *methods)
29
+ end
30
+
31
+ def before_create(*methods)
32
+ store_event_handlers(:before_create, *methods)
33
+ end
34
+
35
+ def before_save(*methods)
36
+ store_event_handlers(:before_save, *methods)
37
+ end
38
+
39
+ def before_update(*methods)
40
+ store_event_handlers(:before_update, *methods)
41
+ end
42
+
43
+ def before_validate(*methods)
44
+ store_event_handlers(:before_validate, *methods)
45
+ end
46
+
47
+ def store_event_handlers(event, *methods)
48
+ if self.instance_of?(Class)
49
+ @@class_events[self] = {} if !@@class_events.include?(self)
50
+ @@class_events[self][event] = [] if !@@class_events[self].include?(event)
51
+ @@class_events[self][event] = @@class_events[self][event].concat(methods).uniq
52
+ else
53
+ self.class.store_event_handlers(event, *methods)
54
+ end
55
+ end
56
+ end
57
+
58
+ # A module that is used to provide the methods the create events.
59
+ module EventSource
60
+ include EventData
61
+
62
+ def fire_events(settings={})
63
+ events = settings[:events]
64
+ if events && !events.empty?
65
+ object = (settings[:target] ? settings[:target] : self)
66
+ handlers = @@class_events[self.instance_of?(Class) ? self : self.class]
67
+ if handlers && !handlers.empty?
68
+ events.each do |event|
69
+ if handlers.include?(event)
70
+ handlers[event].each do |handler|
71
+ if !object.respond_to?(handler)
72
+ raise Error, "Unable to locate a method called '#{event}' for an instance of the #{object.class.name} class."
73
+ end
74
+ object.__send__(handler)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,29 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ module Validations
7
+ def validate_presence_of(field)
8
+ PresenceValidator.new(*field).validate(self)
9
+ end
10
+
11
+ def validate_length_of(field, options={})
12
+ if options.include?(:minimum)
13
+ MinimumLengthValidator.new(options[:minimum], *field).validate(self)
14
+ end
15
+
16
+ if options.include?(:maximum)
17
+ MaximumLengthValidator.new(options[:maximum], *field).validate(self)
18
+ end
19
+ end
20
+
21
+ def validate_inclusion_of(field, *values)
22
+ InclusionValidator.new(values, *field).validate(self)
23
+ end
24
+
25
+ def validate_exclusion_of(field, *values)
26
+ ExclusionValidator.new(values, *field).validate(self)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ require "reorm/modules/database_modules"
6
+ require "reorm/modules/event_modules"
7
+ require "reorm/modules/validation_modules"
@@ -0,0 +1,53 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ class PropertyErrors
7
+ def initialize
8
+ @errors = {}
9
+ end
10
+
11
+ def clear?
12
+ @errors.empty?
13
+ end
14
+
15
+ def reset
16
+ @errors = {}
17
+ end
18
+
19
+ def include?(property)
20
+ @errors.include?(property.to_sym)
21
+ end
22
+
23
+ def add(property, message)
24
+ if ![nil, ""].include?(message)
25
+ @errors[property.to_sym] = [] if !@errors.include?(property.to_sym)
26
+ @errors[property.to_sym] << message
27
+ end
28
+ self
29
+ end
30
+
31
+ def properties
32
+ @errors.keys
33
+ end
34
+
35
+ def messages(property)
36
+ [].concat(@errors.fetch(property.to_sym, []))
37
+ end
38
+
39
+ def each
40
+ @errors.each {|property, messages| yield(property, messages)}
41
+ end
42
+
43
+ def to_s
44
+ text = StringIO.new
45
+ @errors.each do |property, messages|
46
+ messages.each do |message|
47
+ text << "#{text.size > 0 ? "\n" : ""}#{property} #{message}"
48
+ end
49
+ end
50
+ text.string
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ class ExclusionValidator < Validator
7
+ def initialize(values, *field)
8
+ super("is set to an unpermitted value.", *field)
9
+ @values = [].concat(values)
10
+ end
11
+
12
+ def validate(object)
13
+ if @values.include?(field.value(object))
14
+ object.errors.add(field.to_s, message)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ class InclusionValidator < Validator
7
+ def initialize(values, *field)
8
+ super("is not set to one of its permitted values.", *field)
9
+ @values = values
10
+ end
11
+
12
+ def validate(object)
13
+ if !@values.include?(field.value(object))
14
+ object.errors.add(field.to_s, message)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ class MaximumLengthValidator < Validator
7
+ def initialize(length, *field)
8
+ super("is too long (maximum length is #{length} characters).", *field)
9
+ @length = length
10
+ end
11
+
12
+ def validate(object)
13
+ value = field.value(object)
14
+ if value && value.to_s.length > @length
15
+ object.errors.add field.to_s, message
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ class MinimumLengthValidator < Validator
7
+ def initialize(length, *field)
8
+ super("is too short (minimum length is #{length} characters).", *field)
9
+ @length = length
10
+ end
11
+
12
+ def validate(object)
13
+ value = field.value(object)
14
+ if [nil, ""].include?(value) || value.to_s.length < @length
15
+ object.errors.add field.to_s, message
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ class PresenceValidator < Validator
7
+ def initialize(*field)
8
+ super("cannot be blank.", *field)
9
+ end
10
+
11
+ def validate(object)
12
+ if !field.exists?(object) || [nil, ""].include?(field.value(object))
13
+ object.errors.add(field.to_s, message)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ module Reorm
6
+ class Validator
7
+ def initialize(message, *field)
8
+ @field = FieldPath.new(*field)
9
+ @message = message
10
+ end
11
+ attr_reader :field, :message
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ require "reorm/validators/validator"
6
+ require "reorm/validators/presence_validator"
7
+ require "reorm/validators/minimum_length_validator"
8
+ require "reorm/validators/maximum_length_validator"
9
+ require "reorm/validators/inclusion_validator"
10
+ require "reorm/validators/exclusion_validator"
@@ -0,0 +1,6 @@
1
+ module Reorm
2
+ MAJOR_VERSION = 0
3
+ MINOR_VERSION = 1
4
+ BUILD_VERSION = 0
5
+ VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}.#{BUILD_VERSION}"
6
+ end
data/lib/reorm.rb ADDED
@@ -0,0 +1,47 @@
1
+ #! /usr/bin/env ruby
2
+ # Copyright (c), 2015 Peter Wood
3
+ # See the license.txt for details of the licensing of the code in this file.
4
+
5
+ require "active_support/inflector"
6
+ require "configurative"
7
+ require "connection_pool"
8
+ require "logjam"
9
+ require "rethinkdb"
10
+ require "stringio"
11
+ require "reorm/version"
12
+ require "reorm/exceptions"
13
+ require "reorm/configuration"
14
+ require "reorm/field_path"
15
+ require "reorm/modules"
16
+ require "reorm/validators"
17
+ require "reorm/model"
18
+ require "reorm/property_errors"
19
+ require "reorm/cursor"
20
+
21
+ include RethinkDB::Shortcuts
22
+
23
+ module Reorm
24
+ # Constants used with Rethinkdb connections.
25
+ DEFAULT_SIZE = 5
26
+ DEFAULT_TIMEOUT = 5
27
+ SETTINGS_NAMES = [:host, :port, :db, :auth_key, :timeout]
28
+
29
+ # Module property containing the connection pool.
30
+ @@reorm_connections = ConnectionPool.new(size: Configuration.fetch(:size, DEFAULT_SIZE),
31
+ timeout: Configuration.fetch(:timeout, DEFAULT_TIMEOUT)) do
32
+ settings = Configuration.instance.to_h.inject({}) do |store, entry|
33
+ store[entry[0]] = entry[1] if SETTINGS_NAMES.include?(entry[0])
34
+ store
35
+ end
36
+ r.connect(settings)
37
+ end
38
+
39
+ # A method for obtaining a connection from the module connection pool. The
40
+ # intended usage is that you call this method with a block that will be passed
41
+ # the connection as a parameter.
42
+ def self.connection
43
+ @@reorm_connections.with do |connection|
44
+ yield connection
45
+ end
46
+ end
47
+ end
data/reorm.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'reorm/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "reorm"
8
+ spec.version = Reorm::VERSION
9
+ spec.authors = ["Peter Wood"]
10
+ spec.email = ["peter.wood@longboat.com"]
11
+ spec.summary = %q{A library for use with the RethinkDB application.}
12
+ spec.description = %q{A library the wraps RQL and provides a basic model class for the RethinkDB system.}
13
+ spec.homepage = "https://github.com/free-beer/reorm"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.3"
24
+
25
+ spec.add_dependency "activesupport", "~> 4.2"
26
+ spec.add_dependency "configurative", "~> 0.1"
27
+ spec.add_dependency "connection_pool", "~> 2.2"
28
+ spec.add_dependency "logjam", "~> 1.2"
29
+ spec.add_dependency "rethinkdb", "~> 2.0"
30
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ class TimestampedTestClass < Reorm::Model
4
+ extend 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
+ end
@@ -0,0 +1,214 @@
1
+ require "spec_helper"
2
+
3
+ class CursorTestModel < Reorm::Model
4
+ end
5
+
6
+ describe Reorm::Cursor do
7
+ subject {
8
+ Reorm::Cursor.new(CursorTestModel, r.table(CursorTestModel.table_name))
9
+ }
10
+
11
+ before do
12
+ CursorTestModel.create(index: 1)
13
+ CursorTestModel.create(index: 2)
14
+ CursorTestModel.create(index: 3)
15
+ CursorTestModel.create(index: 4)
16
+ CursorTestModel.create(index: 5)
17
+ end
18
+
19
+ describe "#close" do
20
+ it "silently does nothing if the internal cursor isn't open" do
21
+ expect {
22
+ subject.close
23
+ }.not_to raise_exception
24
+ end
25
+
26
+ it "cleans up where the internal cursor is open" do
27
+ expect(subject.next).not_to be_nil
28
+ expect {
29
+ subject.close
30
+ }.not_to raise_exception
31
+ end
32
+ end
33
+
34
+ describe "#count()" do
35
+ it "returns a count of the number of rows that the cursor will iterate across" do
36
+ expect(subject.count).to eq(5)
37
+ end
38
+ end
39
+
40
+ describe "#exhausted?()" do
41
+ it "returns false if the internal cursor isn't open" do
42
+ expect(subject.exhausted?).to eq(false)
43
+ end
44
+
45
+ it "returns false if the internal cursor is open but there are rows remaining" do
46
+ expect(subject.next).not_to be_nil
47
+ expect(subject.exhausted?).to eq(false)
48
+ end
49
+
50
+ it "returns true if the internal cursor is open and there are no more rows remaining" do
51
+ subject.count.times {expect(subject.next).not_to be_nil}
52
+ expect(subject.exhausted?).to eq(true)
53
+ end
54
+ end
55
+
56
+ describe "#find()" do
57
+ it "returns the first row that matches the search block" do
58
+ model = subject.find {|record| record.index == 3}
59
+ expect(model).not_to be_nil
60
+ expect(model.class).to eq(CursorTestModel)
61
+ end
62
+
63
+ it "returns nil if a matching record cannot be found" do
64
+ model = subject.find {|record| record.index == 3000}
65
+ expect(model).to be_nil
66
+ end
67
+ end
68
+
69
+ describe "#next()" do
70
+ describe "when used without an order by clause" do
71
+ it "returns the next record from the cursor" do
72
+ (1..5).each do |index|
73
+ model = subject.next
74
+ expect(model).not_to be_nil
75
+ expect((1..5)).to include(model.index)
76
+ end
77
+ end
78
+
79
+ it "raises an exception if called on an exhausted cursor" do
80
+ 5.times {subject.next}
81
+ expect {
82
+ subject.next
83
+ }.to raise_exception(Reorm::Error, "There are no more matching records.")
84
+ end
85
+ end
86
+
87
+ describe "when used with an order by clause" do
88
+ subject {
89
+ Reorm::Cursor.new(CursorTestModel, r.table(CursorTestModel.table_name)).order_by(:index)
90
+ }
91
+
92
+ it "returns the next record from the cursor" do
93
+ (1..5).each do |index|
94
+ model = subject.next
95
+ expect(model).not_to be_nil
96
+ expect((1..5)).to include(model.index)
97
+ end
98
+ end
99
+
100
+ it "raises an exception if called on an exhausted cursor" do
101
+ 5.times {subject.next}
102
+ expect {
103
+ subject.next
104
+ }.to raise_exception(Reorm::Error, "There are no more matching records.")
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "#each()" do
110
+ describe "when an order by clause hasn't been applied" do
111
+ it "yields each available record to the specified block" do
112
+ indices = [1, 2, 3, 4, 5]
113
+ subject.each do |record|
114
+ indices.delete(record.index)
115
+ end
116
+ expect(indices.empty?).to eq(true)
117
+ end
118
+ end
119
+
120
+ describe "when an order by clause has been applied" do
121
+ subject {
122
+ Reorm::Cursor.new(CursorTestModel, r.table(CursorTestModel.table_name)).order_by(:index)
123
+ }
124
+
125
+ it "yields each available record to the specified block" do
126
+ indices = [1, 2, 3, 4, 5]
127
+ subject.each do |record|
128
+ indices.delete(record.index)
129
+ end
130
+ expect(indices.empty?).to eq(true)
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "#inject()" do
136
+ it "yields each available record to the specified block" do
137
+ output = subject.inject([]) {|list, record| list << record.index}
138
+ expect(output).to include(1)
139
+ expect(output).to include(2)
140
+ expect(output).to include(3)
141
+ expect(output).to include(4)
142
+ expect(output).to include(5)
143
+ end
144
+ end
145
+
146
+ describe "#nth()" do
147
+ it "returns the record at the specified offset" do
148
+ expect(subject.nth(2).index).to eq(subject.to_a[2].index)
149
+ end
150
+
151
+ it "returns nil if an invalid offset is specified" do
152
+ expect(subject.nth(1000)).to be_nil
153
+ expect(subject.nth(-1)).to be_nil
154
+ end
155
+ end
156
+
157
+ describe "#first()" do
158
+ let(:first) {
159
+ subject.nth(0)
160
+ }
161
+
162
+ it "returns the first matching record from the cursor" do
163
+ expect(subject.first.index).to eq(first.index)
164
+ end
165
+ end
166
+
167
+ describe "#last()" do
168
+ let(:last) {
169
+ subject.nth(4)
170
+ }
171
+
172
+ it "returns the last matching record from the cursor" do
173
+ expect(subject.last.index).to eq(last.index)
174
+ end
175
+ end
176
+
177
+ describe "#to_a()" do
178
+ it "returns an array containing all of the matching records from the cursor" do
179
+ array = subject.to_a
180
+ expect(array).not_to be_nil
181
+ expect(array.class).to eq(Array)
182
+ expect(array.size).to eq(5)
183
+ expect(array.inject([]) {|list, model| list << model.index}.sort).to eq([1, 2, 3, 4, 5])
184
+ end
185
+ end
186
+
187
+ describe "#filter()" do
188
+ it "returns a Cursor object" do
189
+ output = subject.filter({index: 4})
190
+ expect(output).not_to be_nil
191
+ expect(output.class).to eq(Reorm::Cursor)
192
+ end
193
+
194
+ it "applies a filter to the cursor records" do
195
+ cursor = subject.filter({index: 4})
196
+ expect(cursor.count).to eq(1)
197
+ expect(cursor.first.index).to eq(4)
198
+ end
199
+ end
200
+
201
+ describe "#order_by()" do
202
+ it "returns a Cursor object" do
203
+ output = subject.order_by(:index)
204
+ expect(output).not_to be_nil
205
+ expect(output.class).to eq(Reorm::Cursor)
206
+ end
207
+
208
+ it "applies an ordering to the cursor records" do
209
+ cursor = subject.order_by(r.desc(:index))
210
+ indices = cursor.inject([]) {|list, record| list << record.index}
211
+ expect(indices).to eq([5, 4, 3, 2, 1])
212
+ end
213
+ end
214
+ end