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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/torque/postgresql.rb +0 -2
  3. data/lib/torque/postgresql/adapter.rb +0 -1
  4. data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
  5. data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
  6. data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
  7. data/lib/torque/postgresql/adapter/schema_dumper.rb +71 -11
  8. data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
  9. data/lib/torque/postgresql/associations.rb +0 -3
  10. data/lib/torque/postgresql/associations/association_scope.rb +18 -60
  11. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +12 -15
  12. data/lib/torque/postgresql/associations/preloader.rb +0 -32
  13. data/lib/torque/postgresql/associations/preloader/association.rb +13 -9
  14. data/lib/torque/postgresql/autosave_association.rb +4 -4
  15. data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
  16. data/lib/torque/postgresql/coder.rb +1 -2
  17. data/lib/torque/postgresql/config.rb +0 -6
  18. data/lib/torque/postgresql/inheritance.rb +13 -17
  19. data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
  20. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -38
  21. data/lib/torque/postgresql/relation.rb +11 -16
  22. data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
  23. data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
  24. data/lib/torque/postgresql/version.rb +1 -1
  25. data/spec/en.yml +19 -0
  26. data/spec/factories/authors.rb +6 -0
  27. data/spec/factories/comments.rb +13 -0
  28. data/spec/factories/posts.rb +6 -0
  29. data/spec/factories/tags.rb +5 -0
  30. data/spec/factories/texts.rb +5 -0
  31. data/spec/factories/users.rb +6 -0
  32. data/spec/factories/videos.rb +5 -0
  33. data/spec/mocks/cache_query.rb +16 -0
  34. data/spec/mocks/create_table.rb +35 -0
  35. data/spec/models/activity.rb +3 -0
  36. data/spec/models/activity_book.rb +4 -0
  37. data/spec/models/activity_post.rb +7 -0
  38. data/spec/models/activity_post/sample.rb +4 -0
  39. data/spec/models/author.rb +4 -0
  40. data/spec/models/author_journalist.rb +4 -0
  41. data/spec/models/comment.rb +3 -0
  42. data/spec/models/course.rb +2 -0
  43. data/spec/models/geometry.rb +2 -0
  44. data/spec/models/guest_comment.rb +4 -0
  45. data/spec/models/post.rb +6 -0
  46. data/spec/models/tag.rb +2 -0
  47. data/spec/models/text.rb +2 -0
  48. data/spec/models/time_keeper.rb +2 -0
  49. data/spec/models/user.rb +8 -0
  50. data/spec/models/video.rb +2 -0
  51. data/spec/schema.rb +141 -0
  52. data/spec/spec_helper.rb +59 -0
  53. data/spec/tests/arel_spec.rb +72 -0
  54. data/spec/tests/auxiliary_statement_spec.rb +593 -0
  55. data/spec/tests/belongs_to_many_spec.rb +240 -0
  56. data/spec/tests/coder_spec.rb +367 -0
  57. data/spec/tests/collector_spec.rb +59 -0
  58. data/spec/tests/distinct_on_spec.rb +65 -0
  59. data/spec/tests/enum_set_spec.rb +306 -0
  60. data/spec/tests/enum_spec.rb +621 -0
  61. data/spec/tests/geometric_builder_spec.rb +221 -0
  62. data/spec/tests/has_many_spec.rb +390 -0
  63. data/spec/tests/interval_spec.rb +167 -0
  64. data/spec/tests/lazy_spec.rb +24 -0
  65. data/spec/tests/period_spec.rb +954 -0
  66. data/spec/tests/quoting_spec.rb +24 -0
  67. data/spec/tests/range_spec.rb +36 -0
  68. data/spec/tests/relation_spec.rb +57 -0
  69. data/spec/tests/table_inheritance_spec.rb +403 -0
  70. metadata +103 -15
  71. data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
  72. 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(preloader)
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 = load_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
- load_records.each do |ids, record|
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
- unless Torque::PostgreSQL::AR521
51
- def associate_records_to_owner(owner, records)
52
- association = owner.association(reflection.name)
53
- association.loaded!
54
- association.target.concat(records)
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
- klass_attr = reflection.active_record_primary_key
29
- source_attr = reflection.foreign_key
28
+ klass_fk = reflection.foreign_key
29
+ acpk = reflection.active_record_primary_key
30
30
 
31
- records = association.target.each_with_object(klass_attr)
32
- write_attribute(source_attr, records.map(&:read_attribute).compact)
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
- # Add the scopes defined by the reflection
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)
@@ -3,8 +3,7 @@ module Torque
3
3
  module Coder
4
4
 
5
5
  # This class represents an Record to be encoded, instead of a literal Array
6
- class Record < Array
7
- end
6
+ Record = Class.new(Array)
8
7
 
9
8
  class << self
10
9
 
@@ -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
- if parent < Base && !parent.abstract_class?
105
- contained = parent.table_name
106
- contained = contained.singularize if parent.pluralize_table_names
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 discriminate_class_for_record(record) # :nodoc:
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
- return super unless record.key?(record_class) &&
152
- record.delete(auto_cast) && record[record_class] != table_name
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 = casted_dependents[record[record_class]]
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
- # Monkey patching for rais 5.0
16
- def torque_join_keys
17
- method(:join_keys).arity.eql?(0) ? join_keys : join_keys(klass)
18
- end
19
-
20
- # Fix for rails 5.2.3 where the join_scope method is the one now
21
- # responsible for building the join condition
22
- if Torque::PostgreSQL::AR523
23
- def join_scope(table, foreign_table, foreign_klass)
24
- return super unless connected_through_array?
25
-
26
- predicate_builder = predicate_builder(table)
27
- scope_chain_items = join_scopes(table, predicate_builder)
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
- scope_chain_items.inject(klass_scope, &:merge!)
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[torque_join_keys.key.to_s]
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[torque_join_keys.foreign_key.to_s]
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[torque_join_keys.key.to_s]
82
- source_attr = foreign_table[torque_join_keys.foreign_key.to_s]
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[:foreign_key] || derive_foreign_key.freeze
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[:primary_key] || derive_primary_key
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
- unless PostgreSQL::AR520
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 derive_foreign_key
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, "Unsupported Hash for attributes on third level" if base
56
- item.map do |key, other_list|
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 += [:cast_records, :itself_only,
142
- :distinct_on, :auxiliary_statements]
138
+ ActiveRecord::QueryMethods::VALID_UNSCOPING_VALUES += %i[cast_records itself_only
139
+ distinct_on auxiliary_statements]
143
140
 
144
- if ActiveRecord::QueryMethods.const_defined?('DEFAULT_VALUES')
145
- Relation::SINGLE_VALUE_METHODS.each do |value|
146
- ActiveRecord::QueryMethods::DEFAULT_VALUES[value] = nil \
147
- if ActiveRecord::QueryMethods::DEFAULT_VALUES[value].nil?
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
- Relation::MULTI_VALUE_METHODS.each do |value|
151
- ActiveRecord::QueryMethods::DEFAULT_VALUES[value] ||= \
152
- ActiveRecord::QueryMethods::FROZEN_EMPTY_ARRAY
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
- if Torque::PostgreSQL::AR521
37
- visitor = ::Arel::Visitors::PostgreSQL.new(ActiveRecord::Base.connection)
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 = Torque::PostgreSQL::AR521 ? super : super()
36
+ arel = super
37
37
  value = self.distinct_on_values
38
38
  arel.distinct_on(resolve_column(value)) if value.present?
39
39
  arel
@@ -1,5 +1,5 @@
1
1
  module Torque
2
2
  module PostgreSQL
3
- VERSION = '1.1.7'
3
+ VERSION = '2.0.0'
4
4
  end
5
5
  end
@@ -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,6 @@
1
+ FactoryBot.define do
2
+ factory :author do
3
+ name { Faker::Name.name }
4
+ specialty { Enum::Specialties.values.sample }
5
+ end
6
+ end