yadm 0.1
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 +8 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +273 -0
- data/Rakefile +10 -0
- data/examples/basic.rb +43 -0
- data/examples/migration.rb +65 -0
- data/lib/yadm.rb +39 -0
- data/lib/yadm/adapters.rb +32 -0
- data/lib/yadm/adapters/common_sql.rb +120 -0
- data/lib/yadm/adapters/memory.rb +175 -0
- data/lib/yadm/adapters/mysql.rb +17 -0
- data/lib/yadm/adapters/postgresql.rb +17 -0
- data/lib/yadm/adapters/sqlite.rb +17 -0
- data/lib/yadm/criteria.rb +32 -0
- data/lib/yadm/criteria/argument.rb +22 -0
- data/lib/yadm/criteria/attribute.rb +15 -0
- data/lib/yadm/criteria/condition.rb +31 -0
- data/lib/yadm/criteria/expression.rb +19 -0
- data/lib/yadm/criteria/limit.rb +25 -0
- data/lib/yadm/criteria/order.rb +48 -0
- data/lib/yadm/criteria_parser.rb +62 -0
- data/lib/yadm/criteria_parser/expression_parser.rb +77 -0
- data/lib/yadm/entity.rb +53 -0
- data/lib/yadm/identity_map.rb +51 -0
- data/lib/yadm/mapper.rb +16 -0
- data/lib/yadm/mapping.rb +71 -0
- data/lib/yadm/mapping/attribute.rb +31 -0
- data/lib/yadm/query.rb +28 -0
- data/lib/yadm/repository.rb +103 -0
- data/lib/yadm/version.rb +3 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/criteria_helpers.rb +33 -0
- data/spec/support/sequel_helpers.rb +25 -0
- data/spec/support/shared_examples_for_a_sequel_adapter.rb +173 -0
- data/spec/yadm/adapters/common_sql_spec.rb +89 -0
- data/spec/yadm/adapters/memory_spec.rb +230 -0
- data/spec/yadm/adapters/mysql_spec.rb +9 -0
- data/spec/yadm/adapters/postgresql_spec.rb +9 -0
- data/spec/yadm/adapters/sqlite_spec.rb +5 -0
- data/spec/yadm/adapters_spec.rb +32 -0
- data/spec/yadm/criteria/condition_spec.rb +50 -0
- data/spec/yadm/criteria/limit_spec.rb +45 -0
- data/spec/yadm/criteria/order_spec.rb +50 -0
- data/spec/yadm/criteria_parser/expression_parser_spec.rb +47 -0
- data/spec/yadm/criteria_parser_spec.rb +55 -0
- data/spec/yadm/criteria_spec.rb +40 -0
- data/spec/yadm/entity_spec.rb +76 -0
- data/spec/yadm/identity_map_spec.rb +128 -0
- data/spec/yadm/mapper_spec.rb +23 -0
- data/spec/yadm/mapping/attribute_spec.rb +35 -0
- data/spec/yadm/mapping_spec.rb +122 -0
- data/spec/yadm/query_spec.rb +45 -0
- data/spec/yadm/repository_spec.rb +175 -0
- data/spec/yadm_spec.rb +45 -0
- data/yadm.gemspec +33 -0
- metadata +254 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module YADM
|
2
|
+
class Criteria
|
3
|
+
class Argument
|
4
|
+
attr_reader :group, :index
|
5
|
+
|
6
|
+
def initialize(group, index)
|
7
|
+
@group = group
|
8
|
+
@index = index
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
%i(group index).all? do |method|
|
13
|
+
other.respond_to?(method) && send(method) == other.send(method)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch_from(values)
|
18
|
+
values[group][index]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module YADM
|
2
|
+
class Criteria
|
3
|
+
class Condition
|
4
|
+
attr_reader :expression
|
5
|
+
|
6
|
+
def initialize(expression)
|
7
|
+
@expression = expression
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
other.respond_to?(:expression) && expression == other.expression
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def merge(first_condition, second_condition)
|
16
|
+
if first_condition && second_condition
|
17
|
+
expression = Expression.new(
|
18
|
+
first_condition.expression,
|
19
|
+
:&,
|
20
|
+
[second_condition.expression]
|
21
|
+
)
|
22
|
+
|
23
|
+
new(expression)
|
24
|
+
else
|
25
|
+
[first_condition, second_condition].compact.first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module YADM
|
2
|
+
class Criteria
|
3
|
+
class Expression
|
4
|
+
attr_reader :receiver, :method_name, :arguments
|
5
|
+
|
6
|
+
def initialize(receiver, method_name, arguments)
|
7
|
+
@receiver = receiver
|
8
|
+
@method_name = method_name
|
9
|
+
@arguments = arguments
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
%i(receiver method_name arguments).all? do |method|
|
14
|
+
other.respond_to?(method) && send(method) == other.send(method)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module YADM
|
2
|
+
class Criteria
|
3
|
+
class Limit
|
4
|
+
attr_reader :limit
|
5
|
+
|
6
|
+
def initialize(limit)
|
7
|
+
@limit = limit
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
other.respond_to?(:limit) && limit == other.limit
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def merge(first_limit, second_limit)
|
16
|
+
if first_limit && second_limit
|
17
|
+
new(second_limit.limit) unless second_limit.limit.nil?
|
18
|
+
else
|
19
|
+
[first_limit, second_limit].compact.first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module YADM
|
2
|
+
class Criteria
|
3
|
+
class Order
|
4
|
+
attr_reader :clauses
|
5
|
+
|
6
|
+
def initialize(clauses)
|
7
|
+
@clauses = clauses
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
other.respond_to?(:clauses) && clauses == other.clauses
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def merge(first_order, second_order)
|
16
|
+
if first_order && second_order
|
17
|
+
new(first_order.clauses + second_order.clauses)
|
18
|
+
else
|
19
|
+
[first_order, second_order].compact.first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Clause
|
25
|
+
attr_reader :type, :expression
|
26
|
+
|
27
|
+
def initialize(type, expression)
|
28
|
+
@type = type.to_sym
|
29
|
+
@expression = expression
|
30
|
+
end
|
31
|
+
|
32
|
+
def asc?
|
33
|
+
type == :asc
|
34
|
+
end
|
35
|
+
|
36
|
+
def desc?
|
37
|
+
type == :desc
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
%i(type expression).all? do |method|
|
42
|
+
other.respond_to?(method) && send(method) == other.send(method)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'yadm/criteria_parser/expression_parser'
|
2
|
+
|
3
|
+
module YADM
|
4
|
+
class CriteriaParser
|
5
|
+
attr_reader :block
|
6
|
+
|
7
|
+
def initialize(block)
|
8
|
+
@block = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def result(arguments_group)
|
12
|
+
arguments = block.arity.times.map do |index|
|
13
|
+
YADM::Criteria::Argument.new(arguments_group, index)
|
14
|
+
end
|
15
|
+
|
16
|
+
instance_exec(*arguments, &block).result
|
17
|
+
end
|
18
|
+
|
19
|
+
%i(with ascending_by descending_by first).each do |method_name|
|
20
|
+
define_method(method_name) do |*args, &block|
|
21
|
+
Criteria.new.send(method_name, *args, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def parse(block, arguments_group)
|
27
|
+
new(block).result(arguments_group)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Criteria < YADM::Criteria
|
32
|
+
def result
|
33
|
+
YADM::Criteria.new(
|
34
|
+
condition: condition,
|
35
|
+
order: order,
|
36
|
+
limit: limit
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def with(&block)
|
41
|
+
expression = ExpressionParser.parse(block)
|
42
|
+
merge Criteria.new(condition: Condition.new(expression))
|
43
|
+
end
|
44
|
+
|
45
|
+
def ascending_by(&block)
|
46
|
+
expression = ExpressionParser.parse(block)
|
47
|
+
clause = Order::Clause.new(:asc, expression)
|
48
|
+
merge Criteria.new(order: Order.new([clause]))
|
49
|
+
end
|
50
|
+
|
51
|
+
def descending_by(&block)
|
52
|
+
expression = ExpressionParser.parse(block)
|
53
|
+
clause = Order::Clause.new(:desc, expression)
|
54
|
+
merge Criteria.new(order: Order.new([clause]))
|
55
|
+
end
|
56
|
+
|
57
|
+
def first(limit)
|
58
|
+
merge Criteria.new(limit: Limit.new(limit))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module YADM
|
2
|
+
class CriteriaParser
|
3
|
+
class ExpressionParser
|
4
|
+
attr_reader :block
|
5
|
+
|
6
|
+
def initialize(block)
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def result
|
11
|
+
instance_eval(&block).result
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method_name, *args, &block)
|
15
|
+
Attribute.new(method_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def parse(block)
|
20
|
+
new(block).result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Operand
|
25
|
+
%i(== != < > <= >= + - * / & |).each do |symbol|
|
26
|
+
define_method(symbol) do |arg|
|
27
|
+
Expression.new(self, symbol, [arg])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Expression
|
33
|
+
include Operand
|
34
|
+
|
35
|
+
attr_reader :receiver, :method_name, :arguments
|
36
|
+
|
37
|
+
def initialize(receiver, method_name, arguments)
|
38
|
+
@receiver = receiver
|
39
|
+
@method_name = method_name
|
40
|
+
@arguments = arguments
|
41
|
+
end
|
42
|
+
|
43
|
+
def result
|
44
|
+
Criteria::Expression.new(
|
45
|
+
receiver.result,
|
46
|
+
method_name,
|
47
|
+
arguments_result
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def arguments_result
|
52
|
+
arguments.map do |argument|
|
53
|
+
if argument.respond_to?(:result)
|
54
|
+
argument.result
|
55
|
+
else
|
56
|
+
argument
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Attribute
|
63
|
+
include Operand
|
64
|
+
|
65
|
+
attr_reader :name
|
66
|
+
|
67
|
+
def initialize(name)
|
68
|
+
@name = name
|
69
|
+
end
|
70
|
+
|
71
|
+
def result
|
72
|
+
Criteria::Attribute.new(name)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/yadm/entity.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module YADM
|
2
|
+
module Entity
|
3
|
+
def initialize(new_attributes)
|
4
|
+
@attributes = {}
|
5
|
+
|
6
|
+
self.class.attributes.each do |attr_name|
|
7
|
+
@attributes[attr_name] = fetch(new_attributes, attr_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes
|
12
|
+
@attributes.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def fetch(hash, key)
|
17
|
+
hash[key] || hash[key.to_s]
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def included(including_class)
|
22
|
+
including_class.extend(DSL)
|
23
|
+
including_class.attribute(:id)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module DSL
|
28
|
+
def attribute(attr_name, readonly: false)
|
29
|
+
attr_name = attr_name.to_sym
|
30
|
+
|
31
|
+
attributes.add(attr_name)
|
32
|
+
|
33
|
+
define_method(attr_name) do
|
34
|
+
@attributes[attr_name]
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method("#{attr_name}=") do |new_value|
|
38
|
+
@attributes[attr_name] = new_value
|
39
|
+
end unless readonly
|
40
|
+
end
|
41
|
+
|
42
|
+
def attributes(*attr_names)
|
43
|
+
if attr_names.empty?
|
44
|
+
@attributes ||= Set.new
|
45
|
+
else
|
46
|
+
attr_names.each do |attr_name|
|
47
|
+
attribute(attr_name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module YADM
|
2
|
+
class IdentityMap
|
3
|
+
attr_reader :data_source, :map
|
4
|
+
private :data_source, :map
|
5
|
+
|
6
|
+
def initialize(data_source)
|
7
|
+
@data_source = data_source
|
8
|
+
|
9
|
+
@map = Hash.new do |map, collection|
|
10
|
+
map[collection] = {}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(collection, id)
|
15
|
+
map[collection][id] ||= data_source.get(collection, id).dup
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(collection, attributes)
|
19
|
+
data_source.add(collection, attributes).tap do |id|
|
20
|
+
map[collection][id] = attributes.merge(id: id)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def change(collection, id, new_attributes)
|
25
|
+
data_source.change(collection, id, new_attributes)
|
26
|
+
|
27
|
+
if attributes = map[collection][id]
|
28
|
+
attributes.update(new_attributes)
|
29
|
+
else
|
30
|
+
map[collection][id] = new_attributes.merge(id: id)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove(collection, id)
|
35
|
+
data_source.remove(collection, id)
|
36
|
+
map[collection].delete(id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def count(collection)
|
40
|
+
data_source.count(collection)
|
41
|
+
end
|
42
|
+
|
43
|
+
def send_query(collection, query)
|
44
|
+
data_source.send_query(collection, query)
|
45
|
+
end
|
46
|
+
|
47
|
+
def migrate(block)
|
48
|
+
data_source.migrate(block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/yadm/mapper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module YADM
|
2
|
+
class Mapper
|
3
|
+
def repository(repository_class, &block)
|
4
|
+
mappings[repository_class] = Mapping.new(&block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def mapping_for(repository)
|
8
|
+
mappings.fetch(repository)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def mappings
|
13
|
+
@mappings ||= {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/yadm/mapping.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'yadm/mapping/attribute'
|
2
|
+
|
3
|
+
module YADM
|
4
|
+
class Mapping
|
5
|
+
def initialize(&block)
|
6
|
+
instance_eval(&block) unless block.nil?
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(id)
|
10
|
+
coerce(data_source.get(collection, id))
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(attributes)
|
14
|
+
data_source.add(collection, attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
def change(id, attributes)
|
18
|
+
data_source.change(collection, id, attributes)
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove(id)
|
22
|
+
data_source.remove(collection, id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def count
|
26
|
+
data_source.count(collection)
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_query(query)
|
30
|
+
data_source.send_query(collection, query).map do |attribute_values|
|
31
|
+
coerce(attribute_values)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def coerce(attribute_values)
|
37
|
+
attributes.each_with_object(Hash.new) do |(attr_name, attribute), hash|
|
38
|
+
raw_value = attribute_values[attr_name]
|
39
|
+
hash[attr_name] = attribute.coerce(raw_value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module DSL
|
44
|
+
def data_source(data_source_identifier = nil)
|
45
|
+
if data_source_identifier.nil?
|
46
|
+
@data_source
|
47
|
+
else
|
48
|
+
@data_source = YADM.data_sources.fetch(data_source_identifier)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def collection(new_collection = nil)
|
53
|
+
if new_collection.nil?
|
54
|
+
@collection
|
55
|
+
else
|
56
|
+
@collection = new_collection
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def attribute(name, type)
|
61
|
+
attributes[name] = Attribute.new(type)
|
62
|
+
end
|
63
|
+
|
64
|
+
def attributes
|
65
|
+
@attributes ||= {}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
include DSL
|
70
|
+
end
|
71
|
+
end
|