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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +273 -0
  8. data/Rakefile +10 -0
  9. data/examples/basic.rb +43 -0
  10. data/examples/migration.rb +65 -0
  11. data/lib/yadm.rb +39 -0
  12. data/lib/yadm/adapters.rb +32 -0
  13. data/lib/yadm/adapters/common_sql.rb +120 -0
  14. data/lib/yadm/adapters/memory.rb +175 -0
  15. data/lib/yadm/adapters/mysql.rb +17 -0
  16. data/lib/yadm/adapters/postgresql.rb +17 -0
  17. data/lib/yadm/adapters/sqlite.rb +17 -0
  18. data/lib/yadm/criteria.rb +32 -0
  19. data/lib/yadm/criteria/argument.rb +22 -0
  20. data/lib/yadm/criteria/attribute.rb +15 -0
  21. data/lib/yadm/criteria/condition.rb +31 -0
  22. data/lib/yadm/criteria/expression.rb +19 -0
  23. data/lib/yadm/criteria/limit.rb +25 -0
  24. data/lib/yadm/criteria/order.rb +48 -0
  25. data/lib/yadm/criteria_parser.rb +62 -0
  26. data/lib/yadm/criteria_parser/expression_parser.rb +77 -0
  27. data/lib/yadm/entity.rb +53 -0
  28. data/lib/yadm/identity_map.rb +51 -0
  29. data/lib/yadm/mapper.rb +16 -0
  30. data/lib/yadm/mapping.rb +71 -0
  31. data/lib/yadm/mapping/attribute.rb +31 -0
  32. data/lib/yadm/query.rb +28 -0
  33. data/lib/yadm/repository.rb +103 -0
  34. data/lib/yadm/version.rb +3 -0
  35. data/spec/spec_helper.rb +26 -0
  36. data/spec/support/criteria_helpers.rb +33 -0
  37. data/spec/support/sequel_helpers.rb +25 -0
  38. data/spec/support/shared_examples_for_a_sequel_adapter.rb +173 -0
  39. data/spec/yadm/adapters/common_sql_spec.rb +89 -0
  40. data/spec/yadm/adapters/memory_spec.rb +230 -0
  41. data/spec/yadm/adapters/mysql_spec.rb +9 -0
  42. data/spec/yadm/adapters/postgresql_spec.rb +9 -0
  43. data/spec/yadm/adapters/sqlite_spec.rb +5 -0
  44. data/spec/yadm/adapters_spec.rb +32 -0
  45. data/spec/yadm/criteria/condition_spec.rb +50 -0
  46. data/spec/yadm/criteria/limit_spec.rb +45 -0
  47. data/spec/yadm/criteria/order_spec.rb +50 -0
  48. data/spec/yadm/criteria_parser/expression_parser_spec.rb +47 -0
  49. data/spec/yadm/criteria_parser_spec.rb +55 -0
  50. data/spec/yadm/criteria_spec.rb +40 -0
  51. data/spec/yadm/entity_spec.rb +76 -0
  52. data/spec/yadm/identity_map_spec.rb +128 -0
  53. data/spec/yadm/mapper_spec.rb +23 -0
  54. data/spec/yadm/mapping/attribute_spec.rb +35 -0
  55. data/spec/yadm/mapping_spec.rb +122 -0
  56. data/spec/yadm/query_spec.rb +45 -0
  57. data/spec/yadm/repository_spec.rb +175 -0
  58. data/spec/yadm_spec.rb +45 -0
  59. data/yadm.gemspec +33 -0
  60. 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,15 @@
1
+ module YADM
2
+ class Criteria
3
+ class Attribute
4
+ attr_reader :name
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+
10
+ def ==(other)
11
+ other.respond_to?(:name) && name == other.name
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -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
@@ -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
@@ -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