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,71 +0,0 @@
1
- module ROM
2
- module Plugins
3
- module Relation
4
- module SQL
5
- # @api private
6
- module AutoCombine
7
- # @api private
8
- def self.included(klass)
9
- super
10
- klass.class_eval do
11
- include(InstanceInterface)
12
- extend(ClassInterface)
13
- end
14
- end
15
-
16
- # @api private
17
- module ClassInterface
18
- # @api private
19
- def inherited(klass)
20
- super
21
- klass.auto_curry :for_combine
22
- klass.auto_curry :preload
23
- end
24
- end
25
-
26
- # @api private
27
- module InstanceInterface
28
- # Default methods for fetching combined relation
29
- #
30
- # This method is used by default by `combine`
31
- #
32
- # @return [SQL::Relation]
33
- #
34
- # @api private
35
- def for_combine(spec)
36
- case spec
37
- when ROM::SQL::Association
38
- spec.call(__registry__, self).preload(spec)
39
- else
40
- preload(spec)
41
- end
42
- end
43
-
44
- # @api private
45
- def preload(spec, source)
46
- case spec
47
- when ROM::SQL::Association::ManyToOne
48
- pk = source.source[source.source.primary_key].qualified
49
-
50
- where(pk => source.pluck(pk.name))
51
- when Hash, ROM::SQL::Association
52
- source_key, target_key = spec.is_a?(Hash) ? spec.flatten(1) : spec.join_keys(__registry__).flatten(1)
53
-
54
- target_pks = source.pluck(source_key.to_sym)
55
- target_pks.uniq!
56
-
57
- where(target_key => target_pks)
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
66
-
67
- ROM.plugins do
68
- adapter :sql do
69
- register :auto_combine, ROM::Plugins::Relation::SQL::AutoCombine, type: :relation
70
- end
71
- end
@@ -1,62 +0,0 @@
1
- module ROM
2
- module Plugins
3
- module Relation
4
- module SQL
5
- # @api private
6
- module AutoWrap
7
- # @api private
8
- def self.included(klass)
9
- super
10
- klass.class_eval do
11
- include(InstanceInterface)
12
- extend(ClassInterface)
13
- end
14
- end
15
-
16
- # @api private
17
- module ClassInterface
18
- # @api private
19
- def inherited(klass)
20
- super
21
- klass.auto_curry :for_wrap
22
- end
23
- end
24
-
25
- # @api private
26
- module InstanceInterface
27
- # Default methods for fetching wrapped relation
28
- #
29
- # This method is used by default by `wrap` and `wrap_parents`
30
- #
31
- # @return [SQL::Relation]
32
- #
33
- # @api private
34
- def for_wrap(keys, name)
35
- rel, other =
36
- if associations.key?(name)
37
- assoc = associations[name]
38
- other = __registry__[assoc.target.relation]
39
-
40
- [assoc.join(__registry__, :inner_join, self, other), other]
41
- else
42
- # TODO: deprecate this before 2.0
43
- other = __registry__[name]
44
- other_dataset = other.name.dataset
45
-
46
- [qualified.inner_join(other_dataset, keys), other]
47
- end
48
-
49
- rel.schema.merge(other.schema.wrap).qualified.(rel)
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
56
- end
57
-
58
- ROM.plugins do
59
- adapter :sql do
60
- register :auto_wrap, ROM::Plugins::Relation::SQL::AutoWrap, type: :relation
61
- end
62
- end
@@ -1,103 +0,0 @@
1
- require 'dry/core/constants'
2
- require 'dry/core/class_attributes'
3
-
4
- require 'rom/types'
5
- require 'rom/initializer'
6
- require 'rom/sql/qualified_attribute'
7
- require 'rom/sql/association/name'
8
-
9
- module ROM
10
- module SQL
11
- # Abstract association class
12
- #
13
- # @api public
14
- class Association
15
- include Dry::Core::Constants
16
- include Dry::Equalizer(:source, :target, :result)
17
- extend Initializer
18
- extend Dry::Core::ClassAttributes
19
-
20
- defines :result
21
-
22
- # @!attribute [r] source
23
- # @return [ROM::Relation::Name] the source relation name
24
- param :source
25
-
26
- # @!attribute [r] target
27
- # @return [ROM::Relation::Name] the target relation name
28
- param :target
29
-
30
- # @!attribute [r] relation
31
- # @return [Symbol] an optional relation identifier for the target
32
- option :relation, Types::Strict::Symbol, optional: true
33
-
34
- # @!attribute [r] result
35
- # @return [Symbol] either :one or :many
36
- option :result, Types::Strict::Symbol, default: -> { self.class.result }
37
-
38
- # @!attribute [r] as
39
- # @return [Symbol] an optional association alias name
40
- option :as, Types::Strict::Symbol, default: -> { target.to_sym }
41
-
42
- # @!attribute [r] foreign_key
43
- # @return [Symbol] an optional association alias name
44
- option :foreign_key, Types::Optional::Strict::Symbol, optional: true
45
-
46
- # @!attribute [r] view
47
- # @return [Symbol] An optional view that should be used to extend assoc relation
48
- option :view, optional: true
49
-
50
- alias_method :name, :as
51
-
52
- # @api public
53
- def self.new(source, target, options = EMPTY_HASH)
54
- super(
55
- Name[source],
56
- Name[options[:relation] || target, target, options[:as] || target],
57
- options
58
- )
59
- end
60
-
61
- # @api public
62
- def join(relations, type, source = relations[self.source], target = relations[self.target])
63
- source.__send__(type, target.name.dataset, join_keys(relations)).qualified
64
- end
65
-
66
- # Returns a qualified attribute name for a given dataset
67
- #
68
- # This is compatible with Sequel's SQL generator and can be used in query
69
- # DSL methods
70
- #
71
- # @param name [ROM::Relation::Name]
72
- # @param attribute [Symbol]
73
- #
74
- # @return [QualifiedAttribute]
75
- #
76
- # @api public
77
- def qualify(name, attribute)
78
- QualifiedAttribute[name.to_sym, attribute]
79
- end
80
-
81
- # @api protected
82
- def apply_view(schema, relation)
83
- view_rel = relation.public_send(view)
84
- schema.merge(view_rel.schema.qualified).uniq(&:to_sql_name).(view_rel)
85
- end
86
-
87
- # @api private
88
- def join_key_map(relations)
89
- join_keys(relations).to_a.flatten.map(&:to_sym)
90
- end
91
-
92
- def self_ref?
93
- source.dataset == target.dataset
94
- end
95
- end
96
- end
97
- end
98
-
99
- require 'rom/sql/association/one_to_many'
100
- require 'rom/sql/association/one_to_one'
101
- require 'rom/sql/association/many_to_many'
102
- require 'rom/sql/association/many_to_one'
103
- require 'rom/sql/association/one_to_one_through'
@@ -1,119 +0,0 @@
1
- require 'rom/types'
2
-
3
- module ROM
4
- module SQL
5
- class Association
6
- class ManyToMany < Association
7
- result :many
8
-
9
- option :through, type: Types::Strict::Symbol.optional
10
-
11
- # @api private
12
- def initialize(*)
13
- super
14
- @through = Relation::Name[
15
- options[:through] || options[:through_relation], options[:through]
16
- ]
17
- end
18
-
19
- # @api public
20
- def call(relations, target_rel = nil)
21
- join_rel = join_relation(relations)
22
- assocs = join_rel.associations
23
-
24
- left = target_rel ? assocs[target].(relations, target_rel) : assocs[target].(relations)
25
- right = relations[target.relation]
26
-
27
- left_fk = foreign_key || join_rel.foreign_key(source.relation)
28
-
29
- schema =
30
- if left.schema.key?(left_fk)
31
- if target_rel
32
- target_rel.schema.merge(left.schema.project(left_fk))
33
- else
34
- left.schema.project(*(right.schema.map(&:name) + [left_fk]))
35
- end
36
- else
37
- right.schema.merge(join_rel.schema.project(left_fk))
38
- end.qualified
39
-
40
- relation = left.inner_join(source, join_keys(relations))
41
-
42
- if view
43
- apply_view(schema, relation)
44
- else
45
- schema.(relation)
46
- end
47
- end
48
-
49
- # @api private
50
- def persist(relations, children, parents)
51
- join_tuples = associate(relations, children, parents)
52
- join_relation = join_relation(relations)
53
- join_relation.multi_insert(join_tuples)
54
- end
55
-
56
- # @api private
57
- def parent_combine_keys(relations)
58
- relations[target].associations[source].combine_keys(relations).to_a.flatten(1)
59
- end
60
-
61
- # @api public
62
- def join(relations, type, source = relations[self.source], target = relations[self.target])
63
- through_assoc = source.associations[through]
64
- joined = through_assoc.join(relations, type, source)
65
- joined.__send__(type, target.name.dataset, join_keys(relations)).qualified
66
- end
67
-
68
- # @api public
69
- def join_keys(relations)
70
- with_keys(relations) { |source_key, target_key|
71
- { qualify(source, source_key) => qualify(through, target_key) }
72
- }
73
- end
74
-
75
- # @api public
76
- def combine_keys(relations)
77
- Hash[*with_keys(relations)]
78
- end
79
-
80
- # @api private
81
- def associate(relations, children, parent)
82
- ((spk, sfk), (tfk, tpk)) = join_key_map(relations)
83
-
84
- case parent
85
- when Array
86
- parent.map { |p| associate(relations, children, p) }.flatten(1)
87
- else
88
- children.map { |tuple|
89
- { sfk => tuple.fetch(spk), tfk => parent.fetch(tpk) }
90
- }
91
- end
92
- end
93
-
94
- # @api private
95
- def join_relation(relations)
96
- relations[through.relation]
97
- end
98
-
99
- protected
100
-
101
- # @api private
102
- def with_keys(relations, &block)
103
- source_key = relations[source.relation].primary_key
104
- target_key = foreign_key || relations[through.relation].foreign_key(source.relation)
105
- return [source_key, target_key] unless block
106
- yield(source_key, target_key)
107
- end
108
-
109
- # @api private
110
- def join_key_map(relations)
111
- left = super
112
- right = join_relation(relations).associations[target].join_key_map(relations)
113
-
114
- [left, right]
115
- end
116
- end
117
- end
118
- end
119
- end
@@ -1,73 +0,0 @@
1
- module ROM
2
- module SQL
3
- class Association
4
- class ManyToOne < Association
5
- result :one
6
-
7
- # @api public
8
- def call(relations, left = relations[target.relation])
9
- right = relations[source.relation]
10
-
11
- left_pk = left.primary_key
12
- right_fk = left.foreign_key(source.relation)
13
-
14
- left_schema = left.schema
15
- right_schema = right.schema.project_pk
16
-
17
- schema =
18
- if left.schema.key?(right_fk)
19
- left_schema
20
- else
21
- left_schema.merge(right_schema.project_fk(left_pk => right_fk))
22
- end.qualified
23
-
24
- relation = left.inner_join(source_table, join_keys(relations))
25
-
26
- if view
27
- apply_view(schema, relation)
28
- else
29
- schema.(relation)
30
- end
31
- end
32
-
33
- # @api public
34
- def combine_keys(relations)
35
- Hash[*with_keys(relations)]
36
- end
37
-
38
- # @api public
39
- def join_keys(relations)
40
- with_keys(relations) { |source_key, target_key|
41
- { qualify(source_alias, source_key) => qualify(target, target_key) }
42
- }
43
- end
44
-
45
- # @api private
46
- def associate(relations, child, parent)
47
- fk, pk = join_key_map(relations)
48
- child.merge(fk => parent.fetch(pk))
49
- end
50
-
51
- protected
52
-
53
- # @api private
54
- def source_table
55
- self_ref? ? Sequel.as(source.dataset, source_alias) : source
56
- end
57
-
58
- # @api private
59
- def source_alias
60
- self_ref? ? :"#{source.dataset.to_s[0]}_0" : source
61
- end
62
-
63
- # @api private
64
- def with_keys(relations, &block)
65
- source_key = foreign_key || relations[source.relation].foreign_key(target.relation)
66
- target_key = relations[target.relation].primary_key
67
- return [source_key, target_key] unless block
68
- yield(source_key, target_key)
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,78 +0,0 @@
1
- require 'dry/equalizer'
2
- require 'rom/relation/name'
3
- require 'dry/core/cache'
4
-
5
- module ROM
6
- module SQL
7
- class Association
8
- class Name
9
- include Dry::Equalizer.new(:relation_name, :key)
10
-
11
- extend Dry::Core::Cache
12
-
13
- attr_reader :relation_name
14
-
15
- attr_reader :key
16
-
17
- alias_method :to_sym, :key
18
-
19
- def self.[](*args)
20
- fetch_or_store(args) do
21
- rel, ds, aliaz = args
22
-
23
- if rel.is_a?(ROM::Relation::Name)
24
- new(rel, rel.dataset)
25
- elsif rel.is_a?(self)
26
- rel
27
- elsif aliaz
28
- new(ROM::Relation::Name[rel, ds], aliaz)
29
- elsif ds.nil?
30
- new(ROM::Relation::Name[rel], rel)
31
- else
32
- new(ROM::Relation::Name[rel, ds], ds)
33
- end
34
- end
35
- end
36
-
37
- def initialize(relation_name, aliaz)
38
- @relation_name = relation_name
39
- @aliased = relation_name.dataset != aliaz
40
- @key = aliased? ? aliaz : relation_name.dataset
41
- end
42
-
43
- def aliased?
44
- @aliased
45
- end
46
-
47
- def inspect
48
- if aliased?
49
- "#{self.class}(#{relation_name.to_s} as #{key})"
50
- else
51
- "#{self.class}(#{relation_name.to_s})"
52
- end
53
- end
54
- alias_method :to_s, :inspect
55
-
56
- def dataset
57
- relation_name.dataset
58
- end
59
-
60
- def relation
61
- relation_name.relation
62
- end
63
-
64
- def as(aliaz)
65
- Name[relation_name.relation, relation_name.dataset, aliaz]
66
- end
67
-
68
- def to_sym
69
- dataset
70
- end
71
-
72
- def sql_literal(ds)
73
- ds.literal(aliased? ? Sequel[dataset] : Sequel.as(dataset, key))
74
- end
75
- end
76
- end
77
- end
78
- end