rom 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +5 -8
  4. data/CHANGELOG.md +28 -1
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +2 -2
  7. data/lib/rom.rb +1 -1
  8. data/lib/rom/command.rb +7 -5
  9. data/lib/rom/command_registry.rb +1 -1
  10. data/lib/rom/commands.rb +0 -2
  11. data/lib/rom/commands/abstract.rb +55 -25
  12. data/lib/rom/commands/composite.rb +13 -1
  13. data/lib/rom/commands/delete.rb +0 -8
  14. data/lib/rom/commands/graph.rb +102 -0
  15. data/lib/rom/commands/graph/class_interface.rb +69 -0
  16. data/lib/rom/commands/lazy.rb +87 -0
  17. data/lib/rom/constants.rb +22 -0
  18. data/lib/rom/env.rb +48 -18
  19. data/lib/rom/gateway.rb +132 -0
  20. data/lib/rom/global.rb +19 -19
  21. data/lib/rom/header.rb +42 -16
  22. data/lib/rom/header/attribute.rb +37 -15
  23. data/lib/rom/lint/gateway.rb +94 -0
  24. data/lib/rom/lint/spec.rb +15 -3
  25. data/lib/rom/lint/test.rb +45 -14
  26. data/lib/rom/mapper.rb +23 -10
  27. data/lib/rom/mapper/attribute_dsl.rb +157 -18
  28. data/lib/rom/memory.rb +1 -1
  29. data/lib/rom/memory/commands.rb +10 -8
  30. data/lib/rom/memory/dataset.rb +22 -2
  31. data/lib/rom/memory/{repository.rb → gateway.rb} +10 -10
  32. data/lib/rom/pipeline.rb +2 -1
  33. data/lib/rom/processor/transproc.rb +105 -14
  34. data/lib/rom/relation.rb +4 -4
  35. data/lib/rom/relation/class_interface.rb +19 -13
  36. data/lib/rom/relation/graph.rb +22 -0
  37. data/lib/rom/relation/lazy.rb +5 -3
  38. data/lib/rom/repository.rb +9 -118
  39. data/lib/rom/setup.rb +21 -14
  40. data/lib/rom/setup/finalize.rb +19 -19
  41. data/lib/rom/setup_dsl/relation.rb +10 -1
  42. data/lib/rom/support/deprecations.rb +21 -3
  43. data/lib/rom/support/enumerable_dataset.rb +1 -1
  44. data/lib/rom/version.rb +1 -1
  45. data/rom.gemspec +2 -4
  46. data/spec/integration/commands/delete_spec.rb +6 -0
  47. data/spec/integration/commands/graph_spec.rb +235 -0
  48. data/spec/integration/mappers/combine_spec.rb +14 -5
  49. data/spec/integration/mappers/definition_dsl_spec.rb +6 -1
  50. data/spec/integration/mappers/exclude_spec.rb +28 -0
  51. data/spec/integration/mappers/fold_spec.rb +16 -0
  52. data/spec/integration/mappers/group_spec.rb +0 -22
  53. data/spec/integration/mappers/prefix_separator_spec.rb +54 -0
  54. data/spec/integration/mappers/prefix_spec.rb +50 -0
  55. data/spec/integration/mappers/reusing_mappers_spec.rb +21 -0
  56. data/spec/integration/mappers/step_spec.rb +120 -0
  57. data/spec/integration/mappers/unfold_spec.rb +93 -0
  58. data/spec/integration/mappers/ungroup_spec.rb +127 -0
  59. data/spec/integration/mappers/unwrap_spec.rb +2 -2
  60. data/spec/integration/multi_repo_spec.rb +11 -11
  61. data/spec/integration/repositories/setting_logger_spec.rb +2 -2
  62. data/spec/integration/setup_spec.rb +11 -1
  63. data/spec/shared/command_behavior.rb +18 -0
  64. data/spec/shared/materializable.rb +4 -2
  65. data/spec/shared/users_and_tasks.rb +3 -3
  66. data/spec/test/memory_repository_lint_test.rb +4 -4
  67. data/spec/unit/rom/commands/graph_spec.rb +198 -0
  68. data/spec/unit/rom/commands/lazy_spec.rb +88 -0
  69. data/spec/unit/rom/commands_spec.rb +2 -2
  70. data/spec/unit/rom/env_spec.rb +26 -0
  71. data/spec/unit/rom/gateway_spec.rb +90 -0
  72. data/spec/unit/rom/global_spec.rb +4 -3
  73. data/spec/unit/rom/mapper/dsl_spec.rb +42 -1
  74. data/spec/unit/rom/mapper_spec.rb +4 -1
  75. data/spec/unit/rom/memory/commands/create_spec.rb +21 -0
  76. data/spec/unit/rom/memory/commands/delete_spec.rb +21 -0
  77. data/spec/unit/rom/memory/commands/update_spec.rb +21 -0
  78. data/spec/unit/rom/memory/relation_spec.rb +42 -10
  79. data/spec/unit/rom/memory/repository_spec.rb +3 -3
  80. data/spec/unit/rom/processor/transproc_spec.rb +75 -0
  81. data/spec/unit/rom/relation/lazy/combine_spec.rb +33 -4
  82. data/spec/unit/rom/relation/lazy_spec.rb +9 -1
  83. data/spec/unit/rom/repository_spec.rb +4 -63
  84. data/spec/unit/rom/setup_spec.rb +19 -5
  85. metadata +28 -38
  86. data/.ruby-version +0 -1
  87. data/lib/rom/lint/repository.rb +0 -94
data/lib/rom/global.rb CHANGED
@@ -16,7 +16,7 @@ module ROM
16
16
  super
17
17
 
18
18
  rom.instance_variable_set('@adapters', {})
19
- rom.instance_variable_set('@repositories', {})
19
+ rom.instance_variable_set('@gateways', {})
20
20
  rom.instance_variable_set('@plugin_registry', PluginRegistry.new)
21
21
  end
22
22
 
@@ -27,12 +27,12 @@ module ROM
27
27
  # @api private
28
28
  attr_reader :adapters
29
29
 
30
- # An internal repo => identifier map used by the setup
30
+ # An internal gateway => identifier map used by the setup
31
31
  #
32
32
  # @return [Hash]
33
33
  #
34
34
  # @api private
35
- attr_reader :repositories
35
+ attr_reader :gateways
36
36
 
37
37
  # An internal identifier => plugin map used by the setup
38
38
  #
@@ -60,29 +60,29 @@ module ROM
60
60
  # Starts the setup process for relations, mappers and commands.
61
61
  #
62
62
  # @overload setup(type, *args)
63
- # Sets up a single-repository environment given a repository type provided
64
- # under the ROM umbrella. For custom repositories, create an instance and
63
+ # Sets up a single-gateway environment given a gateway type provided
64
+ # under the ROM umbrella. For custom gateways, create an instance and
65
65
  # pass it directly.
66
66
  #
67
67
  # @param [Symbol] type
68
68
  # @param [Array] *args
69
69
  #
70
- # @overload setup(repository)
71
- # @param [Repository] repository
70
+ # @overload setup(gateway)
71
+ # @param [Gateway] gateway
72
72
  #
73
- # @overload setup(repositories)
74
- # Sets up multiple repositories.
73
+ # @overload setup(gateways)
74
+ # Sets up multiple gateways.
75
75
  #
76
- # @param [Hash{Symbol=>Symbol,Array}] repositories
76
+ # @param [Hash{Symbol=>Symbol,Array}] gateways
77
77
  #
78
78
  # @return [Setup] boot object
79
79
  #
80
80
  # @example
81
- # # Use the in-memory adapter shipped with ROM as the default repository.
81
+ # # Use the in-memory adapter shipped with ROM as the default gateway.
82
82
  # env = ROM.setup(:memory, 'memory://test')
83
- # # Use `rom-sql` with an in-memory sqlite database as default repository.
83
+ # # Use `rom-sql` with an in-memory sqlite database as default gateway.
84
84
  # ROM.setup(:sql, 'sqlite::memory')
85
- # # Registers a `default` and a `warehouse` repository.
85
+ # # Registers a `default` and a `warehouse` gateway.
86
86
  # env = ROM.setup(
87
87
  # default: [:sql, 'sqlite::memory'],
88
88
  # warehouse: [:sql, 'postgres://localhost/warehouse']
@@ -114,7 +114,7 @@ module ROM
114
114
  # @api public
115
115
  def setup(*args, &block)
116
116
  config = setup_config(*args)
117
- @boot = Setup.new(setup_repositories(config), adapters.keys.first)
117
+ @boot = Setup.new(setup_gateways(config), adapters.keys.first)
118
118
 
119
119
  if block
120
120
  @boot.instance_exec(&block)
@@ -245,16 +245,16 @@ module ROM
245
245
  args.first.is_a?(Hash) ? args.first : { default: args }
246
246
  end
247
247
 
248
- # Build repositories using the setup interface
248
+ # Build gateways using the setup interface
249
249
  #
250
250
  # @api private
251
- def setup_repositories(config)
251
+ def setup_gateways(config)
252
252
  config.each_with_object({}) do |(name, spec), hash|
253
253
  identifier, *args = Array(spec)
254
- repository = Repository.setup(identifier, *(args.flatten))
255
- hash[name] = repository
254
+ gateway = Gateway.setup(identifier, *(args.flatten))
255
+ hash[name] = gateway
256
256
 
257
- repositories[repository] = identifier unless identifier.is_a?(Repository)
257
+ gateways[gateway] = identifier unless identifier.is_a?(Gateway)
258
258
  end
259
259
  end
260
260
  end
data/lib/rom/header.rb CHANGED
@@ -35,6 +35,11 @@ module ROM
35
35
  # @api private
36
36
  attr_reader :tuple_keys
37
37
 
38
+ # @return [Array] all attribute names that are popping from a tuple
39
+ #
40
+ # @api private
41
+ attr_reader :pop_keys
42
+
38
43
  # Coerce array with attribute definitions into a header object
39
44
  #
40
45
  # @param [Array<Array>] input attribute name/option pairs
@@ -62,6 +67,7 @@ module ROM
62
67
  @attributes = attributes
63
68
  initialize_mapping
64
69
  initialize_tuple_keys
70
+ initialize_pop_keys
65
71
  end
66
72
 
67
73
  # Iterate over attributes
@@ -98,13 +104,31 @@ module ROM
98
104
  attributes.fetch(name)
99
105
  end
100
106
 
101
- # Return all Group attributes
107
+ # Return all Combined attributes
108
+ #
109
+ # @return [Array<Combined>]
110
+ #
111
+ # @api private
112
+ def combined
113
+ by_type(Combined)
114
+ end
115
+
116
+ # Returns all attributes that require preprocessing
117
+ #
118
+ # @return [Array<Group,Fold>]
119
+ #
120
+ # @api private
121
+ def preprocessed
122
+ by_type(Group, Fold)
123
+ end
124
+
125
+ # Returns all attributes that require postprocessing
102
126
  #
103
- # @return [Array<Group>]
127
+ # @return [Array<Ungroup,Unfold>]
104
128
  #
105
129
  # @api private
106
- def groups
107
- by_type(Group)
130
+ def postprocessed
131
+ by_type(Ungroup, Unfold)
108
132
  end
109
133
 
110
134
  # Return all Wrap attributes
@@ -116,12 +140,16 @@ module ROM
116
140
  by_type(Wrap)
117
141
  end
118
142
 
143
+ # Return all non-primitive attributes that don't require mapping
144
+ #
145
+ # @return [Array<Group,Fold,Ungroup,Unfold,Wrap,Unwrap>]
146
+ #
119
147
  # @api private
120
- def combined
121
- by_type(Combined)
148
+ def non_primitives
149
+ preprocessed + wraps
122
150
  end
123
151
 
124
- # Return all primitive attributes (no Group and Wrap)
152
+ # Return all primitive attributes that require mapping
125
153
  #
126
154
  # @return [Array<Attribute>]
127
155
  #
@@ -130,15 +158,6 @@ module ROM
130
158
  to_a - non_primitives
131
159
  end
132
160
 
133
- # Return all non-primitive attributes (only Group and Wrap types)
134
- #
135
- # @return [Array<Group,Wrap>]
136
- #
137
- # @api private
138
- def non_primitives
139
- groups + wraps
140
- end
141
-
142
161
  private
143
162
 
144
163
  # Find all attribute matching specific attribute class (not kind)
@@ -163,5 +182,12 @@ module ROM
163
182
  def initialize_tuple_keys
164
183
  @tuple_keys = mapping.keys + non_primitives.flat_map(&:tuple_keys)
165
184
  end
185
+
186
+ # Set all tuple keys from all attributes popping from Unwrap and Ungroup
187
+ #
188
+ # @api private
189
+ def initialize_pop_keys
190
+ @pop_keys = mapping.values + non_primitives.flat_map(&:tuple_keys)
191
+ end
166
192
  end
167
193
  end
@@ -39,21 +39,8 @@ module ROM
39
39
  #
40
40
  # @api private
41
41
  def self.[](meta)
42
- type = meta[:type]
43
-
44
- if meta[:combine]
45
- Combined
46
- elsif meta[:unwrap]
47
- Unwrap
48
- elsif type.equal?(:hash)
49
- meta[:wrap] ? Wrap : Hash
50
- elsif meta[:fold]
51
- Group
52
- elsif type.equal?(:array)
53
- meta[:group] ? Group : Array
54
- else
55
- self
56
- end
42
+ key = (meta.keys & TYPE_MAP.keys).first
43
+ TYPE_MAP.fetch(key || meta[:type], self)
57
44
  end
58
45
 
59
46
  # Coerce an array with attribute meta-data into an attribute object
@@ -135,6 +122,10 @@ module ROM
135
122
  def tuple_keys
136
123
  header.tuple_keys
137
124
  end
125
+
126
+ def pop_keys
127
+ header.pop_keys
128
+ end
138
129
  end
139
130
 
140
131
  # Array is an embedded attribute type
@@ -158,5 +149,36 @@ module ROM
158
149
  # Group is a special type of Array attribute that requires grouping
159
150
  # transformation
160
151
  Group = Class.new(Array)
152
+
153
+ # Ungroup is a special type of Array attribute that requires ungrouping
154
+ # transformation
155
+ Ungroup = Class.new(Array)
156
+
157
+ # Fold is a special type of Array attribute that requires folding
158
+ # transformation
159
+ Fold = Class.new(Array)
160
+
161
+ # Unfold is a special type of Array attribute that requires unfolding
162
+ # transformation
163
+ Unfold = Class.new(Array)
164
+
165
+ # Exclude is a special type of Attribute to be removed
166
+ Exclude = Class.new(Attribute)
167
+
168
+ # TYPE_MAP is a (hash) map of ROM::Header identifiers to ROM::Header types
169
+ #
170
+ # @private
171
+ TYPE_MAP = {
172
+ combine: Combined,
173
+ wrap: Wrap,
174
+ unwrap: Unwrap,
175
+ group: Group,
176
+ ungroup: Ungroup,
177
+ fold: Fold,
178
+ unfold: Unfold,
179
+ hash: Hash,
180
+ array: Array,
181
+ exclude: Exclude
182
+ }
161
183
  end
162
184
  end
@@ -0,0 +1,94 @@
1
+ require 'rom/lint/linter'
2
+
3
+ module ROM
4
+ module Lint
5
+ # Ensures that a [ROM::Gateway] extension provides datasets through the
6
+ # expected methods
7
+ #
8
+ # @api public
9
+ class Gateway < ROM::Lint::Linter
10
+ # The gateway identifier e.g. +:memory+
11
+ #
12
+ # @api public
13
+ attr_reader :identifier
14
+
15
+ # The gateway class
16
+ #
17
+ # @api public
18
+ attr_reader :gateway
19
+
20
+ # The optional URI
21
+ #
22
+ # @api public
23
+ attr_reader :uri
24
+
25
+ # Gateway instance used in lint tests
26
+ #
27
+ # @api private
28
+ attr_reader :gateway_instance
29
+
30
+ # Create a gateway linter
31
+ #
32
+ # @param [Symbol] identifier
33
+ # @param [Class] gateway
34
+ # @param [String] uri optional
35
+ def initialize(identifier, gateway, uri = nil)
36
+ @identifier = identifier
37
+ @gateway = gateway
38
+ @uri = uri
39
+ @gateway_instance = setup_gateway_instance
40
+ end
41
+
42
+ # Lint: Ensure that +gateway+ setups up its instance
43
+ #
44
+ # @api public
45
+ def lint_gateway_setup
46
+ return if gateway_instance.instance_of? gateway
47
+
48
+ complain <<-STRING
49
+ #{gateway}.setup must return a gateway instance but
50
+ returned #{gateway_instance.inspect}
51
+ STRING
52
+ end
53
+
54
+ # Lint: Ensure that +gateway_instance+ responds to +[]+
55
+ #
56
+ # @api public
57
+ def lint_dataset_reader
58
+ return if gateway_instance.respond_to? :[]
59
+
60
+ complain "#{gateway_instance} must respond to []"
61
+ end
62
+
63
+ # Lint: Ensure that +gateway_instance+ responds to +dataset?+
64
+ #
65
+ # @api public
66
+ def lint_dataset_predicate
67
+ return if gateway_instance.respond_to? :dataset?
68
+
69
+ complain "#{gateway_instance} must respond to dataset?"
70
+ end
71
+
72
+ private
73
+
74
+ # Setup gateway instance
75
+ #
76
+ # @api private
77
+ def setup_gateway_instance
78
+ if uri
79
+ ROM::Gateway.setup(identifier, uri)
80
+ else
81
+ ROM::Gateway.setup(identifier)
82
+ end
83
+ end
84
+
85
+ # Run Gateway#disconnect
86
+ #
87
+ # @api private
88
+ def after_lint
89
+ super
90
+ gateway_instance.disconnect
91
+ end
92
+ end
93
+ end
94
+ end
data/lib/rom/lint/spec.rb CHANGED
@@ -1,10 +1,22 @@
1
- require 'rom/lint/repository'
1
+ require 'rom/lint/gateway'
2
2
  require 'rom/lint/enumerable_dataset'
3
3
 
4
4
  RSpec.shared_examples "a rom repository" do
5
- ROM::Lint::Repository.each_lint do |name, linter|
5
+ before(:all) do
6
+ ROM::Deprecations.announce "[Adapter]::Repository is", <<-MSG
7
+ Please use [Adapter]::Gateway instead.
8
+ MSG
9
+ end
10
+
11
+ let(:gateway) { repository }
12
+
13
+ include_examples "a rom gateway"
14
+ end
15
+
16
+ RSpec.shared_examples "a rom gateway" do
17
+ ROM::Lint::Gateway.each_lint do |name, linter|
6
18
  it name do
7
- result = linter.new(identifier, repository, uri).lint(name)
19
+ result = linter.new(identifier, gateway, uri).lint(name)
8
20
  expect(result).to be_truthy
9
21
  end
10
22
  end
data/lib/rom/lint/test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'rom/lint/repository'
1
+ require 'rom/lint/gateway'
2
2
  require 'rom/lint/enumerable_dataset'
3
3
 
4
4
  module ROM
@@ -21,53 +21,84 @@ module ROM
21
21
  end
22
22
  end
23
23
 
24
- # This is a simple lint-test for repository class to ensure the
24
+ # This is a simple lint-test for gateway class to ensure the
25
25
  # basic interfaces are in place
26
26
  #
27
27
  # @example
28
28
  #
29
- # class MyRepoTest < Minitest::Test
30
- # include ROM::Lint::TestRepository
29
+ # class MyGatewayTest < Minitest::Test
30
+ # include ROM::Lint::TestGateway
31
31
  #
32
32
  # def setup
33
- # @repository = MyRepository
33
+ # @gateway = MyGateway
34
34
  # @uri = "super_db://something"
35
35
  # end
36
36
  # end
37
37
  #
38
38
  # @api public
39
- module TestRepository
39
+ module TestGateway
40
40
  extend ROM::Lint::Test
41
41
 
42
- # Returns the repository identifier e.g. +:memory+
42
+ # Returns the gateway identifier e.g. +:memory+
43
43
  #
44
44
  # @api public
45
45
  attr_reader :identifier
46
46
 
47
- # Returns the repository class
47
+ # Returns the gateway class
48
48
  #
49
49
  # @api public
50
- attr_reader :repository
50
+ attr_reader :gateway
51
51
 
52
- # Returns repostory's URI e.g. "super_db://something"
52
+ # Returns gateway's URI e.g. "super_db://something"
53
53
  #
54
54
  # @api public
55
55
  attr_reader :uri
56
56
 
57
- ROM::Lint::Repository.each_lint do |name, linter|
57
+ ROM::Lint::Gateway.each_lint do |name, linter|
58
58
  define_test_method name do
59
- assert linter.new(identifier, repository, uri).lint(name)
59
+ assert linter.new(identifier, gateway, uri).lint(name)
60
60
  end
61
61
  end
62
62
  end
63
63
 
64
- # This is a simple lint-test for an repository dataset class to ensure the
64
+ # This is a transitional lint-test loader for repository class
65
+ # to ensure that the basic interfaces are in place
66
+ #
67
+ # class MyRepositoryTest < Minitest::Test
68
+ # include ROM::Lint::TestRepository
69
+ #
70
+ # def setup
71
+ # @repository = MyRepository
72
+ # @uri = "super_db://something"
73
+ # end
74
+ # end
75
+ #
76
+ # @see [ROM::Lint::TestGateway]
77
+ #
78
+ # @api public
79
+ module TestRepository
80
+ extend TestGateway
81
+
82
+ # Returns the reposiotry class
83
+ #
84
+ # @api public
85
+ attr_reader :repository
86
+
87
+ def setup
88
+ ROM::Deprecations.announce "[Adapter]::Repository is", <<-MSG
89
+ Please use [Adapter]::Gateway instead.
90
+ MSG
91
+ @gateway = repository
92
+ end
93
+ end
94
+
95
+ # This is a simple lint-test for a gateway dataset class to ensure the
65
96
  # basic behavior is correct
66
97
  #
67
98
  # @example
68
99
  #
69
100
  # class MyDatasetLintTest < Minitest::Test
70
- # include ROM::Repository::Lint::TestEnumerableDataset
101
+ # include ROM::Lint::TestEnumerableDataset
71
102
  #
72
103
  # def setup
73
104
  # @data = [{ name: 'Jane', age: 24 }, { name: 'Joe', age: 25 }]