rom-sql 1.0.0.rc1 → 1.0.0.rc2
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.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/lib/rom/plugins/relation/sql/auto_combine.rb +4 -1
- data/lib/rom/sql/association/many_to_many.rb +8 -3
- data/lib/rom/sql/attribute.rb +59 -8
- data/lib/rom/sql/extensions/postgres/commands.rb +1 -1
- data/lib/rom/sql/plugin/associates.rb +14 -5
- data/lib/rom/sql/relation.rb +1 -0
- data/lib/rom/sql/schema/inferrer.rb +8 -0
- data/lib/rom/sql/types.rb +1 -1
- data/lib/rom/sql/version.rb +1 -1
- data/spec/integration/plugins/associates/many_to_many_spec.rb +69 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/types_spec.rb +0 -10
- metadata +23 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93ca7d5248744905dadd655401fb8e9534b466d2
|
4
|
+
data.tar.gz: 0712f79b9575ff7da7017d5bbf7329daae35dc3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c8c09bf8ec81e1f6c5452c79047ef46529e5fbb9aa8b2c017797120d6c6ef74d2e8ce279fb490d9844a640bc4a117aa0a75d2ef428d0aeba20b79244288c358
|
7
|
+
data.tar.gz: fac22ae5b662fbc8bc4bf7529a84efcb9bb0328a62ebd5ab91ca8e0662aaf4697cdfc6b40936211e26d7806bab8e9d717d83beb7a5d19ac5e044300ec7072f26
|
data/Gemfile
CHANGED
@@ -46,7 +46,10 @@ module ROM
|
|
46
46
|
|
47
47
|
# @api private
|
48
48
|
def preload(source_key, target_key, source)
|
49
|
-
|
49
|
+
target_pks = source.pluck(source_key.to_sym).uniq
|
50
|
+
target_pks.uniq!
|
51
|
+
|
52
|
+
where(target_key => target_pks)
|
50
53
|
end
|
51
54
|
end
|
52
55
|
end
|
@@ -64,9 +64,14 @@ module ROM
|
|
64
64
|
def associate(relations, children, parent)
|
65
65
|
((spk, sfk), (tfk, tpk)) = join_key_map(relations)
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
case parent
|
68
|
+
when Array
|
69
|
+
parent.map { |p| associate(relations, children, p) }.flatten(1)
|
70
|
+
else
|
71
|
+
children.map { |tuple|
|
72
|
+
{ sfk => tuple.fetch(spk), tfk => parent.fetch(tpk) }
|
73
|
+
}
|
74
|
+
end
|
70
75
|
end
|
71
76
|
|
72
77
|
# @api private
|
data/lib/rom/sql/attribute.rb
CHANGED
@@ -6,24 +6,26 @@ module ROM
|
|
6
6
|
#
|
7
7
|
# @api public
|
8
8
|
class Attribute < ROM::Schema::Attribute
|
9
|
+
# Error raised when an attribute cannot be qualified
|
9
10
|
QualifyError = Class.new(StandardError)
|
10
11
|
|
11
|
-
# Return a new
|
12
|
+
# Return a new attribute with an alias
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# users[:id].aliased(:user_id)
|
12
16
|
#
|
13
17
|
# @return [SQL::Attribute]
|
14
18
|
#
|
15
|
-
# @api public
|
16
|
-
def foreign_key
|
17
|
-
meta(foreign_key: true)
|
18
|
-
end
|
19
|
-
|
20
19
|
# @api public
|
21
20
|
def aliased(name)
|
22
21
|
super.meta(sql_expr: sql_expr.as(name))
|
23
22
|
end
|
24
23
|
alias_method :as, :aliased
|
25
24
|
|
26
|
-
# Return a new
|
25
|
+
# Return a new attribute marked as qualified
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# users[:id].aliased(:user_id)
|
27
29
|
#
|
28
30
|
# @return [SQL::Attribute]
|
29
31
|
#
|
@@ -40,7 +42,10 @@ module ROM
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
|
-
# Return a new
|
45
|
+
# Return a new attribute marked as joined
|
46
|
+
#
|
47
|
+
# Whenever you join two schemas, the right schema's attribute
|
48
|
+
# will be marked as joined using this method
|
44
49
|
#
|
45
50
|
# @return [SQL::Attribute]
|
46
51
|
#
|
@@ -51,6 +56,12 @@ module ROM
|
|
51
56
|
|
52
57
|
# Return if an attribute was used in a join
|
53
58
|
#
|
59
|
+
# @example
|
60
|
+
# schema = users.schema.join(tasks.schema)
|
61
|
+
#
|
62
|
+
# schema[:id, :tasks].joined?
|
63
|
+
# # => true
|
64
|
+
#
|
54
65
|
# @return [Boolean]
|
55
66
|
#
|
56
67
|
# @api public
|
@@ -60,6 +71,12 @@ module ROM
|
|
60
71
|
|
61
72
|
# Return if an attribute type is qualified
|
62
73
|
#
|
74
|
+
# @example
|
75
|
+
# id = users[:id].qualify
|
76
|
+
#
|
77
|
+
# id.qualified?
|
78
|
+
# # => true
|
79
|
+
#
|
63
80
|
# @return [Boolean]
|
64
81
|
#
|
65
82
|
# @api public
|
@@ -67,6 +84,32 @@ module ROM
|
|
67
84
|
meta[:qualified].equal?(true)
|
68
85
|
end
|
69
86
|
|
87
|
+
# Return a new attribute marked as a FK
|
88
|
+
#
|
89
|
+
# @return [SQL::Attribute]
|
90
|
+
#
|
91
|
+
# @api public
|
92
|
+
def foreign_key
|
93
|
+
meta(foreign_key: true)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return symbol representation of an attribute
|
97
|
+
#
|
98
|
+
# This uses convention from sequel where double underscore in the name
|
99
|
+
# is used for qualifying, and triple underscore means aliasing
|
100
|
+
#
|
101
|
+
# @example
|
102
|
+
# users[:id].qualified.to_sym
|
103
|
+
# # => :users__id
|
104
|
+
#
|
105
|
+
# users[:id].as(:user_id).to_sym
|
106
|
+
# # => :id___user_id
|
107
|
+
#
|
108
|
+
# users[:id].qualified.as(:user_id).to_sym
|
109
|
+
# # => :users__id___user_id
|
110
|
+
#
|
111
|
+
# @return [Symbol]
|
112
|
+
#
|
70
113
|
# @api public
|
71
114
|
def to_sym
|
72
115
|
@_to_sym ||=
|
@@ -81,6 +124,10 @@ module ROM
|
|
81
124
|
end
|
82
125
|
end
|
83
126
|
|
127
|
+
# Sequel calls this method to coerce an attribute into SQL string
|
128
|
+
#
|
129
|
+
# @param [Sequel::Dataset]
|
130
|
+
#
|
84
131
|
# @api private
|
85
132
|
def sql_literal(ds)
|
86
133
|
if sql_expr
|
@@ -92,11 +139,15 @@ module ROM
|
|
92
139
|
|
93
140
|
private
|
94
141
|
|
142
|
+
# Return Sequel Expression object for an attribute
|
143
|
+
#
|
95
144
|
# @api private
|
96
145
|
def sql_expr
|
97
146
|
@sql_expr ||= (meta[:sql_expr] || Sequel[to_sym])
|
98
147
|
end
|
99
148
|
|
149
|
+
# Delegate to sql expression if it responds to a given method
|
150
|
+
#
|
100
151
|
# @api private
|
101
152
|
def method_missing(meth, *args, &block)
|
102
153
|
if sql_expr.respond_to?(meth)
|
@@ -45,7 +45,9 @@ module ROM
|
|
45
45
|
#
|
46
46
|
# @api public
|
47
47
|
def associate(tuples, parent, assoc:, keys:)
|
48
|
-
|
48
|
+
result_type = result
|
49
|
+
|
50
|
+
output_tuples =
|
49
51
|
case assoc
|
50
52
|
when Symbol
|
51
53
|
fk, pk = keys
|
@@ -54,6 +56,8 @@ module ROM
|
|
54
56
|
tuple.merge(fk => parent.fetch(pk))
|
55
57
|
}
|
56
58
|
when Association::ManyToMany
|
59
|
+
result_type = tuples.is_a?(Array) ? :many : :one
|
60
|
+
|
57
61
|
join_tuples = assoc.associate(__registry__, tuples, parent)
|
58
62
|
join_relation = assoc.join_relation(__registry__)
|
59
63
|
join_relation.multi_insert(join_tuples)
|
@@ -62,16 +66,21 @@ module ROM
|
|
62
66
|
.associations[assoc.source]
|
63
67
|
.combine_keys(__registry__).to_a.flatten
|
64
68
|
|
65
|
-
|
66
|
-
|
67
|
-
|
69
|
+
case parent
|
70
|
+
when Array
|
71
|
+
parent.map do |p|
|
72
|
+
tuples.map { |tuple| tuple.merge(fk => p[pk]) }
|
73
|
+
end.flatten(1)
|
74
|
+
else
|
75
|
+
tuples.map { |tuple| Hash(tuple).update(fk => parent[pk]) }
|
76
|
+
end
|
68
77
|
when Association
|
69
78
|
with_input_tuples(tuples).map { |tuple|
|
70
79
|
assoc.associate(relation.__registry__, tuple, parent)
|
71
80
|
}
|
72
81
|
end
|
73
82
|
|
74
|
-
one
|
83
|
+
result_type == :one ? output_tuples[0] : output_tuples
|
75
84
|
end
|
76
85
|
|
77
86
|
# @api public
|
data/lib/rom/sql/relation.rb
CHANGED
@@ -42,6 +42,14 @@ module ROM
|
|
42
42
|
db_registry[type]
|
43
43
|
end
|
44
44
|
|
45
|
+
def self.on_error(relation, e)
|
46
|
+
warn "[#{relation}] failed to infer schema. " \
|
47
|
+
"Make sure tables exist before ROM container is set up. " \
|
48
|
+
"This may also happen when your migration tasks load ROM container, " \
|
49
|
+
"which is not needed for migrations as only the connection is required " \
|
50
|
+
"(#{e.message})"
|
51
|
+
end
|
52
|
+
|
45
53
|
# @api private
|
46
54
|
def call(source, gateway)
|
47
55
|
dataset = source.dataset
|
data/lib/rom/sql/types.rb
CHANGED
data/lib/rom/sql/version.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
RSpec.describe 'Plugins / :associates / with many-to-many', :sqlite do
|
2
|
+
include_context 'database setup'
|
3
|
+
|
4
|
+
let(:tasks) { container.commands[:tasks] }
|
5
|
+
let(:tags) { container.commands[:tags] }
|
6
|
+
|
7
|
+
let(:jane) do
|
8
|
+
relations[:users].by_pk(relations[:users].insert(name: 'Jane')).one
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:john) do
|
12
|
+
relations[:users].by_pk(relations[:users].insert(name: 'John')).one
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
conf.relation(:tasks) do
|
17
|
+
schema(infer: true) do
|
18
|
+
associations do
|
19
|
+
has_many :tags, through: :task_tags
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
conf.relation(:task_tags) do
|
25
|
+
schema(infer: true) do
|
26
|
+
associations do
|
27
|
+
belongs_to :tasks, as: :task
|
28
|
+
belongs_to :tags, as: :tag
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
conf.relation(:tags) do
|
34
|
+
schema(infer: true) do
|
35
|
+
associations do
|
36
|
+
has_many :tasks, through: :task_tags
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
conf.commands(:tags) do
|
42
|
+
define(:create) do
|
43
|
+
result :many
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
conf.commands(:tasks) do
|
48
|
+
define(:create) do
|
49
|
+
result :many
|
50
|
+
associates :tags
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'associates a child with many parents' do
|
56
|
+
create_tags = tags[:create].with([{ name: 'red' }, { name: 'blue' }])
|
57
|
+
create_task = tasks[:create].with(user_id: jane[:id], title: "Jade's task")
|
58
|
+
|
59
|
+
command = create_tags >> create_task
|
60
|
+
|
61
|
+
result = command.call
|
62
|
+
|
63
|
+
expect(result).
|
64
|
+
to eql([
|
65
|
+
{ id: 1, user_id: jane[:id], title: "Jade's task", tag_id: 1 },
|
66
|
+
{ id: 1, user_id: jane[:id], title: "Jade's task", tag_id: 2 }
|
67
|
+
])
|
68
|
+
end
|
69
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -39,6 +39,10 @@ ADAPTERS = DB_URIS.keys
|
|
39
39
|
PG_LTE_95 = ENV.fetch('PG_LTE_95', 'true') == 'true'
|
40
40
|
|
41
41
|
SPEC_ROOT = root = Pathname(__FILE__).dirname
|
42
|
+
|
43
|
+
# Redirect inferrer warnings to a log file
|
44
|
+
$stderr.reopen(SPEC_ROOT.join('../log/warnings.log'), 'w')
|
45
|
+
|
42
46
|
TMP_PATH = root.join('../tmp')
|
43
47
|
|
44
48
|
Dir[root.join('shared/**/*')].each { |f| require f }
|
data/spec/unit/types_spec.rb
CHANGED
@@ -1,16 +1,6 @@
|
|
1
1
|
require 'rom/sql/types'
|
2
2
|
|
3
3
|
RSpec.describe ROM::SQL::Types, :postgres do
|
4
|
-
describe 'ROM::SQL::Types::Serial' do
|
5
|
-
it 'accepts ints > 0' do
|
6
|
-
expect(ROM::SQL::Types::Serial[1]).to be(1)
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'raises when input is <= 0' do
|
10
|
-
expect { ROM::SQL::Types::Serial[0] }.to raise_error(Dry::Types::ConstraintError)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
4
|
describe ROM::SQL::Types::Blob do
|
15
5
|
it 'coerces strings to Sequel::SQL::Blob' do
|
16
6
|
input = 'sutin'
|
metadata
CHANGED
@@ -1,44 +1,45 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom-sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: sequel
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - "~>"
|
17
18
|
- !ruby/object:Gem::Version
|
18
19
|
version: '4.42'
|
19
|
-
name: sequel
|
20
|
-
prerelease: false
|
21
20
|
type: :runtime
|
21
|
+
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.42'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-equalizer
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
31
|
- - "~>"
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: '0.2'
|
33
|
-
name: dry-equalizer
|
34
|
-
prerelease: false
|
35
34
|
type: :runtime
|
35
|
+
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-types
|
42
43
|
requirement: !ruby/object:Gem::Requirement
|
43
44
|
requirements:
|
44
45
|
- - "~>"
|
@@ -47,9 +48,8 @@ dependencies:
|
|
47
48
|
- - ">="
|
48
49
|
- !ruby/object:Gem::Version
|
49
50
|
version: 0.9.4
|
50
|
-
name: dry-types
|
51
|
-
prerelease: false
|
52
51
|
type: :runtime
|
52
|
+
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
55
|
- - "~>"
|
@@ -59,6 +59,7 @@ dependencies:
|
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: 0.9.4
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
|
+
name: dry-core
|
62
63
|
requirement: !ruby/object:Gem::Requirement
|
63
64
|
requirements:
|
64
65
|
- - "~>"
|
@@ -67,9 +68,8 @@ dependencies:
|
|
67
68
|
- - ">="
|
68
69
|
- !ruby/object:Gem::Version
|
69
70
|
version: 0.2.3
|
70
|
-
name: dry-core
|
71
|
-
prerelease: false
|
72
71
|
type: :runtime
|
72
|
+
prerelease: false
|
73
73
|
version_requirements: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
75
|
- - "~>"
|
@@ -79,42 +79,42 @@ dependencies:
|
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: 0.2.3
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
|
+
name: rom
|
82
83
|
requirement: !ruby/object:Gem::Requirement
|
83
84
|
requirements:
|
84
85
|
- - "~>"
|
85
86
|
- !ruby/object:Gem::Version
|
86
87
|
version: 3.0.0.rc
|
87
|
-
name: rom
|
88
|
-
prerelease: false
|
89
88
|
type: :runtime
|
89
|
+
prerelease: false
|
90
90
|
version_requirements: !ruby/object:Gem::Requirement
|
91
91
|
requirements:
|
92
92
|
- - "~>"
|
93
93
|
- !ruby/object:Gem::Version
|
94
94
|
version: 3.0.0.rc
|
95
95
|
- !ruby/object:Gem::Dependency
|
96
|
+
name: bundler
|
96
97
|
requirement: !ruby/object:Gem::Requirement
|
97
98
|
requirements:
|
98
99
|
- - ">="
|
99
100
|
- !ruby/object:Gem::Version
|
100
101
|
version: '0'
|
101
|
-
name: bundler
|
102
|
-
prerelease: false
|
103
102
|
type: :development
|
103
|
+
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
105
|
requirements:
|
106
106
|
- - ">="
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: '0'
|
109
109
|
- !ruby/object:Gem::Dependency
|
110
|
+
name: rake
|
110
111
|
requirement: !ruby/object:Gem::Requirement
|
111
112
|
requirements:
|
112
113
|
- - "~>"
|
113
114
|
- !ruby/object:Gem::Version
|
114
115
|
version: '10.0'
|
115
|
-
name: rake
|
116
|
-
prerelease: false
|
117
116
|
type: :development
|
117
|
+
prerelease: false
|
118
118
|
version_requirements: !ruby/object:Gem::Requirement
|
119
119
|
requirements:
|
120
120
|
- - "~>"
|
@@ -224,6 +224,7 @@ files:
|
|
224
224
|
- spec/integration/commands/upsert_spec.rb
|
225
225
|
- spec/integration/gateway_spec.rb
|
226
226
|
- spec/integration/migration_spec.rb
|
227
|
+
- spec/integration/plugins/associates/many_to_many_spec.rb
|
227
228
|
- spec/integration/plugins/associates_spec.rb
|
228
229
|
- spec/integration/plugins/auto_wrap_spec.rb
|
229
230
|
- spec/integration/relation_schema_spec.rb
|
@@ -299,7 +300,7 @@ homepage: http://rom-rb.org
|
|
299
300
|
licenses:
|
300
301
|
- MIT
|
301
302
|
metadata: {}
|
302
|
-
post_install_message:
|
303
|
+
post_install_message:
|
303
304
|
rdoc_options: []
|
304
305
|
require_paths:
|
305
306
|
- lib
|
@@ -314,9 +315,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
314
315
|
- !ruby/object:Gem::Version
|
315
316
|
version: 1.3.1
|
316
317
|
requirements: []
|
317
|
-
rubyforge_project:
|
318
|
-
rubygems_version: 2.
|
319
|
-
signing_key:
|
318
|
+
rubyforge_project:
|
319
|
+
rubygems_version: 2.5.1
|
320
|
+
signing_key:
|
320
321
|
specification_version: 4
|
321
322
|
summary: SQL databases support for ROM
|
322
323
|
test_files:
|
@@ -344,6 +345,7 @@ test_files:
|
|
344
345
|
- spec/integration/commands/upsert_spec.rb
|
345
346
|
- spec/integration/gateway_spec.rb
|
346
347
|
- spec/integration/migration_spec.rb
|
348
|
+
- spec/integration/plugins/associates/many_to_many_spec.rb
|
347
349
|
- spec/integration/plugins/associates_spec.rb
|
348
350
|
- spec/integration/plugins/auto_wrap_spec.rb
|
349
351
|
- spec/integration/relation_schema_spec.rb
|