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,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