rom-sql 1.0.0.beta2 → 1.0.0.beta3
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/.travis.yml +1 -1
- data/CHANGELOG.md +4 -0
- data/circle.yml +1 -1
- data/lib/rom/plugins/relation/sql/auto_combine.rb +1 -1
- data/lib/rom/sql/association.rb +5 -1
- data/lib/rom/sql/association/many_to_many.rb +8 -4
- data/lib/rom/sql/association/many_to_one.rb +2 -3
- data/lib/rom/sql/association/one_to_many.rb +2 -3
- data/lib/rom/sql/commands/create.rb +8 -1
- data/lib/rom/sql/commands/update.rb +7 -0
- data/lib/rom/sql/extensions.rb +8 -0
- data/lib/rom/sql/extensions/active_support_notifications.rb +7 -18
- data/lib/rom/sql/extensions/mysql.rb +1 -0
- data/lib/rom/sql/extensions/mysql/inferrer.rb +10 -0
- data/lib/rom/sql/extensions/postgres/commands.rb +1 -1
- data/lib/rom/sql/extensions/postgres/inferrer.rb +4 -0
- data/lib/rom/sql/extensions/postgres/types.rb +20 -22
- data/lib/rom/sql/extensions/sqlite.rb +1 -0
- data/lib/rom/sql/extensions/sqlite/inferrer.rb +10 -0
- data/lib/rom/sql/function.rb +6 -2
- data/lib/rom/sql/order_dsl.rb +1 -1
- data/lib/rom/sql/plugin/associates.rb +28 -5
- data/lib/rom/sql/relation.rb +18 -0
- data/lib/rom/sql/relation/reading.rb +2 -2
- data/lib/rom/sql/relation/sequel_api.rb +119 -0
- data/lib/rom/sql/schema/inferrer.rb +26 -2
- data/lib/rom/sql/tasks/migration_tasks.rake +1 -1
- data/lib/rom/sql/type.rb +13 -2
- data/lib/rom/sql/types.rb +5 -3
- data/lib/rom/sql/version.rb +1 -1
- data/spec/extensions/postgres/types_spec.rb +29 -0
- data/spec/integration/association/many_to_many_spec.rb +8 -0
- data/spec/integration/association/many_to_one_spec.rb +11 -0
- data/spec/integration/association/one_to_many_spec.rb +9 -0
- data/spec/integration/schema/inferrer/mysql_spec.rb +36 -0
- data/spec/integration/schema/inferrer/postgres_spec.rb +118 -0
- data/spec/integration/schema/inferrer/sqlite_spec.rb +36 -0
- data/spec/integration/{schema_inference_spec.rb → schema/inferrer_spec.rb} +44 -15
- data/spec/integration/sequel_api_spec.rb +31 -0
- data/spec/support/helpers.rb +4 -0
- data/spec/unit/function_spec.rb +35 -0
- data/spec/unit/order_dsl_spec.rb +35 -0
- data/spec/unit/relation/assoc_spec.rb +38 -0
- data/spec/unit/relation/inner_join_spec.rb +15 -0
- data/spec/unit/types_spec.rb +53 -1
- metadata +23 -6
- data/spec/extensions/postgres/inferrer_spec.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c94e3593d4f6fbe8a49111c097a6721ae9d5dfc
|
4
|
+
data.tar.gz: 0b12c00984a1b6943465d608fe97e66c78beda47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fc3303b1f6c7b5415d44186bc857b7f8a5add043b1b9f990f2b003003bfa8fddd87fc6feb30532759b4548919dbfbffeea2c560a42faff3225b0894933ee2c8
|
7
|
+
data.tar.gz: 449816a9fe18c46ba4b65a64e0397121d57c9593a3fffd1ffdab7da04e29eb33a2a40a49cdf8c1859b3e974c68e71a6971eae3afc47a8316715c9c1d8746958d
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -12,10 +12,14 @@ Please refer to [the upgrading guide](https://github.com/rom-rb/rom-sql/wiki/Upg
|
|
12
12
|
* Extended query API with support for schema attributes (solnic)
|
13
13
|
* Schemas can project relations automatically (solnic)
|
14
14
|
* New `Schema#qualified` (solnic)
|
15
|
+
* New `Relation#assoc` method which is a shortcut for accessing relation created by the given association (solnic)
|
15
16
|
* Schema attribute types are now SQL-specific and compatible with query DSL (ie you can pass relation attributes to `select` and they will be automatically converted to valid SQL expressions) (solnic)
|
16
17
|
* Associations support setting custom `view` that will be used to extend association relation (solnic)
|
17
18
|
* Associations support setting custom `foreign_key` names (solnic)
|
18
19
|
* Support for self-referencing associations (ie categories have_many child categories) (solnic)
|
20
|
+
* Inferrers for mysql and sqlite were added (flash-gordon)
|
21
|
+
* PG's auto-inferrer can handle `inet`/`cidr` data types in a two-way manner, i.e. converting them back and forth on reading and writing. Same for `point` datatype (flash-gordon)
|
22
|
+
* `ROM::SQL::Relation::SequelAPI` extension for backward-compatible query API (this will be deprecated in 1.1.0 and removed in 2.0.0) (solnic)
|
19
23
|
|
20
24
|
### Changed
|
21
25
|
|
data/circle.yml
CHANGED
@@ -32,7 +32,7 @@ module ROM
|
|
32
32
|
source_key, target_key, target =
|
33
33
|
case spec
|
34
34
|
when ROM::SQL::Association
|
35
|
-
[*spec.join_keys(__registry__).flatten, spec.call(__registry__)]
|
35
|
+
[*spec.join_keys(__registry__).flatten, spec.call(__registry__, self)]
|
36
36
|
else
|
37
37
|
[*spec.flatten, self]
|
38
38
|
end
|
data/lib/rom/sql/association.rb
CHANGED
@@ -73,7 +73,11 @@ module ROM
|
|
73
73
|
QualifiedAttribute[name.to_sym, attribute]
|
74
74
|
end
|
75
75
|
|
76
|
-
protected
|
76
|
+
# @api protected
|
77
|
+
def apply_view(schema, relation)
|
78
|
+
view_rel = relation.public_send(view)
|
79
|
+
schema.merge(view_rel.schema.qualified).uniq(&:to_sym).(view_rel)
|
80
|
+
end
|
77
81
|
|
78
82
|
# @api private
|
79
83
|
def join_key_map(relations)
|
@@ -17,18 +17,22 @@ module ROM
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# @api public
|
20
|
-
def call(relations)
|
20
|
+
def call(relations, target_rel = nil)
|
21
21
|
join_rel = join_relation(relations)
|
22
22
|
assocs = join_rel.associations
|
23
23
|
|
24
|
-
left = assocs[target].
|
24
|
+
left = target_rel ? assocs[target].(relations, target_rel) : assocs[target].(relations)
|
25
25
|
right = relations[target.relation]
|
26
26
|
|
27
27
|
left_fk = foreign_key || join_rel.foreign_key(source.relation)
|
28
28
|
|
29
29
|
schema =
|
30
30
|
if left.schema.key?(left_fk)
|
31
|
-
|
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
|
32
36
|
else
|
33
37
|
right.schema.merge(join_rel.schema.project(left_fk))
|
34
38
|
end.qualified
|
@@ -38,7 +42,7 @@ module ROM
|
|
38
42
|
.order(*right.schema.project_pk.qualified)
|
39
43
|
|
40
44
|
if view
|
41
|
-
schema
|
45
|
+
apply_view(schema, relation)
|
42
46
|
else
|
43
47
|
schema.(relation)
|
44
48
|
end
|
@@ -5,8 +5,7 @@ module ROM
|
|
5
5
|
result :one
|
6
6
|
|
7
7
|
# @api public
|
8
|
-
def call(relations)
|
9
|
-
left = relations[target.relation]
|
8
|
+
def call(relations, left = relations[target.relation])
|
10
9
|
right = relations[source.relation]
|
11
10
|
|
12
11
|
left_pk = left.primary_key
|
@@ -27,7 +26,7 @@ module ROM
|
|
27
26
|
.order(*right_schema.qualified)
|
28
27
|
|
29
28
|
if view
|
30
|
-
schema
|
29
|
+
apply_view(schema, relation)
|
31
30
|
else
|
32
31
|
schema.(relation)
|
33
32
|
end
|
@@ -5,8 +5,7 @@ module ROM
|
|
5
5
|
result :many
|
6
6
|
|
7
7
|
# @api public
|
8
|
-
def call(relations)
|
9
|
-
right = relations[target.relation]
|
8
|
+
def call(relations, right = relations[target.relation])
|
10
9
|
schema = right.schema.qualified
|
11
10
|
|
12
11
|
relation = right
|
@@ -14,7 +13,7 @@ module ROM
|
|
14
13
|
.order(*right.schema.project_pk.qualified)
|
15
14
|
|
16
15
|
if view
|
17
|
-
schema
|
16
|
+
apply_view(schema, relation)
|
18
17
|
else
|
19
18
|
schema.(relation)
|
20
19
|
end
|
@@ -16,6 +16,8 @@ module ROM
|
|
16
16
|
use :associates
|
17
17
|
use :schema
|
18
18
|
|
19
|
+
after :finalize
|
20
|
+
|
19
21
|
# Inserts provided tuples into the database table
|
20
22
|
#
|
21
23
|
# @api public
|
@@ -34,6 +36,11 @@ module ROM
|
|
34
36
|
|
35
37
|
private
|
36
38
|
|
39
|
+
# @api private
|
40
|
+
def finalize(tuples, *)
|
41
|
+
tuples.map { |t| relation.output_schema[t] }
|
42
|
+
end
|
43
|
+
|
37
44
|
# Executes insert statement and returns inserted tuples
|
38
45
|
#
|
39
46
|
# @api private
|
@@ -54,7 +61,7 @@ module ROM
|
|
54
61
|
#
|
55
62
|
# @api private
|
56
63
|
def with_input_tuples(tuples)
|
57
|
-
input_tuples = Array([tuples]).flatten.map
|
64
|
+
input_tuples = Array([tuples]).flatten(1).map
|
58
65
|
return input_tuples unless block_given?
|
59
66
|
input_tuples.each { |tuple| yield(tuple) }
|
60
67
|
end
|
@@ -15,6 +15,8 @@ module ROM
|
|
15
15
|
|
16
16
|
use :schema
|
17
17
|
|
18
|
+
after :finalize
|
19
|
+
|
18
20
|
# Updates existing tuple in a relation
|
19
21
|
#
|
20
22
|
# @return [Array<Hash>, Hash]
|
@@ -26,6 +28,11 @@ module ROM
|
|
26
28
|
|
27
29
|
private
|
28
30
|
|
31
|
+
# @api private
|
32
|
+
def finalize(tuples, *)
|
33
|
+
tuples.map { |t| relation.output_schema[t] }
|
34
|
+
end
|
35
|
+
|
29
36
|
# Executes update statement for a given tuple
|
30
37
|
#
|
31
38
|
# @api private
|
data/lib/rom/sql/extensions.rb
CHANGED
@@ -8,6 +8,14 @@ module ROM
|
|
8
8
|
require 'rom/sql/extensions/postgres'
|
9
9
|
end
|
10
10
|
|
11
|
+
register_extension(:mysql) do
|
12
|
+
require 'rom/sql/extensions/mysql'
|
13
|
+
end
|
14
|
+
|
15
|
+
register_extension(:sqlite) do
|
16
|
+
require 'rom/sql/extensions/sqlite'
|
17
|
+
end
|
18
|
+
|
11
19
|
register_extension(:active_support_notifications) do
|
12
20
|
require 'rom/sql/extensions/active_support_notifications'
|
13
21
|
end
|
@@ -4,24 +4,13 @@ require 'active_support/notifications'
|
|
4
4
|
module ROM
|
5
5
|
module SQL
|
6
6
|
module ActiveSupportInstrumentation
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
) { super }
|
15
|
-
end
|
16
|
-
else
|
17
|
-
def log_connection_yield(sql, _conn, args = nil)
|
18
|
-
ActiveSupport::Notifications.instrument(
|
19
|
-
'sql.rom',
|
20
|
-
sql: sql,
|
21
|
-
name: instrumentation_name,
|
22
|
-
binds: args
|
23
|
-
) { super }
|
24
|
-
end
|
7
|
+
def log_connection_yield(sql, _conn, args = nil)
|
8
|
+
ActiveSupport::Notifications.instrument(
|
9
|
+
'sql.rom',
|
10
|
+
sql: sql,
|
11
|
+
name: instrumentation_name,
|
12
|
+
binds: args
|
13
|
+
) { super }
|
25
14
|
end
|
26
15
|
|
27
16
|
private
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'rom/sql/extensions/mysql/inferrer'
|
@@ -20,6 +20,10 @@ module ROM
|
|
20
20
|
'bytea' => Types::Blob,
|
21
21
|
'json' => Types::PG::JSON,
|
22
22
|
'jsonb' => Types::PG::JSONB,
|
23
|
+
'inet' => Types::PG::IPAddress,
|
24
|
+
'cidr' => Types::PG::IPAddress,
|
25
|
+
'macaddr' => Types::String,
|
26
|
+
'point' => Types::PG::PointT
|
23
27
|
).freeze
|
24
28
|
|
25
29
|
db_array_type_matcher Sequel::Postgres::PGArray::EMPTY_BRACKET
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'dry-types'
|
2
2
|
require 'sequel'
|
3
|
+
require 'ipaddr'
|
3
4
|
|
4
5
|
Sequel.extension(*%i(pg_array pg_array_ops pg_json pg_json_ops))
|
5
6
|
|
@@ -22,43 +23,40 @@ module ROM
|
|
22
23
|
|
23
24
|
# JSON
|
24
25
|
|
25
|
-
JSONArray =
|
26
|
-
.new(Sequel::Postgres::JSONArray)
|
27
|
-
.constructor(Sequel.method(:pg_json))
|
26
|
+
JSONArray = Types.Constructor(Sequel::Postgres::JSONArray, &Sequel.method(:pg_json))
|
28
27
|
|
29
|
-
JSONHash =
|
30
|
-
.new(Sequel::Postgres::JSONHash)
|
31
|
-
.constructor(Sequel.method(:pg_json))
|
28
|
+
JSONHash = Types.Constructor(Sequel::Postgres::JSONArray, &Sequel.method(:pg_json))
|
32
29
|
|
33
|
-
JSONOp =
|
34
|
-
.new(Sequel::Postgres::JSONOp)
|
35
|
-
.constructor(Sequel.method(:pg_json))
|
30
|
+
JSONOp = Types.Constructor(Sequel::Postgres::JSONOp, &Sequel.method(:pg_json))
|
36
31
|
|
37
32
|
JSON = JSONArray | JSONHash | JSONOp
|
38
33
|
|
39
34
|
# JSONB
|
40
35
|
|
41
|
-
JSONBArray =
|
42
|
-
.new(Sequel::Postgres::JSONBArray)
|
43
|
-
.constructor(Sequel.method(:pg_jsonb))
|
36
|
+
JSONBArray = Types.Constructor(Sequel::Postgres::JSONBArray, &Sequel.method(:pg_jsonb))
|
44
37
|
|
45
|
-
JSONBHash =
|
46
|
-
.new(Sequel::Postgres::JSONBHash)
|
47
|
-
.constructor(Sequel.method(:pg_jsonb))
|
38
|
+
JSONBHash = Types.Constructor(Sequel::Postgres::JSONBHash, &Sequel.method(:pg_jsonb))
|
48
39
|
|
49
|
-
JSONBOp =
|
50
|
-
.new(Sequel::Postgres::JSONBOp)
|
51
|
-
.constructor(Sequel.method(:pg_jsonb))
|
40
|
+
JSONBOp = Types.Constructor(Sequel::Postgres::JSONBOp, &Sequel.method(:pg_jsonb))
|
52
41
|
|
53
42
|
JSONB = JSONBArray | JSONBHash | JSONBOp
|
54
43
|
|
55
|
-
Bytea =
|
56
|
-
.new(Sequel::SQL::Blob)
|
57
|
-
.constructor(Sequel::SQL::Blob.method(:new))
|
44
|
+
Bytea = Types.Constructor(Sequel::SQL::Blob, &Sequel::SQL::Blob.method(:new))
|
58
45
|
|
59
|
-
|
46
|
+
IPAddressR = Types.Constructor(IPAddr) { |ip| IPAddr.new(ip.to_s) }
|
47
|
+
|
48
|
+
IPAddress = Types.Constructor(IPAddr, &:to_s).meta(read: IPAddressR)
|
60
49
|
|
61
50
|
Money = Types::Decimal
|
51
|
+
|
52
|
+
Point = ::Struct.new(:x, :y)
|
53
|
+
|
54
|
+
PointTR = Types.Constructor(Point) do |p|
|
55
|
+
x, y = p.to_s[1...-1].split(',', 2)
|
56
|
+
Point.new(Float(x), Float(y))
|
57
|
+
end
|
58
|
+
|
59
|
+
PointT = Types.Constructor(Point) { |p| "(#{ p.x },#{ p.y })" }.meta(read: PointTR)
|
62
60
|
end
|
63
61
|
end
|
64
62
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'rom/sql/extensions/sqlite/inferrer'
|
data/lib/rom/sql/function.rb
CHANGED
@@ -20,8 +20,12 @@ module ROM
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def method_missing(meth, *args)
|
23
|
-
if func
|
24
|
-
|
23
|
+
if func
|
24
|
+
if func.respond_to?(meth)
|
25
|
+
meta(func: func.__send__(meth, *args))
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
25
29
|
else
|
26
30
|
meta(func: Sequel::SQL::Function.new(meth.to_s.upcase, *args))
|
27
31
|
end
|
data/lib/rom/sql/order_dsl.rb
CHANGED
@@ -12,9 +12,10 @@ module ROM
|
|
12
12
|
include InstanceMethods
|
13
13
|
defines :associations
|
14
14
|
|
15
|
-
associations
|
15
|
+
associations Hash.new
|
16
16
|
|
17
17
|
option :associations, reader: true, optional: true, default: -> cmd { cmd.class.associations }
|
18
|
+
option :configured_associations, reader: true, optional: true, default: proc { [] }
|
18
19
|
end
|
19
20
|
super
|
20
21
|
end
|
@@ -58,7 +59,17 @@ module ROM
|
|
58
59
|
|
59
60
|
# @api public
|
60
61
|
def with_association(name, opts = EMPTY_HASH)
|
61
|
-
self.class.build(
|
62
|
+
self.class.build(
|
63
|
+
relation, options.merge(associations: associations.merge(name => opts))
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def associations_configured?
|
68
|
+
if configured_associations.empty?
|
69
|
+
false
|
70
|
+
else
|
71
|
+
configured_associations.all? { |name| associations.key?(name) }
|
72
|
+
end
|
62
73
|
end
|
63
74
|
|
64
75
|
# @api private
|
@@ -73,7 +84,13 @@ module ROM
|
|
73
84
|
# @api public
|
74
85
|
def build(relation, options = EMPTY_HASH)
|
75
86
|
command = super
|
87
|
+
|
88
|
+
if command.associations_configured?
|
89
|
+
return command
|
90
|
+
end
|
91
|
+
|
76
92
|
associations = command.associations
|
93
|
+
assoc_names = []
|
77
94
|
|
78
95
|
before_hooks = associations.each_with_object([]) do |(name, opts), acc|
|
79
96
|
relation.associations.try(name) do |assoc|
|
@@ -83,6 +100,8 @@ module ROM
|
|
83
100
|
true
|
84
101
|
end
|
85
102
|
end or acc << { associate: { assoc: name, keys: opts[:key] } }
|
103
|
+
|
104
|
+
assoc_names << name
|
86
105
|
end
|
87
106
|
|
88
107
|
after_hooks = associations.each_with_object([]) do |(name, opts), acc|
|
@@ -92,10 +111,14 @@ module ROM
|
|
92
111
|
|
93
112
|
if assoc.is_a?(Association::ManyToMany)
|
94
113
|
acc << { associate: { assoc: assoc, keys: assoc.join_keys(relation.__registry__) } }
|
114
|
+
assoc_names << name
|
95
115
|
end
|
96
116
|
end
|
97
117
|
|
98
|
-
command.
|
118
|
+
command.
|
119
|
+
with_opts(configured_associations: assoc_names).
|
120
|
+
before(*before_hooks).
|
121
|
+
after(*after_hooks)
|
99
122
|
end
|
100
123
|
|
101
124
|
# Set command to associate tuples with a parent tuple using provided keys
|
@@ -120,12 +143,12 @@ module ROM
|
|
120
143
|
#
|
121
144
|
# @api public
|
122
145
|
def associates(name, options = EMPTY_HASH)
|
123
|
-
if associations.
|
146
|
+
if associations.key?(name)
|
124
147
|
raise ArgumentError,
|
125
148
|
"#{name} association is already defined for #{self.class}"
|
126
149
|
end
|
127
150
|
|
128
|
-
associations(associations.
|
151
|
+
associations(associations.merge(name => options))
|
129
152
|
end
|
130
153
|
end
|
131
154
|
end
|