torque-postgresql 3.4.1 → 4.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/lib/torque/postgresql/adapter/database_statements.rb +63 -84
- data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
- data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
- data/lib/torque/postgresql/adapter/oid.rb +1 -23
- data/lib/torque/postgresql/adapter/quoting.rb +13 -7
- data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
- data/lib/torque/postgresql/adapter/schema_definitions.rb +36 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +90 -34
- data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
- data/lib/torque/postgresql/adapter/schema_statements.rb +64 -49
- data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
- data/lib/torque/postgresql/arel/nodes.rb +2 -2
- data/lib/torque/postgresql/arel/operations.rb +7 -1
- data/lib/torque/postgresql/arel/visitors.rb +3 -9
- data/lib/torque/postgresql/associations/association_scope.rb +23 -31
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
- data/lib/torque/postgresql/attributes/builder/full_text_search.rb +121 -0
- data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
- data/lib/torque/postgresql/attributes/builder.rb +49 -11
- data/lib/torque/postgresql/attributes/enum.rb +7 -7
- data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
- data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
- data/lib/torque/postgresql/attributes/period.rb +2 -2
- data/lib/torque/postgresql/attributes.rb +0 -4
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
- data/lib/torque/postgresql/base.rb +3 -10
- data/lib/torque/postgresql/collector.rb +1 -1
- data/lib/torque/postgresql/config.rb +95 -5
- data/lib/torque/postgresql/function.rb +61 -0
- data/lib/torque/postgresql/inheritance.rb +52 -36
- data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
- data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
- data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
- data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
- data/lib/torque/postgresql/predicate_builder.rb +35 -0
- data/lib/torque/postgresql/railtie.rb +112 -30
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
- data/lib/torque/postgresql/relation/inheritance.rb +4 -7
- data/lib/torque/postgresql/relation.rb +6 -10
- data/lib/torque/postgresql/schema_cache.rb +6 -12
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +2 -1
- data/spec/initialize.rb +58 -0
- data/spec/mocks/cache_query.rb +21 -21
- data/spec/mocks/create_table.rb +6 -26
- data/spec/schema.rb +19 -12
- data/spec/spec_helper.rb +5 -1
- data/spec/tests/arel_spec.rb +32 -7
- data/spec/tests/auxiliary_statement_spec.rb +3 -3
- data/spec/tests/belongs_to_many_spec.rb +72 -5
- data/spec/tests/enum_set_spec.rb +12 -11
- data/spec/tests/enum_spec.rb +4 -2
- data/spec/tests/full_text_seach_test.rb +252 -0
- data/spec/tests/function_spec.rb +42 -0
- data/spec/tests/has_many_spec.rb +21 -8
- data/spec/tests/interval_spec.rb +1 -7
- data/spec/tests/period_spec.rb +61 -61
- data/spec/tests/predicate_builder_spec.rb +132 -0
- data/spec/tests/schema_spec.rb +2 -8
- data/spec/tests/table_inheritance_spec.rb +25 -26
- metadata +34 -39
@@ -4,13 +4,11 @@ module Torque
|
|
4
4
|
module PostgreSQL
|
5
5
|
module Attributes
|
6
6
|
module Builder
|
7
|
-
# TODO: Allow documenting by building the methods outside and importing
|
8
|
-
# only the raw string
|
9
7
|
class Period
|
10
8
|
DIRECT_ACCESS_REGEX = /_?%s_?/
|
11
9
|
SUPPORTED_TYPES = %i[daterange tsrange tstzrange].freeze
|
12
10
|
CURRENT_GETTERS = {
|
13
|
-
daterange: 'Date.
|
11
|
+
daterange: 'Date.current',
|
14
12
|
tsrange: 'Time.zone.now',
|
15
13
|
tstzrange: 'Time.zone.now',
|
16
14
|
}.freeze
|
@@ -21,6 +19,8 @@ module Torque
|
|
21
19
|
tstzrange: :timestamp,
|
22
20
|
}.freeze
|
23
21
|
|
22
|
+
FN = '::Torque::PostgreSQL::FN'
|
23
|
+
|
24
24
|
attr_accessor :klass, :attribute, :options, :type, :default, :current_getter,
|
25
25
|
:type_caster, :threshold, :dynamic_threshold, :klass_module, :instance_module
|
26
26
|
|
@@ -208,11 +208,11 @@ module Torque
|
|
208
208
|
end
|
209
209
|
|
210
210
|
def arel_default_sql
|
211
|
-
@arel_default_sql ||=
|
211
|
+
@arel_default_sql ||= arel_sql_bind(@default.inspect)
|
212
212
|
end
|
213
213
|
|
214
|
-
def
|
215
|
-
"
|
214
|
+
def arel_sql_bind(value)
|
215
|
+
"#{FN}.bind_with(#{arel_attribute}, #{value})"
|
216
216
|
end
|
217
217
|
|
218
218
|
# Check how to provide the threshold value
|
@@ -223,12 +223,12 @@ module Torque
|
|
223
223
|
"arel_table['#{threshold}']"
|
224
224
|
when ActiveSupport::Duration
|
225
225
|
value = "'#{threshold.to_i} seconds'"
|
226
|
-
"::Arel.sql(\"#{value}\").
|
226
|
+
"::Arel.sql(\"#{value}\").pg_cast(:interval)"
|
227
227
|
when Numeric
|
228
228
|
value = threshold.to_i.to_s
|
229
229
|
value << type_caster.eql?(:date) ? ' days' : ' seconds'
|
230
230
|
value = "'#{value}'"
|
231
|
-
"::Arel.sql(\"#{value}\").
|
231
|
+
"::Arel.sql(\"#{value}\").pg_cast(:interval)"
|
232
232
|
end
|
233
233
|
end
|
234
234
|
end
|
@@ -248,7 +248,7 @@ module Torque
|
|
248
248
|
return arel_start_at unless threshold.present?
|
249
249
|
@arel_real_start_at ||= begin
|
250
250
|
result = +"(#{arel_start_at} - #{arel_threshold_value})"
|
251
|
-
result << '.
|
251
|
+
result << '.pg_cast(:date)' if type.eql?(:daterange)
|
252
252
|
result
|
253
253
|
end
|
254
254
|
end
|
@@ -258,7 +258,7 @@ module Torque
|
|
258
258
|
return arel_finish_at unless threshold.present?
|
259
259
|
@arel_real_finish_at ||= begin
|
260
260
|
result = +"(#{arel_finish_at} + #{arel_threshold_value})"
|
261
|
-
result << '.
|
261
|
+
result << '.pg_cast(:date)' if type.eql?(:daterange)
|
262
262
|
result
|
263
263
|
end
|
264
264
|
end
|
@@ -278,9 +278,9 @@ module Torque
|
|
278
278
|
|
279
279
|
# Create an arel named function
|
280
280
|
def arel_named_function(name, *args)
|
281
|
-
result = +"
|
282
|
-
result << '
|
283
|
-
result
|
281
|
+
result = +"#{FN}.#{name}"
|
282
|
+
result << '(' << args.join(', ') << ')' if args.present?
|
283
|
+
result
|
284
284
|
end
|
285
285
|
|
286
286
|
# Create an arel version of +nullif+ function
|
@@ -302,24 +302,24 @@ module Torque
|
|
302
302
|
def arel_daterange(real = false)
|
303
303
|
arel_named_function(
|
304
304
|
'daterange',
|
305
|
-
(real ? arel_real_start_at : arel_start_at) + '.
|
306
|
-
(real ? arel_real_finish_at : arel_finish_at) + '.
|
305
|
+
(real ? arel_real_start_at : arel_start_at) + '.pg_cast(:date)',
|
306
|
+
(real ? arel_real_finish_at : arel_finish_at) + '.pg_cast(:date)',
|
307
307
|
'::Arel.sql("\'[]\'")',
|
308
308
|
)
|
309
309
|
end
|
310
310
|
|
311
311
|
def arel_check_condition(type)
|
312
312
|
checker = arel_nullif(arel_real_attribute, arel_empty_value)
|
313
|
-
checker << ".#{type}(value.
|
313
|
+
checker << ".#{type}(value.pg_cast(#{type_caster.inspect}))"
|
314
314
|
arel_coalesce(checker, arel_default_sql)
|
315
315
|
end
|
316
316
|
|
317
317
|
def arel_formatting_value(condition = nil, value = 'value', cast: nil)
|
318
318
|
[
|
319
319
|
"#{value} = arel_table[#{value}] if #{value}.is_a?(Symbol)",
|
320
|
-
"unless #{value}.respond_to?(:
|
321
|
-
" #{value} =
|
322
|
-
(" #{value} = #{value}.
|
320
|
+
"unless #{value}.respond_to?(:pg_cast)",
|
321
|
+
" #{value} = #{FN}.bind_with(#{arel_attribute}, #{value})",
|
322
|
+
(" #{value} = #{value}.pg_cast(#{cast.inspect})" if cast),
|
323
323
|
'end',
|
324
324
|
condition,
|
325
325
|
].compact.join("\n")
|
@@ -347,14 +347,14 @@ module Torque
|
|
347
347
|
|
348
348
|
def klass_current
|
349
349
|
[
|
350
|
-
"value = #{
|
350
|
+
"value = #{arel_sql_bind(current_getter)}",
|
351
351
|
"where(#{arel_check_condition(:contains)})",
|
352
352
|
].join("\n")
|
353
353
|
end
|
354
354
|
|
355
355
|
def klass_not_current
|
356
356
|
[
|
357
|
-
"value = #{
|
357
|
+
"value = #{arel_sql_bind(current_getter)}",
|
358
358
|
"where.not(#{arel_check_condition(:contains)})",
|
359
359
|
].join("\n")
|
360
360
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'builder/enum'
|
4
4
|
require_relative 'builder/period'
|
5
|
+
require_relative 'builder/full_text_search'
|
5
6
|
|
6
7
|
module Torque
|
7
8
|
module PostgreSQL
|
@@ -12,20 +13,57 @@ module Torque
|
|
12
13
|
return unless table_exists?
|
13
14
|
|
14
15
|
args.each do |attribute|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# Not able to build the attribute, maybe pending migrations
|
25
|
-
end
|
16
|
+
# Generate methods on self class
|
17
|
+
builder = builder_klass.new(self, attribute, extra.merge(options))
|
18
|
+
builder.conflicting?
|
19
|
+
builder.build
|
20
|
+
|
21
|
+
# Additional settings for the builder
|
22
|
+
instance_exec(builder, &block) if block.present?
|
23
|
+
rescue Interrupt
|
24
|
+
# Not able to build the attribute, maybe pending migrations
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
28
|
+
|
29
|
+
def self.search_vector_options(columns:, language: nil, stored: true, **options)
|
30
|
+
weights = to_search_weights(columns)
|
31
|
+
operation = to_search_vector_operation(language, weights).to_sql
|
32
|
+
|
33
|
+
options[:index] = {
|
34
|
+
using: PostgreSQL.config.full_text_search.default_index_type,
|
35
|
+
} if options[:index] == true
|
36
|
+
|
37
|
+
options.merge(type: :tsvector, as: operation, stored: stored)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.to_search_weights(columns)
|
41
|
+
if !columns.is_a?(Hash)
|
42
|
+
extras = columns.size > 3 ? columns.size - 3 : 0
|
43
|
+
weights = %w[A B C] + (['D'] * extras)
|
44
|
+
columns = Array.wrap(columns).zip(weights).to_h
|
45
|
+
end
|
46
|
+
|
47
|
+
columns.transform_keys(&:to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.to_search_vector_operation(language, weights)
|
51
|
+
language ||= PostgreSQL.config.full_text_search.default_language
|
52
|
+
language = ::Arel.sql(language.is_a?(Symbol) ? language.to_s : "'#{language}'")
|
53
|
+
simple = weights.size == 1
|
54
|
+
|
55
|
+
empty_string = ::Arel.sql("''")
|
56
|
+
operations = weights.map do |column, weight|
|
57
|
+
column = ::Arel.sql(column.to_s)
|
58
|
+
weight = ::Arel.sql("'#{weight}'")
|
59
|
+
|
60
|
+
op = FN.to_tsvector(language, FN.coalesce(column, empty_string))
|
61
|
+
op = FN.setweight(op, weight) unless simple
|
62
|
+
op
|
63
|
+
end
|
64
|
+
|
65
|
+
FN.concat(*operations)
|
66
|
+
end
|
29
67
|
end
|
30
68
|
end
|
31
69
|
end
|
@@ -18,7 +18,7 @@ module Torque
|
|
18
18
|
# Find or create the class that will handle the value
|
19
19
|
def lookup(name)
|
20
20
|
const = name.to_s.camelize
|
21
|
-
namespace =
|
21
|
+
namespace = PostgreSQL.config.enum.namespace
|
22
22
|
|
23
23
|
return namespace.const_get(const) if namespace.const_defined?(const)
|
24
24
|
namespace.const_set(const, Class.new(Enum))
|
@@ -27,7 +27,7 @@ module Torque
|
|
27
27
|
# Provide a method on the given class to setup which enums will be
|
28
28
|
# manually initialized
|
29
29
|
def include_on(klass, method_name = nil)
|
30
|
-
method_name ||=
|
30
|
+
method_name ||= PostgreSQL.config.enum.base_method
|
31
31
|
Builder.include_on(klass, method_name, Builder::Enum) do |builder|
|
32
32
|
defined_enums[builder.attribute.to_s] = builder.subtype.klass
|
33
33
|
end
|
@@ -46,7 +46,7 @@ module Torque
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
# List of
|
49
|
+
# List of values as symbols
|
50
50
|
def keys
|
51
51
|
values.map(&:to_sym)
|
52
52
|
end
|
@@ -86,7 +86,7 @@ module Torque
|
|
86
86
|
self.values.include?(value.to_s)
|
87
87
|
end
|
88
88
|
|
89
|
-
# Build an active record scope for a given
|
89
|
+
# Build an active record scope for a given attribute against a value
|
90
90
|
def scope(attribute, value)
|
91
91
|
attribute.eq(value)
|
92
92
|
end
|
@@ -183,7 +183,7 @@ module Torque
|
|
183
183
|
list_from = :i18n_scopes
|
184
184
|
end
|
185
185
|
|
186
|
-
|
186
|
+
PostgreSQL.config.enum.send(list_from).map do |key|
|
187
187
|
(key % values).to_sym
|
188
188
|
end
|
189
189
|
end
|
@@ -209,7 +209,7 @@ module Torque
|
|
209
209
|
end
|
210
210
|
end
|
211
211
|
|
212
|
-
# Throw an exception for invalid
|
212
|
+
# Throw an exception for invalid values
|
213
213
|
def raise_invalid(value)
|
214
214
|
if value.is_a?(Numeric)
|
215
215
|
raise EnumError, "#{value.inspect} is out of bounds of #{self.class.name}"
|
@@ -218,7 +218,7 @@ module Torque
|
|
218
218
|
end
|
219
219
|
end
|
220
220
|
|
221
|
-
# Throw an exception for
|
221
|
+
# Throw an exception for comparison between different enums
|
222
222
|
def raise_comparison(other)
|
223
223
|
raise EnumError, "Comparison of #{self.class.name} with #{self.inspect} failed"
|
224
224
|
end
|
@@ -18,7 +18,7 @@ module Torque
|
|
18
18
|
# Find or create the class that will handle the value
|
19
19
|
def lookup(name, enum_klass)
|
20
20
|
const = name.to_s.camelize + 'Set'
|
21
|
-
namespace =
|
21
|
+
namespace = PostgreSQL.config.enum.namespace
|
22
22
|
|
23
23
|
return namespace.const_get(const) if namespace.const_defined?(const)
|
24
24
|
|
@@ -30,7 +30,7 @@ module Torque
|
|
30
30
|
# Provide a method on the given class to setup which enum sets will be
|
31
31
|
# manually initialized
|
32
32
|
def include_on(klass, method_name = nil)
|
33
|
-
method_name ||=
|
33
|
+
method_name ||= PostgreSQL.config.enum.set_method
|
34
34
|
Builder.include_on(klass, method_name, Builder::Enum, set_features: true) do |builder|
|
35
35
|
defined_enums[builder.attribute.to_s] = builder.subtype
|
36
36
|
end
|
@@ -76,14 +76,14 @@ module Torque
|
|
76
76
|
end.reduce(:+)
|
77
77
|
end
|
78
78
|
|
79
|
-
# Build an active record scope for a given
|
79
|
+
# Build an active record scope for a given attribute against a value
|
80
80
|
def scope(attribute, value)
|
81
|
-
attribute.contains(
|
81
|
+
attribute.contains(FN.bind_with(attribute, value).pg_cast(type_name))
|
82
82
|
end
|
83
83
|
|
84
84
|
private
|
85
85
|
|
86
|
-
# Allows checking value
|
86
|
+
# Allows checking value existence
|
87
87
|
def respond_to_missing?(method_name, include_private = false)
|
88
88
|
valid?(method_name) || super
|
89
89
|
end
|
@@ -226,7 +226,7 @@ module Torque
|
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
229
|
-
# Throw an exception for invalid
|
229
|
+
# Throw an exception for invalid values
|
230
230
|
def raise_invalid(value)
|
231
231
|
if value.is_a?(Numeric)
|
232
232
|
raise EnumSetError, "#{value.inspect} is out of bounds of #{self.class.name}"
|
@@ -235,7 +235,7 @@ module Torque
|
|
235
235
|
end
|
236
236
|
end
|
237
237
|
|
238
|
-
# Throw an exception for
|
238
|
+
# Throw an exception for comparison between different enums
|
239
239
|
def raise_comparison(other)
|
240
240
|
raise EnumSetError, "Comparison of #{self.class.name} with #{self.inspect} failed"
|
241
241
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
module Attributes
|
6
|
+
# For now, full text search doesn't have it's own class
|
7
|
+
module FullTextSearch
|
8
|
+
class << self
|
9
|
+
# Provide a method on the given class to setup which full text search
|
10
|
+
# columns will be manually initialized
|
11
|
+
def include_on(klass, method_name = nil)
|
12
|
+
method_name ||= PostgreSQL.config.full_text_search.base_method
|
13
|
+
Builder.include_on(klass, method_name, Builder::FullTextSearch)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
module Torque
|
4
4
|
module PostgreSQL
|
5
5
|
module Attributes
|
6
|
-
# For
|
6
|
+
# For now, period doesn't have it's own class
|
7
7
|
module Period
|
8
8
|
class << self
|
9
9
|
# Provide a method on the given class to setup which period columns
|
10
10
|
# will be manually initialized
|
11
11
|
def include_on(klass, method_name = nil)
|
12
|
-
method_name ||=
|
12
|
+
method_name ||= PostgreSQL.config.period.base_method
|
13
13
|
Builder.include_on(klass, method_name, Builder::Period)
|
14
14
|
end
|
15
15
|
end
|
@@ -136,10 +136,10 @@ module Torque
|
|
136
136
|
|
137
137
|
col = table[name]
|
138
138
|
base.select_extra_values += [col.as(as)] unless as.nil?
|
139
|
-
parts = [col, source.
|
139
|
+
parts = [col, source.pg_cast(:varchar)]
|
140
140
|
|
141
|
-
columns << ::Arel.array([source]).
|
142
|
-
sub_columns <<
|
141
|
+
columns << ::Arel.array([source]).pg_cast(:varchar, true).as(name)
|
142
|
+
sub_columns << FN.array_append(*parts).as(name)
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
@@ -16,11 +16,12 @@ module Torque
|
|
16
16
|
class_attribute :schema, instance_writer: false
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
class_methods do
|
20
20
|
delegate :distinct_on, :with, :itself_only, :cast_records, to: :all
|
21
21
|
|
22
22
|
# Make sure that table name is an instance of TableName class
|
23
23
|
def reset_table_name
|
24
|
+
return super unless PostgreSQL.config.schemas.enabled
|
24
25
|
self.table_name = TableName.new(self, super)
|
25
26
|
end
|
26
27
|
|
@@ -41,7 +42,7 @@ module Torque
|
|
41
42
|
next klass.table_name unless klass.physically_inheritances?
|
42
43
|
|
43
44
|
query = klass.unscoped.where(subclass.primary_key => id)
|
44
|
-
query.pluck(klass.arel_table['tableoid'].
|
45
|
+
query.pluck(klass.arel_table['tableoid'].pg_cast('regclass')).first
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
@@ -200,14 +201,6 @@ module Torque
|
|
200
201
|
::ActiveRecord::Reflection.add_reflection(self, name, reflection)
|
201
202
|
end
|
202
203
|
|
203
|
-
# Allow extra keyword arguments to be sent to +InsertAll+
|
204
|
-
unless Torque::PostgreSQL::AR720
|
205
|
-
def upsert_all(attributes, **xargs)
|
206
|
-
xargs = xargs.reverse_merge(on_duplicate: :update)
|
207
|
-
::ActiveRecord::InsertAll.new(self, attributes, **xargs).execute
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
204
|
protected
|
212
205
|
|
213
206
|
# Allow optional select attributes to be loaded manually when they are
|
@@ -4,7 +4,7 @@ module Torque
|
|
4
4
|
module PostgreSQL
|
5
5
|
module Collector
|
6
6
|
|
7
|
-
# This
|
7
|
+
# This class helps to collect data in different ways. Used to configure
|
8
8
|
# auxiliary statements
|
9
9
|
def self.new(*args)
|
10
10
|
klass = Class.new
|
@@ -4,10 +4,6 @@ module Torque
|
|
4
4
|
module PostgreSQL
|
5
5
|
include ActiveSupport::Configurable
|
6
6
|
|
7
|
-
# Stores a version check for compatibility purposes
|
8
|
-
AR710 = (ActiveRecord.gem_version >= Gem::Version.new('7.1.0'))
|
9
|
-
AR720 = (ActiveRecord.gem_version >= Gem::Version.new('7.2.0'))
|
10
|
-
|
11
7
|
# Use the same logger as the Active Record one
|
12
8
|
def self.logger
|
13
9
|
ActiveRecord::Base.logger
|
@@ -41,11 +37,19 @@ module Torque
|
|
41
37
|
# default. False means that no validation will be performed
|
42
38
|
assoc.belongs_to_many_required_by_default = false
|
43
39
|
|
40
|
+
# Although +belongs_to_many+ does not need a custom handler when joining
|
41
|
+
# the last chain scope, this can allow devs to pick which way they prefer:
|
42
|
+
# Rails default, or ANY with a single bind to improve prepared statements
|
43
|
+
# assoc.optimize_for_binds = false TODO: Add support
|
44
|
+
|
44
45
|
end
|
45
46
|
|
46
47
|
# Configure multiple schemas
|
47
48
|
config.nested(:schemas) do |schemas|
|
48
49
|
|
50
|
+
# Enables schemas handler by this gem, not Rails's own implementation
|
51
|
+
schemas.enabled = true
|
52
|
+
|
49
53
|
# Defines a list of LIKE-based schemas to not consider for a multiple
|
50
54
|
# schema database
|
51
55
|
schemas.blacklist = %w[information_schema pg_%]
|
@@ -59,6 +63,10 @@ module Torque
|
|
59
63
|
# Configure auxiliary statement features
|
60
64
|
config.nested(:auxiliary_statement) do |cte|
|
61
65
|
|
66
|
+
# Enables auxiliary statements handler by this gem, not Rails's own
|
67
|
+
# implementation
|
68
|
+
cte.enabled = true
|
69
|
+
|
62
70
|
# Define the key that is used on auxiliary statements to send extra
|
63
71
|
# arguments to format string or send on a proc
|
64
72
|
cte.send_arguments_key = :args
|
@@ -76,6 +84,9 @@ module Torque
|
|
76
84
|
# Configure ENUM features
|
77
85
|
config.nested(:enum) do |enum|
|
78
86
|
|
87
|
+
# Enables enum handler by this gem, not Rails's own implementation
|
88
|
+
enum.enabled = true
|
89
|
+
|
79
90
|
# The name of the method to be used on any ActiveRecord::Base to
|
80
91
|
# initialize model-based enum features
|
81
92
|
enum.base_method = :torque_enum
|
@@ -93,7 +104,7 @@ module Torque
|
|
93
104
|
enum.raise_conflicting = false
|
94
105
|
|
95
106
|
# Specify the namespace of each enum type of value
|
96
|
-
enum.namespace =
|
107
|
+
enum.namespace = nil
|
97
108
|
|
98
109
|
# Specify the scopes for I18n translations
|
99
110
|
enum.i18n_scopes = [
|
@@ -117,6 +128,9 @@ module Torque
|
|
117
128
|
# Configure geometry data types
|
118
129
|
config.nested(:geometry) do |geometry|
|
119
130
|
|
131
|
+
# Enables geometry handler by this gem, not Rails's own implementation
|
132
|
+
geometry.enabled = true
|
133
|
+
|
120
134
|
# Define the class that will be handling Point data types after decoding
|
121
135
|
# it. Any class provided here must respond to 'x', and 'y'
|
122
136
|
geometry.point_class = ActiveRecord::Point
|
@@ -162,6 +176,9 @@ module Torque
|
|
162
176
|
# Configure period features
|
163
177
|
config.nested(:period) do |period|
|
164
178
|
|
179
|
+
# Enables period handler by this gem
|
180
|
+
period.enabled = true
|
181
|
+
|
165
182
|
# The name of the method to be used on any ActiveRecord::Base to
|
166
183
|
# initialize model-based period features
|
167
184
|
period.base_method = :period_for
|
@@ -228,5 +245,78 @@ module Torque
|
|
228
245
|
}
|
229
246
|
|
230
247
|
end
|
248
|
+
|
249
|
+
# Configure period features
|
250
|
+
config.nested(:interval) do |interval|
|
251
|
+
|
252
|
+
# Enables interval handler by this gem, not Rails's own implementation
|
253
|
+
interval.enabled = true
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
# Configure arel additional features
|
258
|
+
config.nested(:arel) do |arel|
|
259
|
+
|
260
|
+
# When provided, the initializer will expose the Arel function helper on
|
261
|
+
# the given module
|
262
|
+
config.expose_function_helper_on = nil
|
263
|
+
|
264
|
+
# List of Arel INFIX operators that will be made available for using as
|
265
|
+
# methods on Arel::Nodes::Node and Arel::Attribute
|
266
|
+
arel.infix_operators = {
|
267
|
+
'contained_by' => '<@',
|
268
|
+
'has_key' => '?',
|
269
|
+
'has_all_keys' => '?&',
|
270
|
+
'has_any_keys' => '?|',
|
271
|
+
'strictly_left' => '<<',
|
272
|
+
'strictly_right' => '>>',
|
273
|
+
'doesnt_right_extend' => '&<',
|
274
|
+
'doesnt_left_extend' => '&>',
|
275
|
+
'adjacent_to' => '-|-',
|
276
|
+
}
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
# Configure full text search features
|
281
|
+
config.nested(:full_text_search) do |fts|
|
282
|
+
|
283
|
+
# Enables full text search handler by this gem
|
284
|
+
fts.enabled = true
|
285
|
+
|
286
|
+
# The name of the method to be used on any ActiveRecord::Base to
|
287
|
+
# initialize model-based full text search features
|
288
|
+
fts.base_method = :torque_search_for
|
289
|
+
|
290
|
+
# Defines the default language when generating search vector columns
|
291
|
+
fts.default_language = 'english'
|
292
|
+
|
293
|
+
# Defines the default index type to be used when creating search vector.
|
294
|
+
# It still requires that the column requests an index
|
295
|
+
fts.default_index_type = :gin
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
# Configure predicate builder additional features
|
300
|
+
config.nested(:predicate_builder) do |builder|
|
301
|
+
|
302
|
+
# List which handlers are enabled by default
|
303
|
+
builder.enabled = %i[regexp arel_attribute enumerator_lazy]
|
304
|
+
|
305
|
+
# When active, values provided to array attributes will be handled more
|
306
|
+
# efficiently. It will use the +ANY+ operator on a equality check and
|
307
|
+
# overlaps when the given value is an array
|
308
|
+
builder.handle_array_attributes = false
|
309
|
+
|
310
|
+
# Make sure that the predicate builder will not spend more than 20ms
|
311
|
+
# trying to produce the underlying array
|
312
|
+
builder.lazy_timeout = 0.02
|
313
|
+
|
314
|
+
# Since lazy array is uncommon, it is better to limit the number of
|
315
|
+
# entries we try to pull so we don't cause a timeout or a long wait
|
316
|
+
# iteration
|
317
|
+
builder.lazy_limit = 2_000
|
318
|
+
|
319
|
+
end
|
320
|
+
|
231
321
|
end
|
232
322
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Torque
|
4
|
+
module PostgreSQL
|
5
|
+
# Simplified module for creating arel functions. This is used internally
|
6
|
+
# but can also be made available to other devs on their own projects
|
7
|
+
module Function
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# A facilitator to create a bind param that is fully compatible with
|
11
|
+
# Arel and ActiveRecord
|
12
|
+
def bind(*args)
|
13
|
+
attr = ::ActiveRecord::Relation::QueryAttribute.new(*args)
|
14
|
+
::Arel::Nodes::BindParam.new(attr)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Just a shortcut to create a bind param for a model attribute and a
|
18
|
+
# value for it
|
19
|
+
def bind_for(model, attribute, value)
|
20
|
+
bind(attribute, value, model.attribute_types[attribute])
|
21
|
+
end
|
22
|
+
|
23
|
+
# Another shortcut, when we already have the arel attribute at hand
|
24
|
+
def bind_with(arel_attribute, value)
|
25
|
+
bind(arel_attribute.name, value, arel_attribute.type_caster)
|
26
|
+
end
|
27
|
+
|
28
|
+
# A facilitator to create an infix operation
|
29
|
+
def infix(op, left, right)
|
30
|
+
::Arel::Nodes::InfixOperation.new(op, left, right)
|
31
|
+
end
|
32
|
+
|
33
|
+
# A facilitator to use several Infix operators to concatenate all the
|
34
|
+
# provided arguments. Arguments won't be sanitized, as other methods
|
35
|
+
# under this module
|
36
|
+
def concat(*args)
|
37
|
+
return args.first if args.one?
|
38
|
+
args.reduce { |left, right| infix(:"||", left, right) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# As of now, this indicates that it supports any direct calls, since
|
42
|
+
# the idea is to simply map to an Arel function with the same name,
|
43
|
+
# without checking if it actually exists
|
44
|
+
def respond_to_missing?(*)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method is used to catch any method calls that are not defined
|
49
|
+
# in this module. It will simply return an Arel function with the same
|
50
|
+
# name as the method called, passing all arguments to it, without
|
51
|
+
# any sanitization
|
52
|
+
def method_missing(name, *args, &block)
|
53
|
+
::Arel::Nodes::NamedFunction.new(name.to_s.upcase, args)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
FN = Function
|
60
|
+
end
|
61
|
+
end
|