rom-sql 1.0.0.beta3 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -5
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +4 -1
  5. data/lib/rom/plugins/relation/sql/auto_combine.rb +4 -0
  6. data/lib/rom/plugins/relation/sql/auto_wrap.rb +4 -0
  7. data/lib/rom/sql/{type.rb → attribute.rb} +8 -5
  8. data/lib/rom/sql/commands/update.rb +10 -0
  9. data/lib/rom/sql/dsl.rb +1 -0
  10. data/lib/rom/sql/extensions/postgres/inferrer.rb +2 -1
  11. data/lib/rom/sql/extensions/sqlite.rb +1 -0
  12. data/lib/rom/sql/extensions/sqlite/inferrer.rb +9 -0
  13. data/lib/rom/sql/extensions/sqlite/types.rb +11 -0
  14. data/lib/rom/sql/function.rb +4 -1
  15. data/lib/rom/sql/gateway.rb +64 -28
  16. data/lib/rom/sql/migration.rb +21 -29
  17. data/lib/rom/sql/migration/migrator.rb +8 -0
  18. data/lib/rom/sql/order_dsl.rb +1 -0
  19. data/lib/rom/sql/plugin/associates.rb +21 -0
  20. data/lib/rom/sql/plugin/timestamps.rb +131 -0
  21. data/lib/rom/sql/plugins.rb +3 -0
  22. data/lib/rom/sql/projection_dsl.rb +1 -0
  23. data/lib/rom/sql/relation/reading.rb +256 -75
  24. data/lib/rom/sql/restriction_dsl.rb +1 -0
  25. data/lib/rom/sql/schema/associations_dsl.rb +119 -1
  26. data/lib/rom/sql/schema/dsl.rb +44 -2
  27. data/lib/rom/sql/version.rb +1 -1
  28. data/rom-sql.gemspec +2 -2
  29. data/spec/extensions/sqlite/types_spec.rb +11 -0
  30. data/spec/integration/plugins/associates_spec.rb +79 -0
  31. data/spec/integration/schema/inferrer/postgres_spec.rb +3 -1
  32. data/spec/integration/schema/inferrer/sqlite_spec.rb +2 -0
  33. data/spec/shared/database_setup.rb +10 -1
  34. data/spec/spec_helper.rb +1 -1
  35. data/spec/support/helpers.rb +2 -2
  36. data/spec/unit/plugin/timestamp_spec.rb +77 -0
  37. data/spec/unit/types_spec.rb +1 -1
  38. 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
@@ -2,6 +2,7 @@ require 'rom/sql/dsl'
2
2
 
3
3
  module ROM
4
4
  module SQL
5
+ # @api private
5
6
  class OrderDSL < DSL
6
7
  private
7
8
 
@@ -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
@@ -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
@@ -3,6 +3,7 @@ require 'rom/sql/function'
3
3
 
4
4
  module ROM
5
5
  module SQL
6
+ # @api private
6
7
  class ProjectionDSL < DSL
7
8
  # @api private
8
9
  def respond_to_missing?(name, include_private = false)
@@ -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
- # @example
180
- # users.select(:id, :name).first
181
- # # {:id => 1, :name => "Jane" }
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
- # @example
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
- # @example
208
- # users.distinct(:country)
233
+ # @overload distinct(*columns)
234
+ # Create a distinct statement from column names
209
235
  #
210
- # @param [Array<Symbol>] *args A list with column names
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
- # If block is passed it'll be executed in the context of a condition
278
- # builder object.
313
+ # @overload where(conditions)
314
+ # Restrict a relation using a hash with conditions
279
315
  #
280
- # @example
281
- # users.where(name: 'Jane')
316
+ # @example
317
+ # users.where(name: 'Jane', age: 30)
282
318
  #
283
- # users.where { age >= 18 }
319
+ # @param [Hash] conditions A hash with conditions
284
320
  #
285
- # @param [Hash] *args An optional hash with conditions for WHERE clause
321
+ # @overload where(conditions, &block)
322
+ # Restrict a relation using a hash with conditions and restriction DSL
286
323
  #
287
- # @return [Relation]
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
- # @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
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
- # @example
317
- # users.with_task_count.having( task_count: 2 )
318
- #
319
- # users.with_task_count.having { task_count > 3 }
320
- #
321
- # @param [Hash] *args An optional hash with conditions for HAVING clause
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
- # @example
355
- # users.order(:name)
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
- # @param [Array<Symbol>] *args A list with column names
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
- # @example
386
- # users.limit(1)
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
- # users.limit(10, 2)
479
+ # @overload limit(num, offset)
480
+ # Return a new relation with the limit set to the provided num
389
481
  #
390
- # @param [Integer] limit The limit value
391
- # @param [Integer] offset An optional offset
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, &block)
397
- new(dataset.__send__(__method__, *args, &block))
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(*args, &block)
411
- new(dataset.__send__(__method__, *args, &block))
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
- # @example
417
- # users.inner_join(:tasks, id: :user_id)
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
- # @param [Symbol] relation name
420
- # @param [Hash] join keys
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 other relation using LEFT OUTER JOIN
549
+ # Join with another relation using LEFT OUTER JOIN
431
550
  #
432
- # @example
433
- # users.left_join(:tasks, id: :user_id)
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
- # @param [Symbol] relation name
436
- # @param [Hash] join keys
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 other relation using RIGHT JOIN
588
+ # Join with another relation using RIGHT JOIN
446
589
  #
447
- # @example
448
- # users.right_join(:tasks, id: :user_id)
449
- # users.right_join(tasks)
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
- # @param [Symbol] relation name
452
- # @param [Hash] join keys
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
- # @example
464
- # tasks.group(:user_id)
629
+ # @overload group(*columns)
630
+ # Return a new relation grouped by provided columns
465
631
  #
466
- # @param [Array<Symbol>] *args A list of column names
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__)