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,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Container do
3
+ RSpec.describe ROM::Container do
4
4
  include_context 'container'
5
5
  include_context 'users and tasks'
6
6
 
@@ -8,16 +8,22 @@ describe ROM::Container do
8
8
  configuration
9
9
 
10
10
  users = Class.new(ROM::Relation[:memory]) do
11
- register_as :users
12
- dataset :users
11
+ schema(:users) do
12
+ attribute :name, ROM::Types::String
13
+ attribute :email, ROM::Types::String
14
+ end
15
+
13
16
  def by_name(name)
14
17
  restrict(name: name).project(:name)
15
18
  end
16
19
  end
17
20
 
18
21
  tasks = Class.new(ROM::Relation[:memory]) do
19
- register_as :tasks
20
- dataset :tasks
22
+ schema(:tasks) do
23
+ attribute :name, ROM::Types::String
24
+ attribute :title, ROM::Types::String
25
+ attribute :priority, ROM::Types::Int
26
+ end
21
27
  end
22
28
 
23
29
  create_user = Class.new(ROM::Commands::Create[:memory]) do
@@ -83,6 +89,10 @@ describe ROM::Container do
83
89
  expect(container.relation(:tasks)).to be_kind_of(ROM::Relation)
84
90
  expect(container.relation(:tasks).mappers.elements).to be_empty
85
91
  end
92
+
93
+ it 'returns a relation with finalized schema' do
94
+ expect(container.relation(:tasks).schema.relations[:users]).to be(container.relations[:users])
95
+ end
86
96
  end
87
97
 
88
98
  describe '#mappers' do
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::CreateContainer do
3
+ RSpec.describe ROM::CreateContainer do
4
4
  describe '#finalize' do
5
5
  include_context 'container'
6
6
 
@@ -0,0 +1,15 @@
1
+ require 'rom/enumerable_dataset'
2
+
3
+ RSpec.describe ROM::EnumerableDataset do
4
+ let(:klass) do
5
+ Class.new do
6
+ include ROM::EnumerableDataset
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'
15
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Gateway do
3
+ RSpec.describe ROM::Gateway do
4
4
 
5
5
  subject(:gateway) { Class.new(ROM::Gateway).new }
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::MapperRegistry do
3
+ RSpec.describe ROM::MapperRegistry do
4
4
  subject(:registry) { ROM::MapperRegistry.new }
5
5
 
6
6
  let(:user_mapper) { double('user_mapper') }
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'rom/memory'
3
3
 
4
- describe ROM::Memory::Commands do
4
+ RSpec.describe ROM::Memory::Commands do
5
5
  let(:relation) do
6
6
  Class.new(ROM::Relation[:memory]) do
7
7
  schema do
@@ -3,7 +3,7 @@ require 'rom/lint/spec'
3
3
 
4
4
  require 'rom/memory/dataset'
5
5
 
6
- describe ROM::Memory::Dataset do
6
+ RSpec.describe ROM::Memory::Dataset do
7
7
  subject(:dataset) { ROM::Memory::Dataset.new(data) }
8
8
 
9
9
  let(:data) do
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
  require 'rom/lint/spec'
3
3
  require 'rom/memory'
4
4
 
5
- describe ROM::Memory::Gateway do
5
+ RSpec.describe ROM::Memory::Gateway do
6
6
  let(:gateway) { ROM::Memory::Gateway }
7
7
  let(:uri) { nil }
8
8
 
@@ -0,0 +1,32 @@
1
+ require 'rom/memory'
2
+
3
+ RSpec.describe ROM::Memory::Relation, '.inherited' do
4
+ subject(:relation) do
5
+ Class.new(ROM::Relation[:test]) do
6
+ schema(:users) do
7
+ attribute :name, ROM::Types::String
8
+ end
9
+ end.new([])
10
+ end
11
+
12
+ before do
13
+ module Test
14
+ module AnotherAdapter
15
+ class Relation < ROM::Memory::Relation
16
+ adapter :test
17
+ end
18
+ end
19
+ end
20
+
21
+ ROM.register_adapter(:test, Test::AnotherAdapter)
22
+ end
23
+
24
+ after do
25
+ ROM.adapters.delete(:test)
26
+ end
27
+
28
+ it 'extends subclass with core methods' do
29
+ expect(relation.name.to_sym).to be(:users)
30
+ expect(relation.schema.map(&:name)).to eql(%i[name])
31
+ end
32
+ end
@@ -4,8 +4,16 @@ require 'rom/lint/spec'
4
4
  require 'rom/memory/dataset'
5
5
  require 'rom/memory/relation'
6
6
 
7
- describe ROM::Memory::Relation do
8
- subject(:relation) { ROM::Memory::Relation.new(dataset) }
7
+ RSpec.describe ROM::Memory::Relation do
8
+ subject(:relation) do
9
+ Class.new(ROM::Memory::Relation) do
10
+ schema do
11
+ attribute :name, ROM::Types::String
12
+ attribute :email, ROM::Types::String
13
+ attribute :age, ROM::Types::Int
14
+ end
15
+ end.new(dataset)
16
+ end
9
17
 
10
18
  let(:dataset) do
11
19
  ROM::Memory::Dataset.new([
@@ -30,7 +38,11 @@ describe ROM::Memory::Relation do
30
38
 
31
39
  describe '#project' do
32
40
  it 'projects tuples with the provided keys' do
33
- expect(relation.project(:name, :age)).to match_array([
41
+ projected = relation.project(:name, :age)
42
+
43
+ expect(projected.schema).to eql(relation.schema.project(:name, :age))
44
+
45
+ expect(projected).to match_array([
34
46
  { name: 'Jane', age: 10 },
35
47
  { name: 'Jade', age: 11 },
36
48
  { name: 'Joe', age: 12 },
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'rom/memory/storage'
3
3
 
4
- describe ROM::Memory::Storage do
4
+ RSpec.describe ROM::Memory::Storage do
5
5
  describe 'thread safe' do
6
6
  let(:threads) { 4 }
7
7
  let(:operations) { 5000 }
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "ROM::PluginRegistry" do
3
+ RSpec.describe "ROM::PluginRegistry" do
4
4
  include_context 'container'
5
5
 
6
6
  before do
@@ -38,17 +38,17 @@ RSpec.describe ROM::Plugins::Command::Schema do
38
38
 
39
39
  context 'when relation has a schema' do
40
40
  let(:relation) do
41
- instance_double(ROM::Relation, schema?: true, schema_hash: schema_hash)
41
+ instance_double(ROM::Relation, schema?: true, input_schema: input_schema)
42
42
  end
43
43
 
44
- let(:schema_hash) do
45
- double(:schema_hash)
44
+ let(:input_schema) do
45
+ double(:input_schema)
46
46
  end
47
47
 
48
48
  it 'sets schema hash as input handler' do
49
49
  command = Class.new(command_class).build(relation)
50
50
 
51
- expect(command.input).to be(schema_hash)
51
+ expect(command.input).to be(input_schema)
52
52
  end
53
53
 
54
54
  it 'sets a composed input handler with schema hash and a custom one' do
@@ -57,7 +57,7 @@ RSpec.describe ROM::Plugins::Command::Schema do
57
57
  command = Class.new(command_class) { input my_handler }.build(relation)
58
58
 
59
59
  expect(my_handler).to receive(:[]).with('some value').and_return('my handler')
60
- expect(schema_hash).to receive(:[]).with('my handler').and_return('a tuple')
60
+ expect(input_schema).to receive(:[]).with('my handler').and_return('a tuple')
61
61
 
62
62
  expect(command.input['some value']).to eql('a tuple')
63
63
  end
@@ -58,7 +58,7 @@ RSpec.describe ROM::Plugins::Relation::KeyInference do
58
58
  use :key_inference
59
59
 
60
60
  schema :posts do
61
- attribute :author_id, ROM::Types::Int.meta(foreign_key: true, relation: :users)
61
+ attribute :author_id, ROM::Types::Int.meta(foreign_key: true, target: :users)
62
62
  end
63
63
  end
64
64
  end
@@ -0,0 +1,86 @@
1
+ require 'rom/registry'
2
+
3
+ RSpec.shared_examples_for 'registry fetch' do
4
+ it 'raises an ArgumentError when nil is used as a key' do
5
+ expect {
6
+ registry.public_send(fetch_method, nil)
7
+ }.to raise_error(ArgumentError, "key cannot be nil")
8
+ end
9
+
10
+ it 'returns registered element identified by name' do
11
+ expect(registry.public_send(fetch_method, :mars)).to be(mars)
12
+ end
13
+
14
+ it 'raises error when element is not found' do
15
+ expect { registry.public_send(fetch_method, :twix) }.to raise_error(
16
+ ROM::Registry::ElementNotFoundError,
17
+ ":twix doesn't exist in Candy registry"
18
+ )
19
+ end
20
+
21
+ it 'returns the value from an optional block when key is not found' do
22
+ value = registry.public_send(fetch_method, :candy) { :twix }
23
+
24
+ expect(value).to eq(:twix)
25
+ end
26
+
27
+ it 'calls #to_sym on a key before fetching' do
28
+ expect(registry.public_send(fetch_method, double(to_sym: :mars))).to be(mars)
29
+ end
30
+ end
31
+
32
+ RSpec.describe ROM::Registry do
33
+ subject(:registry) { registry_class.new(mars: mars) }
34
+
35
+ let(:mars) { double('mars') }
36
+
37
+ let(:registry_class) do
38
+ Class.new(ROM::Registry) do
39
+ def self.name
40
+ 'Candy'
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#fetch' do
46
+ let(:fetch_method) { :fetch }
47
+
48
+ it_behaves_like 'registry fetch'
49
+ end
50
+
51
+ describe '#[]' do
52
+ let(:fetch_method) { :[] }
53
+
54
+ it_behaves_like 'registry fetch'
55
+ end
56
+
57
+ describe '#method_missing' do
58
+ it 'returns registered element identified by name' do
59
+ expect(registry.mars).to be(mars)
60
+ end
61
+
62
+ it 'raises no-method error when element is not there' do
63
+ expect { registry.twix }.to raise_error(NoMethodError)
64
+ end
65
+ end
66
+
67
+ describe '#key?' do
68
+ let(:mars) { double(to_sym: :mars) }
69
+
70
+ it 'calls #to_sym on a key before checking if it exists' do
71
+ registry.key?(:mars)
72
+ end
73
+
74
+ it 'returns true for an existing key' do
75
+ expect(registry.key?(:mars)).to be(true)
76
+ end
77
+
78
+ it 'returns false for a non-existing key' do
79
+ expect(registry.key?(:twix)).to be(false)
80
+ end
81
+
82
+ it 'returns false for a nil key' do
83
+ expect(registry.key?(nil)).to be(false)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,17 @@
1
+ require 'rom/memory'
2
+
3
+ RSpec.describe ROM::Relation, '#[]' do
4
+ it 'defines a canonical schema for a relation' do
5
+ class Test::Users < ROM::Relation[:memory]
6
+ schema do
7
+ attribute :id, Types::Int
8
+ attribute :name, Types::String
9
+ end
10
+ end
11
+
12
+ relation = Test::Users.new([])
13
+
14
+ expect(relation[:id]).to be(Test::Users.schema[:id])
15
+ expect(relation[:name]).to be(Test::Users.schema[:name])
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ require 'rom/relation'
2
+
3
+ RSpec.describe ROM::Relation, '#call' do
4
+ subject(:relation) do
5
+ relation_class.new(data)
6
+ end
7
+
8
+ context 'without read types in schema' do
9
+ let(:relation_class) do
10
+ Class.new(ROM::Relation[:memory]) do
11
+ schema do
12
+ attribute :id, ROM::Types::Int
13
+ attribute :name, ROM::Types::String
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:data) do
19
+ [{ id: '1', name: 'Jane' }, { id: '2', name: 'John'} ]
20
+ end
21
+
22
+ it 'has noop output_schema' do
23
+ expect(relation.output_schema).to be(ROM::Relation::NOOP_OUTPUT_SCHEMA)
24
+ end
25
+
26
+ it 'returns loaded relation with data' do
27
+ expect(relation.call.collection).
28
+ to eql(data)
29
+ end
30
+ end
31
+
32
+ context 'with read types in schema' do
33
+ let(:relation_class) do
34
+ Class.new(ROM::Relation[:memory]) do
35
+ schema do
36
+ attribute :id, ROM::Types::String, read: ROM::Types::Coercible::Int
37
+ attribute :name, ROM::Types::String
38
+ end
39
+ end
40
+ end
41
+
42
+ let(:data) do
43
+ [{ id: '1', name: 'Jane' }, { id: '2', name: 'John'} ]
44
+ end
45
+
46
+ it 'returns loaded relation with coerced data' do
47
+ expect(relation.call.collection).
48
+ to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'John'} ])
49
+ end
50
+ end
51
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Relation::Composite do
3
+ RSpec.describe ROM::Relation::Composite do
4
4
  include_context 'gateway only'
5
5
  include_context 'users and tasks'
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Relation::Graph do
3
+ RSpec.describe ROM::Relation::Graph do
4
4
  include_context 'gateway only'
5
5
  include_context 'users and tasks'
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Relation, '#combine' do
3
+ RSpec.describe ROM::Relation, '#combine' do
4
4
  include_context 'gateway only'
5
5
  include_context 'users and tasks'
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Relation do
3
+ RSpec.describe ROM::Relation do
4
4
  include_context 'gateway only'
5
5
  include_context 'users and tasks'
6
6
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe ROM::Relation::Loaded do
3
+ RSpec.describe ROM::Relation::Loaded do
4
4
  include_context 'no container'
5
5
  include_context 'users and tasks'
6
6
 
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'rom/memory'
3
3
 
4
- describe ROM::Relation, '.schema' do
4
+ RSpec.describe ROM::Relation, '.schema' do
5
5
  it 'defines a canonical schema for a relation' do
6
6
  class Test::Users < ROM::Relation[:memory]
7
7
  schema do
@@ -13,16 +13,42 @@ describe ROM::Relation, '.schema' do
13
13
 
14
14
  Test::Users.schema.finalize!
15
15
 
16
- schema = ROM::Schema.new(
16
+ relation_name = ROM::Relation::Name[:test_users]
17
+
18
+ schema = ROM::Memory::Schema.define(
17
19
  ROM::Relation::Name.new(:test_users),
18
- id: ROM::Memory::Types::Int.meta(primary_key: true, name: :id),
19
- name: ROM::Memory::Types::String.meta(name: :name),
20
- admin: ROM::Memory::Types::Bool.meta(name: :admin)
20
+ attributes: [
21
+ ROM::Memory::Types::Int.meta(primary_key: true, name: :id, source: relation_name),
22
+ ROM::Memory::Types::String.meta(name: :name, source: relation_name),
23
+ ROM::Memory::Types::Bool.meta(name: :admin, source: relation_name)
24
+ ]
21
25
  ).finalize!
22
26
 
23
27
  expect(Test::Users.schema.primary_key).to eql([Test::Users.schema[:id]])
24
28
 
25
29
  expect(Test::Users.schema).to eql(schema)
30
+
31
+ expect(Test::Users.schema.relations).to be_empty
32
+ end
33
+
34
+ it 'allows defining types for reading tuples' do
35
+ module Test
36
+ module Types
37
+ CoercibleDate = ROM::Types::Date.constructor(Date.method(:parse))
38
+ end
39
+ end
40
+
41
+ class Test::Users < ROM::Relation[:memory]
42
+ schema do
43
+ attribute :id, Types::Int
44
+ attribute :date, Types::Coercible::String, read: Test::Types::CoercibleDate
45
+ end
46
+ end
47
+
48
+ schema = Test::Users.schema
49
+
50
+ expect(schema.to_output_hash).
51
+ to eql(ROM::Types::Coercible::Hash.schema(id: schema[:id].type, date: schema[:date].meta[:read]))
26
52
  end
27
53
 
28
54
  it 'allows setting composite primary key' do
@@ -81,6 +107,24 @@ describe ROM::Relation, '.schema' do
81
107
  expect(Test::Users.register_as).to be(:test_users)
82
108
  end
83
109
 
110
+ it 'allows setting both dataset and relation alias' do
111
+ class Test::Users < ROM::Relation[:memory]
112
+ schema(:test_users, as: :users) { }
113
+ end
114
+
115
+ expect(Test::Users.register_as).to be(:users)
116
+ expect(Test::Users.dataset).to be(:test_users)
117
+ end
118
+
119
+ it 'raises error when schema_class is missing' do
120
+ class Test::Users < ROM::Relation[:memory]
121
+ schema_class nil
122
+ end
123
+
124
+ expect { Test::Users.schema(:test) { } }.
125
+ to raise_error(ROM::MissingSchemaClassError, "Test::Users relation is missing schema_class")
126
+ end
127
+
84
128
  describe '#schema' do
85
129
  it 'returns defined schema' do
86
130
  class Test::Users < ROM::Relation[:memory]
@@ -111,7 +155,7 @@ describe ROM::Relation, '.schema' do
111
155
  end
112
156
  end
113
157
 
114
- expect(Test::Users.schema[:admin]).to eql(ROM::Types::Bool.meta(name: :admin))
158
+ expect(Test::Users.schema[:admin]).to eql(ROM::Types::Bool.meta(name: :admin, source: ROM::Relation::Name[:test_users]))
115
159
  end
116
160
  end
117
161
  end
@@ -0,0 +1,122 @@
1
+ require 'rom'
2
+ require 'rom/memory'
3
+
4
+ RSpec.describe ROM::Relation, '.view' do
5
+ subject(:relation) { relation_class.new(ROM::Memory::Dataset.new([])) }
6
+
7
+ it 'returns view method name' do
8
+ klass = Class.new(ROM::Relation[:memory]) {
9
+ schema { attribute :id, ROM::Types::Int }
10
+ }
11
+
12
+ name = klass.view(:by_id, klass.schema) { self }
13
+
14
+ expect(name).to be(:by_id)
15
+ end
16
+
17
+ shared_context 'relation with views' do
18
+ before do
19
+ relation << { id: 1, name: 'Joe' }
20
+ relation << { id: 2, name: 'Jane' }
21
+ end
22
+
23
+ it 'uses projected schema for view schema' do
24
+ expect(relation.schemas[:names].map(&:name)).to eql(%i[name])
25
+ end
26
+
27
+ it 'auto-projects the relation via schema' do
28
+ new_rel = relation_class.new([{ name: 'Jane' }, { name: 'Joe' }])
29
+ names_schema = relation_class.schemas[:names]
30
+
31
+ expect(names_schema).to receive(:call).with(relation).and_return(new_rel)
32
+ expect(relation.names).to eql(new_rel)
33
+ end
34
+
35
+ it 'auto-projects a restricted relation via schema' do
36
+ new_rel = relation_class.new([{ id: 2 }])
37
+ ids_schema = relation_class.schemas[:ids_for_names]
38
+
39
+ expect(ids_schema).to receive(:call).with(relation.restrict(name: ['Jane'])).and_return(new_rel)
40
+ expect(relation.ids_for_names(['Jane'])).to eql(new_rel)
41
+ end
42
+ end
43
+
44
+ context 'with an explicit schema' do
45
+ before do
46
+ # this is normally called automatically during setup
47
+ relation_class.finalize({}, relation)
48
+ end
49
+
50
+ include_context 'relation with views' do
51
+ let(:relation_class) do
52
+ Class.new(ROM::Memory::Relation) do
53
+ schema(:users) do
54
+ attribute :id, ROM::Types::Int
55
+ attribute :name, ROM::Types::String
56
+ end
57
+
58
+ view(:names) do
59
+ schema do
60
+ project(:name)
61
+ end
62
+
63
+ relation do
64
+ self
65
+ end
66
+ end
67
+
68
+ view(:ids_for_names) do
69
+ schema do
70
+ project(:id)
71
+ end
72
+
73
+ relation do |names|
74
+ restrict(name: names)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'with an inferred schema' do
83
+ before do
84
+ # this is normally called automatically during setup
85
+ relation_class.schema.finalize!
86
+ relation_class.finalize({}, relation)
87
+ end
88
+
89
+ include_context 'relation with views' do
90
+ let(:relation_class) do
91
+ Class.new(ROM::Memory::Relation) do
92
+ schema_inferrer -> dataset, gateway {
93
+ [[ROM::Types::Int.meta(name: :id, source: :users),
94
+ ROM::Types::String.meta(name: :name, source: :users)], []]
95
+ }
96
+
97
+ schema(:users, infer: true)
98
+
99
+ view(:names) do
100
+ schema do
101
+ project(:name)
102
+ end
103
+
104
+ relation do
105
+ self
106
+ end
107
+ end
108
+
109
+ view(:ids_for_names) do
110
+ schema do
111
+ project(:id)
112
+ end
113
+
114
+ relation do |names|
115
+ restrict(name: names)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end