active_mapper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +62 -0
- data/Rakefile +1 -0
- data/active_mapper.gemspec +26 -0
- data/lib/active_mapper/adapter/active_record/order/attribute.rb +34 -0
- data/lib/active_mapper/adapter/active_record/order.rb +30 -0
- data/lib/active_mapper/adapter/active_record/query/attribute.rb +63 -0
- data/lib/active_mapper/adapter/active_record/query/expression.rb +63 -0
- data/lib/active_mapper/adapter/active_record/query.rb +23 -0
- data/lib/active_mapper/adapter/active_record.rb +96 -0
- data/lib/active_mapper/adapter/memory/order/attribute.rb +40 -0
- data/lib/active_mapper/adapter/memory/order.rb +31 -0
- data/lib/active_mapper/adapter/memory/query/attribute.rb +61 -0
- data/lib/active_mapper/adapter/memory/query/expression.rb +81 -0
- data/lib/active_mapper/adapter/memory/query.rb +22 -0
- data/lib/active_mapper/adapter/memory.rb +80 -0
- data/lib/active_mapper/adapter.rb +7 -0
- data/lib/active_mapper/mapper.rb +107 -0
- data/lib/active_mapper/relation.rb +133 -0
- data/lib/active_mapper/version.rb +3 -0
- data/lib/active_mapper.rb +24 -0
- data/spec/active_mapper/adapter/active_record/order_spec.rb +11 -0
- data/spec/active_mapper/adapter/active_record_spec.rb +164 -0
- data/spec/active_mapper/adapter/memory_spec.rb +158 -0
- data/spec/active_mapper/mapper_spec.rb +195 -0
- data/spec/active_mapper/relation_spec.rb +190 -0
- data/spec/active_mapper_spec.rb +25 -0
- data/spec/features/active_record_spec.rb +7 -0
- data/spec/features/memory_spec.rb +5 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/models/user.rb +20 -0
- data/spec/support/shared/active_mapper_integration.rb +129 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c6a0962e9a48619f43dcd83bd2c1c537d86d431d
|
4
|
+
data.tar.gz: ce7a305b11b716922e48dd4eee63ea31791c7c27
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f7dd85906718afeb6de5c2422e2508ff8449b649a842552df2869fa3aaf3df6120f3b120c2958d68ad2bab818c452cde1bf7f137785ac11b1ca389825194aeee
|
7
|
+
data.tar.gz: bbfe65e3421d429cebad52d4fc04e59578619dfe856f18cd695a8819ec8d422fd0a478a18b46e72ed18c532b54a82c8802be20460f8e6a3af05817e3bef39c2b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
active_mapper
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Artin Boghosian
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# ActiveMapper
|
2
|
+
|
3
|
+
[![Code Climate](https://codeclimate.com/github/artinboghosian/active_mapper.png)](https://codeclimate.com/github/artinboghosian/active_mapper)
|
4
|
+
|
5
|
+
ActiveMapper is a data-mapper using ActiveRecord/Arel as a backend. It allows you to create models using PORO and comes with a Memory adapter which you can use during testing to make your tests faster and not reliant on a database. The only requirements are that your models have an id attribute (auto-incrementing int) and respond to a valid? and .model_name ( ActiveSupport::Name). These can easily be injected into your models by including ActiveModel::Model.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'active_mapper'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install active_mapper
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
class Post
|
24
|
+
include ActiveModel::Model
|
25
|
+
|
26
|
+
attr_accessor :id, :title, :content, :status, :created_at, :updated_at
|
27
|
+
|
28
|
+
def persisted?
|
29
|
+
id
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
post = Post.new(title: 'Post', content: 'My First Post')
|
34
|
+
|
35
|
+
# ActiveMapper[Class] will automatically create and wire up the mapper
|
36
|
+
# if it does not exist. The only requirement is that the table exists
|
37
|
+
# with the proper columns (e.g. use ActiveRecord migrations).
|
38
|
+
ActiveMapper[Post].save(post)
|
39
|
+
|
40
|
+
record = ActiveMapper[Post].find(post.id)
|
41
|
+
### Querying
|
42
|
+
mapper = ActiveMapper[Post]
|
43
|
+
|
44
|
+
# some methods return an ActiveMapper::Relation which will not execute
|
45
|
+
# any queries until #to_a, #each or #map are called.
|
46
|
+
|
47
|
+
mapper.select { |post| post.title.starts_with('Post') }
|
48
|
+
mapper.reject { |post| post.status == 'draft' }
|
49
|
+
|
50
|
+
# some methods actually execute the query immediately
|
51
|
+
|
52
|
+
mapper.first { |post| !(post.status == 'draft') }
|
53
|
+
mapper.last { |post| (post.status == 'published') & (post.created_at > 5.days.ago) }
|
54
|
+
mapper.all { |post| (post.content.contains('monkey')) & (post.status == 'published) }
|
55
|
+
|
56
|
+
## Contributing
|
57
|
+
|
58
|
+
1. Fork it ( http://github.com/<my-github-username>/active_mapper/fork )
|
59
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
60
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
61
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
62
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'active_mapper/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "active_mapper"
|
8
|
+
spec.version = ActiveMapper::VERSION
|
9
|
+
spec.authors = ["Artin Boghosian"]
|
10
|
+
spec.email = ["artinboghosian@gmail.com"]
|
11
|
+
spec.summary = %q{Data mapper using ActiveRecord for data access}
|
12
|
+
spec.description = %q{Data mapper using ActiveRecord for data access}
|
13
|
+
spec.homepage = "https://github.com/artinboghosian/active_mapper"
|
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_runtime_dependency "activerecord"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
spec.add_development_dependency "sqlite3"
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActiveMapper
|
2
|
+
module Adapter
|
3
|
+
class ActiveRecord
|
4
|
+
class Order
|
5
|
+
class Attribute
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@direction = :asc
|
9
|
+
end
|
10
|
+
|
11
|
+
def -@
|
12
|
+
@direction = :desc
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def reverse
|
17
|
+
@direction = asc? ? :desc : :asc
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_sql
|
22
|
+
{ @name => @direction }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def asc?
|
28
|
+
@direction == :asc
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'active_mapper/adapter/active_record/order/attribute'
|
2
|
+
|
3
|
+
module ActiveMapper
|
4
|
+
module Adapter
|
5
|
+
class ActiveRecord
|
6
|
+
class Order
|
7
|
+
def initialize(&block)
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_sql
|
12
|
+
[attributes].flatten.inject({}) do |memo, attribute|
|
13
|
+
memo = memo.merge(attribute.to_sql)
|
14
|
+
memo
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(name, *args, &block)
|
19
|
+
Attribute.new(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def attributes
|
25
|
+
@block ? @block.call(self) : []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ActiveMapper
|
2
|
+
module Adapter
|
3
|
+
class ActiveRecord
|
4
|
+
class Query
|
5
|
+
class Attribute
|
6
|
+
def initialize(attribute)
|
7
|
+
@attribute = attribute
|
8
|
+
end
|
9
|
+
|
10
|
+
def in(*collection)
|
11
|
+
Expression.new(@attribute, :in, collection)
|
12
|
+
end
|
13
|
+
|
14
|
+
def not_in(*collection)
|
15
|
+
Expression.new(@attribute, :not_in, collection)
|
16
|
+
end
|
17
|
+
|
18
|
+
def starts_with(value)
|
19
|
+
matches("#{value}%")
|
20
|
+
end
|
21
|
+
|
22
|
+
def contains(value)
|
23
|
+
matches("%#{value}%")
|
24
|
+
end
|
25
|
+
|
26
|
+
def ends_with(value)
|
27
|
+
matches("%#{value}")
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(value)
|
31
|
+
Expression.new(@attribute, :eq, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def !=(value)
|
35
|
+
Expression.new(@attribute, :not_eq, value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def >(value)
|
39
|
+
Expression.new(@attribute, :gt, value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def >=(value)
|
43
|
+
Expression.new(@attribute, :gteq, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def <(value)
|
47
|
+
Expression.new(@attribute, :lt, value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def <=(value)
|
51
|
+
Expression.new(@attribute, :lteq, value)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def matches(value)
|
57
|
+
Expression.new(@attribute, :matches, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ActiveMapper
|
2
|
+
module Adapter
|
3
|
+
class ActiveRecord
|
4
|
+
class Query
|
5
|
+
class Expression
|
6
|
+
def initialize(attribute, comparator, value)
|
7
|
+
@attribute = attribute
|
8
|
+
@comparator = comparator
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_sql
|
13
|
+
@attribute.send(@comparator, @value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def !
|
17
|
+
NotExpression.new(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def &(expression)
|
21
|
+
AndExpression.new(self, expression)
|
22
|
+
end
|
23
|
+
|
24
|
+
def |(expression)
|
25
|
+
OrExpression.new(self, expression)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class NotExpression < Expression
|
30
|
+
def initialize(expression)
|
31
|
+
@expression = expression
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_sql
|
35
|
+
@expression.to_sql.not
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class AndExpression < Expression
|
40
|
+
def initialize(left, right)
|
41
|
+
@left = left
|
42
|
+
@right = right
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_sql
|
46
|
+
@left.to_sql.and(@right.to_sql)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class OrExpression < Expression
|
51
|
+
def initialize(left, right)
|
52
|
+
@left = left
|
53
|
+
@right = right
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_sql
|
57
|
+
@left.to_sql.or(@right.to_sql)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_mapper/adapter/active_record/query/attribute'
|
2
|
+
require 'active_mapper/adapter/active_record/query/expression'
|
3
|
+
|
4
|
+
module ActiveMapper
|
5
|
+
module Adapter
|
6
|
+
class ActiveRecord
|
7
|
+
class Query
|
8
|
+
def initialize(table, &block)
|
9
|
+
@table = table
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_sql
|
14
|
+
@block ? @block.call(self).to_sql : {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(name, *args, &block)
|
18
|
+
Attribute.new(@table[name])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_mapper/adapter/active_record/query'
|
3
|
+
require 'active_mapper/adapter/active_record/order'
|
4
|
+
|
5
|
+
module ActiveMapper
|
6
|
+
module Adapter
|
7
|
+
class ActiveRecord
|
8
|
+
def initialize
|
9
|
+
@collection = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def find(klass, id)
|
13
|
+
collection(klass).find_by(id: id)
|
14
|
+
end
|
15
|
+
|
16
|
+
def where(klass, options = {}, &block)
|
17
|
+
active_record = collection(klass)
|
18
|
+
query = Query.new(active_record.arel_table, &block)
|
19
|
+
order = Order.new(&options[:order])
|
20
|
+
|
21
|
+
records = active_record.where(query.to_sql)
|
22
|
+
records = records.limit(options[:limit]) if options[:limit]
|
23
|
+
records = records.offset(options[:offset]) if options[:offset]
|
24
|
+
records = records.order(order.to_sql)
|
25
|
+
|
26
|
+
records
|
27
|
+
end
|
28
|
+
|
29
|
+
def count(klass, &block)
|
30
|
+
where(klass, &block).count
|
31
|
+
end
|
32
|
+
|
33
|
+
def minimum(klass, attribute, &block)
|
34
|
+
calculate(:minimum, klass, attribute, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def maximum(klass, attribute, &block)
|
38
|
+
calculate(:maximum, klass, attribute, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def average(klass, attribute, &block)
|
42
|
+
calculate(:average, klass, attribute, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def sum(klass, attribute, &block)
|
46
|
+
calculate(:sum, klass, attribute, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def insert(klass, object)
|
50
|
+
active_record = collection(klass)
|
51
|
+
attributes = serialize(klass, object)
|
52
|
+
|
53
|
+
record = active_record.new(attributes)
|
54
|
+
record.save
|
55
|
+
record.id
|
56
|
+
end
|
57
|
+
|
58
|
+
def update(klass, object)
|
59
|
+
find(klass, object.id).update_columns(serialize(klass, object))
|
60
|
+
end
|
61
|
+
|
62
|
+
def serialize(klass, object)
|
63
|
+
collection(klass).column_names.dup.inject({}) do |memo, attribute|
|
64
|
+
memo[attribute] = object.send(attribute)
|
65
|
+
memo
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def unserialize(klass, object)
|
70
|
+
klass.new(serialize(klass, object))
|
71
|
+
end
|
72
|
+
|
73
|
+
def delete(klass, object)
|
74
|
+
find(klass, object.id).delete
|
75
|
+
end
|
76
|
+
|
77
|
+
def delete_all(klass, &block)
|
78
|
+
where(klass, &block).delete_all
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def collection(klass)
|
84
|
+
@collection[klass] ||= begin
|
85
|
+
active_record = Class.new(::ActiveRecord::Base)
|
86
|
+
active_record.table_name = klass.model_name.plural
|
87
|
+
active_record
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def calculate(operation, klass, attribute, &block)
|
92
|
+
where(klass, &block).send(operation, attribute)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ActiveMapper
|
2
|
+
module Adapter
|
3
|
+
class Memory
|
4
|
+
class Order
|
5
|
+
class Attribute
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@direction = :asc
|
9
|
+
end
|
10
|
+
|
11
|
+
def -@
|
12
|
+
@direction = :desc
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def reverse
|
17
|
+
@direction = asc? ? :desc : :asc
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_proc
|
22
|
+
proc do |x,y|
|
23
|
+
if asc?
|
24
|
+
x.send(@name) <=> y.send(@name)
|
25
|
+
else
|
26
|
+
y.send(@name) <=> x.send(@name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def asc?
|
34
|
+
@direction == :asc
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'active_mapper/adapter/memory/order/attribute'
|
2
|
+
|
3
|
+
module ActiveMapper
|
4
|
+
module Adapter
|
5
|
+
class Memory
|
6
|
+
class Order
|
7
|
+
def initialize(&block)
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_proc
|
12
|
+
proc do |x,y|
|
13
|
+
[attributes].flatten.sum do |attribute|
|
14
|
+
attribute.to_proc.call(x,y)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(name, *args, &block)
|
20
|
+
Attribute.new(name)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def attributes
|
26
|
+
@block ? @block.call(self) : [id]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ActiveMapper
|
2
|
+
module Adapter
|
3
|
+
class Memory
|
4
|
+
class Attribute
|
5
|
+
def initialize(attribute)
|
6
|
+
@attribute = attribute
|
7
|
+
end
|
8
|
+
|
9
|
+
def in(*collection)
|
10
|
+
InvertedExpression.new(collection, :include?, @attribute)
|
11
|
+
end
|
12
|
+
|
13
|
+
def not_in(*collection)
|
14
|
+
!(self.in(*collection))
|
15
|
+
end
|
16
|
+
|
17
|
+
def starts_with(value)
|
18
|
+
matches(/^#{value}/i)
|
19
|
+
end
|
20
|
+
|
21
|
+
def contains(value)
|
22
|
+
matches(/#{value}/i)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ends_with(value)
|
26
|
+
matches(/#{value}$/i)
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(value)
|
30
|
+
Expression.new(@attribute, :==, value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def !=(value)
|
34
|
+
Expression.new(@attribute, :!=, value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def >(value)
|
38
|
+
Expression.new(@attribute, :>, value)
|
39
|
+
end
|
40
|
+
|
41
|
+
def >=(value)
|
42
|
+
Expression.new(@attribute, :>=, value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def <(value)
|
46
|
+
Expression.new(@attribute, :<, value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def <=(value)
|
50
|
+
Expression.new(@attribute, :<=, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def matches(regexp)
|
56
|
+
Expression.new(@attribute, :match, regexp)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ActiveMapper
|
2
|
+
module Adapter
|
3
|
+
class Memory
|
4
|
+
class Expression
|
5
|
+
def initialize(attribute, comparator, value)
|
6
|
+
@attribute = attribute
|
7
|
+
@comparator = comparator
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_proc
|
12
|
+
proc do |object|
|
13
|
+
object.send(@attribute).send(@comparator, @value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def !
|
18
|
+
NotExpression.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def &(expression)
|
22
|
+
AndExpression.new(self, expression)
|
23
|
+
end
|
24
|
+
|
25
|
+
def |(expression)
|
26
|
+
OrExpression.new(self, expression)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class InvertedExpression < Expression
|
31
|
+
def initialize(value, comparator, attribute)
|
32
|
+
super(attribute, comparator, value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_proc
|
36
|
+
proc do |object|
|
37
|
+
@value.send(@comparator, object.send(@attribute))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class NotExpression < Expression
|
43
|
+
def initialize(expression)
|
44
|
+
@expression = expression
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_proc
|
48
|
+
proc do |object|
|
49
|
+
!@expression.to_proc.call(object)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class AndExpression < Expression
|
55
|
+
def initialize(left, right)
|
56
|
+
@left = left
|
57
|
+
@right = right
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_proc
|
61
|
+
proc do |object|
|
62
|
+
@left.to_proc.call(object) && @right.to_proc.call(object)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class OrExpression < Expression
|
68
|
+
def initialize(left, right)
|
69
|
+
@left = left
|
70
|
+
@right = right
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_proc
|
74
|
+
proc do |object|
|
75
|
+
@left.to_proc.call(object) || @right.to_proc.call(object)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|