torque-postgresql 1.1.4 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/torque/postgresql.rb +0 -2
- data/lib/torque/postgresql/adapter.rb +0 -1
- data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
- data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
- data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
- data/lib/torque/postgresql/adapter/schema_dumper.rb +74 -11
- data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
- data/lib/torque/postgresql/associations.rb +0 -3
- data/lib/torque/postgresql/associations/association_scope.rb +18 -60
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +2 -1
- data/lib/torque/postgresql/associations/preloader.rb +0 -24
- data/lib/torque/postgresql/associations/preloader/association.rb +13 -9
- data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
- data/lib/torque/postgresql/coder.rb +1 -2
- data/lib/torque/postgresql/config.rb +0 -4
- data/lib/torque/postgresql/inheritance.rb +13 -17
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
- data/lib/torque/postgresql/relation.rb +11 -16
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
- data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
- data/lib/torque/postgresql/version.rb +1 -1
- data/spec/en.yml +19 -0
- data/spec/factories/authors.rb +6 -0
- data/spec/factories/comments.rb +13 -0
- data/spec/factories/posts.rb +6 -0
- data/spec/factories/tags.rb +5 -0
- data/spec/factories/texts.rb +5 -0
- data/spec/factories/users.rb +6 -0
- data/spec/factories/videos.rb +5 -0
- data/spec/mocks/cache_query.rb +16 -0
- data/spec/mocks/create_table.rb +35 -0
- data/spec/models/activity.rb +3 -0
- data/spec/models/activity_book.rb +4 -0
- data/spec/models/activity_post.rb +7 -0
- data/spec/models/activity_post/sample.rb +4 -0
- data/spec/models/author.rb +4 -0
- data/spec/models/author_journalist.rb +4 -0
- data/spec/models/comment.rb +3 -0
- data/spec/models/course.rb +2 -0
- data/spec/models/geometry.rb +2 -0
- data/spec/models/guest_comment.rb +4 -0
- data/spec/models/post.rb +6 -0
- data/spec/models/tag.rb +2 -0
- data/spec/models/text.rb +2 -0
- data/spec/models/time_keeper.rb +2 -0
- data/spec/models/user.rb +8 -0
- data/spec/models/video.rb +2 -0
- data/spec/schema.rb +141 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/tests/arel_spec.rb +72 -0
- data/spec/tests/auxiliary_statement_spec.rb +593 -0
- data/spec/tests/belongs_to_many_spec.rb +240 -0
- data/spec/tests/coder_spec.rb +367 -0
- data/spec/tests/collector_spec.rb +59 -0
- data/spec/tests/distinct_on_spec.rb +65 -0
- data/spec/tests/enum_set_spec.rb +306 -0
- data/spec/tests/enum_spec.rb +628 -0
- data/spec/tests/geometric_builder_spec.rb +221 -0
- data/spec/tests/has_many_spec.rb +390 -0
- data/spec/tests/interval_spec.rb +167 -0
- data/spec/tests/lazy_spec.rb +24 -0
- data/spec/tests/period_spec.rb +954 -0
- data/spec/tests/quoting_spec.rb +24 -0
- data/spec/tests/range_spec.rb +36 -0
- data/spec/tests/relation_spec.rb +57 -0
- data/spec/tests/table_inheritance_spec.rb +416 -0
- metadata +103 -16
- data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
- data/lib/torque/postgresql/schema_dumper.rb +0 -88
@@ -8,7 +8,7 @@ module Torque
|
|
8
8
|
|
9
9
|
# For reflections connected through an array, make sure to properly
|
10
10
|
# decuple the list of ids and set them as associated with the owner
|
11
|
-
def run
|
11
|
+
def run
|
12
12
|
return super unless connected_through_array?
|
13
13
|
send("run_array_for_#{@reflection.macro}")
|
14
14
|
end
|
@@ -18,7 +18,7 @@ module Torque
|
|
18
18
|
# Specific run for belongs_many association
|
19
19
|
def run_array_for_belongs_to_many
|
20
20
|
# Add reverse to has_many
|
21
|
-
records =
|
21
|
+
records = groupped_records
|
22
22
|
owners.each do |owner|
|
23
23
|
items = records.values_at(*Array.wrap(owner[owner_key_name]))
|
24
24
|
associate_records_to_owner(owner, items.flatten)
|
@@ -29,7 +29,7 @@ module Torque
|
|
29
29
|
def run_array_for_has_many
|
30
30
|
# Add reverse to belongs_to_many
|
31
31
|
records = Hash.new { |h, k| h[k] = [] }
|
32
|
-
|
32
|
+
groupped_records.each do |ids, record|
|
33
33
|
ids.each { |id| records[id].concat(Array.wrap(record)) }
|
34
34
|
end
|
35
35
|
|
@@ -47,14 +47,18 @@ module Torque
|
|
47
47
|
scope.where(condition).load(&block)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
50
|
+
def associate_records_to_owner(owner, records)
|
51
|
+
return super unless connected_through_array?
|
52
|
+
association = owner.association(reflection.name)
|
53
|
+
association.loaded!
|
54
|
+
association.target.concat(records)
|
56
55
|
end
|
57
56
|
|
57
|
+
def groupped_records
|
58
|
+
preloaded_records.group_by do |record|
|
59
|
+
convert_key(record[association_key_name])
|
60
|
+
end
|
61
|
+
end
|
58
62
|
end
|
59
63
|
|
60
64
|
::ActiveRecord::Associations::Preloader::Association.prepend(Association)
|
@@ -211,19 +211,7 @@ module Torque
|
|
211
211
|
foreign_table = ::Arel::Table.new(association.plural_name)
|
212
212
|
end
|
213
213
|
|
214
|
-
|
215
|
-
# Possibilities:
|
216
|
-
# table
|
217
|
-
# table, foreign_klass
|
218
|
-
# table, foreign_table, foreign_klass
|
219
|
-
if association.respond_to?(:join_scope)
|
220
|
-
arity = association.method(:join_scope).arity
|
221
|
-
args = [@query.arel_table, foreign_table, base]
|
222
|
-
args.delete_at(1) if arity <= 2 # Delete foreign_table
|
223
|
-
args.delete_at(1) if arity <= 1 # Delete base (foreign_klass)
|
224
|
-
|
225
|
-
@query.merge(association.join_scope(*args))
|
226
|
-
end
|
214
|
+
@query.merge(association.join_scope(@query.arel_table, foreign_table, base))
|
227
215
|
|
228
216
|
# Add the join constraints
|
229
217
|
constraint = association.build_join_constraint(table, foreign_table)
|
@@ -2,10 +2,6 @@ module Torque
|
|
2
2
|
module PostgreSQL
|
3
3
|
include ActiveSupport::Configurable
|
4
4
|
|
5
|
-
# Stores a version check for compatibility purposes
|
6
|
-
AR521 = (ActiveRecord.gem_version >= Gem::Version.new('5.2.1'))
|
7
|
-
AR523 = (ActiveRecord.gem_version >= Gem::Version.new('5.2.3'))
|
8
|
-
|
9
5
|
# Use the same logger as the Active Record one
|
10
6
|
def self.logger
|
11
7
|
ActiveRecord::Base.logger
|
@@ -18,14 +18,7 @@ module Torque
|
|
18
18
|
klass.find(self.id)
|
19
19
|
end
|
20
20
|
|
21
|
-
private
|
22
|
-
|
23
|
-
def using_single_table_inheritance?(record) # :nodoc:
|
24
|
-
self.class.physically_inherited? || super
|
25
|
-
end
|
26
|
-
|
27
21
|
module ClassMethods
|
28
|
-
|
29
22
|
delegate :_auto_cast_attribute, :_record_class_attribute, to: ActiveRecord::Relation
|
30
23
|
|
31
24
|
# Get a full list of all attributes from a model and all its dependents
|
@@ -101,9 +94,10 @@ module Torque
|
|
101
94
|
|
102
95
|
# Get the final decorated table, regardless of any special condition
|
103
96
|
def decorated_table_name
|
104
|
-
|
105
|
-
|
106
|
-
contained =
|
97
|
+
parent_class = try(:module_parent) || try(:parent)
|
98
|
+
if parent_class < Base && !parent_class.abstract_class?
|
99
|
+
contained = parent_class.table_name
|
100
|
+
contained = contained.singularize if parent_class.pluralize_table_names
|
107
101
|
contained += "_"
|
108
102
|
end
|
109
103
|
|
@@ -144,17 +138,19 @@ module Torque
|
|
144
138
|
|
145
139
|
private
|
146
140
|
|
147
|
-
def
|
141
|
+
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
142
|
+
return super unless klass.physically_inheritances?
|
143
|
+
|
148
144
|
auto_cast = _auto_cast_attribute.to_s
|
149
145
|
record_class = _record_class_attribute.to_s
|
146
|
+
return super unless attributes.key?(record_class) &&
|
147
|
+
attributes.delete(auto_cast) && attributes[record_class] != table_name
|
150
148
|
|
151
|
-
|
152
|
-
|
149
|
+
klass = casted_dependents[attributes[record_class]]
|
150
|
+
raise_unable_to_cast(attributes[record_class]) if klass.nil?
|
151
|
+
filter_attributes_for_cast(attributes, klass)
|
153
152
|
|
154
|
-
klass
|
155
|
-
raise_unable_to_cast(record[record_class]) if klass.nil?
|
156
|
-
filter_attributes_for_cast(record, klass)
|
157
|
-
klass
|
153
|
+
super(klass, attributes, column_types, &block)
|
158
154
|
end
|
159
155
|
|
160
156
|
# Filter the record attributes to be loaded to not included those from
|
@@ -12,28 +12,21 @@ module Torque
|
|
12
12
|
false
|
13
13
|
end
|
14
14
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
klass_scope = klass_join_scope(table, predicate_builder)
|
29
|
-
|
30
|
-
klass_scope.where!(build_id_constraint_between(table, foreign_table))
|
31
|
-
klass_scope.where!(type => foreign_klass.polymorphic_name) if type
|
32
|
-
klass_scope.where!(klass.send(:type_condition, table)) \
|
33
|
-
if klass.finder_needs_type_condition?
|
15
|
+
# Fix where the join_scope method is the one now responsible for
|
16
|
+
# building the join condition
|
17
|
+
def join_scope(table, foreign_table, foreign_klass)
|
18
|
+
return super unless connected_through_array?
|
19
|
+
|
20
|
+
predicate_builder = predicate_builder(table)
|
21
|
+
scope_chain_items = join_scopes(table, predicate_builder)
|
22
|
+
klass_scope = klass_join_scope(table, predicate_builder)
|
23
|
+
|
24
|
+
klass_scope.where!(build_id_constraint_between(table, foreign_table))
|
25
|
+
klass_scope.where!(type => foreign_klass.polymorphic_name) if type
|
26
|
+
klass_scope.where!(klass.send(:type_condition, table)) \
|
27
|
+
if klass.finder_needs_type_condition?
|
34
28
|
|
35
|
-
|
36
|
-
end
|
29
|
+
scope_chain_items.inject(klass_scope, &:merge!)
|
37
30
|
end
|
38
31
|
|
39
32
|
# Manually build the join constraint
|
@@ -50,9 +43,9 @@ module Torque
|
|
50
43
|
return klass_attr.eq(source_attr) unless connected_through_array?
|
51
44
|
|
52
45
|
# Klass and key are associated with the reflection Class
|
53
|
-
klass_type = klass.columns_hash[
|
46
|
+
klass_type = klass.columns_hash[join_keys.key.to_s]
|
54
47
|
# active_record and foreign_key are associated with the source Class
|
55
|
-
source_type = active_record.columns_hash[
|
48
|
+
source_type = active_record.columns_hash[join_keys.foreign_key.to_s]
|
56
49
|
|
57
50
|
# If both are attributes but the left side is not an array, and the
|
58
51
|
# right side is, use the ANY operation
|
@@ -78,8 +71,8 @@ module Torque
|
|
78
71
|
private
|
79
72
|
|
80
73
|
def build_id_constraint_between(table, foreign_table)
|
81
|
-
klass_attr = table[
|
82
|
-
source_attr = foreign_table[
|
74
|
+
klass_attr = table[join_keys.key.to_s]
|
75
|
+
source_attr = foreign_table[join_keys.foreign_key.to_s]
|
83
76
|
|
84
77
|
build_id_constraint(klass_attr, source_attr)
|
85
78
|
end
|
@@ -105,6 +98,7 @@ module Torque
|
|
105
98
|
# returns either +nil+ or the inverse association name that it finds.
|
106
99
|
def automatic_inverse_of
|
107
100
|
return super unless connected_through_array?
|
101
|
+
|
108
102
|
if can_find_inverse_of_automatically?(self)
|
109
103
|
inverse_name = options[:as] || active_record.name.demodulize
|
110
104
|
inverse_name = ActiveSupport::Inflector.underscore(inverse_name)
|
@@ -52,11 +52,8 @@ module Torque
|
|
52
52
|
when Array
|
53
53
|
resolve_column(item, base)
|
54
54
|
when Hash
|
55
|
-
raise ArgumentError,
|
56
|
-
item.map
|
57
|
-
other_list = [other_list] unless other_list.kind_of? Enumerable
|
58
|
-
resolve_column(other_list, key)
|
59
|
-
end
|
55
|
+
raise ArgumentError, 'Unsupported Hash for attributes on third level' if base
|
56
|
+
item.map { |key, other_list| resolve_column(Array.wrap(other_list), key) }
|
60
57
|
else
|
61
58
|
raise ArgumentError, "Unsupported argument type: #{value} (#{value.class})"
|
62
59
|
end
|
@@ -138,19 +135,17 @@ module Torque
|
|
138
135
|
ActiveRecord::Relation::SINGLE_VALUE_METHODS += Relation::SINGLE_VALUE_METHODS
|
139
136
|
ActiveRecord::Relation::MULTI_VALUE_METHODS += Relation::MULTI_VALUE_METHODS
|
140
137
|
ActiveRecord::Relation::VALUE_METHODS += Relation::VALUE_METHODS
|
141
|
-
ActiveRecord::QueryMethods::VALID_UNSCOPING_VALUES += [
|
142
|
-
|
138
|
+
ActiveRecord::QueryMethods::VALID_UNSCOPING_VALUES += %i[cast_records itself_only
|
139
|
+
distinct_on auxiliary_statements]
|
143
140
|
|
144
|
-
|
145
|
-
|
146
|
-
ActiveRecord::QueryMethods::DEFAULT_VALUES[value]
|
147
|
-
|
148
|
-
end
|
141
|
+
Relation::SINGLE_VALUE_METHODS.each do |value|
|
142
|
+
ActiveRecord::QueryMethods::DEFAULT_VALUES[value] = nil \
|
143
|
+
if ActiveRecord::QueryMethods::DEFAULT_VALUES[value].nil?
|
144
|
+
end
|
149
145
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
146
|
+
Relation::MULTI_VALUE_METHODS.each do |value|
|
147
|
+
ActiveRecord::QueryMethods::DEFAULT_VALUES[value] ||= \
|
148
|
+
ActiveRecord::QueryMethods::FROZEN_EMPTY_ARRAY
|
154
149
|
end
|
155
150
|
|
156
151
|
$VERBOSE = warn_level
|
@@ -33,14 +33,8 @@ module Torque
|
|
33
33
|
# Get all auxiliary statements bound attributes and the base bound
|
34
34
|
# attributes as well
|
35
35
|
def bound_attributes
|
36
|
-
|
37
|
-
|
38
|
-
visitor.accept(self.arel.ast, ::Arel::Collectors::Bind.new).value
|
39
|
-
else
|
40
|
-
return super unless self.auxiliary_statements_values.present?
|
41
|
-
bindings = self.auxiliary_statements_values.map(&:bound_attributes)
|
42
|
-
(bindings + super).flatten
|
43
|
-
end
|
36
|
+
visitor = ::Arel::Visitors::PostgreSQL.new(ActiveRecord::Base.connection)
|
37
|
+
visitor.accept(self.arel.ast, ::Arel::Collectors::Bind.new).value
|
44
38
|
end
|
45
39
|
|
46
40
|
private
|
@@ -33,7 +33,7 @@ module Torque
|
|
33
33
|
|
34
34
|
# Hook arel build to add the distinct on clause
|
35
35
|
def build_arel(*)
|
36
|
-
arel =
|
36
|
+
arel = super
|
37
37
|
value = self.distinct_on_values
|
38
38
|
arel.distinct_on(resolve_column(value)) if value.present?
|
39
39
|
arel
|
data/spec/en.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
en:
|
2
|
+
torque: 'Torque Rocks!'
|
3
|
+
activerecord:
|
4
|
+
attributes:
|
5
|
+
user:
|
6
|
+
role:
|
7
|
+
visitor: 'A simple Visitor'
|
8
|
+
role:
|
9
|
+
assistant: 'An Assistant'
|
10
|
+
enums:
|
11
|
+
content_status:
|
12
|
+
created: '1 - Created'
|
13
|
+
roles:
|
14
|
+
manager: 'The Manager'
|
15
|
+
enum:
|
16
|
+
content_status:
|
17
|
+
draft: 'Draft (2)'
|
18
|
+
published: 'Finally published'
|
19
|
+
admin: 'Super Duper Admin'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :comment do
|
3
|
+
content { Faker::Lorem.paragraph }
|
4
|
+
|
5
|
+
factory :comment_recursive do
|
6
|
+
comment_id { Comment.order('RANDOM()').first.id }
|
7
|
+
end
|
8
|
+
|
9
|
+
trait :random_user do
|
10
|
+
user_id { User.order('RANDOM()').first.id }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Mocks
|
2
|
+
module CacheQuery
|
3
|
+
def get_last_executed_query(&block)
|
4
|
+
conn = ActiveRecord::Base.connection
|
5
|
+
conn.instance_variable_set(:@query_cache_enabled, true)
|
6
|
+
|
7
|
+
block.call
|
8
|
+
result = conn.query_cache.keys.first
|
9
|
+
|
10
|
+
conn.instance_variable_set(:@query_cache_enabled, false)
|
11
|
+
conn.instance_variable_get(:@query_cache).delete(result)
|
12
|
+
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Mocks
|
2
|
+
module CreateTable
|
3
|
+
def mock_create_table
|
4
|
+
path = ActiveRecord::Base.connection.method(:create_table).super_method.source_location[0]
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
ActiveRecord::ConnectionAdapters::SchemaStatements.send(:define_method, :create_table) do |table_name, **options, &block|
|
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
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
after :all do
|
31
|
+
load path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|