torque-postgresql 1.1.7 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- 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 +71 -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 +12 -15
- data/lib/torque/postgresql/associations/preloader.rb +0 -32
- data/lib/torque/postgresql/associations/preloader/association.rb +13 -9
- data/lib/torque/postgresql/autosave_association.rb +4 -4
- data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
- data/lib/torque/postgresql/coder.rb +1 -2
- data/lib/torque/postgresql/config.rb +0 -6
- data/lib/torque/postgresql/inheritance.rb +13 -17
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -38
- 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 +621 -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 +403 -0
- metadata +103 -15
- data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
- data/lib/torque/postgresql/schema_dumper.rb +0 -91
@@ -1,33 +1 @@
|
|
1
1
|
require_relative 'preloader/association'
|
2
|
-
|
3
|
-
unless Torque::PostgreSQL::AR521
|
4
|
-
module Torque
|
5
|
-
module PostgreSQL
|
6
|
-
module Associations
|
7
|
-
module Preloader
|
8
|
-
class BelongsToMany < ::ActiveRecord::Associations::Preloader::HasMany
|
9
|
-
def association_key_name
|
10
|
-
reflection.active_record_primary_key
|
11
|
-
end
|
12
|
-
|
13
|
-
def owner_key_name
|
14
|
-
reflection.foreign_key
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def preloader_for(reflection, owners, *)
|
19
|
-
return AlreadyLoaded \
|
20
|
-
if owners.first.association(reflection.name).loaded?
|
21
|
-
|
22
|
-
return BelongsToMany \
|
23
|
-
if reflection.macro.eql?(:belongs_to_many)
|
24
|
-
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
::ActiveRecord::Associations::Preloader.prepend(Preloader)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -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)
|
@@ -25,11 +25,11 @@ module Torque
|
|
25
25
|
association = association_instance_get(reflection.name)
|
26
26
|
return unless association
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
klass_fk = reflection.foreign_key
|
29
|
+
acpk = reflection.active_record_primary_key
|
30
30
|
|
31
|
-
records = association.target.each_with_object(
|
32
|
-
write_attribute(
|
31
|
+
records = association.target.each_with_object(klass_fk)
|
32
|
+
write_attribute(acpk, records.map(&:read_attribute).compact)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -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,12 +2,6 @@ module Torque
|
|
2
2
|
module PostgreSQL
|
3
3
|
include ActiveSupport::Configurable
|
4
4
|
|
5
|
-
# Stores a version check for compatibility purposes
|
6
|
-
AR510 = (ActiveRecord.gem_version >= Gem::Version.new('5.1.0'))
|
7
|
-
AR520 = (ActiveRecord.gem_version >= Gem::Version.new('5.2.0'))
|
8
|
-
AR521 = (ActiveRecord.gem_version >= Gem::Version.new('5.2.1'))
|
9
|
-
AR523 = (ActiveRecord.gem_version >= Gem::Version.new('5.2.3'))
|
10
|
-
|
11
5
|
# Use the same logger as the Active Record one
|
12
6
|
def self.logger
|
13
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)
|
@@ -10,10 +10,6 @@ module Torque
|
|
10
10
|
true
|
11
11
|
end
|
12
12
|
|
13
|
-
def belongs_to?
|
14
|
-
true
|
15
|
-
end
|
16
|
-
|
17
13
|
def collection?
|
18
14
|
true
|
19
15
|
end
|
@@ -23,7 +19,7 @@ module Torque
|
|
23
19
|
end
|
24
20
|
|
25
21
|
def foreign_key
|
26
|
-
@foreign_key ||= options[:
|
22
|
+
@foreign_key ||= options[:primary_key] || derive_foreign_key.freeze
|
27
23
|
end
|
28
24
|
|
29
25
|
def association_foreign_key
|
@@ -31,46 +27,16 @@ module Torque
|
|
31
27
|
end
|
32
28
|
|
33
29
|
def active_record_primary_key
|
34
|
-
@active_record_primary_key ||= options[:
|
35
|
-
end
|
36
|
-
|
37
|
-
unless PostgreSQL::AR510
|
38
|
-
def join_keys(*)
|
39
|
-
JoinKeys.new(join_pk, join_fk)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
if PostgreSQL::AR520
|
44
|
-
def join_primary_key(*)
|
45
|
-
active_record_primary_key
|
46
|
-
end
|
47
|
-
|
48
|
-
def join_foreign_key
|
49
|
-
foreign_key
|
50
|
-
end
|
51
|
-
else
|
52
|
-
def join_id_for(owner)
|
53
|
-
owner[foreign_key]
|
54
|
-
end
|
30
|
+
@active_record_primary_key ||= options[:foreign_key] || derive_primary_key
|
55
31
|
end
|
56
32
|
|
57
33
|
private
|
58
34
|
|
59
|
-
|
60
|
-
def join_pk(*)
|
61
|
-
active_record_primary_key
|
62
|
-
end
|
63
|
-
|
64
|
-
def join_fk
|
65
|
-
foreign_key
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def derive_primary_key
|
35
|
+
def derive_foreign_key
|
70
36
|
klass.primary_key
|
71
37
|
end
|
72
38
|
|
73
|
-
def
|
39
|
+
def derive_primary_key
|
74
40
|
"#{name.to_s.singularize}_ids"
|
75
41
|
end
|
76
42
|
end
|
@@ -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'
|