rom-sql 0.7.0 → 0.8.0
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/.rspec +1 -0
- data/.travis.yml +12 -7
- data/CHANGELOG.md +28 -0
- data/Gemfile +6 -9
- data/README.md +5 -4
- data/circle.yml +10 -0
- data/lib/rom/plugins/relation/sql/auto_combine.rb +16 -3
- data/lib/rom/plugins/relation/sql/auto_wrap.rb +3 -2
- data/lib/rom/sql/association.rb +75 -0
- data/lib/rom/sql/association/many_to_many.rb +86 -0
- data/lib/rom/sql/association/many_to_one.rb +60 -0
- data/lib/rom/sql/association/name.rb +70 -0
- data/lib/rom/sql/association/one_to_many.rb +9 -0
- data/lib/rom/sql/association/one_to_one.rb +46 -0
- data/lib/rom/sql/association/one_to_one_through.rb +9 -0
- data/lib/rom/sql/commands.rb +2 -0
- data/lib/rom/sql/commands/create.rb +2 -2
- data/lib/rom/sql/commands/delete.rb +0 -1
- data/lib/rom/sql/commands/postgres.rb +76 -0
- data/lib/rom/sql/commands/update.rb +6 -3
- data/lib/rom/sql/commands_ext/postgres.rb +17 -0
- data/lib/rom/sql/gateway.rb +23 -15
- data/lib/rom/sql/header.rb +7 -1
- data/lib/rom/sql/plugin/assoc_macros.rb +3 -3
- data/lib/rom/sql/plugin/associates.rb +50 -9
- data/lib/rom/sql/qualified_attribute.rb +53 -0
- data/lib/rom/sql/relation.rb +76 -25
- data/lib/rom/sql/relation/reading.rb +138 -35
- data/lib/rom/sql/relation/writing.rb +21 -0
- data/lib/rom/sql/schema.rb +35 -0
- data/lib/rom/sql/schema/associations_dsl.rb +68 -0
- data/lib/rom/sql/schema/dsl.rb +27 -0
- data/lib/rom/sql/schema/inferrer.rb +80 -0
- data/lib/rom/sql/support/active_support_notifications.rb +27 -17
- data/lib/rom/sql/types.rb +11 -0
- data/lib/rom/sql/types/pg.rb +26 -0
- data/lib/rom/sql/version.rb +1 -1
- data/rom-sql.gemspec +4 -2
- data/spec/integration/association/many_to_many_spec.rb +137 -0
- data/spec/integration/association/many_to_one_spec.rb +110 -0
- data/spec/integration/association/one_to_many_spec.rb +58 -0
- data/spec/integration/association/one_to_one_spec.rb +57 -0
- data/spec/integration/association/one_to_one_through_spec.rb +90 -0
- data/spec/integration/combine_spec.rb +24 -24
- data/spec/integration/commands/create_spec.rb +215 -168
- data/spec/integration/commands/delete_spec.rb +88 -46
- data/spec/integration/commands/update_spec.rb +141 -60
- data/spec/integration/commands/upsert_spec.rb +83 -0
- data/spec/integration/gateway_spec.rb +9 -17
- data/spec/integration/migration_spec.rb +3 -5
- data/spec/integration/plugins/associates_spec.rb +168 -0
- data/spec/integration/plugins/auto_wrap_spec.rb +46 -0
- data/spec/integration/read_spec.rb +80 -77
- data/spec/integration/relation_schema_spec.rb +180 -0
- data/spec/integration/schema_inference_spec.rb +67 -0
- data/spec/integration/setup_spec.rb +22 -0
- data/spec/{support → integration/support}/active_support_notifications_spec.rb +0 -0
- data/spec/{support → integration/support}/rails_log_subscriber_spec.rb +0 -0
- data/spec/shared/database_setup.rb +46 -8
- data/spec/shared/relations.rb +8 -0
- data/spec/shared/users_and_accounts.rb +10 -0
- data/spec/shared/users_and_tasks.rb +20 -2
- data/spec/spec_helper.rb +64 -11
- data/spec/support/helpers.rb +9 -0
- data/spec/unit/association/many_to_many_spec.rb +89 -0
- data/spec/unit/association/many_to_one_spec.rb +81 -0
- data/spec/unit/association/name_spec.rb +68 -0
- data/spec/unit/association/one_to_many_spec.rb +62 -0
- data/spec/unit/association/one_to_one_spec.rb +62 -0
- data/spec/unit/association/one_to_one_through_spec.rb +69 -0
- data/spec/unit/association_errors_spec.rb +2 -4
- data/spec/unit/gateway_spec.rb +12 -3
- data/spec/unit/migration_tasks_spec.rb +3 -3
- data/spec/unit/migrator_spec.rb +2 -4
- data/spec/unit/{combined_associations_spec.rb → plugin/assoc_macros/combined_associations_spec.rb} +13 -19
- data/spec/unit/{many_to_many_spec.rb → plugin/assoc_macros/many_to_many_spec.rb} +9 -15
- data/spec/unit/{many_to_one_spec.rb → plugin/assoc_macros/many_to_one_spec.rb} +9 -14
- data/spec/unit/plugin/assoc_macros/one_to_many_spec.rb +78 -0
- data/spec/unit/plugin/base_view_spec.rb +11 -11
- data/spec/unit/plugin/pagination_spec.rb +62 -62
- data/spec/unit/relation_spec.rb +218 -146
- data/spec/unit/schema_spec.rb +15 -14
- data/spec/unit/types_spec.rb +40 -0
- metadata +105 -21
- data/.rubocop.yml +0 -74
- data/.rubocop_todo.yml +0 -21
- data/spec/unit/one_to_many_spec.rb +0 -83
@@ -2,6 +2,27 @@ module ROM
|
|
2
2
|
module SQL
|
3
3
|
class Relation < ROM::Relation
|
4
4
|
module Writing
|
5
|
+
# Add upsert option (only PostgreSQL >= 9.5)
|
6
|
+
# Uses internal Sequel implementation
|
7
|
+
# Default - ON CONFLICT DO NOTHING
|
8
|
+
# more options: http://sequel.jeremyevans.net/rdoc-adapters/classes/Sequel/Postgres/DatasetMethods.html#method-i-insert_conflict
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# users.upsert({ name: 'Jane', email: 'jane@foo.com' },
|
12
|
+
# { target: :email, update: { name: :excluded__name } }
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
def upsert(*args, &block)
|
16
|
+
if args.size > 1 && args[-1].is_a?(Hash)
|
17
|
+
*values, opts = args
|
18
|
+
else
|
19
|
+
values = args
|
20
|
+
opts = EMPTY_HASH
|
21
|
+
end
|
22
|
+
|
23
|
+
dataset.insert_conflict(opts).insert(*values, &block)
|
24
|
+
end
|
25
|
+
|
5
26
|
# Insert tuple into relation
|
6
27
|
#
|
7
28
|
# @example
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rom/schema'
|
2
|
+
require 'rom/support/constants'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module SQL
|
6
|
+
class Schema < ROM::Schema
|
7
|
+
# @!attribute [r] primary_key_name
|
8
|
+
# @return [Symbol] The name of the primary key. This is set because in
|
9
|
+
# most of the cases relations don't have composite pks
|
10
|
+
attr_reader :primary_key_name
|
11
|
+
|
12
|
+
# @!attribute [r] primary_key_names
|
13
|
+
# @return [Array<Symbol>] A list of all pk names
|
14
|
+
attr_reader :primary_key_names
|
15
|
+
|
16
|
+
def initialize(*)
|
17
|
+
super
|
18
|
+
@primary_key_name = nil
|
19
|
+
@primary_key_names = EMPTY_ARRAY
|
20
|
+
end
|
21
|
+
|
22
|
+
# @api private
|
23
|
+
def finalize!(*)
|
24
|
+
super do
|
25
|
+
if primary_key.size > 0
|
26
|
+
@primary_key_name = primary_key[0].meta[:name]
|
27
|
+
@primary_key_names = primary_key.map { |type| type.meta[:name] }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rom/sql/schema/dsl'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rom/sql/association'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module SQL
|
5
|
+
class Schema < ROM::Schema
|
6
|
+
class AssociationsDSL < BasicObject
|
7
|
+
attr_reader :source, :registry
|
8
|
+
|
9
|
+
def initialize(source, &block)
|
10
|
+
@source = source
|
11
|
+
@registry = {}
|
12
|
+
instance_exec(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def one_to_many(target, options = {})
|
16
|
+
if options[:through]
|
17
|
+
many_to_many(target, options)
|
18
|
+
else
|
19
|
+
add(Association::OneToMany.new(source, target, options))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
alias_method :has_many, :one_to_many
|
23
|
+
|
24
|
+
def one_to_one(target, options = {})
|
25
|
+
if options[:through]
|
26
|
+
one_to_one_through(target, options)
|
27
|
+
else
|
28
|
+
add(Association::OneToOne.new(source, target, options))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def one_to_one_through(target, options = {})
|
33
|
+
add(Association::OneToOneThrough.new(source, target, options))
|
34
|
+
end
|
35
|
+
|
36
|
+
def many_to_many(target, options = {})
|
37
|
+
add(Association::ManyToMany.new(source, target, options))
|
38
|
+
end
|
39
|
+
|
40
|
+
def many_to_one(target, options = {})
|
41
|
+
add(Association::ManyToOne.new(source, target, options))
|
42
|
+
end
|
43
|
+
|
44
|
+
def belongs_to(name, options = {})
|
45
|
+
many_to_one(dataset_name(name), options.merge(as: options[:as] || name))
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_one(name, options = {})
|
49
|
+
one_to_one(dataset_name(name), options.merge(as: options[:as] || name))
|
50
|
+
end
|
51
|
+
|
52
|
+
def call
|
53
|
+
AssociationSet.new(registry)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def add(association)
|
59
|
+
registry[association.name] = association
|
60
|
+
end
|
61
|
+
|
62
|
+
def dataset_name(name)
|
63
|
+
Inflector.pluralize(name).to_sym
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rom/sql/schema/inferrer'
|
2
|
+
require 'rom/sql/schema/associations_dsl'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module SQL
|
6
|
+
class Schema < ROM::Schema
|
7
|
+
class DSL < ROM::Schema::DSL
|
8
|
+
attr_reader :associations_dsl
|
9
|
+
|
10
|
+
def associations(&block)
|
11
|
+
@associations_dsl = AssociationsDSL.new(name, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
SQL::Schema.new(name, attributes, opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
def opts
|
19
|
+
opts = {}
|
20
|
+
opts[:associations] = associations_dsl.call if associations_dsl
|
21
|
+
opts[:inferrer] = inferrer.new(self) if inferrer
|
22
|
+
opts
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module ROM
|
2
|
+
module SQL
|
3
|
+
class Schema < ROM::Schema
|
4
|
+
class Inferrer
|
5
|
+
extend ClassMacros
|
6
|
+
|
7
|
+
defines :type_mapping, :pk_type
|
8
|
+
|
9
|
+
type_mapping(
|
10
|
+
integer: Types::Strict::Int,
|
11
|
+
string: Types::Strict::String,
|
12
|
+
date: Types::Strict::Date,
|
13
|
+
datetime: Types::Strict::Time,
|
14
|
+
boolean: Types::Strict::Bool,
|
15
|
+
decimal: Types::Strict::Decimal,
|
16
|
+
blob: Types::Strict::String
|
17
|
+
).freeze
|
18
|
+
|
19
|
+
pk_type Types::Serial
|
20
|
+
|
21
|
+
attr_reader :dsl
|
22
|
+
|
23
|
+
def initialize(dsl)
|
24
|
+
@dsl = dsl
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def call(dataset, gateway)
|
29
|
+
columns = gateway.connection.schema(dataset)
|
30
|
+
fks = fks_for(gateway, dataset)
|
31
|
+
|
32
|
+
columns.each do |(name, definition)|
|
33
|
+
dsl.attribute name, build_type(definition.merge(foreign_key: fks[name]))
|
34
|
+
end
|
35
|
+
|
36
|
+
pks = columns
|
37
|
+
.map { |(name, definition)| name if definition.fetch(:primary_key) }
|
38
|
+
.compact
|
39
|
+
|
40
|
+
dsl.primary_key(*pks) if pks.any?
|
41
|
+
|
42
|
+
dsl.attributes
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
def build_type(primary_key: , type: , allow_null: , foreign_key: , **rest)
|
49
|
+
if primary_key
|
50
|
+
self.class.pk_type
|
51
|
+
else
|
52
|
+
type = self.class.type_mapping.fetch(type)
|
53
|
+
type = type.optional if allow_null
|
54
|
+
type = type.meta(foreign_key: true, relation: foreign_key) if foreign_key
|
55
|
+
type
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @api private
|
60
|
+
def fks_for(gateway, dataset)
|
61
|
+
gateway.connection.foreign_key_list(dataset).each_with_object({}) do |definition, fks|
|
62
|
+
column, fk = build_fk(definition)
|
63
|
+
|
64
|
+
fks[column] = fk if fk
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @api private
|
69
|
+
def build_fk(columns: , table: , **rest)
|
70
|
+
if columns.size == 1
|
71
|
+
[columns[0], table]
|
72
|
+
else
|
73
|
+
# We don't have support for multicolumn foreign keys
|
74
|
+
columns[0]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,26 +1,36 @@
|
|
1
1
|
require 'sequel/database/logging'
|
2
2
|
require 'active_support/notifications'
|
3
3
|
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
module ROM
|
5
|
+
module SQL
|
6
|
+
module ActiveSupportInstrumentation
|
7
|
+
if Sequel::MAJOR == 4 && Sequel::MINOR < 35
|
8
|
+
def log_yield(sql, args = nil)
|
9
|
+
ActiveSupport::Notifications.instrument(
|
10
|
+
'sql.rom',
|
11
|
+
sql: sql,
|
12
|
+
name: instrumentation_name,
|
13
|
+
binds: args
|
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
|
14
25
|
end
|
15
|
-
end
|
16
|
-
|
17
|
-
alias_method :log_yield_without_instrumentation, :log_yield
|
18
|
-
alias_method :log_yield, :log_yield_with_instrumentation
|
19
26
|
|
20
|
-
|
27
|
+
private
|
21
28
|
|
22
|
-
|
23
|
-
|
29
|
+
def instrumentation_name
|
30
|
+
"ROM[#{database_type}]"
|
31
|
+
end
|
24
32
|
end
|
25
33
|
end
|
26
34
|
end
|
35
|
+
|
36
|
+
Sequel::Database.send(:prepend, ROM::SQL::ActiveSupportInstrumentation)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'dry-types'
|
2
|
+
require 'sequel'
|
3
|
+
|
4
|
+
module ROM
|
5
|
+
module SQL
|
6
|
+
module Types
|
7
|
+
module PG
|
8
|
+
Sequel.extension(:pg_json)
|
9
|
+
|
10
|
+
Array = Dry::Types::Definition
|
11
|
+
.new(Sequel::Postgres::JSONArray)
|
12
|
+
.constructor(Sequel.method(:pg_json))
|
13
|
+
|
14
|
+
Hash = Dry::Types::Definition
|
15
|
+
.new(Sequel::Postgres::JSONHash)
|
16
|
+
.constructor(Sequel.method(:pg_json))
|
17
|
+
|
18
|
+
JSON = Array | Hash
|
19
|
+
|
20
|
+
Bytea = Dry::Types::Definition
|
21
|
+
.new(Sequel::SQL::Blob)
|
22
|
+
.constructor(Sequel::SQL::Blob.method(:new))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/rom/sql/version.rb
CHANGED
data/rom-sql.gemspec
CHANGED
@@ -18,9 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "sequel", "~> 4.
|
21
|
+
spec.add_runtime_dependency "sequel", "~> 4.25"
|
22
22
|
spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
|
23
|
-
spec.add_runtime_dependency "
|
23
|
+
spec.add_runtime_dependency "dry-types", "~> 0.8"
|
24
|
+
spec.add_runtime_dependency "rom", "~> 2.0"
|
25
|
+
spec.add_runtime_dependency "rom-support", "~> 2.0"
|
24
26
|
|
25
27
|
spec.add_development_dependency "bundler"
|
26
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -0,0 +1,137 @@
|
|
1
|
+
RSpec.describe ROM::SQL::Association::ManyToMany do
|
2
|
+
include_context 'users and tasks'
|
3
|
+
|
4
|
+
with_adapters :sqlite do
|
5
|
+
context 'with two associations pointing to the same target relation' do
|
6
|
+
let(:container) do
|
7
|
+
ROM.container(:sql, uri) do |conf|
|
8
|
+
conf.default.create_table(:users_tasks) do
|
9
|
+
foreign_key :user_id, :users
|
10
|
+
foreign_key :task_id, :tasks
|
11
|
+
primary_key [:user_id, :task_id]
|
12
|
+
end
|
13
|
+
|
14
|
+
conf.relation(:users) do
|
15
|
+
schema(infer: true) do
|
16
|
+
associations do
|
17
|
+
has_many :users_tasks
|
18
|
+
has_many :tasks, through: :users_tasks
|
19
|
+
has_many :tasks, as: :priv_tasks
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
conf.relation(:users_tasks) do
|
25
|
+
schema(infer: true) do
|
26
|
+
associations do
|
27
|
+
belongs_to :user
|
28
|
+
belongs_to :task
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
conf.relation(:tasks) do
|
34
|
+
schema(infer: true) do
|
35
|
+
associations do
|
36
|
+
has_many :users_tasks
|
37
|
+
has_many :users, through: :users_tasks
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not conflict with two FKs' do
|
45
|
+
users = container.relations[:users]
|
46
|
+
tasks = container.relations[:tasks]
|
47
|
+
assoc = users.associations[:tasks]
|
48
|
+
|
49
|
+
relation = tasks.for_combine(assoc).call(users.call)
|
50
|
+
|
51
|
+
expect(relation.to_a).to be_empty
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'preloads using FK' do
|
55
|
+
users = container.relations[:users]
|
56
|
+
tasks = container.relations[:tasks]
|
57
|
+
assoc = users.associations[:priv_tasks]
|
58
|
+
|
59
|
+
relation = tasks.for_combine(assoc).call(users.where(id: 2).call)
|
60
|
+
|
61
|
+
expect(relation.to_a).to eql([id: 1, user_id: 2, title: "Joe's task"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
with_adapters do
|
67
|
+
subject(:assoc) {
|
68
|
+
ROM::SQL::Association::ManyToMany.new(:tasks, :tags, through: :task_tags)
|
69
|
+
}
|
70
|
+
|
71
|
+
let(:tasks) { container.relations[:tasks] }
|
72
|
+
let(:tags) { container.relations[:tags] }
|
73
|
+
|
74
|
+
before do
|
75
|
+
conf.relation(:task_tags) do
|
76
|
+
schema do
|
77
|
+
attribute :task_id, ROM::SQL::Types::ForeignKey(:tasks)
|
78
|
+
attribute :tag_id, ROM::SQL::Types::ForeignKey(:tags)
|
79
|
+
|
80
|
+
primary_key :task_id, :tag_id
|
81
|
+
|
82
|
+
associations do
|
83
|
+
many_to_one :tasks
|
84
|
+
many_to_one :tags
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
conf.relation(:tasks) do
|
90
|
+
schema do
|
91
|
+
attribute :id, ROM::SQL::Types::Serial
|
92
|
+
attribute :user_id, ROM::SQL::Types::ForeignKey(:users)
|
93
|
+
attribute :title, ROM::SQL::Types::String
|
94
|
+
|
95
|
+
associations do
|
96
|
+
one_to_many :task_tags
|
97
|
+
one_to_many :tags, through: :task_tags
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#result' do
|
104
|
+
specify { expect(ROM::SQL::Association::ManyToMany.result).to be(:many) }
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#call' do
|
108
|
+
it 'prepares joined relations' do
|
109
|
+
relation = assoc.call(container.relations)
|
110
|
+
|
111
|
+
expect(relation.attributes).to eql(%i[id name task_id])
|
112
|
+
expect(relation.to_a).to eql([id: 1, name: 'important', task_id: 1])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe ':through another assoc' do
|
117
|
+
subject(:assoc) do
|
118
|
+
ROM::SQL::Association::ManyToMany.new(:users, :tags, through: :tasks)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'prepares joined relations through other association' do
|
122
|
+
relation = assoc.call(container.relations)
|
123
|
+
|
124
|
+
expect(relation.attributes).to eql(%i[id name user_id])
|
125
|
+
expect(relation.to_a).to eql([id: 1, name: 'important', user_id: 2])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe ROM::Plugins::Relation::SQL::AutoCombine, '#for_combine' do
|
130
|
+
it 'preloads relation based on association' do
|
131
|
+
relation = tags.for_combine(assoc).call(tasks.call)
|
132
|
+
|
133
|
+
expect(relation.to_a).to eql([id: 1, name: 'important', task_id: 1])
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|