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
@@ -0,0 +1,109 @@
1
+ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
2
+ include_context 'database setup'
3
+
4
+ before do
5
+ conn.drop_table?(:users)
6
+ end
7
+
8
+ let(:table_name) { :users }
9
+
10
+ subject(:gateway) { container.gateways[:default] }
11
+
12
+ let(:inferrer) { ROM::SQL::Schema::Inferrer.new }
13
+
14
+ let(:migrated_schema) do
15
+ empty = define_schema(table_name)
16
+ empty.with(inferrer.(empty, gateway))
17
+ end
18
+
19
+ let(:attributes) { migrated_schema.to_a }
20
+
21
+ describe 'create table' do
22
+ describe 'one-column indexes' do
23
+ before do
24
+ conf.relation(:users) do
25
+ schema do
26
+ attribute :id, ROM::SQL::Types::Serial
27
+ attribute :name, ROM::SQL::Types::String.meta(index: true)
28
+ end
29
+ end
30
+ end
31
+
32
+ it 'creates ordinary b-tree indexes' do
33
+ gateway.auto_migrate!(conf)
34
+
35
+ expect(attributes.map(&:to_ast))
36
+ .to eql([
37
+ [:attribute,
38
+ [:id,
39
+ [:definition, [Integer, {}]],
40
+ primary_key: true, source: :users]],
41
+ [:attribute,
42
+ [:name,
43
+ [:definition, [String, {}]],
44
+ source: :users]],
45
+ ])
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'alter table' do
51
+ describe 'one-column indexes' do
52
+ context 'adding' do
53
+ before do
54
+ conf.relation(:users) do
55
+ schema do
56
+ attribute :id, ROM::SQL::Types::Serial
57
+ attribute :name, ROM::SQL::Types::String.meta(index: true)
58
+ end
59
+ end
60
+ end
61
+
62
+ it 'adds indexed column' do
63
+ conn.create_table :users do
64
+ primary_key :id
65
+ end
66
+
67
+ gateway.auto_migrate!(conf)
68
+
69
+ expect(migrated_schema.attributes[1].name).to eql(:name)
70
+ expect(migrated_schema.indexes.map { |idx| idx.attributes.map(&:name) }).to eql([%i(name)])
71
+ end
72
+
73
+ it 'adds index to existing column' do
74
+ conn.create_table :users do
75
+ primary_key :id
76
+ column :name, String
77
+ end
78
+
79
+ gateway.auto_migrate!(conf)
80
+
81
+ expect(migrated_schema.indexes.map { |idx| idx.attributes.map(&:name) }).to eql([%i(name)])
82
+ end
83
+ end
84
+
85
+ context 'removing' do
86
+ before do
87
+ conf.relation(:users) do
88
+ schema do
89
+ attribute :id, ROM::SQL::Types::Serial
90
+ attribute :name, ROM::SQL::Types::String
91
+ end
92
+ end
93
+
94
+ conn.create_table :users do
95
+ primary_key :id
96
+ column :name, String
97
+
98
+ index :name
99
+ end
100
+ end
101
+
102
+ it 'removes index' do
103
+ gateway.auto_migrate!(conf)
104
+ expect(migrated_schema.indexes).to be_empty
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,156 @@
1
+ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
2
+ include_context 'database setup'
3
+
4
+ before do
5
+ conn.drop_table?(:users)
6
+ end
7
+
8
+ before do
9
+ conf.relation(:users) do
10
+ schema do
11
+ attribute :id, ROM::SQL::Types::Serial
12
+ attribute :name, ROM::SQL::Types::String
13
+ attribute :email, ROM::SQL::Types::String.optional
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:table_name) { :users }
19
+
20
+ subject(:gateway) { container.gateways[:default] }
21
+
22
+ let(:inferrer) { ROM::SQL::Schema::Inferrer.new }
23
+
24
+ let(:migrated_schema) do
25
+ empty = define_schema(table_name)
26
+ empty.with(inferrer.(empty, gateway))
27
+ end
28
+
29
+ let(:attributes) { migrated_schema.to_a }
30
+
31
+ describe 'create a table' do
32
+ it 'creates a table from a relation' do
33
+ gateway.auto_migrate!(conf)
34
+
35
+ expect(attributes.map(&:to_ast))
36
+ .to eql([
37
+ [:attribute,
38
+ [:id,
39
+ [:definition, [Integer, {}]],
40
+ primary_key: true, source: :users]],
41
+ [:attribute, [:name, [:definition, [String, {}]], source: :users]],
42
+ [:attribute,
43
+ [:email,
44
+ [:sum,
45
+ [[:constrained,
46
+ [[:definition, [NilClass, {}]],
47
+ [:predicate, [:type?, [[:type, NilClass], [:input, ROM::Undefined]]]],
48
+ {}]],
49
+ [:definition, [String, {}]],
50
+ {}]],
51
+ source: :users]]
52
+ ])
53
+ end
54
+ end
55
+
56
+ describe 'adding columns' do
57
+ before do
58
+ conn.create_table :users do
59
+ primary_key :id
60
+ end
61
+ end
62
+
63
+ it 'adds columns to an existing table' do
64
+ gateway.auto_migrate!(conf)
65
+
66
+ expect(attributes[1].to_ast)
67
+ .to eql(
68
+ [:attribute, [:name, [:definition, [String, {}]], source: :users]]
69
+ )
70
+ expect(attributes[2].to_ast)
71
+ .to eql(
72
+ [:attribute,
73
+ [:email,
74
+ [:sum,
75
+ [[:constrained,
76
+ [[:definition, [NilClass, {}]],
77
+ [:predicate, [:type?, [[:type, NilClass], [:input, ROM::Undefined]]]],
78
+ {}]],
79
+ [:definition, [String, {}]],
80
+ {}]],
81
+ source: :users]]
82
+ )
83
+ end
84
+ end
85
+
86
+ describe 'removing columns' do
87
+ before do
88
+ conn.create_table :users do
89
+ primary_key :id
90
+ column :name, String, null: false
91
+ column :email, String
92
+ column :age, Integer, null: false
93
+ end
94
+ end
95
+
96
+ it 'removes columns from a table' do
97
+ gateway.auto_migrate!(conf)
98
+
99
+ expect(attributes.map(&:name)).to eql(%i(id name email))
100
+ end
101
+ end
102
+
103
+ describe 'empty diff' do
104
+ before do
105
+ conn.create_table :users do
106
+ primary_key :id
107
+ column :name, String, null: false
108
+ column :email, String
109
+ end
110
+ end
111
+
112
+ it 'leaves existing schema' do
113
+ current = container.relations[:users].schema
114
+
115
+ gateway.auto_migrate!(conf)
116
+
117
+ expect(attributes).to eql(current.to_a)
118
+ end
119
+ end
120
+
121
+ describe 'changing NOTNULL' do
122
+ describe 'adding' do
123
+ before do
124
+ conn.create_table :users do
125
+ primary_key :id
126
+ column :name, String
127
+ column :email, String
128
+ end
129
+ end
130
+
131
+ it 'adds the constraint' do
132
+ gateway.auto_migrate!(conf)
133
+
134
+ expect(attributes[1].name).to eql(:name)
135
+ expect(attributes[1]).not_to be_optional
136
+ end
137
+ end
138
+
139
+ describe 'removing' do
140
+ before do
141
+ conn.create_table :users do
142
+ primary_key :id
143
+ column :name, String, null: false
144
+ column :email, String, null: false
145
+ end
146
+ end
147
+
148
+ it 'removes the constraint' do
149
+ gateway.auto_migrate!(conf)
150
+
151
+ expect(attributes[2].name).to eql(:email)
152
+ expect(attributes[2]).to be_optional
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,63 @@
1
+ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
2
+ include_context 'database setup'
3
+
4
+ before do
5
+ conn.drop_table?(:test_pg_types)
6
+ end
7
+
8
+ let(:table_name) { :test_pg_types }
9
+
10
+ subject(:gateway) { container.gateways[:default] }
11
+
12
+ let(:inferrer) { ROM::SQL::Schema::Inferrer.new }
13
+
14
+ let(:migrated_schema) do
15
+ empty = define_schema(table_name)
16
+ empty.with(inferrer.(empty, gateway))
17
+ end
18
+
19
+ let(:attributes) { migrated_schema.to_a }
20
+
21
+ describe 'common types' do
22
+ before do
23
+ conf.relation(:test_pg_types) do
24
+ schema do
25
+ attribute :id, ROM::SQL::Types::Serial
26
+ attribute :string, ROM::SQL::Types::String
27
+ attribute :int, ROM::SQL::Types::Int
28
+ attribute :time, ROM::SQL::Types::Time
29
+ attribute :date, ROM::SQL::Types::Date
30
+ attribute :decimal, ROM::SQL::Types::Decimal
31
+ attribute :string_nullable, ROM::SQL::Types::String.optional
32
+ end
33
+ end
34
+ end
35
+
36
+ it 'has support for PG data types' do
37
+ gateway.auto_migrate!(conf)
38
+
39
+ expect(attributes.map(&:to_ast))
40
+ .to eql([
41
+ [:attribute,
42
+ [:id,
43
+ [:definition, [Integer, {}]],
44
+ primary_key: true, source: :test_pg_types]],
45
+ [:attribute, [:string, [:definition, [String, {}]], source: :test_pg_types]],
46
+ [:attribute, [:int, [:definition, [Integer, {}]], source: :test_pg_types]],
47
+ [:attribute, [:time, [:definition, [Time, {}]], source: :test_pg_types]],
48
+ [:attribute, [:date, [:definition, [Date, {}]], source: :test_pg_types]],
49
+ [:attribute, [:decimal, [:definition, [BigDecimal, {}]], source: :test_pg_types]],
50
+ [:attribute,
51
+ [:string_nullable,
52
+ [:sum,
53
+ [[:constrained,
54
+ [[:definition, [NilClass, {}]],
55
+ [:predicate, [:type?, [[:type, NilClass], [:input, ROM::Undefined]]]],
56
+ {}]],
57
+ [:definition, [String, {}]],
58
+ {}]],
59
+ source: :test_pg_types]]
60
+ ])
61
+ end
62
+ end
63
+ end
@@ -137,10 +137,8 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
137
137
  end
138
138
 
139
139
  it 'uses relation schema for the default input handler' do
140
- conf.relation(:users) do
141
- register_as :users_with_schema
142
-
143
- schema do
140
+ conf.relation(:users_with_schema) do
141
+ schema(:users) do
144
142
  attribute :id, ROM::SQL::Types::Serial
145
143
  attribute :name, ROM::SQL::Types::String
146
144
  end
@@ -63,7 +63,7 @@ RSpec.describe 'Commands / Delete' do
63
63
  describe '#execute' do
64
64
  context 'with a single record' do
65
65
  it 'materializes the result' do
66
- result = container.command(:users).delete.by_name(%w(Jade)).execute
66
+ result = container.commands[:users].delete.by_name(%w(Jade)).execute
67
67
  expect(result).to eq([
68
68
  { id: 3, name: 'Jade' }
69
69
  ])
@@ -72,7 +72,7 @@ RSpec.describe 'Commands / Delete' do
72
72
 
73
73
  context 'with multiple records' do
74
74
  it 'materializes the results' do
75
- result = container.command(:users).delete.by_name(%w(Jade John)).execute
75
+ result = container.commands[:users].delete.by_name(%w(Jade John)).execute
76
76
  expect(result).to eq([
77
77
  { id: 3, name: 'Jade' },
78
78
  { id: 4, name: 'John' }
@@ -16,6 +16,8 @@ RSpec.describe 'Commands / Update', seeds: false do
16
16
  }
17
17
 
18
18
  conf.relation(:users) do
19
+ schema(infer: true)
20
+
19
21
  def by_id(id)
20
22
  where(id: id)
21
23
  end
@@ -4,18 +4,24 @@ RSpec.describe 'Eager loading' do
4
4
  with_adapters do
5
5
  before do
6
6
  conf.relation(:users) do
7
+ auto_map false
8
+
7
9
  def by_name(name)
8
10
  where(name: name)
9
11
  end
10
12
  end
11
13
 
12
14
  conf.relation(:tasks) do
15
+ auto_map false
16
+
13
17
  def for_users(users)
14
18
  where(user_id: users.map { |tuple| tuple[:id] })
15
19
  end
16
20
  end
17
21
 
18
22
  conf.relation(:tags) do
23
+ auto_map false
24
+
19
25
  def for_tasks(tasks)
20
26
  inner_join(:task_tags, task_id: :id)
21
27
  .where(task_id: tasks.map { |tuple| tuple[:id] })
@@ -24,9 +30,9 @@ RSpec.describe 'Eager loading' do
24
30
  end
25
31
 
26
32
  it 'issues 3 queries for 3.graphd relations' do
27
- users = container.relation(:users).by_name('Piotr')
28
- tasks = container.relation(:tasks)
29
- tags = container.relation(:tags)
33
+ users = container.relations[:users].by_name('Piotr')
34
+ tasks = container.relations[:tasks]
35
+ tags = container.relations[:tags]
30
36
 
31
37
  relation = users.graph(tasks.for_users.graph(tags.for_tasks))
32
38
 
@@ -8,6 +8,17 @@ RSpec.describe 'Plugins / :associates', seeds: false do
8
8
  let(:tags) { container.commands[:tags] }
9
9
 
10
10
  before do
11
+ conf.relation(:tasks) do
12
+ schema(infer: true) do
13
+ associations do
14
+ many_to_one :users, as: :user
15
+ many_to_one :users, as: :other
16
+ one_to_many :task_tags
17
+ one_to_many :tags, through: :task_tags
18
+ end
19
+ end
20
+ end
21
+
11
22
  conf.commands(:users) do
12
23
  define(:create) { result :one }
13
24
  end
@@ -82,35 +93,12 @@ RSpec.describe 'Plugins / :associates', seeds: false do
82
93
  end
83
94
  end
84
95
 
85
- context 'without a schema' do
86
- include_context 'automatic FK setting' do
87
- before do
88
- conf.commands(:tasks) do
89
- define(:create) do
90
- register_as :create_many
91
- associates :user, key: [:user_id, :id]
92
- end
93
-
94
- define(:create) do
95
- register_as :create_one
96
- result :one
97
- associates :user, key: [:user_id, :id]
98
- end
99
- end
100
- end
101
- end
102
- end
103
-
104
96
  context 'with a schema' do
105
97
  include_context 'automatic FK setting'
106
98
 
107
99
  before do
108
- conf.relation_classes[1].class_eval do
109
- schema do
110
- attribute :id, ROM::SQL::Types::Serial
111
- attribute :user_id, ROM::SQL::Types::ForeignKey(:users)
112
- attribute :title, ROM::SQL::Types::String
113
-
100
+ conf.relation(:tasks) do
101
+ schema(infer: true) do
114
102
  associations do
115
103
  many_to_one :users, as: :user
116
104
  one_to_many :task_tags
@@ -136,10 +124,7 @@ RSpec.describe 'Plugins / :associates', seeds: false do
136
124
  context 'with many-to-many association' do
137
125
  before do
138
126
  conf.relation(:tags) do
139
- schema do
140
- attribute :id, ROM::SQL::Types::Serial
141
- attribute :name, ROM::SQL::Types::String
142
-
127
+ schema(infer: true) do
143
128
  associations do
144
129
  one_to_many :task_tags
145
130
  one_to_many :tasks, through: :task_tags
@@ -183,7 +168,7 @@ RSpec.describe 'Plugins / :associates', seeds: false do
183
168
  command = create_user >> create_task >> create_tags
184
169
 
185
170
  result = command.call
186
- tags = relations[:tasks].associations[:tags].call(relations).to_a
171
+ tags = relations[:tasks].associations[:tags].().to_a
187
172
 
188
173
  expect(result).to eql([
189
174
  { id: 1, task_id: 1, name: 'red' }, { id: 2, task_id: 1, name: 'blue' }
@@ -231,7 +216,7 @@ RSpec.describe 'Plugins / :associates', seeds: false do
231
216
  end
232
217
 
233
218
  before do
234
- conf.relation_classes[1].class_eval do
219
+ conf.relation(:tasks) do
235
220
  schema(infer: true) do
236
221
  associations do
237
222
  belongs_to :user
@@ -262,28 +247,4 @@ RSpec.describe 'Plugins / :associates', seeds: false do
262
247
  end
263
248
  end
264
249
  end
265
-
266
- context 'misconfigured assocs', :sqlite do
267
- subject(:command) do
268
- container.commands[:users][:create]
269
- end
270
-
271
- context 'when keys are missing in class-level config' do
272
- before do
273
- conf.commands(:users) do
274
- define(:create) do
275
- associates :tasks
276
- end
277
- end
278
- end
279
-
280
- it 'raises error' do
281
- expect { command }.
282
- to raise_error(
283
- ROM::SQL::Plugin::Associates::MissingJoinKeysError,
284
- ':create command for :users relation is missing join keys configuration for :tasks association'
285
- )
286
- end
287
- end
288
- end
289
250
  end