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
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'relation/distinct_on'
|
4
|
-
require_relative 'relation/auxiliary_statement'
|
5
4
|
require_relative 'relation/inheritance'
|
6
5
|
|
7
6
|
require_relative 'relation/merger'
|
@@ -12,7 +11,6 @@ module Torque
|
|
12
11
|
extend ActiveSupport::Concern
|
13
12
|
|
14
13
|
include DistinctOn
|
15
|
-
include AuxiliaryStatement
|
16
14
|
include Inheritance
|
17
15
|
|
18
16
|
SINGLE_VALUE_METHODS = [:itself_only]
|
@@ -107,7 +105,7 @@ module Torque
|
|
107
105
|
end
|
108
106
|
end
|
109
107
|
|
110
|
-
|
108
|
+
class_methods do
|
111
109
|
# Easy and storable way to access the name used to get the record table
|
112
110
|
# name when using inheritance tables
|
113
111
|
def _record_class_attribute
|
@@ -115,7 +113,7 @@ module Torque
|
|
115
113
|
.inheritance.record_class_column_name.to_sym
|
116
114
|
end
|
117
115
|
|
118
|
-
# Easy and storable way to access the name used to get the
|
116
|
+
# Easy and storable way to access the name used to get the indicate of
|
119
117
|
# auto casting inherited records
|
120
118
|
def _auto_cast_attribute
|
121
119
|
@@auto_cast ||= Torque::PostgreSQL.config
|
@@ -135,16 +133,14 @@ module Torque
|
|
135
133
|
end
|
136
134
|
|
137
135
|
# Allow extra keyword arguments to be sent to +InsertAll+
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
::ActiveRecord::InsertAll.execute(self, attributes, **xargs)
|
142
|
-
end
|
136
|
+
def upsert_all(attributes, **xargs)
|
137
|
+
xargs = xargs.reverse_merge(on_duplicate: :update)
|
138
|
+
::ActiveRecord::InsertAll.execute(self, attributes, **xargs)
|
143
139
|
end
|
144
140
|
end
|
145
141
|
end
|
146
142
|
|
147
|
-
# Include the
|
143
|
+
# Include the methods here provided and then change the constants to ensure
|
148
144
|
# the operation of ActiveRecord Relation
|
149
145
|
ActiveRecord::Relation.include Relation
|
150
146
|
ActiveRecord::Relation.prepend Relation::Initializer
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'torque/postgresql/schema_cache/inheritance'
|
4
|
-
|
5
|
-
|
6
|
-
require 'torque/postgresql/schema_cache/schema_reflection'
|
7
|
-
require 'torque/postgresql/schema_cache/bound_schema_reflection'
|
8
|
-
end
|
4
|
+
require 'torque/postgresql/schema_cache/schema_reflection'
|
5
|
+
require 'torque/postgresql/schema_cache/bound_schema_reflection'
|
9
6
|
|
10
7
|
module Torque
|
11
8
|
module PostgreSQL
|
@@ -73,7 +70,7 @@ module Torque
|
|
73
70
|
end
|
74
71
|
|
75
72
|
def clear_data_source_cache!(connection_or_name, name = connection_or_name) # :nodoc:
|
76
|
-
|
73
|
+
super
|
77
74
|
@data_sources_model_names.delete name
|
78
75
|
@inheritance_dependencies.delete name
|
79
76
|
@inheritance_associations.delete name
|
@@ -130,8 +127,7 @@ module Torque
|
|
130
127
|
def reload_inheritance_data!(source)
|
131
128
|
return if @inheritance_loaded
|
132
129
|
|
133
|
-
|
134
|
-
source.public_send(method_name) do |connection|
|
130
|
+
source.with_connection do |connection|
|
135
131
|
@inheritance_dependencies = connection.inherited_tables
|
136
132
|
@inheritance_associations = generate_associations
|
137
133
|
@inheritance_loaded = true
|
@@ -145,14 +141,12 @@ module Torque
|
|
145
141
|
end
|
146
142
|
|
147
143
|
# Use this method to also load any irregular model name
|
148
|
-
|
149
|
-
|
150
|
-
Torque::PostgreSQL::AR710 ? super(source) : super()
|
144
|
+
def add_all(source = nil)
|
145
|
+
super
|
151
146
|
|
152
147
|
data_sources = source.present? ? tables_to_cache(source) : @data_sources.keys
|
153
148
|
@data_sources_model_names = prepare_irregular_models(data_sources)
|
154
149
|
end
|
155
|
-
|
156
150
|
end
|
157
151
|
|
158
152
|
ActiveRecord::ConnectionAdapters::SchemaCache.prepend SchemaCache
|
data/lib/torque/postgresql.rb
CHANGED
@@ -12,6 +12,7 @@ require 'torque/postgresql/config'
|
|
12
12
|
require 'torque/postgresql/version'
|
13
13
|
require 'torque/postgresql/collector'
|
14
14
|
require 'torque/postgresql/geometry_builder'
|
15
|
+
require 'torque/postgresql/predicate_builder'
|
15
16
|
|
16
17
|
require 'torque/postgresql/i18n'
|
17
18
|
require 'torque/postgresql/arel'
|
@@ -19,7 +20,6 @@ require 'torque/postgresql/adapter'
|
|
19
20
|
require 'torque/postgresql/associations'
|
20
21
|
require 'torque/postgresql/attributes'
|
21
22
|
require 'torque/postgresql/autosave_association'
|
22
|
-
require 'torque/postgresql/auxiliary_statement'
|
23
23
|
require 'torque/postgresql/inheritance'
|
24
24
|
require 'torque/postgresql/base' # Needs to be after inheritance
|
25
25
|
require 'torque/postgresql/insert_all'
|
@@ -28,5 +28,6 @@ require 'torque/postgresql/relation'
|
|
28
28
|
require 'torque/postgresql/reflection'
|
29
29
|
require 'torque/postgresql/schema_cache'
|
30
30
|
require 'torque/postgresql/table_name'
|
31
|
+
require 'torque/postgresql/function'
|
31
32
|
|
32
33
|
require 'torque/postgresql/railtie' if defined?(Rails)
|
data/spec/initialize.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative '../lib/torque/postgresql/auxiliary_statement'
|
2
|
+
|
3
|
+
require_relative '../lib/torque/postgresql/adapter/schema_overrides'
|
4
|
+
|
5
|
+
require_relative '../lib/torque/postgresql/adapter/oid/box'
|
6
|
+
require_relative '../lib/torque/postgresql/adapter/oid/circle'
|
7
|
+
require_relative '../lib/torque/postgresql/adapter/oid/enum'
|
8
|
+
require_relative '../lib/torque/postgresql/adapter/oid/enum_set'
|
9
|
+
require_relative '../lib/torque/postgresql/adapter/oid/interval'
|
10
|
+
require_relative '../lib/torque/postgresql/adapter/oid/line'
|
11
|
+
require_relative '../lib/torque/postgresql/adapter/oid/segment'
|
12
|
+
|
13
|
+
require_relative '../lib/torque/postgresql/attributes/enum'
|
14
|
+
require_relative '../lib/torque/postgresql/attributes/enum_set'
|
15
|
+
require_relative '../lib/torque/postgresql/attributes/period'
|
16
|
+
require_relative '../lib/torque/postgresql/attributes/full_text_search'
|
17
|
+
|
18
|
+
require_relative '../lib/torque/postgresql/relation/auxiliary_statement'
|
19
|
+
|
20
|
+
module Torque
|
21
|
+
module PostgreSQL
|
22
|
+
ActiveRecord::Base.belongs_to_many_required_by_default = false
|
23
|
+
|
24
|
+
Attributes::Enum.include_on(ActiveRecord::Base)
|
25
|
+
Attributes::EnumSet.include_on(ActiveRecord::Base)
|
26
|
+
Attributes::Period.include_on(ActiveRecord::Base)
|
27
|
+
Attributes::FullTextSearch.include_on(ActiveRecord::Base)
|
28
|
+
|
29
|
+
Relation.include(Relation::AuxiliaryStatement)
|
30
|
+
|
31
|
+
::Object.const_set('TorqueCTE', AuxiliaryStatement)
|
32
|
+
::Object.const_set('TorqueRecursiveCTE', AuxiliaryStatement::Recursive)
|
33
|
+
|
34
|
+
config.enum.namespace = ::Object.const_set('Enum', Module.new)
|
35
|
+
config.enum.namespace.define_singleton_method(:const_missing) do |name|
|
36
|
+
Attributes::Enum.lookup(name)
|
37
|
+
end
|
38
|
+
|
39
|
+
config.enum.namespace.define_singleton_method(:sample) do |name|
|
40
|
+
Attributes::Enum.lookup(name).sample
|
41
|
+
end
|
42
|
+
|
43
|
+
ar_type = ActiveRecord::Type
|
44
|
+
ar_type.register(:enum, Adapter::OID::Enum, adapter: :postgresql)
|
45
|
+
ar_type.register(:enum_set, Adapter::OID::EnumSet, adapter: :postgresql)
|
46
|
+
|
47
|
+
ar_type.register(:box, Adapter::OID::Box, adapter: :postgresql)
|
48
|
+
ar_type.register(:circle, Adapter::OID::Circle, adapter: :postgresql)
|
49
|
+
ar_type.register(:line, Adapter::OID::Line, adapter: :postgresql)
|
50
|
+
ar_type.register(:segment, Adapter::OID::Segment, adapter: :postgresql)
|
51
|
+
|
52
|
+
ar_type.register(:interval, Adapter::OID::Interval, adapter: :postgresql)
|
53
|
+
|
54
|
+
Arel.build_operations(config.arel.infix_operators)
|
55
|
+
|
56
|
+
ActiveRecord::Base.connection.torque_load_additional_types
|
57
|
+
end
|
58
|
+
end
|
data/spec/mocks/cache_query.rb
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
module Mocks
|
2
2
|
module CacheQuery
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
cache.instance_variable_set(:@enabled, true)
|
3
|
+
def get_last_executed_query(&block)
|
4
|
+
cache = ActiveRecord::Base.connection.query_cache
|
5
|
+
cache.instance_variable_set(:@enabled, true)
|
7
6
|
|
8
|
-
|
7
|
+
map = cache.instance_variable_get(:@map)
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
block.call
|
10
|
+
result = map.keys.first
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
cache.instance_variable_set(:@enabled, false)
|
13
|
+
map.delete(result)
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
else
|
19
|
-
def get_last_executed_query(&block)
|
20
|
-
conn = ActiveRecord::Base.connection
|
21
|
-
conn.instance_variable_set(:@query_cache_enabled, true)
|
22
|
-
|
23
|
-
block.call
|
24
|
-
result = conn.query_cache.keys.first
|
15
|
+
result
|
16
|
+
end
|
25
17
|
|
26
|
-
|
27
|
-
|
18
|
+
def get_query_with_binds(&block)
|
19
|
+
result = nil
|
28
20
|
|
29
|
-
|
21
|
+
original_method = ActiveRecord::Base.connection.method(:raw_execute)
|
22
|
+
original_method.receiver.define_singleton_method(:raw_execute) do |*args, **xargs, &block|
|
23
|
+
result ||= [args.first, args.third]
|
24
|
+
super(*args, **xargs, &block)
|
30
25
|
end
|
26
|
+
|
27
|
+
block.call
|
28
|
+
original_method.receiver.define_singleton_method(:raw_execute, &original_method.to_proc)
|
29
|
+
|
30
|
+
result
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/spec/mocks/create_table.rb
CHANGED
@@ -1,34 +1,14 @@
|
|
1
1
|
module Mocks
|
2
2
|
module CreateTable
|
3
3
|
def mock_create_table
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
td = create_table_definition(table_name, **options)
|
9
|
-
|
10
|
-
# Does things as the same as schema statements
|
11
|
-
if options[:id] != false && !options[:as]
|
12
|
-
pk = options.fetch(:primary_key) do
|
13
|
-
ActiveRecord::Base.get_primary_key table_name.to_s.singularize
|
14
|
-
end
|
15
|
-
|
16
|
-
if pk.is_a?(Array)
|
17
|
-
td.primary_keys pk
|
18
|
-
else
|
19
|
-
td.primary_key pk, options.fetch(:id, :primary_key), **options
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
block.call(td) if block.present?
|
24
|
-
|
25
|
-
# Now generate the SQL and return it
|
26
|
-
schema_creation.accept td
|
4
|
+
around do |example|
|
5
|
+
original_method = ActiveRecord::Base.connection.method(:log)
|
6
|
+
original_method.receiver.define_singleton_method(:log) do |sql, *, **, &block|
|
7
|
+
sql
|
27
8
|
end
|
28
|
-
end
|
29
9
|
|
30
|
-
|
31
|
-
|
10
|
+
example.run
|
11
|
+
original_method.receiver.define_singleton_method(:log, &original_method.to_proc)
|
32
12
|
end
|
33
13
|
end
|
34
14
|
end
|
data/spec/schema.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
version =
|
13
|
+
version = 5
|
14
14
|
|
15
15
|
return if ActiveRecord::Migrator.current_version == version
|
16
16
|
ActiveRecord::Schema.define(version: version) do
|
@@ -60,6 +60,7 @@ ActiveRecord::Schema.define(version: version) do
|
|
60
60
|
t.enum "type", enum_type: :types
|
61
61
|
t.enum "conflicts", enum_type: :conflicts, array: true
|
62
62
|
t.jsonb "metadata"
|
63
|
+
# t.column "pieces", :int4multirange
|
63
64
|
t.datetime "created_at", null: false
|
64
65
|
t.datetime "updated_at", null: false
|
65
66
|
end
|
@@ -92,12 +93,14 @@ ActiveRecord::Schema.define(version: version) do
|
|
92
93
|
end
|
93
94
|
|
94
95
|
create_table "courses", force: :cascade do |t|
|
95
|
-
t.integer
|
96
|
-
t.string
|
97
|
-
t.interval
|
98
|
-
t.enum
|
99
|
-
t.
|
100
|
-
t.
|
96
|
+
t.integer "category_id"
|
97
|
+
t.string "title", null: false
|
98
|
+
t.interval "duration"
|
99
|
+
t.enum "types", enum_type: :types, array: true
|
100
|
+
t.search_language "lang", null: false, default: 'english'
|
101
|
+
t.search_vector "search_vector", columns: :title, language: :lang
|
102
|
+
t.datetime "created_at", null: false
|
103
|
+
t.datetime "updated_at", null: false
|
101
104
|
end
|
102
105
|
|
103
106
|
create_table "images", force: :cascade, id: false do |t|
|
@@ -105,11 +108,12 @@ ActiveRecord::Schema.define(version: version) do
|
|
105
108
|
end
|
106
109
|
|
107
110
|
create_table "posts", force: :cascade do |t|
|
108
|
-
t.integer
|
109
|
-
t.integer
|
110
|
-
t.string
|
111
|
-
t.text
|
112
|
-
t.enum
|
111
|
+
t.integer "author_id"
|
112
|
+
t.integer "activity_id"
|
113
|
+
t.string "title"
|
114
|
+
t.text "content"
|
115
|
+
t.enum "status", enum_type: :content_status
|
116
|
+
t.search_vector "search_vector", columns: %i[title content]
|
113
117
|
t.index ["author_id"], name: "index_posts_on_author_id", using: :btree
|
114
118
|
end
|
115
119
|
|
@@ -172,6 +176,9 @@ ActiveRecord::Schema.define(version: version) do
|
|
172
176
|
# create_table "activity_images", force: :cascade, inherits: [:activities, :images]
|
173
177
|
|
174
178
|
add_foreign_key "posts", "authors"
|
179
|
+
rescue Exception => e
|
180
|
+
byebug
|
181
|
+
raise
|
175
182
|
end
|
176
183
|
|
177
184
|
ActiveRecord::Base.connection.schema_cache.clear!
|
data/spec/spec_helper.rb
CHANGED
@@ -21,7 +21,11 @@ cleaner = ->() do
|
|
21
21
|
cache.instance_variable_set(:@inheritance_associations, {})
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
# Load all the files that are optional and managed by Railtie
|
25
|
+
require_relative 'initialize'
|
26
|
+
|
27
|
+
# This needs to come after loading all optional features
|
28
|
+
require_relative 'schema'
|
25
29
|
Dir.glob(File.join('spec', '{models,factories,mocks}', '**', '*.rb')) do |file|
|
26
30
|
require file[5..-4]
|
27
31
|
end
|
data/spec/tests/arel_spec.rb
CHANGED
@@ -2,7 +2,6 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe 'Arel' do
|
4
4
|
context 'on inflix operation' do
|
5
|
-
let(:list) { Torque::PostgreSQL::Arel::INFLIX_OPERATION }
|
6
5
|
let(:collector) { ::Arel::Collectors::SQLString }
|
7
6
|
let(:attribute) { ::Arel::Table.new('a')['sample'] }
|
8
7
|
let(:conn) { ActiveRecord::Base.connection }
|
@@ -21,12 +20,13 @@ RSpec.describe 'Arel' do
|
|
21
20
|
[:doesnt_right_extend, ::Arel.sql('numrange(5, 6)'), 'numrange(5, 6)'],
|
22
21
|
[:doesnt_left_extend, ::Arel.sql('numrange(7, 8)'), 'numrange(7, 8)'],
|
23
22
|
[:adjacent_to, ::Arel.sql('numrange(9, 0)'), 'numrange(9, 0)'],
|
24
|
-
].each do |(
|
25
|
-
klass_name =
|
23
|
+
].each do |(operation, value, quoted_value)|
|
24
|
+
klass_name = operation.to_s.camelize
|
26
25
|
|
27
|
-
context "##{
|
26
|
+
context "##{operation}" do
|
27
|
+
let(:operator) { instance.operator }
|
28
28
|
let(:instance) do
|
29
|
-
attribute.public_send(
|
29
|
+
attribute.public_send(operation, value.is_a?(Array) ? ::Arel.array(value) : value)
|
30
30
|
end
|
31
31
|
|
32
32
|
context 'for attribute' do
|
@@ -41,7 +41,7 @@ RSpec.describe 'Arel' do
|
|
41
41
|
let(:result) { visitor.accept(instance, collector.new).value }
|
42
42
|
|
43
43
|
it 'returns a formatted operation' do
|
44
|
-
expect(result).to be_eql("\"a\".\"sample\" #{
|
44
|
+
expect(result).to be_eql("\"a\".\"sample\" #{operator} #{quoted_value}")
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -123,9 +123,34 @@ RSpec.describe 'Arel' do
|
|
123
123
|
quoted = ::Arel::Nodes::build_quoted([1])
|
124
124
|
casted = ::Arel::Nodes::build_quoted(1, attribute)
|
125
125
|
|
126
|
+
expect(attribute.pg_cast('text').to_sql).to be_eql('"a"."sample"::text')
|
127
|
+
expect(quoted.pg_cast('bigint', true).to_sql).to be_eql('ARRAY[1]::bigint[]')
|
128
|
+
expect(casted.pg_cast('string').to_sql).to be_eql("1::string")
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'provides proper support to cast methods' do
|
132
|
+
attribute = ::Arel::Table.new('a')['sample']
|
133
|
+
quoted = ::Arel::Nodes::build_quoted([1])
|
134
|
+
casted = ::Arel::Nodes::build_quoted(1)
|
135
|
+
|
126
136
|
expect(attribute.cast('text').to_sql).to be_eql('"a"."sample"::text')
|
127
137
|
expect(quoted.cast('bigint', true).to_sql).to be_eql('ARRAY[1]::bigint[]')
|
128
|
-
|
138
|
+
|
139
|
+
changed_result = ActiveRecord.gem_version >= Gem::Version.new('8.0.2')
|
140
|
+
changed_result = changed_result ? 'CAST(1 AS string)' : '1::string'
|
141
|
+
expect(casted.pg_cast('string').to_sql).to be_eql("1::string")
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'properly works combined on a query' do
|
145
|
+
condition = Video.arel_table[:tag_ids].contains([1,2]).cast(:bigint, :array)
|
146
|
+
query = Video.all.where(condition).to_sql
|
147
|
+
|
148
|
+
expect(query).to include('WHERE "videos"."tag_ids" @> ARRAY[1, 2]::bigint[]')
|
149
|
+
|
150
|
+
condition = QuestionSelect.arel_table[:options].overlaps(%w[a b]).cast(:string, :array)
|
151
|
+
query = QuestionSelect.all.where(condition).to_sql
|
152
|
+
|
153
|
+
expect(query).to include('"options" && ARRAY[\'a\', \'b\']::string[]')
|
129
154
|
end
|
130
155
|
end
|
131
156
|
end
|
@@ -587,7 +587,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
587
587
|
result << ' FROM "categories"'
|
588
588
|
result << ' WHERE "categories"."parent_id" IS NULL'
|
589
589
|
result << ' UNION'
|
590
|
-
result << ' SELECT "categories"."id", "categories"."parent_id",
|
590
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY_APPEND("all_categories"."path", "categories"."id"::varchar) AS path'
|
591
591
|
result << ' FROM "categories", "all_categories"'
|
592
592
|
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
593
593
|
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
@@ -607,7 +607,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
607
607
|
result << ' FROM "categories"'
|
608
608
|
result << ' WHERE "categories"."parent_id" IS NULL'
|
609
609
|
result << ' UNION'
|
610
|
-
result << ' SELECT "categories"."id", "categories"."parent_id",
|
610
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY_APPEND("all_categories"."p", "categories"."name"::varchar) AS p'
|
611
611
|
result << ' FROM "categories", "all_categories"'
|
612
612
|
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
613
613
|
result << ' ) SELECT "courses".*, "all_categories"."p" AS category_path FROM "courses" INNER JOIN "all_categories"'
|
@@ -889,7 +889,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
889
889
|
result << ' FROM "categories"'
|
890
890
|
result << ' WHERE "categories"."parent_id" IS NULL'
|
891
891
|
result << ' UNION'
|
892
|
-
result << ' SELECT "categories"."id", "categories"."parent_id",
|
892
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY_APPEND("category"."a", "categories"."b"::varchar) AS a'
|
893
893
|
result << ' FROM "categories", "category"'
|
894
894
|
result << ' WHERE "categories"."parent_id" = "category"."id"'
|
895
895
|
result << ' ) SELECT "courses".*, "category"."a" AS c FROM "courses" INNER JOIN "category"'
|
@@ -3,9 +3,9 @@ require 'spec_helper'
|
|
3
3
|
RSpec.describe 'BelongsToMany' do
|
4
4
|
context 'on model' do
|
5
5
|
let(:model) { Video }
|
6
|
+
let(:key) { :tests }
|
6
7
|
let(:builder) { Torque::PostgreSQL::Associations::Builder::BelongsToMany }
|
7
8
|
let(:reflection) { Torque::PostgreSQL::Reflection::BelongsToManyReflection }
|
8
|
-
let(:key) { Torque::PostgreSQL::AR720 ? :tests : 'tests' }
|
9
9
|
|
10
10
|
after { model._reflections = {} }
|
11
11
|
|
@@ -34,8 +34,8 @@ RSpec.describe 'BelongsToMany' do
|
|
34
34
|
|
35
35
|
context 'on association' do
|
36
36
|
let(:other) { Tag }
|
37
|
+
let(:key) { :tags }
|
37
38
|
let(:initial) { FactoryBot.create(:tag) }
|
38
|
-
let(:key) { Torque::PostgreSQL::AR720 ? :tags : 'tags' }
|
39
39
|
|
40
40
|
before { Video.belongs_to_many(:tags) }
|
41
41
|
subject { Video.create(title: 'A') }
|
@@ -58,7 +58,7 @@ RSpec.describe 'BelongsToMany' do
|
|
58
58
|
it 'loads associated records' do
|
59
59
|
subject.update(tag_ids: [initial.id])
|
60
60
|
expect(subject.tags.to_sql).to be_eql(<<-SQL.squish)
|
61
|
-
SELECT "tags".* FROM "tags" WHERE "tags"."id"
|
61
|
+
SELECT "tags".* FROM "tags" WHERE "tags"."id" = #{initial.id}
|
62
62
|
SQL
|
63
63
|
|
64
64
|
expect(subject.tags.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|
@@ -370,6 +370,18 @@ RSpec.describe 'BelongsToMany' do
|
|
370
370
|
expect { query.load }.not_to raise_error
|
371
371
|
end
|
372
372
|
|
373
|
+
context 'when handling binds' do
|
374
|
+
let(:tag_ids) { FactoryBot.create_list(:tag, 5).map(&:id) }
|
375
|
+
let!(:record) { Video.new(tag_ids: tag_ids) }
|
376
|
+
|
377
|
+
it 'uses rails default with in and several binds' do
|
378
|
+
sql, binds = get_query_with_binds { record.tags.load }
|
379
|
+
|
380
|
+
expect(sql).to include(' WHERE "tags"."id" IN ($1, $2, $3, $4, $5)')
|
381
|
+
expect(binds.size).to be_eql(5)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
373
385
|
context 'when the attribute has a default value' do
|
374
386
|
subject { FactoryBot.create(:item) }
|
375
387
|
|
@@ -424,16 +436,28 @@ RSpec.describe 'BelongsToMany' do
|
|
424
436
|
|
425
437
|
subject { game.create }
|
426
438
|
|
427
|
-
it 'loads associated records' do
|
439
|
+
it 'loads one associated records' do
|
428
440
|
subject.update(player_ids: [other.id])
|
429
441
|
expect(subject.players.to_sql).to be_eql(<<-SQL.squish)
|
430
|
-
SELECT "players".* FROM "players" WHERE "players"."id"
|
442
|
+
SELECT "players".* FROM "players" WHERE "players"."id" = '#{other.id}'
|
431
443
|
SQL
|
432
444
|
|
433
445
|
expect(subject.players.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|
434
446
|
expect(subject.players.to_a).to be_eql([other])
|
435
447
|
end
|
436
448
|
|
449
|
+
it 'loads several associated records' do
|
450
|
+
entries = [other, player.create]
|
451
|
+
subject.update(player_ids: entries.map(&:id))
|
452
|
+
expect(subject.players.to_sql).to be_eql(<<-SQL.squish)
|
453
|
+
SELECT "players".* FROM "players"
|
454
|
+
WHERE "players"."id" IN ('#{entries[0].id}', '#{entries[1].id}')
|
455
|
+
SQL
|
456
|
+
|
457
|
+
expect(subject.players.load).to be_a(ActiveRecord::Associations::CollectionProxy)
|
458
|
+
expect(subject.players.to_a).to be_eql(entries)
|
459
|
+
end
|
460
|
+
|
437
461
|
it 'can preload records' do
|
438
462
|
records = 5.times.map { player.create }
|
439
463
|
subject.players.concat(records)
|
@@ -452,6 +476,49 @@ RSpec.describe 'BelongsToMany' do
|
|
452
476
|
end
|
453
477
|
end
|
454
478
|
|
479
|
+
context 'using callbacks' do
|
480
|
+
let(:tags) { FactoryBot.create_list(:tag, 3) }
|
481
|
+
let(:collectors) { Hash.new { |h, k| h[k] = [] } }
|
482
|
+
|
483
|
+
subject { Video.create(title: 'A') }
|
484
|
+
|
485
|
+
after do
|
486
|
+
Video.reset_callbacks(:save)
|
487
|
+
Video._reflections = {}
|
488
|
+
end
|
489
|
+
|
490
|
+
before do
|
491
|
+
subject.update_attribute(:tag_ids, tags.first(2).pluck(:id))
|
492
|
+
Video.belongs_to_many(:tags,
|
493
|
+
before_add: ->(_, tag) { collectors[:before_add] << tag },
|
494
|
+
after_add: ->(_, tag) { collectors[:after_add] << tag },
|
495
|
+
before_remove: ->(_, tag) { collectors[:before_remove] << tag },
|
496
|
+
after_remove: ->(_, tag) { collectors[:after_remove] << tag },
|
497
|
+
)
|
498
|
+
end
|
499
|
+
|
500
|
+
it 'works with id changes' do
|
501
|
+
subject.tag_ids = tags.drop(1).pluck(:id)
|
502
|
+
subject.save!
|
503
|
+
|
504
|
+
expect(collectors[:before_add]).to be_eql([tags.last])
|
505
|
+
expect(collectors[:after_add]).to be_eql([tags.last])
|
506
|
+
|
507
|
+
expect(collectors[:before_remove]).to be_eql([tags.first])
|
508
|
+
expect(collectors[:after_remove]).to be_eql([tags.first])
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'works with record changes' do
|
512
|
+
subject.tags = tags.drop(1)
|
513
|
+
|
514
|
+
expect(collectors[:before_add]).to be_eql([tags.last])
|
515
|
+
expect(collectors[:after_add]).to be_eql([tags.last])
|
516
|
+
|
517
|
+
expect(collectors[:before_remove]).to be_eql([tags.first])
|
518
|
+
expect(collectors[:after_remove]).to be_eql([tags.first])
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
455
522
|
context 'using custom keys' do
|
456
523
|
let(:connection) { ActiveRecord::Base.connection }
|
457
524
|
let(:post) { Post }
|
data/spec/tests/enum_set_spec.rb
CHANGED
@@ -43,13 +43,7 @@ RSpec.describe 'Enum' do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
context 'on schema' do
|
46
|
-
let(:source)
|
47
|
-
if Torque::PostgreSQL::AR720
|
48
|
-
ActiveRecord::Base.connection_pool
|
49
|
-
else
|
50
|
-
ActiveRecord::Base.connection
|
51
|
-
end
|
52
|
-
end
|
46
|
+
let(:source) { ActiveRecord::Base.connection_pool }
|
53
47
|
|
54
48
|
let(:dump_result) do
|
55
49
|
ActiveRecord::SchemaDumper.dump(source, (dump_result = StringIO.new))
|
@@ -275,10 +269,11 @@ RSpec.describe 'Enum' do
|
|
275
269
|
end
|
276
270
|
|
277
271
|
context 'on model' do
|
272
|
+
let(:instance) { Course.new }
|
273
|
+
|
278
274
|
before(:each) { decorate(Course, :types) }
|
279
275
|
|
280
276
|
subject { Course }
|
281
|
-
let(:instance) { Course.new }
|
282
277
|
|
283
278
|
it 'has all enum set methods' do
|
284
279
|
expect(subject).to respond_to(:types)
|
@@ -301,17 +296,23 @@ RSpec.describe 'Enum' do
|
|
301
296
|
|
302
297
|
it 'scope the model correctly' do
|
303
298
|
query = subject.a.to_sql
|
304
|
-
expect(query).to
|
299
|
+
expect(query).to include(%{WHERE "courses"."types" @> '{A}'::types[]})
|
305
300
|
end
|
306
301
|
|
307
302
|
it 'has a match all scope' do
|
308
303
|
query = subject.has_types('B', 'A').to_sql
|
309
|
-
expect(query).to
|
304
|
+
expect(query).to include(%{WHERE "courses"."types" @> '{B,A}'::types[]})
|
310
305
|
end
|
311
306
|
|
312
307
|
it 'has a match any scope' do
|
313
308
|
query = subject.has_any_types('B', 'A').to_sql
|
314
|
-
expect(query).to
|
309
|
+
expect(query).to include(%{WHERE "courses"."types" && '{B,A}'::types[]})
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'uses bind param instead of raw value' do
|
313
|
+
sql, binds = get_query_with_binds { subject.has_any_types('B', 'A').load }
|
314
|
+
expect(sql).to include('WHERE "courses"."types" && $1::types[]')
|
315
|
+
expect(binds.first.value).to eq(%w[B A])
|
315
316
|
end
|
316
317
|
end
|
317
318
|
end
|