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