rom 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/.rspec +2 -0
  2. data/.travis.yml +22 -0
  3. data/Changelog.md +16 -0
  4. data/Gemfile +13 -6
  5. data/Gemfile.devtools +71 -0
  6. data/Guardfile +19 -0
  7. data/LICENSE +1 -1
  8. data/README.md +52 -30
  9. data/Rakefile +3 -0
  10. data/config/devtools.yml +4 -0
  11. data/config/flay.yml +3 -0
  12. data/config/flog.yml +2 -0
  13. data/config/mutant.yml +5 -0
  14. data/config/reek.yml +103 -0
  15. data/config/rubocop.yml +62 -0
  16. data/config/yardstick.yml +2 -0
  17. data/lib/rom.rb +13 -5
  18. data/lib/rom/constants.rb +16 -0
  19. data/lib/rom/environment.rb +105 -0
  20. data/lib/rom/environment/builder.rb +71 -0
  21. data/lib/rom/mapper.rb +176 -0
  22. data/lib/rom/mapper/attribute.rb +108 -0
  23. data/lib/rom/mapper/builder.rb +58 -0
  24. data/lib/rom/mapper/builder/definition.rb +162 -0
  25. data/lib/rom/mapper/header.rb +103 -0
  26. data/lib/rom/mapper/loader_builder.rb +26 -0
  27. data/lib/rom/relation.rb +375 -0
  28. data/lib/rom/repository.rb +71 -0
  29. data/lib/rom/schema.rb +21 -0
  30. data/lib/rom/schema/builder.rb +59 -0
  31. data/lib/rom/schema/definition.rb +84 -0
  32. data/lib/rom/schema/definition/relation.rb +80 -0
  33. data/lib/rom/schema/definition/relation/base.rb +27 -0
  34. data/lib/rom/session.rb +111 -0
  35. data/lib/rom/session/environment.rb +67 -0
  36. data/lib/rom/session/identity_map.rb +43 -0
  37. data/lib/rom/session/mapper.rb +62 -0
  38. data/lib/rom/session/relation.rb +140 -0
  39. data/lib/rom/session/state.rb +59 -0
  40. data/lib/rom/session/state/created.rb +22 -0
  41. data/lib/rom/session/state/deleted.rb +25 -0
  42. data/lib/rom/session/state/persisted.rb +34 -0
  43. data/lib/rom/session/state/transient.rb +20 -0
  44. data/lib/rom/session/state/updated.rb +29 -0
  45. data/lib/rom/session/tracker.rb +62 -0
  46. data/lib/rom/support/axiom/adapter.rb +111 -0
  47. data/lib/rom/support/axiom/adapter/data_objects.rb +38 -0
  48. data/lib/rom/support/axiom/adapter/memory.rb +25 -0
  49. data/lib/rom/support/axiom/adapter/postgres.rb +19 -0
  50. data/lib/rom/support/axiom/adapter/sqlite3.rb +20 -0
  51. data/lib/version.rb +1 -1
  52. data/rom.gemspec +7 -3
  53. data/spec/integration/environment_setup_spec.rb +24 -0
  54. data/spec/integration/grouped_mappers_spec.rb +87 -0
  55. data/spec/integration/join_and_group_spec.rb +76 -0
  56. data/spec/integration/join_and_wrap_spec.rb +68 -0
  57. data/spec/integration/mapping_embedded_relations_spec.rb +73 -0
  58. data/spec/integration/mapping_relations_spec.rb +120 -0
  59. data/spec/integration/schema_definition_spec.rb +152 -0
  60. data/spec/integration/session_spec.rb +87 -0
  61. data/spec/integration/wrapped_mappers_spec.rb +73 -0
  62. data/spec/shared/unit/environment_context.rb +6 -0
  63. data/spec/shared/unit/loader.rb +11 -0
  64. data/spec/shared/unit/loader_identity.rb +13 -0
  65. data/spec/shared/unit/mapper_context.rb +11 -0
  66. data/spec/shared/unit/relation_context.rb +82 -0
  67. data/spec/shared/unit/session_environment_context.rb +11 -0
  68. data/spec/shared/unit/session_relation_context.rb +18 -0
  69. data/spec/spec_helper.rb +49 -0
  70. data/spec/support/helper.rb +34 -0
  71. data/spec/support/ice_nine_config.rb +10 -0
  72. data/spec/support/test_mapper.rb +110 -0
  73. data/spec/unit/rom/environment/builder/mapping_spec.rb +24 -0
  74. data/spec/unit/rom/environment/builder/schema_spec.rb +33 -0
  75. data/spec/unit/rom/environment/class_methods/setup_spec.rb +18 -0
  76. data/spec/unit/rom/environment/repository_spec.rb +21 -0
  77. data/spec/unit/rom/mapper/attribute/embedded_collection/to_ast_spec.rb +18 -0
  78. data/spec/unit/rom/mapper/attribute/embedded_value/to_ast_spec.rb +16 -0
  79. data/spec/unit/rom/mapper/attribute/rename_spec.rb +9 -0
  80. data/spec/unit/rom/mapper/attribute/to_ast_spec.rb +9 -0
  81. data/spec/unit/rom/mapper/builder/class_methods/call_spec.rb +61 -0
  82. data/spec/unit/rom/mapper/class_methods/build_spec.rb +55 -0
  83. data/spec/unit/rom/mapper/dump_spec.rb +11 -0
  84. data/spec/unit/rom/mapper/group_spec.rb +35 -0
  85. data/spec/unit/rom/mapper/header/each_spec.rb +26 -0
  86. data/spec/unit/rom/mapper/header/element_reader_spec.rb +21 -0
  87. data/spec/unit/rom/mapper/header/group_spec.rb +18 -0
  88. data/spec/unit/rom/mapper/header/join_spec.rb +14 -0
  89. data/spec/unit/rom/mapper/header/keys_spec.rb +29 -0
  90. data/spec/unit/rom/mapper/header/project_spec.rb +13 -0
  91. data/spec/unit/rom/mapper/header/rename_spec.rb +11 -0
  92. data/spec/unit/rom/mapper/header/to_ast_spec.rb +11 -0
  93. data/spec/unit/rom/mapper/header/wrap_spec.rb +18 -0
  94. data/spec/unit/rom/mapper/identity_from_tuple_spec.rb +11 -0
  95. data/spec/unit/rom/mapper/identity_spec.rb +11 -0
  96. data/spec/unit/rom/mapper/join_spec.rb +15 -0
  97. data/spec/unit/rom/mapper/load_spec.rb +11 -0
  98. data/spec/unit/rom/mapper/new_object_spec.rb +14 -0
  99. data/spec/unit/rom/mapper/project_spec.rb +11 -0
  100. data/spec/unit/rom/mapper/rename_spec.rb +16 -0
  101. data/spec/unit/rom/mapper/wrap_spec.rb +35 -0
  102. data/spec/unit/rom/relation/delete_spec.rb +15 -0
  103. data/spec/unit/rom/relation/drop_spec.rb +11 -0
  104. data/spec/unit/rom/relation/each_spec.rb +23 -0
  105. data/spec/unit/rom/relation/first_spec.rb +19 -0
  106. data/spec/unit/rom/relation/group_spec.rb +29 -0
  107. data/spec/unit/rom/relation/inject_mapper_spec.rb +17 -0
  108. data/spec/unit/rom/relation/insert_spec.rb +13 -0
  109. data/spec/unit/rom/relation/last_spec.rb +19 -0
  110. data/spec/unit/rom/relation/one_spec.rb +49 -0
  111. data/spec/unit/rom/relation/rename_spec.rb +21 -0
  112. data/spec/unit/rom/relation/replace_spec.rb +13 -0
  113. data/spec/unit/rom/relation/restrict_spec.rb +25 -0
  114. data/spec/unit/rom/relation/sort_by_spec.rb +25 -0
  115. data/spec/unit/rom/relation/take_spec.rb +11 -0
  116. data/spec/unit/rom/relation/to_a_spec.rb +20 -0
  117. data/spec/unit/rom/relation/update_spec.rb +25 -0
  118. data/spec/unit/rom/relation/wrap_spec.rb +29 -0
  119. data/spec/unit/rom/repository/class_methods/build_spec.rb +27 -0
  120. data/spec/unit/rom/repository/element_reader_spec.rb +21 -0
  121. data/spec/unit/rom/repository/element_writer_spec.rb +18 -0
  122. data/spec/unit/rom/schema/builder/class_methods/build_spec.rb +103 -0
  123. data/spec/unit/rom/schema/element_reader_spec.rb +15 -0
  124. data/spec/unit/rom/session/class_methods/start_spec.rb +23 -0
  125. data/spec/unit/rom/session/clean_predicate_spec.rb +21 -0
  126. data/spec/unit/rom/session/environment/element_reader_spec.rb +13 -0
  127. data/spec/unit/rom/session/flush_spec.rb +58 -0
  128. data/spec/unit/rom/session/mapper/load_spec.rb +47 -0
  129. data/spec/unit/rom/session/relation/delete_spec.rb +28 -0
  130. data/spec/unit/rom/session/relation/dirty_predicate_spec.rb +35 -0
  131. data/spec/unit/rom/session/relation/identity_spec.rb +11 -0
  132. data/spec/unit/rom/session/relation/new_spec.rb +50 -0
  133. data/spec/unit/rom/session/relation/save_spec.rb +50 -0
  134. data/spec/unit/rom/session/relation/state_spec.rb +23 -0
  135. data/spec/unit/rom/session/relation/track_spec.rb +23 -0
  136. data/spec/unit/rom/session/relation/tracking_predicate_spec.rb +23 -0
  137. data/spec/unit/rom/session/relation/update_attributes_spec.rb +45 -0
  138. data/spec/unit/rom/session/state_spec.rb +79 -0
  139. metadata +212 -11
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rom/support/axiom/adapter'
4
+
5
+ module ROM
6
+
7
+ # A repository with a given +name+ and +adapter+
8
+ #
9
+ # @api private
10
+ class Repository
11
+ include Concord.new(:name, :adapter, :relations)
12
+
13
+ # Build a repository with a given +name+ and +uri+
14
+ #
15
+ # @param [Symbol] name
16
+ # the repository's name
17
+ #
18
+ # @param [Addressable::URI] uri
19
+ # the uri for initializing the adapter
20
+ #
21
+ # @return [Repository]
22
+ #
23
+ # @api private
24
+ def self.build(name, uri, relations = {})
25
+ new(name, Axiom::Adapter.build(uri), relations)
26
+ end
27
+
28
+ # Return the relation identified by +name+
29
+ #
30
+ # @example
31
+ #
32
+ # repo = Repository.coerce(:test, 'in_memory://test')
33
+ # repo.register(:foo, [[:id, String], [:foo, String]])
34
+ # repo[:foo]
35
+ #
36
+ # # => <Axiom::Relation header=Axiom::Header ...>
37
+ #
38
+ # @param [Symbol] name
39
+ # the name of the relation
40
+ #
41
+ # @return [Axiom::Relation]
42
+ #
43
+ # @raise [KeyError]
44
+ #
45
+ # @api public
46
+ def [](name)
47
+ relations.fetch(name)
48
+ end
49
+
50
+ # Register a relation with this repository
51
+ #
52
+ # @param [Axiom::Relation::Base] relation
53
+ #
54
+ # @return [Object] relation gateway
55
+ #
56
+ # @api public
57
+ def []=(name, relation)
58
+ relation =
59
+ if adapter.respond_to?(:gateway)
60
+ adapter.gateway(relation)
61
+ else
62
+ adapter[name] = relation
63
+ adapter[name]
64
+ end
65
+
66
+ relations[name] = relation
67
+ end
68
+
69
+ end # Repository
70
+
71
+ end # ROM
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ module ROM
4
+
5
+ # ROM's relation schema
6
+ #
7
+ class Schema
8
+ include Concord.new(:relations), Adamantium::Flat
9
+
10
+ # Return a relation identified by name
11
+ #
12
+ # @param [Symbol] name of the relation
13
+ #
14
+ # @return [Relation]
15
+ def [](name)
16
+ relations.fetch(name)
17
+ end
18
+
19
+ end # Schema
20
+
21
+ end # ROM
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rom/schema'
4
+ require 'rom/schema/definition'
5
+
6
+ module ROM
7
+ class Schema
8
+
9
+ # Schema builder DSL
10
+ #
11
+ class Builder
12
+ include Concord.new(:definition), Adamantium::Flat
13
+
14
+ # Build a relation schema
15
+ #
16
+ # @example
17
+ #
18
+ # Schema.build do
19
+ # base_relation :users do
20
+ # repository :test
21
+ # attribute :id, :name
22
+ # end
23
+ # end
24
+ #
25
+ # @return [Schema]
26
+ #
27
+ # @api public
28
+ def self.build(repositories, &block)
29
+ new(Definition.new(repositories, &block))
30
+ end
31
+
32
+ # Return defined relation identified by name
33
+ #
34
+ # @example
35
+ #
36
+ # schema[:users] # => #<Axiom::Relation::Base ..>
37
+ #
38
+ # @return [Axiom::Relation, Axiom::Relation::Base]
39
+ #
40
+ # @api public
41
+ def [](name)
42
+ definition[name]
43
+ end
44
+
45
+ # @api private
46
+ def call(&block)
47
+ definition.instance_eval(&block)
48
+ self
49
+ end
50
+
51
+ # @api private
52
+ def finalize
53
+ Schema.new(definition.relations)
54
+ end
55
+
56
+ end # Builder
57
+
58
+ end # Schema
59
+ end # ROM
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rom/schema/definition/relation/base'
4
+
5
+ module ROM
6
+ class Schema
7
+
8
+ # Builder object used by schema DSL to establish Axiom relations
9
+ #
10
+ # @private
11
+ class Definition
12
+ include Equalizer.new(:repositories, :relations)
13
+
14
+ attr_reader :repositories, :relations
15
+
16
+ # @api private
17
+ def initialize(repositories, &block)
18
+ @repositories = repositories
19
+ @relations = {}
20
+ instance_eval(&block) if block
21
+ end
22
+
23
+ # Build a base relation
24
+ #
25
+ # @example
26
+ #
27
+ # Schema.build do
28
+ # base_relation :users do
29
+ # # ...
30
+ # end
31
+ # end
32
+ #
33
+ # @return [Definition]
34
+ #
35
+ # @api private
36
+ def base_relation(name, &block)
37
+ builder = Relation::Base.new(relations, &block)
38
+ repository = repositories.fetch(builder.repository)
39
+
40
+ repository[name] = builder.call(name)
41
+ relations[name] = repository[name]
42
+ end
43
+
44
+ # Build a relation
45
+ #
46
+ # @example
47
+ #
48
+ # Schema.build do
49
+ # relation :users do
50
+ # # ...
51
+ # end
52
+ # end
53
+ #
54
+ # @return [Definition]
55
+ #
56
+ # @api private
57
+ def relation(name, &block)
58
+ relations[name] = instance_eval(&block)
59
+ end
60
+
61
+ # Return relation identified by name
62
+ #
63
+ # @return [Axiom::Relation, Axiom::Relation::Base]
64
+ #
65
+ # @api private
66
+ def [](name)
67
+ relations[name]
68
+ end
69
+
70
+ private
71
+
72
+ # Method missing hook
73
+ #
74
+ # @return [Axiom::Relation, Axiom::Relation::Base]
75
+ #
76
+ # @api private
77
+ def method_missing(*args)
78
+ self[args.first] || super
79
+ end
80
+
81
+ end # Definition
82
+
83
+ end # Schema
84
+ end # ROM
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+
3
+ module ROM
4
+ class Schema
5
+ class Definition
6
+
7
+ # Builder object for Axiom relation
8
+ #
9
+ # @private
10
+ class Relation
11
+ include Equalizer.new(:header, :keys)
12
+
13
+ attr_reader :registry
14
+
15
+ # @api private
16
+ def initialize(registry, &block)
17
+ @registry = registry
18
+ @header = []
19
+ @keys = []
20
+ @wrappings = []
21
+ @groupings = []
22
+ instance_eval(&block)
23
+ end
24
+
25
+ # @api private
26
+ def call(name)
27
+ relation = Axiom::Relation::Base.new(name, build_header)
28
+
29
+ if @wrappings.any?
30
+ @wrappings.each { |wrapping| relation = relation.wrap(wrapping) }
31
+ end
32
+
33
+ if @groupings.any?
34
+ @groupings.each { |grouping| relation = relation.group(grouping) }
35
+ end
36
+
37
+ renames = @header.each_with_object({}) { |ary, mapping|
38
+ mapping[ary.first] = ary.last[:rename] if ary.last[:rename]
39
+ }
40
+
41
+ relation.rename(renames).optimize
42
+ end
43
+
44
+ # @api private
45
+ def attribute(name, type, options = {})
46
+ @header << [name, type, options]
47
+ end
48
+
49
+ # @api private
50
+ def wrap(wrapping)
51
+ @wrappings << wrapping
52
+ end
53
+
54
+ # @api private
55
+ def group(grouping)
56
+ @groupings << grouping
57
+ end
58
+
59
+ # @api private
60
+ def key(*attribute_names)
61
+ @keys.concat(attribute_names)
62
+ end
63
+
64
+ private
65
+
66
+ # @api private
67
+ def method_missing(*args)
68
+ registry[args.first] || super
69
+ end
70
+
71
+ # @api private
72
+ def build_header
73
+ Axiom::Relation::Header.coerce(@header, keys: @keys)
74
+ end
75
+
76
+ end # Relation
77
+
78
+ end # Definition
79
+ end # Schema
80
+ end # ROM
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rom/schema/definition/relation'
4
+
5
+ module ROM
6
+ class Schema
7
+ class Definition
8
+ class Relation
9
+
10
+ # Base relation builder object
11
+ #
12
+ class Base < self
13
+
14
+ def repository(name = Undefined)
15
+ if name == Undefined
16
+ @repository
17
+ else
18
+ @repository = name
19
+ end
20
+ end
21
+
22
+ end # Base
23
+
24
+ end # Relation
25
+ end # Definition
26
+ end # Schema
27
+ end # ROM
@@ -0,0 +1,111 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rom/session/environment'
4
+ require 'rom/session/tracker'
5
+ require 'rom/session/identity_map'
6
+ require 'rom/session/relation'
7
+ require 'rom/session/mapper'
8
+
9
+ require 'rom/session/state'
10
+ require 'rom/session/state/transient'
11
+ require 'rom/session/state/persisted'
12
+ require 'rom/session/state/created'
13
+ require 'rom/session/state/updated'
14
+ require 'rom/session/state/deleted'
15
+
16
+ module ROM
17
+
18
+ # Extended ROM::Environment with session support
19
+ class Environment
20
+
21
+ # Start a new session for this environment
22
+ #
23
+ # @example
24
+ # env.session do |session|
25
+ # # ...
26
+ # end
27
+ #
28
+ # @see Session.start
29
+ #
30
+ # @api public
31
+ def session(&block)
32
+ Session.start(self, &block)
33
+ end
34
+ end
35
+
36
+ # Session with IdentityMap and state-tracking functionality
37
+ #
38
+ # @example
39
+ #
40
+ # env.session do |session|
41
+ # user = session[:users].new(id: 1, name: 'Jane')
42
+ #
43
+ # session[:users].save(user)
44
+ #
45
+ # session.flush
46
+ # end
47
+ #
48
+ # @api public
49
+ class Session
50
+ include Concord.new(:environment)
51
+
52
+ # Raised when an object is expected to be tracked and it's not
53
+ #
54
+ class ObjectNotTrackedError < StandardError
55
+ def initialize(identity)
56
+ super("Tracker doesn't include object with identity #{identity.inspect}")
57
+ end
58
+ end
59
+
60
+ # Start a new session
61
+ #
62
+ # @example
63
+ #
64
+ # ROM::Session.start(env) do |session|
65
+ # user = session[:users].new(name: 'Jane')
66
+ # session[:users].save(user)
67
+ # session[:users].flush
68
+ # end
69
+ #
70
+ # @param [ROM::Environment] rom's environment
71
+ #
72
+ # @yieldparam [Session::Environment]
73
+ #
74
+ # @api public
75
+ def self.start(environment)
76
+ yield(new(Environment.build(environment)))
77
+ end
78
+
79
+ # Return a session relation identified by name
80
+ #
81
+ # @param [Symbol] relation name
82
+ #
83
+ # @return [Session::Relation]
84
+ #
85
+ # @api public
86
+ def [](relation_name)
87
+ environment[relation_name]
88
+ end
89
+
90
+ # Flush this session committing all the state changes
91
+ #
92
+ # @return [Session]
93
+ #
94
+ # @api public
95
+ def flush
96
+ environment.commit
97
+ self
98
+ end
99
+
100
+ # Return if there are any pending state changes
101
+ #
102
+ # @return [Boolean]
103
+ #
104
+ # @api public
105
+ def clean?
106
+ environment.clean?
107
+ end
108
+
109
+ end # Session
110
+
111
+ end # ROM