rom-sql 1.0.0.beta3 → 1.0.0.rc1
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 +5 -5
- data/.yardopts +2 -0
- data/CHANGELOG.md +4 -1
- data/lib/rom/plugins/relation/sql/auto_combine.rb +4 -0
- data/lib/rom/plugins/relation/sql/auto_wrap.rb +4 -0
- data/lib/rom/sql/{type.rb → attribute.rb} +8 -5
- data/lib/rom/sql/commands/update.rb +10 -0
- data/lib/rom/sql/dsl.rb +1 -0
- data/lib/rom/sql/extensions/postgres/inferrer.rb +2 -1
- data/lib/rom/sql/extensions/sqlite.rb +1 -0
- data/lib/rom/sql/extensions/sqlite/inferrer.rb +9 -0
- data/lib/rom/sql/extensions/sqlite/types.rb +11 -0
- data/lib/rom/sql/function.rb +4 -1
- data/lib/rom/sql/gateway.rb +64 -28
- data/lib/rom/sql/migration.rb +21 -29
- data/lib/rom/sql/migration/migrator.rb +8 -0
- data/lib/rom/sql/order_dsl.rb +1 -0
- data/lib/rom/sql/plugin/associates.rb +21 -0
- data/lib/rom/sql/plugin/timestamps.rb +131 -0
- data/lib/rom/sql/plugins.rb +3 -0
- data/lib/rom/sql/projection_dsl.rb +1 -0
- data/lib/rom/sql/relation/reading.rb +256 -75
- data/lib/rom/sql/restriction_dsl.rb +1 -0
- data/lib/rom/sql/schema/associations_dsl.rb +119 -1
- data/lib/rom/sql/schema/dsl.rb +44 -2
- data/lib/rom/sql/version.rb +1 -1
- data/rom-sql.gemspec +2 -2
- data/spec/extensions/sqlite/types_spec.rb +11 -0
- data/spec/integration/plugins/associates_spec.rb +79 -0
- data/spec/integration/schema/inferrer/postgres_spec.rb +3 -1
- data/spec/integration/schema/inferrer/sqlite_spec.rb +2 -0
- data/spec/shared/database_setup.rb +10 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/support/helpers.rb +2 -2
- data/spec/unit/plugin/timestamp_spec.rb +77 -0
- data/spec/unit/types_spec.rb +1 -1
- metadata +37 -24
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
|
2
3
|
require 'rom/types'
|
3
4
|
require 'rom/initializer'
|
4
5
|
|
5
6
|
module ROM
|
6
7
|
module SQL
|
7
8
|
module Migration
|
9
|
+
# @api private
|
8
10
|
class Migrator
|
9
11
|
extend Initializer
|
10
12
|
|
@@ -15,18 +17,22 @@ module ROM
|
|
15
17
|
|
16
18
|
option :path, type: ROM::Types.Definition(Pathname), reader: true, default: proc { DEFAULT_PATH }
|
17
19
|
|
20
|
+
# @api private
|
18
21
|
def run(options = {})
|
19
22
|
Sequel::Migrator.run(connection, path.to_s, options)
|
20
23
|
end
|
21
24
|
|
25
|
+
# @api private
|
22
26
|
def pending?
|
23
27
|
!Sequel::Migrator.is_current?(connection, path.to_s)
|
24
28
|
end
|
25
29
|
|
30
|
+
# @api private
|
26
31
|
def migration(&block)
|
27
32
|
Sequel.migration(&block)
|
28
33
|
end
|
29
34
|
|
35
|
+
# @api private
|
30
36
|
def create_file(name, version = generate_version)
|
31
37
|
filename = "#{version}_#{name}.rb"
|
32
38
|
dirname = Pathname(path)
|
@@ -38,10 +44,12 @@ module ROM
|
|
38
44
|
fullpath
|
39
45
|
end
|
40
46
|
|
47
|
+
# @api private
|
41
48
|
def generate_version
|
42
49
|
Time.now.utc.strftime(VERSION_FORMAT)
|
43
50
|
end
|
44
51
|
|
52
|
+
# @api private
|
45
53
|
def migration_file_content
|
46
54
|
File.read(Pathname(__FILE__).dirname.join('template.rb').realpath)
|
47
55
|
end
|
data/lib/rom/sql/order_dsl.rb
CHANGED
@@ -5,6 +5,21 @@ module ROM
|
|
5
5
|
#
|
6
6
|
# @api private
|
7
7
|
module Associates
|
8
|
+
class MissingJoinKeysError < StandardError
|
9
|
+
ERROR_TEMPLATE = ':%{command} command for :%{relation} relation ' \
|
10
|
+
'is missing join keys configuration for :%{name} association'
|
11
|
+
|
12
|
+
def initialize(command, assoc_name)
|
13
|
+
super(ERROR_TEMPLATE % tokens(command, assoc_name))
|
14
|
+
end
|
15
|
+
|
16
|
+
def tokens(command, assoc_name)
|
17
|
+
{ command: command.register_as,
|
18
|
+
relation: command.relation,
|
19
|
+
name: assoc_name }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
8
23
|
# @api private
|
9
24
|
def self.included(klass)
|
10
25
|
klass.class_eval do
|
@@ -55,6 +70,8 @@ module ROM
|
|
55
70
|
assoc.associate(relation.__registry__, tuple, parent)
|
56
71
|
}
|
57
72
|
end
|
73
|
+
|
74
|
+
one? ? input_tuples[0] : input_tuples
|
58
75
|
end
|
59
76
|
|
60
77
|
# @api public
|
@@ -115,6 +132,10 @@ module ROM
|
|
115
132
|
end
|
116
133
|
end
|
117
134
|
|
135
|
+
[*before_hooks, *after_hooks].
|
136
|
+
map { |hook| hook[:associate] }.
|
137
|
+
each { |conf| raise MissingJoinKeysError.new(self, conf[:assoc]) unless conf[:keys] }
|
138
|
+
|
118
139
|
command.
|
119
140
|
with_opts(configured_associations: assoc_names).
|
120
141
|
before(*before_hooks).
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module SQL
|
5
|
+
module Plugin
|
6
|
+
# Make a command that automatically fills in timestamp attributes on
|
7
|
+
# input tuples
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
module Timestamps
|
11
|
+
# @api private
|
12
|
+
def self.included(klass)
|
13
|
+
klass.extend(ClassInterface)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassInterface
|
18
|
+
# @api private
|
19
|
+
def self.extended(klass)
|
20
|
+
klass.defines :timestamp_columns, :datestamp_columns
|
21
|
+
klass.timestamp_columns Set.new
|
22
|
+
klass.datestamp_columns Set.new
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# Set up attributes to timestamp when the command is called
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# class CreateTask < ROM::Commands::Create[:sql]
|
31
|
+
# result :one
|
32
|
+
# use :timestamps
|
33
|
+
# timestamps :created_at, :updated_at
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# create_user = rom.command(:user).create.with(name: 'Jane')
|
37
|
+
#
|
38
|
+
# result = create_user.call
|
39
|
+
# result[:created_at] #=> Time.now.utc
|
40
|
+
#
|
41
|
+
# @param [Symbol] name of the attribute to set
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def timestamps(*args)
|
45
|
+
timestamp_columns timestamp_columns.merge(args)
|
46
|
+
|
47
|
+
include InstanceMethods
|
48
|
+
end
|
49
|
+
alias timestamp timestamps
|
50
|
+
|
51
|
+
# Set up attributes to datestamp when the command is called
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
# class CreateTask < ROM::Commands::Create[:sql]
|
55
|
+
# result :one
|
56
|
+
# use :timestamps
|
57
|
+
# datestamps :created_on, :updated_on
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# create_user = rom.command(:user).create.with(name: 'Jane')
|
61
|
+
#
|
62
|
+
# result = create_user.call
|
63
|
+
# result[:created_at] #=> Date.today
|
64
|
+
#
|
65
|
+
# @param [Symbol] name of the attribute to set
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def datestamps(*args)
|
69
|
+
datestamp_columns datestamp_columns.merge(args)
|
70
|
+
|
71
|
+
include InstanceMethods
|
72
|
+
end
|
73
|
+
alias datestamp datestamps
|
74
|
+
end
|
75
|
+
|
76
|
+
module InstanceMethods
|
77
|
+
# @api private
|
78
|
+
def self.included(base)
|
79
|
+
base.before :set_timestamps
|
80
|
+
end
|
81
|
+
|
82
|
+
# @api private
|
83
|
+
def timestamp_columns
|
84
|
+
self.class.timestamp_columns
|
85
|
+
end
|
86
|
+
|
87
|
+
# @api private
|
88
|
+
def datestamp_columns
|
89
|
+
self.class.datestamp_columns
|
90
|
+
end
|
91
|
+
|
92
|
+
# Set the timestamp attributes on the given tuples
|
93
|
+
#
|
94
|
+
# @param [Array<Hash>, Hash] tuples the input tuple(s)
|
95
|
+
#
|
96
|
+
# @return [Array<Hash>, Hash]
|
97
|
+
#
|
98
|
+
# @api private
|
99
|
+
def set_timestamps(tuples)
|
100
|
+
timestamps = build_timestamps
|
101
|
+
|
102
|
+
case tuples
|
103
|
+
when Hash
|
104
|
+
timestamps.merge(tuples)
|
105
|
+
when Array
|
106
|
+
tuples.map { |t| timestamps.merge(t) }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# @api private
|
113
|
+
def build_timestamps
|
114
|
+
time = Time.now.utc
|
115
|
+
date = Date.today
|
116
|
+
timestamps = {}
|
117
|
+
timestamp_columns.each do |column|
|
118
|
+
timestamps[column.to_sym] = time
|
119
|
+
end
|
120
|
+
|
121
|
+
datestamp_columns.each do |column|
|
122
|
+
timestamps[column.to_sym] = date
|
123
|
+
end
|
124
|
+
|
125
|
+
timestamps
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/rom/sql/plugins.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'rom/sql/plugin/associates'
|
2
2
|
require 'rom/sql/plugin/pagination'
|
3
|
+
require 'rom/sql/plugin/timestamps'
|
3
4
|
|
4
5
|
ROM.plugins do
|
5
6
|
adapter :sql do
|
6
7
|
register :pagination, ROM::SQL::Plugin::Pagination, type: :relation
|
7
8
|
register :associates, ROM::SQL::Plugin::Associates, type: :command
|
9
|
+
|
10
|
+
register :timestamps, ROM::SQL::Plugin::Timestamps, type: :command
|
8
11
|
end
|
9
12
|
end
|
@@ -83,7 +83,8 @@ module ROM
|
|
83
83
|
# This method is intended to be used internally within a relation object
|
84
84
|
#
|
85
85
|
# @example
|
86
|
-
# users.qualified
|
86
|
+
# users.qualified.dataset.sql
|
87
|
+
# # SELECT "users"."id", "users"."name" ...
|
87
88
|
#
|
88
89
|
# @return [Relation]
|
89
90
|
#
|
@@ -141,22 +142,6 @@ module ROM
|
|
141
142
|
map(name)
|
142
143
|
end
|
143
144
|
|
144
|
-
# Project a relation
|
145
|
-
#
|
146
|
-
# This method is intended to be used internally within a relation object
|
147
|
-
#
|
148
|
-
# @example
|
149
|
-
# users.project(:id, :name) }
|
150
|
-
#
|
151
|
-
# @param [Symbol] *names A list of symbol column names
|
152
|
-
#
|
153
|
-
# @return [Relation]
|
154
|
-
#
|
155
|
-
# @api public
|
156
|
-
def project(*names)
|
157
|
-
schema.project(*names).(self)
|
158
|
-
end
|
159
|
-
|
160
145
|
# Rename columns in a relation
|
161
146
|
#
|
162
147
|
# This method is intended to be used internally within a relation object
|
@@ -176,9 +161,53 @@ module ROM
|
|
176
161
|
|
177
162
|
# Select specific columns for select clause
|
178
163
|
#
|
179
|
-
# @
|
180
|
-
#
|
181
|
-
#
|
164
|
+
# @overload select(*columns)
|
165
|
+
# Project relation using column names
|
166
|
+
#
|
167
|
+
# @example using column names
|
168
|
+
# users.select(:id, :name).first
|
169
|
+
# # {:id => 1, :name => "Jane"}
|
170
|
+
#
|
171
|
+
# @param [Array<Symbol>] columns A list of column names
|
172
|
+
#
|
173
|
+
# @overload select(*attributes)
|
174
|
+
# Project relation using schema attributes
|
175
|
+
#
|
176
|
+
# @example using attributes
|
177
|
+
# users.select(:id, :name).first
|
178
|
+
# # {:id => 1, :name => "Jane"}
|
179
|
+
#
|
180
|
+
# @example using schema
|
181
|
+
# users.select(*schema.project(:id)).first
|
182
|
+
# # {:id => 1}
|
183
|
+
#
|
184
|
+
# @param [Array<SQL::Attribute>] columns A list of schema attributes
|
185
|
+
#
|
186
|
+
# @overload select(&block)
|
187
|
+
# Project relation using projection DSL
|
188
|
+
#
|
189
|
+
# @example using attributes
|
190
|
+
# users.select { id.as(:user_id) }
|
191
|
+
# # {:user_id => 1}
|
192
|
+
#
|
193
|
+
# users.select { [id, name] }
|
194
|
+
# # {:id => 1, :name => "Jane"}
|
195
|
+
#
|
196
|
+
# @example using SQL functions
|
197
|
+
# users.select { string::concat(id, '-', name).as(:uid) }.first
|
198
|
+
# # {:uid => "1-Jane"}
|
199
|
+
#
|
200
|
+
# @overload select(*columns, &block)
|
201
|
+
# Project relation using column names and projection DSL
|
202
|
+
#
|
203
|
+
# @example using attributes
|
204
|
+
# users.select(:id) { int::count(id).as(:count) }.group(:id).first
|
205
|
+
# # {:id => 1, :count => 1}
|
206
|
+
#
|
207
|
+
# users.select { [id, name] }
|
208
|
+
# # {:id => 1, :name => "Jane"}
|
209
|
+
#
|
210
|
+
# @param [Array<SQL::Attribute>] columns A list of schema attributes
|
182
211
|
#
|
183
212
|
# @return [Relation]
|
184
213
|
#
|
@@ -186,14 +215,11 @@ module ROM
|
|
186
215
|
def select(*args, &block)
|
187
216
|
schema.project(*args, &block).(self)
|
188
217
|
end
|
218
|
+
alias_method :project, :select
|
189
219
|
|
190
220
|
# Append specific columns to select clause
|
191
221
|
#
|
192
|
-
# @
|
193
|
-
# users.select(:id, :name).select_append(:email)
|
194
|
-
# # {:id => 1, :name => "Jane", :email => "jane@doe.org"}
|
195
|
-
#
|
196
|
-
# @param [Array<Symbol>] *args A list with column names
|
222
|
+
# @see Relation#select
|
197
223
|
#
|
198
224
|
# @return [Relation]
|
199
225
|
#
|
@@ -204,10 +230,20 @@ module ROM
|
|
204
230
|
|
205
231
|
# Returns a copy of the relation with a SQL DISTINCT clause.
|
206
232
|
#
|
207
|
-
# @
|
208
|
-
#
|
233
|
+
# @overload distinct(*columns)
|
234
|
+
# Create a distinct statement from column names
|
209
235
|
#
|
210
|
-
#
|
236
|
+
# @example
|
237
|
+
# users.distinct(:country)
|
238
|
+
#
|
239
|
+
# @param [Array<Symbol>] columns A list with column names
|
240
|
+
#
|
241
|
+
# @overload distinct(&block)
|
242
|
+
# Create a distinct statement from a block
|
243
|
+
#
|
244
|
+
# @example
|
245
|
+
# users.distinct { func(id) }
|
246
|
+
# # SELECT DISTINCT ON (count("id")) "id" ...
|
211
247
|
#
|
212
248
|
# @return [Relation]
|
213
249
|
#
|
@@ -274,19 +310,30 @@ module ROM
|
|
274
310
|
|
275
311
|
# Restrict a relation to match criteria
|
276
312
|
#
|
277
|
-
#
|
278
|
-
#
|
313
|
+
# @overload where(conditions)
|
314
|
+
# Restrict a relation using a hash with conditions
|
279
315
|
#
|
280
|
-
#
|
281
|
-
#
|
316
|
+
# @example
|
317
|
+
# users.where(name: 'Jane', age: 30)
|
282
318
|
#
|
283
|
-
#
|
319
|
+
# @param [Hash] conditions A hash with conditions
|
284
320
|
#
|
285
|
-
# @
|
321
|
+
# @overload where(conditions, &block)
|
322
|
+
# Restrict a relation using a hash with conditions and restriction DSL
|
286
323
|
#
|
287
|
-
#
|
324
|
+
# @example
|
325
|
+
# users.where(name: 'Jane') { age > 18 }
|
326
|
+
#
|
327
|
+
# @param [Hash] conditions A hash with conditions
|
328
|
+
#
|
329
|
+
# @overload where(&block)
|
330
|
+
# Restrict a relation using restriction DSL
|
288
331
|
#
|
289
|
-
#
|
332
|
+
# @example
|
333
|
+
# users.where { age > 18 }
|
334
|
+
# users.where { (id < 10) | (id > 20) }
|
335
|
+
#
|
336
|
+
# @return [Relation]
|
290
337
|
#
|
291
338
|
# @api public
|
292
339
|
def where(*args, &block)
|
@@ -313,17 +360,36 @@ module ROM
|
|
313
360
|
|
314
361
|
# Restrict a relation to match grouping criteria
|
315
362
|
#
|
316
|
-
# @
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
#
|
363
|
+
# @overload having(conditions)
|
364
|
+
# Return a new relation with having clause from conditions hash
|
365
|
+
#
|
366
|
+
# @example
|
367
|
+
# users.
|
368
|
+
# qualified.
|
369
|
+
# left_join(tasks).
|
370
|
+
# select { [id, name, int::count(:tasks__id).as(:task_count)] }.
|
371
|
+
# group(users[:id].qualified).
|
372
|
+
# having(task_count: 2)
|
373
|
+
# first
|
374
|
+
# # {:id => 1, :name => "Jane", :task_count => 2}
|
375
|
+
#
|
376
|
+
# @param [Hash] conditions A hash with conditions
|
377
|
+
#
|
378
|
+
# @overload having(&block)
|
379
|
+
# Return a new relation with having clause created from restriction DSL
|
380
|
+
#
|
381
|
+
# @example
|
382
|
+
# users.
|
383
|
+
# qualified.
|
384
|
+
# left_join(tasks).
|
385
|
+
# select { [id, name, int::count(:tasks__id).as(:task_count)] }.
|
386
|
+
# group(users[:id].qualified).
|
387
|
+
# having { count(id.qualified) >= 1 }.
|
388
|
+
# first
|
389
|
+
# # {:id => 1, :name => "Jane", :task_count => 2}
|
322
390
|
#
|
323
391
|
# @return [Relation]
|
324
392
|
#
|
325
|
-
# @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
|
326
|
-
#
|
327
393
|
# @api public
|
328
394
|
def having(*args, &block)
|
329
395
|
if block
|
@@ -351,11 +417,31 @@ module ROM
|
|
351
417
|
|
352
418
|
# Set order for the relation
|
353
419
|
#
|
354
|
-
# @
|
355
|
-
#
|
356
|
-
# users.order { [name.desc, id.qualified.desc]}
|
420
|
+
# @overload order(*columns)
|
421
|
+
# Return a new relation ordered by provided columns (ASC by default)
|
357
422
|
#
|
358
|
-
#
|
423
|
+
# @example
|
424
|
+
# users.order(:name, :id)
|
425
|
+
#
|
426
|
+
# @param [Array<Symbol>] columns A list with column names
|
427
|
+
#
|
428
|
+
# @overload order(*attributes)
|
429
|
+
# Return a new relation ordered by provided schema attributes
|
430
|
+
#
|
431
|
+
# @example
|
432
|
+
# users.order(self[:name].qualified.desc, self[:id].qualified.desc)
|
433
|
+
#
|
434
|
+
# @param [Array<SQL::Attribute>] attributes A list with schema attributes
|
435
|
+
#
|
436
|
+
# @overload order(&block)
|
437
|
+
# Return a new relation ordered using order DSL
|
438
|
+
#
|
439
|
+
# @example using attribute
|
440
|
+
# users.order { id.desc }
|
441
|
+
# users.order { price.desc(nulls: :first) }
|
442
|
+
#
|
443
|
+
# @example using a function
|
444
|
+
# users.order { nullif(name.qualified, `''`).desc(nulls: :first) }
|
359
445
|
#
|
360
446
|
# @return [Relation]
|
361
447
|
#
|
@@ -382,19 +468,28 @@ module ROM
|
|
382
468
|
|
383
469
|
# Limit a relation to a specific number of tuples
|
384
470
|
#
|
385
|
-
# @
|
386
|
-
#
|
471
|
+
# @overload limit(num)
|
472
|
+
# Return a new relation with the limit set to the provided num
|
473
|
+
#
|
474
|
+
# @example
|
475
|
+
# users.limit(1)
|
476
|
+
#
|
477
|
+
# @param [Integer] num The limit value
|
387
478
|
#
|
388
|
-
#
|
479
|
+
# @overload limit(num, offset)
|
480
|
+
# Return a new relation with the limit set to the provided num
|
389
481
|
#
|
390
|
-
#
|
391
|
-
#
|
482
|
+
# @example
|
483
|
+
# users.limit(10, 2)
|
484
|
+
#
|
485
|
+
# @param [Integer] num The limit value
|
486
|
+
# @param [Integer] offset The offset value
|
392
487
|
#
|
393
488
|
# @return [Relation]
|
394
489
|
#
|
395
490
|
# @api public
|
396
|
-
def limit(*args
|
397
|
-
new(dataset.__send__(__method__, *args
|
491
|
+
def limit(*args)
|
492
|
+
new(dataset.__send__(__method__, *args))
|
398
493
|
end
|
399
494
|
|
400
495
|
# Set offset for the relation
|
@@ -407,17 +502,41 @@ module ROM
|
|
407
502
|
# @return [Relation]
|
408
503
|
#
|
409
504
|
# @api public
|
410
|
-
def offset(
|
411
|
-
new(dataset.__send__(__method__,
|
505
|
+
def offset(num)
|
506
|
+
new(dataset.__send__(__method__, num))
|
412
507
|
end
|
413
508
|
|
414
509
|
# Join with another relation using INNER JOIN
|
415
510
|
#
|
416
|
-
# @
|
417
|
-
#
|
511
|
+
# @overload join(dataset, join_conditions)
|
512
|
+
# Join with another relation using dataset name and join conditions
|
513
|
+
#
|
514
|
+
# @example
|
515
|
+
# users.join(:tasks, id: :user_id)
|
516
|
+
#
|
517
|
+
# @param [Symbol] dataset Join table name
|
518
|
+
# @param [Hash] join_conditions A hash with join conditions
|
519
|
+
#
|
520
|
+
# @overload join(dataset, join_conditions, options)
|
521
|
+
# Join with another relation using dataset name and join conditions
|
522
|
+
# with additional join options
|
523
|
+
#
|
524
|
+
# @example
|
525
|
+
# users.join(:tasks, { id: :user_id }, { table_alias: :tasks_1 })
|
526
|
+
#
|
527
|
+
# @param [Symbol] dataset Join table name
|
528
|
+
# @param [Hash] join_conditions A hash with join conditions
|
529
|
+
# @param [Hash] options Additional join options
|
418
530
|
#
|
419
|
-
# @
|
420
|
-
#
|
531
|
+
# @overload join(relation)
|
532
|
+
# Join with another relation
|
533
|
+
#
|
534
|
+
# Join conditions are automatically set based on schema association
|
535
|
+
#
|
536
|
+
# @example
|
537
|
+
# users.join(tasks)
|
538
|
+
#
|
539
|
+
# @param [Relation] relation A relation for join
|
421
540
|
#
|
422
541
|
# @return [Relation]
|
423
542
|
#
|
@@ -427,13 +546,37 @@ module ROM
|
|
427
546
|
end
|
428
547
|
alias_method :inner_join, :join
|
429
548
|
|
430
|
-
# Join
|
549
|
+
# Join with another relation using LEFT OUTER JOIN
|
431
550
|
#
|
432
|
-
# @
|
433
|
-
#
|
551
|
+
# @overload left_join(dataset, left_join_conditions)
|
552
|
+
# Left_Join with another relation using dataset name and left_join conditions
|
553
|
+
#
|
554
|
+
# @example
|
555
|
+
# users.left_join(:tasks, id: :user_id)
|
556
|
+
#
|
557
|
+
# @param [Symbol] dataset Left_Join table name
|
558
|
+
# @param [Hash] left_join_conditions A hash with left_join conditions
|
434
559
|
#
|
435
|
-
# @
|
436
|
-
#
|
560
|
+
# @overload left_join(dataset, left_join_conditions, options)
|
561
|
+
# Left_Join with another relation using dataset name and left_join conditions
|
562
|
+
# with additional left_join options
|
563
|
+
#
|
564
|
+
# @example
|
565
|
+
# users.left_join(:tasks, { id: :user_id }, { table_alias: :tasks_1 })
|
566
|
+
#
|
567
|
+
# @param [Symbol] dataset Left_Join table name
|
568
|
+
# @param [Hash] left_join_conditions A hash with left_join conditions
|
569
|
+
# @param [Hash] options Additional left_join options
|
570
|
+
#
|
571
|
+
# @overload left_join(relation)
|
572
|
+
# Left_Join with another relation
|
573
|
+
#
|
574
|
+
# Left_Join conditions are automatically set based on schema association
|
575
|
+
#
|
576
|
+
# @example
|
577
|
+
# users.left_join(tasks)
|
578
|
+
#
|
579
|
+
# @param [Relation] relation A relation for left_join
|
437
580
|
#
|
438
581
|
# @return [Relation]
|
439
582
|
#
|
@@ -442,14 +585,37 @@ module ROM
|
|
442
585
|
__join__(__method__, *args, &block)
|
443
586
|
end
|
444
587
|
|
445
|
-
# Join
|
588
|
+
# Join with another relation using RIGHT JOIN
|
446
589
|
#
|
447
|
-
# @
|
448
|
-
#
|
449
|
-
#
|
590
|
+
# @overload right_join(dataset, right_join_conditions)
|
591
|
+
# Right_Join with another relation using dataset name and right_join conditions
|
592
|
+
#
|
593
|
+
# @example
|
594
|
+
# users.right_join(:tasks, id: :user_id)
|
595
|
+
#
|
596
|
+
# @param [Symbol] dataset Right_Join table name
|
597
|
+
# @param [Hash] right_join_conditions A hash with right_join conditions
|
598
|
+
#
|
599
|
+
# @overload right_join(dataset, right_join_conditions, options)
|
600
|
+
# Right_Join with another relation using dataset name and right_join conditions
|
601
|
+
# with additional right_join options
|
602
|
+
#
|
603
|
+
# @example
|
604
|
+
# users.right_join(:tasks, { id: :user_id }, { table_alias: :tasks_1 })
|
605
|
+
#
|
606
|
+
# @param [Symbol] dataset Right_Join table name
|
607
|
+
# @param [Hash] right_join_conditions A hash with right_join conditions
|
608
|
+
# @param [Hash] options Additional right_join options
|
450
609
|
#
|
451
|
-
# @
|
452
|
-
#
|
610
|
+
# @overload right_join(relation)
|
611
|
+
# Right_Join with another relation
|
612
|
+
#
|
613
|
+
# Right_Join conditions are automatically set based on schema association
|
614
|
+
#
|
615
|
+
# @example
|
616
|
+
# users.right_join(tasks)
|
617
|
+
#
|
618
|
+
# @param [Relation] relation A relation for right_join
|
453
619
|
#
|
454
620
|
# @return [Relation]
|
455
621
|
#
|
@@ -460,10 +626,21 @@ module ROM
|
|
460
626
|
|
461
627
|
# Group by specific columns
|
462
628
|
#
|
463
|
-
# @
|
464
|
-
#
|
629
|
+
# @overload group(*columns)
|
630
|
+
# Return a new relation grouped by provided columns
|
465
631
|
#
|
466
|
-
#
|
632
|
+
# @example
|
633
|
+
# tasks.group(:user_id)
|
634
|
+
#
|
635
|
+
# @param [Array<Symbol>] columns A list with column names
|
636
|
+
#
|
637
|
+
# @overload group(*attributes)
|
638
|
+
# Return a new relation grouped by provided schema attributes
|
639
|
+
#
|
640
|
+
# @example
|
641
|
+
# tasks.group(tasks[:id], tasks[:title])
|
642
|
+
#
|
643
|
+
# @param [Array<SQL::Attribute>] columns A list with column names
|
467
644
|
#
|
468
645
|
# @return [Relation]
|
469
646
|
#
|
@@ -557,6 +734,8 @@ module ROM
|
|
557
734
|
|
558
735
|
private
|
559
736
|
|
737
|
+
# Common join method used by other join methods
|
738
|
+
#
|
560
739
|
# @api private
|
561
740
|
def __join__(type, other, join_cond = EMPTY_HASH, opts = EMPTY_HASH, &block)
|
562
741
|
case other
|
@@ -569,6 +748,8 @@ module ROM
|
|
569
748
|
end
|
570
749
|
end
|
571
750
|
|
751
|
+
# Return join key conditions for the provided relation
|
752
|
+
#
|
572
753
|
# @api private
|
573
754
|
def join_keys(other)
|
574
755
|
other.associations[name].join_keys(__registry__)
|