rom 2.0.2 → 3.0.0

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 (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