rom 2.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -7
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +35 -1
  5. data/Gemfile +17 -2
  6. data/Rakefile +7 -2
  7. data/lib/rom/array_dataset.rb +44 -0
  8. data/lib/rom/association_set.rb +11 -5
  9. data/lib/rom/auto_curry.rb +55 -0
  10. data/lib/rom/command.rb +331 -47
  11. data/lib/rom/command_registry.rb +7 -18
  12. data/lib/rom/commands/class_interface.rb +120 -6
  13. data/lib/rom/commands/composite.rb +0 -1
  14. data/lib/rom/commands/graph.rb +7 -15
  15. data/lib/rom/commands/lazy/update.rb +1 -1
  16. data/lib/rom/configuration.rb +2 -0
  17. data/lib/rom/configuration_dsl/command.rb +6 -8
  18. data/lib/rom/configuration_dsl/mapper.rb +2 -3
  19. data/lib/rom/configuration_dsl/mapper_dsl.rb +0 -1
  20. data/lib/rom/configuration_dsl/relation.rb +4 -4
  21. data/lib/rom/configuration_dsl.rb +0 -4
  22. data/lib/rom/constants.rb +7 -1
  23. data/lib/rom/container.rb +11 -17
  24. data/lib/rom/create_container.rb +0 -2
  25. data/lib/rom/data_proxy.rb +94 -0
  26. data/lib/rom/enumerable_dataset.rb +68 -0
  27. data/lib/rom/gateway.rb +74 -32
  28. data/lib/rom/global/plugin_dsl.rb +0 -2
  29. data/lib/rom/global.rb +0 -2
  30. data/lib/rom/initializer.rb +26 -0
  31. data/lib/rom/lint/gateway.rb +17 -0
  32. data/lib/rom/mapper_registry.rb +1 -1
  33. data/lib/rom/memory/commands.rb +0 -2
  34. data/lib/rom/memory/dataset.rb +1 -2
  35. data/lib/rom/memory/relation.rb +14 -1
  36. data/lib/rom/memory/schema.rb +13 -0
  37. data/lib/rom/plugin_registry.rb +1 -1
  38. data/lib/rom/plugins/command/schema.rb +2 -2
  39. data/lib/rom/plugins/configuration/configuration_dsl.rb +6 -2
  40. data/lib/rom/plugins/relation/key_inference.rb +4 -2
  41. data/lib/rom/plugins/relation/registry_reader.rb +5 -1
  42. data/lib/rom/registry.rb +50 -0
  43. data/lib/rom/relation/class_interface.rb +142 -30
  44. data/lib/rom/relation/curried.rb +15 -15
  45. data/lib/rom/relation/view_dsl.rb +31 -0
  46. data/lib/rom/relation.rb +101 -41
  47. data/lib/rom/schema/attribute.rb +367 -0
  48. data/lib/rom/schema/dsl.rb +14 -10
  49. data/lib/rom/schema.rb +337 -19
  50. data/lib/rom/setup/auto_registration.rb +20 -17
  51. data/lib/rom/setup/auto_registration_strategies/base.rb +8 -3
  52. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +4 -3
  53. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +5 -4
  54. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +3 -3
  55. data/lib/rom/setup/finalize/finalize_commands.rb +1 -1
  56. data/lib/rom/setup/finalize/finalize_mappers.rb +1 -1
  57. data/lib/rom/setup/finalize/finalize_relations.rb +4 -2
  58. data/lib/rom/setup/finalize.rb +1 -1
  59. data/lib/rom/transaction.rb +24 -0
  60. data/lib/rom/types.rb +9 -1
  61. data/lib/rom/version.rb +1 -1
  62. data/lib/rom.rb +4 -8
  63. data/rom.gemspec +5 -4
  64. data/spec/integration/command_registry_spec.rb +1 -14
  65. data/spec/integration/commands/create_spec.rb +5 -25
  66. data/spec/integration/commands/delete_spec.rb +1 -1
  67. data/spec/integration/commands/error_handling_spec.rb +1 -1
  68. data/spec/integration/commands/graph_spec.rb +20 -14
  69. data/spec/integration/commands/update_spec.rb +4 -27
  70. data/spec/integration/commands_spec.rb +1 -1
  71. data/spec/integration/{repositories → gateways}/extending_relations_spec.rb +1 -1
  72. data/spec/integration/{repositories → gateways}/setting_logger_spec.rb +2 -2
  73. data/spec/integration/mappers/combine_spec.rb +1 -1
  74. data/spec/integration/mappers/deep_embedded_spec.rb +1 -1
  75. data/spec/integration/mappers/definition_dsl_spec.rb +1 -1
  76. data/spec/integration/mappers/embedded_spec.rb +1 -1
  77. data/spec/integration/mappers/exclude_spec.rb +1 -1
  78. data/spec/integration/mappers/fold_spec.rb +1 -1
  79. data/spec/integration/mappers/group_spec.rb +1 -1
  80. data/spec/integration/mappers/overwrite_attributes_value_spec.rb +1 -1
  81. data/spec/integration/mappers/prefix_separator_spec.rb +1 -1
  82. data/spec/integration/mappers/prefix_spec.rb +1 -1
  83. data/spec/integration/mappers/prefixing_attributes_spec.rb +1 -1
  84. data/spec/integration/mappers/registering_custom_mappers_spec.rb +1 -1
  85. data/spec/integration/mappers/renaming_attributes_spec.rb +1 -1
  86. data/spec/integration/mappers/reusing_mappers_spec.rb +1 -1
  87. data/spec/integration/mappers/step_spec.rb +1 -1
  88. data/spec/integration/mappers/symbolizing_attributes_spec.rb +1 -1
  89. data/spec/integration/mappers/unfold_spec.rb +1 -1
  90. data/spec/integration/mappers/ungroup_spec.rb +2 -2
  91. data/spec/integration/mappers/unwrap_spec.rb +2 -2
  92. data/spec/integration/mappers/wrap_spec.rb +1 -1
  93. data/spec/integration/memory/commands/create_spec.rb +1 -1
  94. data/spec/integration/memory/commands/delete_spec.rb +1 -1
  95. data/spec/integration/memory/commands/update_spec.rb +1 -1
  96. data/spec/integration/multi_env_spec.rb +1 -1
  97. data/spec/integration/multi_repo_spec.rb +1 -1
  98. data/spec/integration/relations/default_dataset_spec.rb +1 -1
  99. data/spec/integration/relations/reading_spec.rb +1 -1
  100. data/spec/integration/relations/registry_dsl_spec.rb +1 -1
  101. data/spec/integration/setup_spec.rb +10 -4
  102. data/spec/shared/command_graph.rb +8 -4
  103. data/spec/shared/enumerable_dataset.rb +1 -1
  104. data/spec/spec_helper.rb +7 -9
  105. data/spec/support/schema.rb +14 -0
  106. data/spec/unit/rom/array_dataset_spec.rb +59 -0
  107. data/spec/unit/rom/association_set_spec.rb +4 -0
  108. data/spec/unit/rom/auto_curry_spec.rb +63 -0
  109. data/spec/unit/rom/commands/graph_spec.rb +12 -11
  110. data/spec/unit/rom/commands/lazy_spec.rb +8 -5
  111. data/spec/unit/rom/commands/pre_and_post_processors_spec.rb +336 -0
  112. data/spec/unit/rom/commands/result_spec.rb +1 -1
  113. data/spec/unit/rom/commands_spec.rb +26 -3
  114. data/spec/unit/rom/configuration_spec.rb +1 -1
  115. data/spec/unit/rom/container_spec.rb +15 -5
  116. data/spec/unit/rom/create_container_spec.rb +1 -1
  117. data/spec/unit/rom/enumerable_dataset_spec.rb +15 -0
  118. data/spec/unit/rom/gateway_spec.rb +1 -1
  119. data/spec/unit/rom/mapper_registry_spec.rb +1 -1
  120. data/spec/unit/rom/memory/commands_spec.rb +1 -1
  121. data/spec/unit/rom/memory/dataset_spec.rb +1 -1
  122. data/spec/unit/rom/memory/{repository_spec.rb → gateway_spec.rb} +1 -1
  123. data/spec/unit/rom/memory/inheritance_spec.rb +32 -0
  124. data/spec/unit/rom/memory/relation_spec.rb +15 -3
  125. data/spec/unit/rom/memory/storage_spec.rb +1 -1
  126. data/spec/unit/rom/plugin_spec.rb +1 -1
  127. data/spec/unit/rom/plugins/command/schema_spec.rb +5 -5
  128. data/spec/unit/rom/plugins/relation/key_inference_spec.rb +1 -1
  129. data/spec/unit/rom/registry_spec.rb +86 -0
  130. data/spec/unit/rom/relation/attribute_reader_spec.rb +17 -0
  131. data/spec/unit/rom/relation/call_spec.rb +51 -0
  132. data/spec/unit/rom/relation/composite_spec.rb +1 -1
  133. data/spec/unit/rom/relation/graph_spec.rb +1 -1
  134. data/spec/unit/rom/relation/lazy/combine_spec.rb +1 -1
  135. data/spec/unit/rom/relation/lazy_spec.rb +1 -1
  136. data/spec/unit/rom/relation/loaded_spec.rb +1 -1
  137. data/spec/unit/rom/relation/schema_spec.rb +50 -6
  138. data/spec/unit/rom/relation/view_spec.rb +122 -0
  139. data/spec/unit/rom/relation_spec.rb +20 -5
  140. data/spec/unit/rom/schema/accessing_attributes_spec.rb +52 -0
  141. data/spec/unit/rom/schema/append_spec.rb +17 -0
  142. data/spec/unit/rom/schema/exclude_spec.rb +15 -0
  143. data/spec/unit/rom/schema/finalize_spec.rb +59 -0
  144. data/spec/unit/rom/schema/key_predicate_spec.rb +15 -0
  145. data/spec/unit/rom/schema/merge_spec.rb +17 -0
  146. data/spec/unit/rom/schema/prefix_spec.rb +16 -0
  147. data/spec/unit/rom/schema/project_spec.rb +15 -0
  148. data/spec/unit/rom/schema/rename_spec.rb +22 -0
  149. data/spec/unit/rom/schema/type_spec.rb +49 -0
  150. data/spec/unit/rom/schema/uniq_spec.rb +21 -0
  151. data/spec/unit/rom/schema/wrap_spec.rb +17 -0
  152. data/spec/unit/rom/schema_spec.rb +2 -2
  153. metadata +79 -17
  154. data/lib/rom/plugins/relation/view/dsl.rb +0 -32
  155. data/lib/rom/plugins/relation/view.rb +0 -95
  156. data/spec/unit/rom/plugins/relation/view_spec.rb +0 -51
@@ -1,4 +1,4 @@
1
- shared_examples_for "an enumerable dataset" do
1
+ RSpec.shared_examples_for "an enumerable dataset" do
2
2
  subject(:dataset) { klass.new(data) }
3
3
 
4
4
  let(:data) do
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,4 @@
1
- # encoding: utf-8
2
-
3
- # this is needed for guard to work, not sure why :(
4
- require "bundler"
5
- Bundler.setup
6
-
7
- if ENV['COVERAGE'] == 'true' && RUBY_ENGINE == 'ruby' && RUBY_VERSION='2.3.1'
1
+ if ENV['COVERAGE'] == 'true' && RUBY_ENGINE == 'ruby' && RUBY_VERSION == '2.3.1'
8
2
  require "simplecov"
9
3
  SimpleCov.start do
10
4
  add_filter '/spec/'
@@ -13,8 +7,8 @@ end
13
7
 
14
8
  SPEC_ROOT = root = Pathname(__FILE__).dirname
15
9
 
16
- require 'rom/support/deprecations'
17
- ROM::Deprecations.set_logger!(SPEC_ROOT.join('../log/deprecations.log'))
10
+ require 'dry/core/deprecations'
11
+ Dry::Core::Deprecations.set_logger!(SPEC_ROOT.join('../log/deprecations.log'))
18
12
 
19
13
  require 'rom'
20
14
 
@@ -42,6 +36,8 @@ def T(*args)
42
36
  end
43
37
 
44
38
  RSpec.configure do |config|
39
+ config.include(SchemaHelpers)
40
+
45
41
  config.after do
46
42
  Test.remove_constants
47
43
  end
@@ -49,4 +45,6 @@ RSpec.configure do |config|
49
45
  config.around do |example|
50
46
  ConstantLeakFinder.find(example)
51
47
  end
48
+
49
+ config.disable_monkey_patching!
52
50
  end
@@ -0,0 +1,14 @@
1
+ require 'dry-types'
2
+
3
+ module SchemaHelpers
4
+ def define_schema(source, attrs)
5
+ ROM::Schema.define(
6
+ source,
7
+ attributes: attrs.map { |name, type| define_type(name, type, source: source) }
8
+ )
9
+ end
10
+
11
+ def define_type(name, id, **opts)
12
+ ROM::Types.const_get(id).meta(name: name, **opts)
13
+ end
14
+ end
@@ -0,0 +1,59 @@
1
+ require 'rom/array_dataset'
2
+
3
+ RSpec.describe ROM::ArrayDataset do
4
+ let(:klass) do
5
+ Class.new do
6
+ include ROM::ArrayDataset
7
+
8
+ def self.row_proc
9
+ -> i { i.each_with_object({}) { |(k,v),h| h[k.to_sym] = v } }
10
+ end
11
+ end
12
+ end
13
+
14
+ it_behaves_like 'an enumerable dataset' do
15
+ describe '#flatten' do
16
+ let(:data) { [[{ 'name' => 'Jane' }], [{ 'name' => 'Joe' }]] }
17
+
18
+ it 'returns a new dataset with flattened data' do
19
+ result = dataset.flatten
20
+
21
+ expect(result).to match_array([{ name: 'Jane' }, { name: 'Joe' }])
22
+ end
23
+ end
24
+
25
+ describe '#map!' do
26
+ context 'with a block' do
27
+ it 'returns a new dataset with mapped data' do
28
+ dataset.map! do |row|
29
+ row.merge(age: 21)
30
+ end
31
+
32
+ expect(dataset).to match_array([
33
+ { name: 'Jane', age: 21 }, { name: 'Joe', age: 21 }
34
+ ])
35
+ end
36
+ end
37
+
38
+ context 'without a block' do
39
+ it 'returns an enumerator' do
40
+ result = dataset.map!
41
+
42
+ expect(result).to be_instance_of(Enumerator)
43
+
44
+ expect(result).to match_array([
45
+ { name: 'Jane' }, { name: 'Joe' }
46
+ ])
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#values_at' do
52
+ it 'returns a new dataset with rows at given indices' do
53
+ result = dataset.values_at(1)
54
+
55
+ expect(result).to match_array([{ name: 'Joe' }])
56
+ end
57
+ end
58
+ end
59
+ end
@@ -7,6 +7,10 @@ RSpec.describe ROM::AssociationSet do
7
7
  assoc_set.try(:users, &:done)
8
8
 
9
9
  expect(assoc).to have_received(:done)
10
+
11
+ assoc_set.try(:user, &:done)
12
+
13
+ expect(assoc).to have_received(:done)
10
14
  end
11
15
 
12
16
  it 'returns false when assoc is not found' do
@@ -0,0 +1,63 @@
1
+ require 'rom/auto_curry'
2
+
3
+ RSpec.describe ROM::AutoCurry do
4
+ subject(:object) { klass.new }
5
+
6
+ let(:klass) do
7
+ Class.new do
8
+ extend ROM::AutoCurry
9
+
10
+ def self.curried(*)
11
+ self
12
+ end
13
+
14
+ def initialize(*)
15
+ end
16
+
17
+ def arity_0
18
+ 0
19
+ end
20
+
21
+ def arity_1(x)
22
+ x
23
+ end
24
+
25
+ def arity_2(x, y)
26
+ [x,y]
27
+ end
28
+
29
+ def arity_many(*args)
30
+ args
31
+ end
32
+
33
+ protected
34
+
35
+ def leave_me_alone(foo)
36
+ foo
37
+ end
38
+ end
39
+ end
40
+
41
+ it 'registers auto-curried methods' do
42
+ expect(object.class.auto_curried_methods).to eql(%i[arity_1 arity_2 arity_many])
43
+ end
44
+
45
+ it 'auto-curries method with arity == 0' do
46
+ expect(object.arity_0).to be(0)
47
+ end
48
+
49
+ it 'auto-curries method with arity == 1' do
50
+ expect(object.arity_1).to be_instance_of(klass)
51
+ expect(object.arity_1(1)).to be(1)
52
+ end
53
+
54
+ it 'auto-curries method with arity > 0' do
55
+ expect(object.arity_2).to be_instance_of(klass)
56
+ expect(object.arity_2(1, 2)).to eql([1, 2])
57
+ end
58
+
59
+ it 'auto-curries method with arity < 0' do
60
+ expect(object.arity_many).to eql([])
61
+ expect(object.arity_many(1, 2)).to eql([1, 2])
62
+ end
63
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'dry-struct'
3
3
 
4
- describe ROM::Commands::Graph do
4
+ RSpec.describe ROM::Commands::Graph do
5
5
  shared_examples_for 'a persisted graph' do
6
6
  it 'returns nested results' do
7
7
  expect(command.call).to match_array([
@@ -75,17 +75,19 @@ describe ROM::Commands::Graph do
75
75
  configuration.commands(:tasks) do
76
76
  define(:create) do
77
77
  result :one
78
+ before :associate
78
79
 
79
- def execute(task, user)
80
- super(task.merge(user: user[:name]))
80
+ def associate(task, user)
81
+ task.merge(user: user[:name])
81
82
  end
82
83
  end
83
84
 
84
85
  define(:create) do
85
86
  register_as :create_many
87
+ before :associate
86
88
 
87
- def execute(tasks, user)
88
- super(tasks.map { |t| t.merge(user: user[:name]) })
89
+ def associate(tasks, user)
90
+ tasks.map { |t| t.merge(user: user[:name]) }
89
91
  end
90
92
  end
91
93
  end
@@ -93,13 +95,12 @@ describe ROM::Commands::Graph do
93
95
  configuration.commands(:tags) do
94
96
  define(:create) do
95
97
  register_as :create_many
98
+ before :associate
96
99
 
97
- def execute(tags, tasks)
98
- super(
99
- Array([tasks]).flatten.map { |task|
100
- tags.map { |tag| tag.merge(task: task[:title]) }
101
- }.flatten
102
- )
100
+ def associate(tags, tasks)
101
+ Array([tasks]).flatten.map { |task|
102
+ tags.map { |tag| tag.merge(task: task[:title]) }
103
+ }.flatten
103
104
  end
104
105
  end
105
106
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Commands::Lazy do
3
+ RSpec.describe ROM::Commands::Lazy do
4
4
  include_context 'container'
5
5
 
6
6
  let(:create_user) { container.command(:users).create }
@@ -56,16 +56,19 @@ describe ROM::Commands::Lazy do
56
56
  end
57
57
 
58
58
  define(:create_many, type: :create) do
59
- def execute(tuples, user)
60
- super(tuples.map { |tuple| tuple.merge(user: user[:name]) })
59
+ before :associate
60
+
61
+ def associate(tuples, user)
62
+ tuples.map { |tuple| tuple.merge(user: user[:name]) }
61
63
  end
62
64
  end
63
65
 
64
66
  define(:update) do
65
67
  result :one
68
+ before :associate
66
69
 
67
- def execute(tuple, user)
68
- super(tuple.merge(user: user[:name]))
70
+ def associate(tuple, user)
71
+ tuple.merge(user: user[:name])
69
72
  end
70
73
  end
71
74
  end
@@ -0,0 +1,336 @@
1
+ require 'rom/command'
2
+ require 'rom/memory'
3
+
4
+ RSpec.describe ROM::Commands::Create[:memory], 'before/after hooks' do
5
+ let(:relation) do
6
+ spy(:relation)
7
+ end
8
+
9
+ describe '#before' do
10
+ subject(:command) do
11
+ Class.new(ROM::Commands::Create[:memory]) do
12
+ before :init
13
+ before :normalize
14
+
15
+ def init(*)
16
+ end
17
+
18
+ def normalize(*)
19
+ end
20
+
21
+ def prepare(*)
22
+ end
23
+ end.build(relation)
24
+ end
25
+
26
+ it 'returns a new command with configured before hooks' do
27
+ expect(command.before(:prepare).before_hooks).to eql(%i[init normalize prepare])
28
+ end
29
+ end
30
+
31
+ describe '#after' do
32
+ subject(:command) do
33
+ Class.new(ROM::Commands::Create[:memory]) do
34
+ after :finalize
35
+ after :filter
36
+
37
+ def finalize(*)
38
+ end
39
+
40
+ def filter(*)
41
+ end
42
+
43
+ def prepare(*)
44
+ end
45
+ end.build(relation)
46
+ end
47
+
48
+ it 'returns a new command with configured after hooks' do
49
+ expect(command.after(:prepare).after_hooks).to eql(%i[finalize filter prepare])
50
+ end
51
+ end
52
+
53
+ context 'without curried args' do
54
+ subject(:command) do
55
+ Class.new(ROM::Commands::Create[:memory]) do
56
+ result :many
57
+ before :prepare
58
+ after :finalize
59
+
60
+ def execute(tuples)
61
+ input = tuples.map.with_index { |tuple, idx| tuple.merge(id: idx + 1) }
62
+ relation.insert(input)
63
+ input
64
+ end
65
+
66
+ def prepare(tuples)
67
+ tuples.map { |tuple| tuple.merge(prepared: true) }
68
+ end
69
+
70
+ def finalize(tuples)
71
+ tuples.map { |tuple| tuple.merge(finalized: true) }
72
+ end
73
+ end.build(relation)
74
+ end
75
+
76
+ let(:tuples) do
77
+ [{ name: 'Jane' }, { name: 'Joe' }]
78
+ end
79
+
80
+ it 'applies before/after hooks' do
81
+ insert_tuples = [
82
+ { id: 1, name: 'Jane', prepared: true },
83
+ { id: 2, name: 'Joe', prepared: true }
84
+ ]
85
+
86
+ result = [
87
+ { id: 1, name: 'Jane', prepared: true, finalized: true },
88
+ { id: 2, name: 'Joe', prepared: true, finalized: true }
89
+ ]
90
+
91
+ expect(command.call(tuples)).to eql(result)
92
+
93
+ expect(relation).to have_received(:insert).with(insert_tuples)
94
+ end
95
+ end
96
+
97
+ context 'with one curried arg' do
98
+ subject(:command) do
99
+ Class.new(ROM::Commands::Create[:memory]) do
100
+ result :many
101
+ before :prepare
102
+ after :finalize
103
+
104
+ def execute(tuples)
105
+ input = tuples.map.with_index { |tuple, idx| tuple.merge(id: idx + 1) }
106
+ relation.insert(input)
107
+ input
108
+ end
109
+
110
+ def prepare(tuples, name)
111
+ tuples.map.with_index { |tuple, idx| tuple.merge(name: "#{name} #{idx + 1}") }
112
+ end
113
+
114
+ def finalize(tuples, *)
115
+ tuples.map { |tuple| tuple.merge(finalized: true) }
116
+ end
117
+ end.build(relation)
118
+ end
119
+
120
+ let(:tuples) do
121
+ [{ email: 'user-1@test.com' }, { email: 'user-2@test.com' }]
122
+ end
123
+
124
+ let(:relation) do
125
+ spy(:relation)
126
+ end
127
+
128
+ it 'applies before/after hooks' do
129
+ insert_tuples = [
130
+ { id: 1, email: 'user-1@test.com', name: 'User 1' },
131
+ { id: 2, email: 'user-2@test.com', name: 'User 2' }
132
+ ]
133
+
134
+ result = [
135
+ { id: 1, email: 'user-1@test.com', name: 'User 1', finalized: true },
136
+ { id: 2, email: 'user-2@test.com', name: 'User 2', finalized: true }
137
+ ]
138
+
139
+ expect(command.with(tuples).call('User')).to eql(result)
140
+
141
+ expect(relation).to have_received(:insert).with(insert_tuples)
142
+ end
143
+ end
144
+
145
+ context 'with 2 curried args' do
146
+ subject(:command) do
147
+ Class.new(ROM::Commands::Create[:memory]) do
148
+ result :many
149
+ before :prepare
150
+ after :finalize
151
+
152
+ def execute(tuples)
153
+ input = tuples.map.with_index { |tuple, idx| tuple.merge(id: idx + 1) }
154
+ relation.insert(input)
155
+ input
156
+ end
157
+
158
+ def prepare(tuples, name)
159
+ tuples.map.with_index { |tuple, idx| tuple.merge(name: "#{name} #{idx + 1}") }
160
+ end
161
+
162
+ def finalize(tuples, *)
163
+ tuples.map { |tuple| tuple.merge(finalized: true) }
164
+ end
165
+ end.build(relation)
166
+ end
167
+
168
+ let(:tuples) do
169
+ [{ email: 'user-1@test.com' }, { email: 'user-2@test.com' }]
170
+ end
171
+
172
+ let(:relation) do
173
+ spy(:relation)
174
+ end
175
+
176
+ it 'applies before/after hooks' do
177
+ insert_tuples = [
178
+ { id: 1, email: 'user-1@test.com', name: 'User 1' },
179
+ { id: 2, email: 'user-2@test.com', name: 'User 2' }
180
+ ]
181
+
182
+ result = [
183
+ { id: 1, email: 'user-1@test.com', name: 'User 1', finalized: true },
184
+ { id: 2, email: 'user-2@test.com', name: 'User 2', finalized: true }
185
+ ]
186
+
187
+ expect(command.with(tuples, 'User').call).to eql(result)
188
+
189
+ expect(relation).to have_received(:insert).with(insert_tuples)
190
+ end
191
+ end
192
+
193
+ context 'with pre-set opts' do
194
+ subject(:command) do
195
+ Class.new(ROM::Commands::Create[:memory]) do
196
+ result :many
197
+ before prepare: { prepared: true }
198
+ after finalize: { finalized: true }
199
+
200
+ def execute(tuples)
201
+ input = tuples.map.with_index { |tuple, idx| tuple.merge(id: idx + 1) }
202
+ relation.insert(input)
203
+ input
204
+ end
205
+
206
+ def prepare(tuples, opts)
207
+ tuples.map { |tuple| tuple.merge(opts) }
208
+ end
209
+
210
+ def finalize(tuples, opts)
211
+ tuples.map { |tuple| tuple.merge(opts) }
212
+ end
213
+ end.build(relation)
214
+ end
215
+
216
+ let(:tuples) do
217
+ [{ name: 'Jane' }, { name: 'Joe' }]
218
+ end
219
+
220
+ let(:relation) do
221
+ spy(:relation)
222
+ end
223
+
224
+ it 'applies before/after hooks' do
225
+ insert_tuples = [
226
+ { id: 1, name: 'Jane', prepared: true },
227
+ { id: 2, name: 'Joe', prepared: true }
228
+ ]
229
+
230
+ result = [
231
+ { id: 1, name: 'Jane', prepared: true, finalized: true },
232
+ { id: 2, name: 'Joe', prepared: true, finalized: true }
233
+ ]
234
+
235
+ expect(command.call(tuples)).to eql(result)
236
+
237
+ expect(relation).to have_received(:insert).with(insert_tuples)
238
+ end
239
+ end
240
+
241
+ context 'with pre-set opts for a curried command' do
242
+ subject(:command) do
243
+ Class.new(ROM::Commands::Create[:memory]) do
244
+ result :many
245
+ before prepare: { prepared: true }
246
+ after finalize: { finalized: true }
247
+
248
+ def execute(tuples)
249
+ input = tuples.map.with_index { |tuple, idx| tuple.merge(id: idx + 1) }
250
+ relation.insert(input)
251
+ input
252
+ end
253
+
254
+ def prepare(tuples, parent, opts)
255
+ tuples.map { |tuple| tuple.merge(opts).merge(parent_size: parent.size) }
256
+ end
257
+
258
+ def finalize(tuples, parent, opts)
259
+ tuples.map { |tuple| tuple.merge(opts).merge(user_id: parent[:id]) }
260
+ end
261
+ end.build(relation)
262
+ end
263
+
264
+ let(:tuples) do
265
+ [{ name: 'Jane' }, { name: 'Joe' }]
266
+ end
267
+
268
+ let(:relation) do
269
+ spy(:relation)
270
+ end
271
+
272
+ it 'applies before/after hooks' do
273
+ insert_tuples = [
274
+ { id: 1, name: 'Jane', parent_size: 1, prepared: true },
275
+ { id: 2, name: 'Joe', parent_size: 1, prepared: true }
276
+ ]
277
+
278
+ result = [
279
+ { id: 1, name: 'Jane', parent_size: 1, user_id: 1, prepared: true, finalized: true },
280
+ { id: 2, name: 'Joe', parent_size: 1, user_id: 1, prepared: true, finalized: true }
281
+ ]
282
+
283
+ expect(command.with(tuples).call(id: 1)).to eql(result)
284
+
285
+ expect(relation).to have_received(:insert).with(insert_tuples)
286
+ end
287
+ end
288
+
289
+ context 'calling with multiple args' do
290
+ subject(:command) do
291
+ Class.new(ROM::Commands::Create[:memory]) do
292
+ result :many
293
+ before prepare: { prepared: true }
294
+ after finalize: { finalized: true }
295
+
296
+ def execute(tuples)
297
+ input = tuples.map.with_index { |tuple, idx| tuple.merge(id: idx + 1) }
298
+ relation.insert(input)
299
+ input
300
+ end
301
+
302
+ def prepare(tuples, parent, opts)
303
+ tuples.map { |tuple| tuple.merge(opts).merge(parent_size: parent.size) }
304
+ end
305
+
306
+ def finalize(tuples, parent, opts)
307
+ tuples.map { |tuple| tuple.merge(opts).merge(user_id: parent[:id]) }
308
+ end
309
+ end.build(relation)
310
+ end
311
+
312
+ let(:tuples) do
313
+ [{ name: 'Jane' }, { name: 'Joe' }]
314
+ end
315
+
316
+ let(:relation) do
317
+ spy(:relation)
318
+ end
319
+
320
+ it 'applies before/after hooks' do
321
+ insert_tuples = [
322
+ { id: 1, name: 'Jane', parent_size: 1, prepared: true },
323
+ { id: 2, name: 'Joe', parent_size: 1, prepared: true }
324
+ ]
325
+
326
+ result = [
327
+ { id: 1, name: 'Jane', parent_size: 1, user_id: 1, prepared: true, finalized: true },
328
+ { id: 2, name: 'Joe', parent_size: 1, user_id: 1, prepared: true, finalized: true }
329
+ ]
330
+
331
+ expect(command.call(tuples, id: 1)).to eql(result)
332
+
333
+ expect(relation).to have_received(:insert).with(insert_tuples)
334
+ end
335
+ end
336
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Commands::Result do
3
+ RSpec.describe ROM::Commands::Result do
4
4
  describe '#value' do
5
5
  subject(:result) { ROM::Commands::Result::Success }
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'Commands' do
3
+ RSpec.describe 'Commands' do
4
4
  include_context 'gateway only'
5
5
  include_context 'users and tasks'
6
6
 
@@ -99,8 +99,14 @@ describe 'Commands' do
99
99
  }.build(users)
100
100
 
101
101
  create_task = Class.new(ROM::Commands::Create) {
102
- def execute(task_input, user_tuple)
103
- relation.insert(task_input.merge(user_id: user_tuple[:user_id]))
102
+ before :associate
103
+
104
+ def associate(task_input, user_tuple)
105
+ task_input.merge(user_id: user_tuple[:user_id])
106
+ end
107
+
108
+ def execute(task_input)
109
+ relation.insert(task_input)
104
110
  end
105
111
  }.build(tasks)
106
112
 
@@ -162,4 +168,21 @@ describe 'Commands' do
162
168
  expect(command.call('foo')).to be(ROM::EMPTY_ARRAY)
163
169
  end
164
170
  end
171
+
172
+ describe '#with_opts' do
173
+ subject(:command) do
174
+ Class.new(ROM::Command::Create).build(relation, options)
175
+ end
176
+
177
+ let(:relation) { double(:relation) }
178
+ let(:options) { { result: :one } }
179
+
180
+ it 'returns a new command with updated options' do
181
+ new_command = command.with_opts(before: :test)
182
+
183
+ expect(new_command.relation).to be(relation)
184
+ expect(new_command.result).to be(:one)
185
+ expect(new_command.before_hooks).to eql([:test])
186
+ end
187
+ end
165
188
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Configuration do
3
+ RSpec.describe ROM::Configuration do
4
4
  it 'is configurable via settings hash' do
5
5
  configuration = ROM::Configuration.new(:memory, 'something', infer_schema: false)
6
6