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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +219 -0
- data/Rakefile +2 -0
- data/config/database.yml +11 -0
- data/lib/reorm/configuration.rb +12 -0
- data/lib/reorm/cursor.rb +162 -0
- data/lib/reorm/exceptions.rb +13 -0
- data/lib/reorm/field_path.rb +53 -0
- data/lib/reorm/model.rb +132 -0
- data/lib/reorm/modules/database_modules.rb +67 -0
- data/lib/reorm/modules/event_modules.rb +82 -0
- data/lib/reorm/modules/validation_modules.rb +29 -0
- data/lib/reorm/modules.rb +7 -0
- data/lib/reorm/property_errors.rb +53 -0
- data/lib/reorm/validators/exclusion_validator.rb +18 -0
- data/lib/reorm/validators/inclusion_validator.rb +18 -0
- data/lib/reorm/validators/maximum_length_validator.rb +19 -0
- data/lib/reorm/validators/minimum_length_validator.rb +19 -0
- data/lib/reorm/validators/presence_validator.rb +17 -0
- data/lib/reorm/validators/validator.rb +13 -0
- data/lib/reorm/validators.rb +10 -0
- data/lib/reorm/version.rb +6 -0
- data/lib/reorm.rb +47 -0
- data/reorm.gemspec +30 -0
- data/spec/catwalk/modules/timestamped_spec.rb +17 -0
- data/spec/reorm/cursor_spec.rb +214 -0
- data/spec/reorm/field_path_spec.rb +65 -0
- data/spec/reorm/model_spec.rb +268 -0
- data/spec/reorm/modules/event_source_spec.rb +49 -0
- data/spec/reorm/modules/table_backed_spec.rb +46 -0
- data/spec/reorm/modules/timestamped_spec.rb +28 -0
- data/spec/reorm/modules/validation_modules_spec.rb +157 -0
- data/spec/reorm/property_errors_spec.rb +120 -0
- data/spec/reorm/validators/exclusion_validator_spec.rb +34 -0
- data/spec/reorm/validators/inclusion_validator_spec.rb +36 -0
- data/spec/reorm/validators/maximum_length_validator_spec.rb +37 -0
- data/spec/reorm/validators/minimum_length_validator_spec.rb +39 -0
- data/spec/reorm/validators/presence_validator_spec.rb +44 -0
- data/spec/reorm/validators/validator_spec.rb +23 -0
- data/spec/spec_helper.rb +118 -0
- 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,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"
|
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
|