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.
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__)