rom-sql 1.3.5 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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