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
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rom/support/cache'
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module SQL
|
5
|
+
# Used as a pair table name + field name.
|
6
|
+
# Similar to Sequel::SQL::QualifiedIdentifier but we don't want
|
7
|
+
# Sequel types to leak into ROM
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class QualifiedAttribute
|
11
|
+
include Dry::Equalizer(:dataset, :attribute)
|
12
|
+
|
13
|
+
extend Cache
|
14
|
+
|
15
|
+
# Dataset (table) name
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
attr_reader :dataset
|
19
|
+
|
20
|
+
# Attribute (field, column) name
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
attr_reader :attribute
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
def self.[](*args)
|
27
|
+
fetch_or_store(args) { new(*args) }
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
def initialize(dataset, attribute)
|
32
|
+
@dataset = dataset
|
33
|
+
@attribute = attribute
|
34
|
+
end
|
35
|
+
|
36
|
+
# Used by Sequel for building SQL statements
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
def sql_literal_append(ds, sql)
|
40
|
+
ds.qualified_identifier_sql_append(sql, dataset, attribute)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Convinient interface for attribute names
|
44
|
+
#
|
45
|
+
# @return [Symbol]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
def to_sym
|
49
|
+
attribute
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/rom/sql/relation.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'rom/sql/header'
|
2
|
+
require 'rom/sql/types'
|
3
|
+
|
4
|
+
require 'rom/sql/schema'
|
2
5
|
|
3
6
|
require 'rom/sql/relation/reading'
|
4
7
|
require 'rom/sql/relation/writing'
|
@@ -9,11 +12,17 @@ require 'rom/plugins/relation/sql/base_view'
|
|
9
12
|
require 'rom/plugins/relation/sql/auto_combine'
|
10
13
|
require 'rom/plugins/relation/sql/auto_wrap'
|
11
14
|
|
15
|
+
require 'rom/support/deprecations'
|
16
|
+
require 'rom/support/constants'
|
17
|
+
|
12
18
|
module ROM
|
13
19
|
module SQL
|
14
20
|
# Sequel-specific relation extensions
|
15
21
|
#
|
22
|
+
# @api public
|
16
23
|
class Relation < ROM::Relation
|
24
|
+
include SQL
|
25
|
+
|
17
26
|
adapter :sql
|
18
27
|
|
19
28
|
use :key_inference
|
@@ -25,14 +34,6 @@ module ROM
|
|
25
34
|
include Writing
|
26
35
|
include Reading
|
27
36
|
|
28
|
-
# @attr_reader [Header] header Internal lazy-initialized header
|
29
|
-
attr_reader :header
|
30
|
-
|
31
|
-
# Name of the table used in FROM clause
|
32
|
-
#
|
33
|
-
# @attr_reader [Symbol] table
|
34
|
-
attr_reader :table
|
35
|
-
|
36
37
|
# Set default dataset for a relation sub-class
|
37
38
|
#
|
38
39
|
# @api private
|
@@ -40,38 +41,74 @@ module ROM
|
|
40
41
|
super
|
41
42
|
|
42
43
|
klass.class_eval do
|
44
|
+
schema_dsl SQL::Schema::DSL
|
45
|
+
schema_inferrer ROM::SQL::Schema::Inferrer
|
46
|
+
|
43
47
|
dataset do
|
44
48
|
table = opts[:from].first
|
45
49
|
|
46
50
|
if db.table_exists?(table)
|
47
|
-
|
48
|
-
|
49
|
-
# TODO: add a way of setting a pk explicitly on a relation
|
50
|
-
pk =
|
51
|
-
if db.respond_to?(:primary_key)
|
52
|
-
Array(db.primary_key(table))
|
53
|
-
else
|
54
|
-
[:id]
|
55
|
-
end.map { |name| :"#{table}__#{name}" }
|
56
|
-
|
57
|
-
select(*columns).order(*pk)
|
51
|
+
pk_header = klass.primary_key_header(db, table)
|
52
|
+
select(*columns).order(*pk_header.qualified)
|
58
53
|
else
|
59
54
|
self
|
60
55
|
end
|
61
56
|
end
|
57
|
+
|
58
|
+
# @!method by_pk(pk)
|
59
|
+
# Return a relation restricted by its primary key
|
60
|
+
# @param [Object] pk The primary key value
|
61
|
+
# @return [SQL::Relation]
|
62
|
+
# @api public
|
63
|
+
view(:by_pk, attributes[:base]) do |pk|
|
64
|
+
where(primary_key => pk)
|
65
|
+
end
|
62
66
|
end
|
63
67
|
end
|
64
68
|
|
69
|
+
# @api private
|
70
|
+
def self.associations
|
71
|
+
schema.associations
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
def self.primary_key_header(db, table)
|
76
|
+
names =
|
77
|
+
if schema
|
78
|
+
schema.primary_key_names
|
79
|
+
elsif db.respond_to?(:primary_key)
|
80
|
+
Array(db.primary_key(table))
|
81
|
+
else
|
82
|
+
[:id]
|
83
|
+
end
|
84
|
+
Header.new(names, table)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Set primary key
|
88
|
+
#
|
89
|
+
# @deprecated
|
90
|
+
#
|
91
|
+
# @api public
|
65
92
|
def self.primary_key(value)
|
93
|
+
Deprecations.announce(
|
94
|
+
:primary_key, "use schema definition to configure primary key"
|
95
|
+
)
|
66
96
|
option :primary_key, reader: true, default: value
|
67
97
|
end
|
68
98
|
|
69
|
-
primary_key :
|
99
|
+
option :primary_key, reader: true, default: -> rel {
|
100
|
+
rel.schema? ? rel.schema.primary_key_name : :id
|
101
|
+
}
|
70
102
|
|
103
|
+
# Return table name from relation's sql statement
|
104
|
+
#
|
105
|
+
# This value is used by `header` for prefixing column names
|
106
|
+
#
|
107
|
+
# @return [Symbol]
|
108
|
+
#
|
71
109
|
# @api private
|
72
|
-
def
|
73
|
-
|
74
|
-
@table = dataset.opts[:from].first
|
110
|
+
def table
|
111
|
+
@table ||= dataset.opts[:from].first
|
75
112
|
end
|
76
113
|
|
77
114
|
# Return a header for this relation
|
@@ -80,7 +117,7 @@ module ROM
|
|
80
117
|
#
|
81
118
|
# @api private
|
82
119
|
def header
|
83
|
-
@header ||= Header.new(
|
120
|
+
@header ||= Header.new(selected_columns, table)
|
84
121
|
end
|
85
122
|
|
86
123
|
# Return raw column names
|
@@ -89,7 +126,21 @@ module ROM
|
|
89
126
|
#
|
90
127
|
# @api private
|
91
128
|
def columns
|
92
|
-
dataset.columns
|
129
|
+
@columns ||= dataset.columns
|
130
|
+
end
|
131
|
+
|
132
|
+
protected
|
133
|
+
|
134
|
+
# Return a list of columns from *the sql select* statement or default to
|
135
|
+
# dataset columns
|
136
|
+
#
|
137
|
+
# This is used to construct relation's header
|
138
|
+
#
|
139
|
+
# @return [Array<Symbol>]
|
140
|
+
#
|
141
|
+
# @api private
|
142
|
+
def selected_columns
|
143
|
+
@selected_columns ||= dataset.opts.fetch(:select, columns)
|
93
144
|
end
|
94
145
|
end
|
95
146
|
end
|
@@ -1,11 +1,30 @@
|
|
1
1
|
module ROM
|
2
2
|
module SQL
|
3
3
|
class Relation < ROM::Relation
|
4
|
+
# Query API for SQL::Relation
|
5
|
+
#
|
6
|
+
# @api public
|
4
7
|
module Reading
|
8
|
+
# Fetch a tuple identified by the pk
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# users.fetch(1)
|
12
|
+
# # {:id => 1, name: "Jane"}
|
13
|
+
#
|
14
|
+
# @return [Relation]
|
15
|
+
#
|
16
|
+
# @raise [ROM::TupleCountMismatchError] When 0 or more than 1 tuples were found
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def fetch(pk)
|
20
|
+
by_pk(pk).one!
|
21
|
+
end
|
22
|
+
|
5
23
|
# Return relation count
|
6
24
|
#
|
7
25
|
# @example
|
8
|
-
# users.count
|
26
|
+
# users.count
|
27
|
+
# # => 12
|
9
28
|
#
|
10
29
|
# @return [Relation]
|
11
30
|
#
|
@@ -18,8 +37,9 @@ module ROM
|
|
18
37
|
#
|
19
38
|
# @example
|
20
39
|
# users.first
|
40
|
+
# # {:id => 1, :name => "Jane"}
|
21
41
|
#
|
22
|
-
# @return [
|
42
|
+
# @return [Hash]
|
23
43
|
#
|
24
44
|
# @api public
|
25
45
|
def first
|
@@ -30,8 +50,9 @@ module ROM
|
|
30
50
|
#
|
31
51
|
# @example
|
32
52
|
# users.last
|
53
|
+
# # {:id => 2, :name => "Joe"}
|
33
54
|
#
|
34
|
-
# @return [
|
55
|
+
# @return [Hash]
|
35
56
|
#
|
36
57
|
# @api public
|
37
58
|
def last
|
@@ -43,7 +64,8 @@ module ROM
|
|
43
64
|
# This method is intended to be used internally within a relation object
|
44
65
|
#
|
45
66
|
# @example
|
46
|
-
#
|
67
|
+
# users.prefix(:user).to_a
|
68
|
+
# # {:user_id => 1, :user_name => "Jane"}
|
47
69
|
#
|
48
70
|
# @param [Symbol] name The prefix
|
49
71
|
#
|
@@ -59,7 +81,7 @@ module ROM
|
|
59
81
|
# This method is intended to be used internally within a relation object
|
60
82
|
#
|
61
83
|
# @example
|
62
|
-
#
|
84
|
+
# users.qualified
|
63
85
|
#
|
64
86
|
# @return [Relation]
|
65
87
|
#
|
@@ -72,39 +94,49 @@ module ROM
|
|
72
94
|
#
|
73
95
|
# This method is intended to be used internally within a relation object
|
74
96
|
#
|
75
|
-
# @
|
97
|
+
# @example
|
98
|
+
# users.qualified_columns
|
99
|
+
# # [:users__id, :users__name]
|
100
|
+
#
|
101
|
+
# @return [Array<Symbol>]
|
76
102
|
#
|
77
103
|
# @api public
|
78
104
|
def qualified_columns
|
79
105
|
header.qualified.to_a
|
80
106
|
end
|
81
107
|
|
82
|
-
#
|
108
|
+
# Map tuples from the relation
|
83
109
|
#
|
84
110
|
# @example
|
85
|
-
# users.
|
111
|
+
# users.map { |user| user[:id] }
|
112
|
+
# # [1, 2, 3]
|
86
113
|
#
|
87
|
-
# users.
|
114
|
+
# users.map(:id).to_a
|
115
|
+
# # [1, 2, 3]
|
88
116
|
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# @param [Hash] criteria hash for the where clause
|
92
|
-
#
|
93
|
-
# @return [Relation]
|
117
|
+
# @param [Symbol] key An optional name of the key for extracting values
|
118
|
+
# from tuples
|
94
119
|
#
|
95
120
|
# @api public
|
96
|
-
def
|
97
|
-
|
121
|
+
def map(key = nil, &block)
|
122
|
+
if key
|
123
|
+
dataset.map(key, &block)
|
124
|
+
else
|
125
|
+
dataset.map(&block)
|
126
|
+
end
|
98
127
|
end
|
99
128
|
|
100
|
-
#
|
129
|
+
# Pluck values from a specific column
|
101
130
|
#
|
102
131
|
# @example
|
103
|
-
# users.
|
132
|
+
# users.pluck(:id)
|
133
|
+
# # [1, 2, 3]
|
134
|
+
#
|
135
|
+
# @return [Array]
|
104
136
|
#
|
105
137
|
# @api public
|
106
|
-
def
|
107
|
-
|
138
|
+
def pluck(name)
|
139
|
+
map(name)
|
108
140
|
end
|
109
141
|
|
110
142
|
# Project a relation
|
@@ -112,9 +144,9 @@ module ROM
|
|
112
144
|
# This method is intended to be used internally within a relation object
|
113
145
|
#
|
114
146
|
# @example
|
115
|
-
#
|
147
|
+
# users.project(:id, :name) }
|
116
148
|
#
|
117
|
-
# @param [Symbol] names A list of symbol column names
|
149
|
+
# @param [Symbol] *names A list of symbol column names
|
118
150
|
#
|
119
151
|
# @return [Relation]
|
120
152
|
#
|
@@ -128,9 +160,10 @@ module ROM
|
|
128
160
|
# This method is intended to be used internally within a relation object
|
129
161
|
#
|
130
162
|
# @example
|
131
|
-
#
|
163
|
+
# users.rename(name: :user_name).first
|
164
|
+
# # {:id => 1, :user_name => "Jane" }
|
132
165
|
#
|
133
|
-
# @param [Hash] options A name => new_name map
|
166
|
+
# @param [Hash<Symbol=>Symbol>] options A name => new_name map
|
134
167
|
#
|
135
168
|
# @return [Relation]
|
136
169
|
#
|
@@ -142,7 +175,8 @@ module ROM
|
|
142
175
|
# Select specific columns for select clause
|
143
176
|
#
|
144
177
|
# @example
|
145
|
-
# users.select(:id, :name)
|
178
|
+
# users.select(:id, :name).first
|
179
|
+
# # {:id => 1, :name => "Jane" }
|
146
180
|
#
|
147
181
|
# @return [Relation]
|
148
182
|
#
|
@@ -155,6 +189,9 @@ module ROM
|
|
155
189
|
#
|
156
190
|
# @example
|
157
191
|
# users.select(:id, :name).select_append(:email)
|
192
|
+
# # {:id => 1, :name => "Jane", :email => "jane@doe.org"}
|
193
|
+
#
|
194
|
+
# @param [Array<Symbol>] *args A list with column names
|
158
195
|
#
|
159
196
|
# @return [Relation]
|
160
197
|
#
|
@@ -168,6 +205,8 @@ module ROM
|
|
168
205
|
# @example
|
169
206
|
# users.distinct(:country)
|
170
207
|
#
|
208
|
+
# @param [Array<Symbol>] *args A list with column names
|
209
|
+
#
|
171
210
|
# @return [Relation]
|
172
211
|
#
|
173
212
|
# @api public
|
@@ -180,7 +219,9 @@ module ROM
|
|
180
219
|
# @example
|
181
220
|
# users.sum(:age)
|
182
221
|
#
|
183
|
-
# @
|
222
|
+
# @param [Array<Symbol>] *args A list with column names
|
223
|
+
#
|
224
|
+
# @return [Integer]
|
184
225
|
#
|
185
226
|
# @api public
|
186
227
|
def sum(*args)
|
@@ -192,6 +233,8 @@ module ROM
|
|
192
233
|
# @example
|
193
234
|
# users.min(:age)
|
194
235
|
#
|
236
|
+
# @param [Array<Symbol>] *args A list with column names
|
237
|
+
#
|
195
238
|
# @return Number
|
196
239
|
#
|
197
240
|
# @api public
|
@@ -204,6 +247,8 @@ module ROM
|
|
204
247
|
# @example
|
205
248
|
# users.max(:age)
|
206
249
|
#
|
250
|
+
# @param [Array<Symbol>] *args A list with column names
|
251
|
+
#
|
207
252
|
# @return Number
|
208
253
|
#
|
209
254
|
# @api public
|
@@ -216,6 +261,8 @@ module ROM
|
|
216
261
|
# @example
|
217
262
|
# users.avg(:age)
|
218
263
|
#
|
264
|
+
# @param [Array<Symbol>] *args A list with column names
|
265
|
+
#
|
219
266
|
# @return Number
|
220
267
|
#
|
221
268
|
# @api public
|
@@ -225,11 +272,20 @@ module ROM
|
|
225
272
|
|
226
273
|
# Restrict a relation to match criteria
|
227
274
|
#
|
275
|
+
# If block is passed it'll be executed in the context of a condition
|
276
|
+
# builder object.
|
277
|
+
#
|
228
278
|
# @example
|
229
279
|
# users.where(name: 'Jane')
|
230
280
|
#
|
281
|
+
# users.where { age >= 18 }
|
282
|
+
#
|
283
|
+
# @param [Hash] *args An optional hash with conditions for WHERE clause
|
284
|
+
#
|
231
285
|
# @return [Relation]
|
232
286
|
#
|
287
|
+
# @see http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
|
288
|
+
#
|
233
289
|
# @api public
|
234
290
|
def where(*args, &block)
|
235
291
|
__new__(dataset.__send__(__method__, *args, &block))
|
@@ -240,6 +296,8 @@ module ROM
|
|
240
296
|
# @example
|
241
297
|
# users.exclude(name: 'Jane')
|
242
298
|
#
|
299
|
+
# @param [Hash] *args A hash with conditions for exclusion
|
300
|
+
#
|
243
301
|
# @return [Relation]
|
244
302
|
#
|
245
303
|
# @api public
|
@@ -247,7 +305,8 @@ module ROM
|
|
247
305
|
__new__(dataset.__send__(__method__, *args, &block))
|
248
306
|
end
|
249
307
|
|
250
|
-
# Inverts a
|
308
|
+
# Inverts the current WHERE and HAVING clauses. If there is neither a
|
309
|
+
# WHERE or HAVING clause, adds a WHERE clause that is always false.
|
251
310
|
#
|
252
311
|
# @example
|
253
312
|
# users.exclude(name: 'Jane').invert
|
@@ -258,8 +317,8 @@ module ROM
|
|
258
317
|
# @return [Relation]
|
259
318
|
#
|
260
319
|
# @api public
|
261
|
-
def invert
|
262
|
-
__new__(dataset.
|
320
|
+
def invert
|
321
|
+
__new__(dataset.invert)
|
263
322
|
end
|
264
323
|
|
265
324
|
# Set order for the relation
|
@@ -267,6 +326,8 @@ module ROM
|
|
267
326
|
# @example
|
268
327
|
# users.order(:name)
|
269
328
|
#
|
329
|
+
# @param [Array<Symbol>] *args A list with column names
|
330
|
+
#
|
270
331
|
# @return [Relation]
|
271
332
|
#
|
272
333
|
# @api public
|
@@ -291,6 +352,11 @@ module ROM
|
|
291
352
|
# @example
|
292
353
|
# users.limit(1)
|
293
354
|
#
|
355
|
+
# users.limit(10, 2)
|
356
|
+
#
|
357
|
+
# @param [Integer] limit The limit value
|
358
|
+
# @param [Integer] offset An optional offset
|
359
|
+
#
|
294
360
|
# @return [Relation]
|
295
361
|
#
|
296
362
|
# @api public
|
@@ -303,6 +369,8 @@ module ROM
|
|
303
369
|
# @example
|
304
370
|
# users.limit(10).offset(2)
|
305
371
|
#
|
372
|
+
# @param [Integer] num The offset value
|
373
|
+
#
|
306
374
|
# @return [Relation]
|
307
375
|
#
|
308
376
|
# @api public
|
@@ -310,7 +378,10 @@ module ROM
|
|
310
378
|
__new__(dataset.__send__(__method__, *args, &block))
|
311
379
|
end
|
312
380
|
|
313
|
-
# Join
|
381
|
+
# Join with another relation using INNER JOIN
|
382
|
+
#
|
383
|
+
# @example
|
384
|
+
# users.inner_join(:tasks, id: :user_id)
|
314
385
|
#
|
315
386
|
# @param [Symbol] relation name
|
316
387
|
# @param [Hash] join keys
|
@@ -322,7 +393,10 @@ module ROM
|
|
322
393
|
__new__(dataset.__send__(__method__, *args, &block))
|
323
394
|
end
|
324
395
|
|
325
|
-
# Join other relation using
|
396
|
+
# Join other relation using LEFT OUTER JOIN
|
397
|
+
#
|
398
|
+
# @example
|
399
|
+
# users.left_join(:tasks, id: :user_id)
|
326
400
|
#
|
327
401
|
# @param [Symbol] relation name
|
328
402
|
# @param [Hash] join keys
|
@@ -339,6 +413,8 @@ module ROM
|
|
339
413
|
# @example
|
340
414
|
# tasks.group(:user_id)
|
341
415
|
#
|
416
|
+
# @param [Array<Symbol>] *args A list of column names
|
417
|
+
#
|
342
418
|
# @return [Relation]
|
343
419
|
#
|
344
420
|
# @api public
|
@@ -352,6 +428,8 @@ module ROM
|
|
352
428
|
# tasks.group_and_count(:user_id)
|
353
429
|
# # => [{ user_id: 1, count: 2 }, { user_id: 2, count: 3 }]
|
354
430
|
#
|
431
|
+
# @param [Array<Symbol>] *args A list of column names
|
432
|
+
#
|
355
433
|
# @return [Relation]
|
356
434
|
#
|
357
435
|
# @api public
|
@@ -365,6 +443,8 @@ module ROM
|
|
365
443
|
# tasks.select_group(:user_id)
|
366
444
|
# # => [{ user_id: 1 }, { user_id: 2 }]
|
367
445
|
#
|
446
|
+
# @param [Array<Symbol>] *args A list of column names
|
447
|
+
#
|
368
448
|
# @return [Relation]
|
369
449
|
#
|
370
450
|
# @api public
|
@@ -374,17 +454,40 @@ module ROM
|
|
374
454
|
|
375
455
|
# Adds a UNION clause for relation dataset using second relation dataset
|
376
456
|
#
|
377
|
-
# @param [Relation] relation object
|
378
|
-
#
|
379
457
|
# @example
|
380
458
|
# users.where(id: 1).union(users.where(id: 2))
|
381
459
|
# # => [{ id: 1, name: 'Piotr' }, { id: 2, name: 'Jane' }]
|
382
460
|
#
|
461
|
+
# @param [Relation] relation Another relation
|
462
|
+
#
|
463
|
+
# @param [Hash] options Options for union
|
464
|
+
# @option options [Symbol] :alias Use the given value as the #from_self alias
|
465
|
+
# @option options [TrueClass, FalseClass] :all Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
|
466
|
+
# @option options [TrueClass, FalseClass] :from_self Set to false to not wrap the returned dataset in a #from_self, use with care.
|
467
|
+
#
|
383
468
|
# @return [Relation]
|
384
469
|
#
|
385
470
|
# @api public
|
386
|
-
def union(relation,
|
387
|
-
__new__(dataset.__send__(__method__, relation.dataset,
|
471
|
+
def union(relation, options = EMPTY_HASH, &block)
|
472
|
+
__new__(dataset.__send__(__method__, relation.dataset, options, &block))
|
473
|
+
end
|
474
|
+
|
475
|
+
# Return if a restricted relation has 0 tuples
|
476
|
+
#
|
477
|
+
# @example
|
478
|
+
# users.unique?(email: 'jane@doe.org') # true
|
479
|
+
#
|
480
|
+
# users.insert(email: 'jane@doe.org')
|
481
|
+
#
|
482
|
+
# users.unique?(email: 'jane@doe.org') # false
|
483
|
+
#
|
484
|
+
# @param [Hash] criteria The condition hash for WHERE clause
|
485
|
+
#
|
486
|
+
# @return [TrueClass, FalseClass]
|
487
|
+
#
|
488
|
+
# @api public
|
489
|
+
def unique?(criteria)
|
490
|
+
where(criteria).count.zero?
|
388
491
|
end
|
389
492
|
end
|
390
493
|
end
|