rom-sql 1.3.5 → 2.0.0.beta1

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -7
  3. data/Gemfile +7 -5
  4. data/lib/rom/plugins/relation/sql/auto_restrictions.rb +11 -17
  5. data/lib/rom/sql.rb +3 -2
  6. data/lib/rom/sql/associations.rb +5 -0
  7. data/lib/rom/sql/associations/core.rb +20 -0
  8. data/lib/rom/sql/associations/many_to_many.rb +83 -0
  9. data/lib/rom/sql/associations/many_to_one.rb +55 -0
  10. data/lib/rom/sql/associations/one_to_many.rb +31 -0
  11. data/lib/rom/sql/{association → associations}/one_to_one.rb +3 -2
  12. data/lib/rom/sql/{association → associations}/one_to_one_through.rb +3 -2
  13. data/lib/rom/sql/associations/self_ref.rb +39 -0
  14. data/lib/rom/sql/attribute.rb +44 -54
  15. data/lib/rom/sql/errors.rb +2 -0
  16. data/lib/rom/sql/extensions/mysql.rb +1 -1
  17. data/lib/rom/sql/extensions/mysql/attributes_inferrer.rb +10 -0
  18. data/lib/rom/sql/extensions/postgres.rb +1 -1
  19. data/lib/rom/sql/extensions/postgres/{inferrer.rb → attributes_inferrer.rb} +4 -4
  20. data/lib/rom/sql/extensions/postgres/types.rb +9 -19
  21. data/lib/rom/sql/extensions/sqlite.rb +1 -1
  22. data/lib/rom/sql/extensions/sqlite/{inferrer.rb → attributes_inferrer.rb} +2 -2
  23. data/lib/rom/sql/gateway.rb +29 -30
  24. data/lib/rom/sql/index.rb +13 -0
  25. data/lib/rom/sql/migration.rb +10 -0
  26. data/lib/rom/sql/migration/inline_runner.rb +86 -0
  27. data/lib/rom/sql/migration/migrator.rb +17 -0
  28. data/lib/rom/sql/migration/schema_diff.rb +177 -0
  29. data/lib/rom/sql/plugin/associates.rb +11 -45
  30. data/lib/rom/sql/plugin/pagination.rb +4 -4
  31. data/lib/rom/sql/relation.rb +22 -42
  32. data/lib/rom/sql/relation/reading.rb +3 -3
  33. data/lib/rom/sql/schema.rb +14 -21
  34. data/lib/rom/sql/schema/associations_dsl.rb +7 -6
  35. data/lib/rom/sql/schema/attributes_inferrer.rb +164 -0
  36. data/lib/rom/sql/schema/inferrer.rb +40 -141
  37. data/lib/rom/sql/type_extensions.rb +44 -0
  38. data/lib/rom/sql/version.rb +1 -1
  39. data/lib/rom/sql/wrap.rb +25 -0
  40. data/rom-sql.gemspec +2 -2
  41. data/spec/integration/{association → associations}/many_to_many/custom_fks_spec.rb +4 -2
  42. data/spec/integration/{association → associations}/many_to_many/from_view_spec.rb +2 -2
  43. data/spec/integration/{association → associations}/many_to_many_spec.rb +25 -30
  44. data/spec/integration/{association → associations}/many_to_one/custom_fks_spec.rb +5 -3
  45. data/spec/integration/{association → associations}/many_to_one/from_view_spec.rb +3 -3
  46. data/spec/integration/{association → associations}/many_to_one/self_ref_spec.rb +2 -2
  47. data/spec/integration/{association → associations}/many_to_one_spec.rb +20 -38
  48. data/spec/integration/{association → associations}/one_to_many/custom_fks_spec.rb +4 -2
  49. data/spec/integration/{association → associations}/one_to_many/from_view_spec.rb +2 -2
  50. data/spec/integration/{association → associations}/one_to_many/self_ref_spec.rb +2 -2
  51. data/spec/integration/{association → associations}/one_to_many_spec.rb +24 -11
  52. data/spec/integration/{association → associations}/one_to_one_spec.rb +13 -9
  53. data/spec/integration/{association → associations}/one_to_one_through_spec.rb +15 -11
  54. data/spec/integration/auto_migrations/errors_spec.rb +31 -0
  55. data/spec/integration/auto_migrations/indexes_spec.rb +109 -0
  56. data/spec/integration/auto_migrations/managing_columns_spec.rb +156 -0
  57. data/spec/integration/auto_migrations/postgres/column_types_spec.rb +63 -0
  58. data/spec/integration/commands/create_spec.rb +2 -4
  59. data/spec/integration/commands/delete_spec.rb +2 -2
  60. data/spec/integration/commands/update_spec.rb +2 -0
  61. data/spec/integration/graph_spec.rb +9 -3
  62. data/spec/integration/plugins/associates_spec.rb +16 -55
  63. data/spec/integration/plugins/auto_restrictions_spec.rb +0 -11
  64. data/spec/integration/relation_schema_spec.rb +49 -25
  65. data/spec/integration/schema/inferrer/postgres_spec.rb +1 -1
  66. data/spec/integration/schema/inferrer_spec.rb +7 -18
  67. data/spec/integration/setup_spec.rb +4 -0
  68. data/spec/integration/{plugins/auto_wrap_spec.rb → wrap_spec.rb} +13 -36
  69. data/spec/shared/accounts.rb +4 -0
  70. data/spec/shared/database_setup.rb +2 -1
  71. data/spec/shared/notes.rb +2 -0
  72. data/spec/shared/posts.rb +2 -0
  73. data/spec/shared/puppies.rb +2 -0
  74. data/spec/shared/relations.rb +2 -2
  75. data/spec/shared/users.rb +2 -0
  76. data/spec/shared/users_and_tasks.rb +4 -0
  77. data/spec/spec_helper.rb +3 -6
  78. data/spec/support/helpers.rb +11 -8
  79. data/spec/support/test_configuration.rb +16 -0
  80. data/spec/unit/plugin/associates_spec.rb +5 -10
  81. data/spec/unit/plugin/pagination_spec.rb +9 -9
  82. data/spec/unit/plugin/timestamp_spec.rb +9 -9
  83. data/spec/unit/relation/dataset_spec.rb +7 -5
  84. data/spec/unit/relation/inner_join_spec.rb +2 -15
  85. data/spec/unit/relation/primary_key_spec.rb +1 -1
  86. data/spec/unit/schema_spec.rb +6 -4
  87. metadata +65 -70
  88. data/lib/rom/plugins/relation/sql/auto_combine.rb +0 -71
  89. data/lib/rom/plugins/relation/sql/auto_wrap.rb +0 -62
  90. data/lib/rom/sql/association.rb +0 -103
  91. data/lib/rom/sql/association/many_to_many.rb +0 -119
  92. data/lib/rom/sql/association/many_to_one.rb +0 -73
  93. data/lib/rom/sql/association/name.rb +0 -78
  94. data/lib/rom/sql/association/one_to_many.rb +0 -60
  95. data/lib/rom/sql/extensions/mysql/inferrer.rb +0 -10
  96. data/lib/rom/sql/qualified_attribute.rb +0 -53
  97. data/lib/rom/sql/schema/dsl.rb +0 -75
  98. data/spec/unit/association/many_to_many_spec.rb +0 -89
  99. data/spec/unit/association/many_to_one_spec.rb +0 -81
  100. data/spec/unit/association/name_spec.rb +0 -68
  101. data/spec/unit/association/one_to_many_spec.rb +0 -82
  102. data/spec/unit/association/one_to_one_spec.rb +0 -83
  103. data/spec/unit/association/one_to_one_through_spec.rb +0 -69
@@ -1,60 +0,0 @@
1
- module ROM
2
- module SQL
3
- class Association
4
- class OneToMany < Association
5
- result :many
6
-
7
- # @api public
8
- def call(relations, right = relations[target.relation])
9
- schema = right.schema.qualified
10
-
11
- relation = right.inner_join(source_table, join_keys(relations))
12
-
13
- if view
14
- apply_view(schema, relation)
15
- else
16
- schema.(relation)
17
- end
18
- end
19
-
20
- # @api public
21
- def combine_keys(relations)
22
- Hash[*with_keys(relations)]
23
- end
24
-
25
- # @api public
26
- def join_keys(relations)
27
- with_keys(relations) { |source_key, target_key|
28
- { qualify(source_alias, source_key) => qualify(target, target_key) }
29
- }
30
- end
31
-
32
- # @api private
33
- def associate(relations, child, parent)
34
- pk, fk = join_key_map(relations)
35
- child.merge(fk => parent.fetch(pk))
36
- end
37
-
38
- protected
39
-
40
- # @api private
41
- def source_table
42
- self_ref? ? Sequel.as(source.dataset, source_alias) : source
43
- end
44
-
45
- # @api private
46
- def source_alias
47
- self_ref? ? :"#{source.dataset.to_s[0]}_0" : source
48
- end
49
-
50
- # @api private
51
- def with_keys(relations, &block)
52
- source_key = relations[source.relation].primary_key
53
- target_key = foreign_key || relations[target.relation].foreign_key(source.relation)
54
- return [source_key, target_key] unless block
55
- yield(source_key, target_key)
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,10 +0,0 @@
1
- require 'rom/sql/schema/inferrer'
2
-
3
- module ROM
4
- module SQL
5
- class Schema
6
- class MysqlInferrer < Inferrer[:mysql]
7
- end
8
- end
9
- end
10
- end
@@ -1,53 +0,0 @@
1
- require 'dry/core/cache'
2
-
3
- module ROM
4
- module SQL
5
- # Used as a pair table name + field name.
6
- # Similar to Sequel::SQL::QualifiedIdentifier but we don't want
7
- # Sequel types to leak into ROM
8
- #
9
- # @api private
10
- class QualifiedAttribute
11
- include Dry::Equalizer(:dataset, :attribute)
12
-
13
- extend Dry::Core::Cache
14
-
15
- # Dataset (table) name
16
- #
17
- # @api private
18
- attr_reader :dataset
19
-
20
- # Attribute (field, column) name
21
- #
22
- # @api private
23
- attr_reader :attribute
24
-
25
- # @api private
26
- def self.[](*args)
27
- fetch_or_store(args) { new(*args) }
28
- end
29
-
30
- # @api private
31
- def initialize(dataset, attribute)
32
- @dataset = dataset
33
- @attribute = attribute
34
- end
35
-
36
- # Used by Sequel for building SQL statements
37
- #
38
- # @api private
39
- def sql_literal_append(ds, sql)
40
- ds.qualified_identifier_sql_append(sql, dataset, attribute)
41
- end
42
-
43
- # Convinient interface for attribute names
44
- #
45
- # @return [Symbol]
46
- #
47
- # @api private
48
- def to_sym
49
- attribute
50
- end
51
- end
52
- end
53
- end
@@ -1,75 +0,0 @@
1
- require 'rom/sql/attribute'
2
- require 'rom/sql/schema/inferrer'
3
- require 'rom/sql/schema/associations_dsl'
4
-
5
- module ROM
6
- module SQL
7
- class Schema < ROM::Schema
8
- # Extended schema DSL
9
- #
10
- # @api private
11
- class DSL < ROM::Schema::DSL
12
- attr_reader :associations_dsl
13
-
14
- # Define associations for a relation
15
- #
16
- # @example
17
- # class Users < ROM::Relation[:sql]
18
- # schema(infer: true) do
19
- # associations do
20
- # has_many :tasks
21
- # has_many :posts
22
- # has_many :posts, as: :priority_posts, view: :prioritized
23
- # belongs_to :account
24
- # end
25
- # end
26
- # end
27
- #
28
- # class Posts < ROM::Relation[:sql]
29
- # schema(infer: true) do
30
- # associations do
31
- # belongs_to :users, as: :author
32
- # end
33
- # end
34
- #
35
- # view(:prioritized) do
36
- # where { priority <= 3 }
37
- # end
38
- # end
39
- #
40
- # @return [AssociationDSL]
41
- #
42
- # @api public
43
- def associations(&block)
44
- @associations_dsl = AssociationsDSL.new(relation, &block)
45
- end
46
-
47
- # Return a schema
48
- #
49
- # @api private
50
- def call
51
- SQL::Schema.define(
52
- relation, opts.merge(attributes: attributes.values, attr_class: SQL::Attribute)
53
- )
54
- end
55
-
56
- private
57
-
58
- # Return schema opts
59
- #
60
- # @return [Hash]
61
- #
62
- # @api private
63
- def opts
64
- opts = { inferrer: inferrer }
65
-
66
- if associations_dsl
67
- { **opts, associations: associations_dsl.call }
68
- else
69
- opts
70
- end
71
- end
72
- end
73
- end
74
- end
75
- end
@@ -1,89 +0,0 @@
1
- RSpec.describe ROM::SQL::Association::ManyToMany, helpers: true do
2
- subject(:assoc) do
3
- ROM::SQL::Association::ManyToMany.new(source, target, options)
4
- end
5
-
6
- let(:options) { { through: :tasks_tags } }
7
-
8
- let(:tags) { double(:tags, primary_key: :id) }
9
- let(:tasks) { double(:tasks, primary_key: :id) }
10
- let(:tasks_tags) { double(:tasks, primary_key: [:task_id, :tag_id]) }
11
-
12
- let(:source) { :tasks }
13
- let(:target) { :tags }
14
-
15
- let(:relations) do
16
- { tasks: tasks, tags: tags, tasks_tags: tasks_tags }
17
- end
18
-
19
- shared_examples_for 'many-to-many association' do
20
- describe '#combine_keys' do
21
- it 'returns a hash with combine keys' do
22
- expect(tasks_tags).to receive(:foreign_key).with(:tasks).and_return(:tag_id)
23
-
24
- expect(assoc.combine_keys(relations)).to eql(id: :tag_id)
25
- end
26
- end
27
- end
28
-
29
- describe '#result' do
30
- it 'is :many' do
31
- expect(assoc.result).to be(:many)
32
- end
33
- end
34
-
35
- describe '#associate' do
36
- let(:join_assoc) { double(:join_assoc) }
37
-
38
- let(:source) { :tags }
39
- let(:target) { :tasks }
40
-
41
- it 'returns a list of join keys for given child tuples' do
42
- expect(tasks_tags).to receive(:associations).and_return(assoc.target => join_assoc)
43
- expect(join_assoc).to receive(:join_key_map).with(relations).and_return([:task_id, :id])
44
- expect(tasks_tags).to receive(:foreign_key).with(:tags).and_return(:tag_id)
45
-
46
- task_tuple = { id: 3 }
47
- tag_tuples = [{ id: 1 }, { id: 2 }]
48
-
49
- expect(assoc.associate(relations, tag_tuples, task_tuple)).to eql([
50
- { tag_id: 1, task_id: 3 }, { tag_id: 2, task_id: 3 }
51
- ])
52
- end
53
- end
54
-
55
- context 'with default names' do
56
- it_behaves_like 'many-to-many association'
57
-
58
- describe '#join_keys' do
59
- it 'returns a hash with combine keys' do
60
- expect(tasks_tags).to receive(:foreign_key).with(:tasks).and_return(:tag_id)
61
-
62
- expect(assoc.join_keys(relations)).to eql(
63
- qualified_attribute(:tasks, :id) => qualified_attribute(:tasks_tags, :tag_id)
64
- )
65
- end
66
- end
67
- end
68
-
69
- context 'with custom relation names' do
70
- let(:source) { assoc_name(:tasks, :user_tasks) }
71
- let(:target) { assoc_name(:tags, :user_tags) }
72
-
73
- let(:relations) do
74
- { tasks: tasks, tags: tags, tasks_tags: tasks_tags }
75
- end
76
-
77
- it_behaves_like 'many-to-many association'
78
-
79
- describe '#join_keys' do
80
- it 'returns a hash with combine keys' do
81
- expect(tasks_tags).to receive(:foreign_key).with(:tasks).and_return(:tag_id)
82
-
83
- expect(assoc.join_keys(relations)).to eql(
84
- qualified_attribute(:user_tasks, :id) => qualified_attribute(:tasks_tags, :tag_id)
85
- )
86
- end
87
- end
88
- end
89
- end
@@ -1,81 +0,0 @@
1
- RSpec.describe ROM::SQL::Association::ManyToOne, helpers: true do
2
- subject(:assoc) do
3
- ROM::SQL::Association::ManyToOne.new(source, target, options)
4
- end
5
-
6
- let(:options) { {} }
7
-
8
- let(:users) { double(:users, primary_key: :id) }
9
- let(:tasks) { double(:tasks) }
10
-
11
- let(:source) { :tasks }
12
- let(:target) { :users }
13
-
14
- let(:relations) do
15
- { users: users, tasks: tasks }
16
- end
17
-
18
- describe '#result' do
19
- it 'is :one' do
20
- expect(assoc.result).to be(:one)
21
- end
22
- end
23
-
24
- describe '#associate' do
25
- it 'returns child tuple with FK set' do
26
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
27
-
28
- task_tuple = { title: 'Task' }
29
- user_tuple = { id: 3 }
30
-
31
- expect(assoc.associate(relations, task_tuple, user_tuple)).to eql(
32
- user_id: 3, title: 'Task'
33
- )
34
- end
35
- end
36
-
37
- shared_examples_for 'many-to-many association' do
38
- describe '#combine_keys' do
39
- it 'returns a hash with combine keys' do
40
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
41
-
42
- expect(assoc.combine_keys(relations)).to eql(user_id: :id)
43
- end
44
- end
45
- end
46
-
47
- context 'with default names' do
48
- it_behaves_like 'many-to-many association'
49
-
50
- describe '#join_keys' do
51
- it 'returns a hash with combine keys' do
52
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
53
-
54
- expect(assoc.join_keys(relations)).to eql(
55
- qualified_attribute(:tasks, :user_id) => qualified_attribute(:users, :id)
56
- )
57
- end
58
- end
59
- end
60
-
61
- context 'with custom relation names' do
62
- let(:source) { assoc_name(:tasks, :user_tasks) }
63
- let(:target) { assoc_name(:users, :people) }
64
-
65
- let(:relations) do
66
- { users: users, tasks: tasks }
67
- end
68
-
69
- it_behaves_like 'many-to-many association'
70
-
71
- describe '#join_keys' do
72
- it 'returns a hash with combine keys' do
73
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
74
-
75
- expect(assoc.join_keys(relations)).to eql(
76
- qualified_attribute(:user_tasks, :user_id) => qualified_attribute(:people, :id)
77
- )
78
- end
79
- end
80
- end
81
- end
@@ -1,68 +0,0 @@
1
- RSpec.describe ROM::SQL::Association::Name do
2
- describe '.[]' do
3
- it 'returns a name object from a relation name' do
4
- rel_name = ROM::Relation::Name[:users]
5
- assoc_name = ROM::SQL::Association::Name[rel_name]
6
-
7
- expect(assoc_name).to eql(ROM::SQL::Association::Name.new(rel_name, :users))
8
- end
9
-
10
- it 'returns a name object from a relation and a dataset symbols' do
11
- rel_name = ROM::Relation::Name[:users, :people]
12
- assoc_name = ROM::SQL::Association::Name[:users, :people]
13
-
14
- expect(assoc_name).to eql(ROM::SQL::Association::Name.new(rel_name, :people))
15
- end
16
-
17
- it 'returns a name object from a relation and a dataset symbols and an alias' do
18
- rel_name = ROM::Relation::Name[:users, :people]
19
- assoc_name = ROM::SQL::Association::Name[:users, :people, :author]
20
-
21
- expect(assoc_name).to eql(ROM::SQL::Association::Name.new(rel_name, :author))
22
- end
23
-
24
- it 'caches names' do
25
- name = ROM::SQL::Association::Name[:users]
26
-
27
- expect(name).to be(ROM::SQL::Association::Name[:users])
28
-
29
- name = ROM::SQL::Association::Name[:users, :people]
30
-
31
- expect(name).to be(ROM::SQL::Association::Name[:users, :people])
32
-
33
- name = ROM::SQL::Association::Name[:users, :people, :author]
34
-
35
- expect(name).to be(ROM::SQL::Association::Name[:users, :people, :author])
36
- end
37
- end
38
-
39
- describe '#aliased?' do
40
- it 'returns true if a name has an alias' do
41
- expect(ROM::SQL::Association::Name[:users, :people, :author]).to be_aliased
42
- end
43
-
44
- it 'returns false if a name has no alias' do
45
- expect(ROM::SQL::Association::Name[:users, :people]).to_not be_aliased
46
- end
47
- end
48
-
49
- describe '#inspect' do
50
- it 'includes info about the relation name' do
51
- expect(ROM::SQL::Association::Name[:users].inspect).to eql(
52
- "ROM::SQL::Association::Name(users)"
53
- )
54
- end
55
-
56
- it 'includes info about the relation name and its dataset' do
57
- expect(ROM::SQL::Association::Name[:users, :people].inspect).to eql(
58
- "ROM::SQL::Association::Name(users on people)"
59
- )
60
- end
61
-
62
- it 'includes info about the relation name, its dataset and alias' do
63
- expect(ROM::SQL::Association::Name[:users, :people, :author].inspect).to eql(
64
- "ROM::SQL::Association::Name(users on people as author)"
65
- )
66
- end
67
- end
68
- end
@@ -1,82 +0,0 @@
1
- RSpec.describe ROM::SQL::Association::OneToMany, helpers: true do
2
- subject(:assoc) do
3
- ROM::SQL::Association::OneToMany.new(source, target, options)
4
- end
5
-
6
- let(:options) { {} }
7
-
8
- let(:users) { double(:users, primary_key: :id) }
9
- let(:tasks) { double(:tasks) }
10
-
11
- describe '#associate' do
12
- let(:source) { :users }
13
- let(:target) { :tasks }
14
-
15
- let(:relations) do
16
- { users: users, tasks: tasks }
17
- end
18
-
19
- it 'returns child tuple with FK set' do
20
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
21
-
22
- task_tuple = { title: 'Task' }
23
- user_tuple = { id: 3 }
24
-
25
- expect(assoc.associate(relations, task_tuple, user_tuple)).to eql(
26
- user_id: 3, title: 'Task'
27
- )
28
- end
29
- end
30
-
31
- shared_examples_for 'one-to-many association' do
32
- describe '#combine_keys' do
33
- it 'returns a hash with combine keys' do
34
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
35
-
36
- expect(assoc.combine_keys(relations)).to eql(id: :user_id)
37
- end
38
- end
39
- end
40
-
41
- context 'with default names' do
42
- let(:source) { :users }
43
- let(:target) { :tasks }
44
-
45
- let(:relations) do
46
- { users: users, tasks: tasks }
47
- end
48
-
49
- it_behaves_like 'one-to-many association'
50
-
51
- describe '#join_keys' do
52
- it 'returns a hash with combine keys' do
53
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
54
-
55
- expect(assoc.join_keys(relations)).to eql(
56
- qualified_attribute(:users, :id) => qualified_attribute(:tasks, :user_id)
57
- )
58
- end
59
- end
60
- end
61
-
62
- context 'with custom relation names' do
63
- let(:source) { assoc_name(:users, :people) }
64
- let(:target) { assoc_name(:tasks, :user_tasks) }
65
-
66
- let(:relations) do
67
- { users: users, tasks: tasks }
68
- end
69
-
70
- it_behaves_like 'one-to-many association'
71
-
72
- describe '#join_keys' do
73
- it 'returns a hash with combine keys' do
74
- expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
75
-
76
- expect(assoc.join_keys(relations)).to eql(
77
- qualified_attribute(:people, :id) => qualified_attribute(:user_tasks, :user_id)
78
- )
79
- end
80
- end
81
- end
82
- end