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.
Files changed (70) hide show
  1. checksums.yaml +5 -5
  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 +74 -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 +2 -1
  12. data/lib/torque/postgresql/associations/preloader.rb +0 -24
  13. data/lib/torque/postgresql/associations/preloader/association.rb +13 -9
  14. data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
  15. data/lib/torque/postgresql/coder.rb +1 -2
  16. data/lib/torque/postgresql/config.rb +0 -4
  17. data/lib/torque/postgresql/inheritance.rb +13 -17
  18. data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
  19. data/lib/torque/postgresql/relation.rb +11 -16
  20. data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
  21. data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
  22. data/lib/torque/postgresql/version.rb +1 -1
  23. data/spec/en.yml +19 -0
  24. data/spec/factories/authors.rb +6 -0
  25. data/spec/factories/comments.rb +13 -0
  26. data/spec/factories/posts.rb +6 -0
  27. data/spec/factories/tags.rb +5 -0
  28. data/spec/factories/texts.rb +5 -0
  29. data/spec/factories/users.rb +6 -0
  30. data/spec/factories/videos.rb +5 -0
  31. data/spec/mocks/cache_query.rb +16 -0
  32. data/spec/mocks/create_table.rb +35 -0
  33. data/spec/models/activity.rb +3 -0
  34. data/spec/models/activity_book.rb +4 -0
  35. data/spec/models/activity_post.rb +7 -0
  36. data/spec/models/activity_post/sample.rb +4 -0
  37. data/spec/models/author.rb +4 -0
  38. data/spec/models/author_journalist.rb +4 -0
  39. data/spec/models/comment.rb +3 -0
  40. data/spec/models/course.rb +2 -0
  41. data/spec/models/geometry.rb +2 -0
  42. data/spec/models/guest_comment.rb +4 -0
  43. data/spec/models/post.rb +6 -0
  44. data/spec/models/tag.rb +2 -0
  45. data/spec/models/text.rb +2 -0
  46. data/spec/models/time_keeper.rb +2 -0
  47. data/spec/models/user.rb +8 -0
  48. data/spec/models/video.rb +2 -0
  49. data/spec/schema.rb +141 -0
  50. data/spec/spec_helper.rb +59 -0
  51. data/spec/tests/arel_spec.rb +72 -0
  52. data/spec/tests/auxiliary_statement_spec.rb +593 -0
  53. data/spec/tests/belongs_to_many_spec.rb +240 -0
  54. data/spec/tests/coder_spec.rb +367 -0
  55. data/spec/tests/collector_spec.rb +59 -0
  56. data/spec/tests/distinct_on_spec.rb +65 -0
  57. data/spec/tests/enum_set_spec.rb +306 -0
  58. data/spec/tests/enum_spec.rb +628 -0
  59. data/spec/tests/geometric_builder_spec.rb +221 -0
  60. data/spec/tests/has_many_spec.rb +390 -0
  61. data/spec/tests/interval_spec.rb +167 -0
  62. data/spec/tests/lazy_spec.rb +24 -0
  63. data/spec/tests/period_spec.rb +954 -0
  64. data/spec/tests/quoting_spec.rb +24 -0
  65. data/spec/tests/range_spec.rb +36 -0
  66. data/spec/tests/relation_spec.rb +57 -0
  67. data/spec/tests/table_inheritance_spec.rb +416 -0
  68. metadata +103 -16
  69. data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
  70. 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(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)
@@ -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,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
- 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)
@@ -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.4'
3
+ VERSION = '2.0.2'
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
@@ -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,6 @@
1
+ FactoryBot.define do
2
+ factory :post do
3
+ title { Faker::Lorem.sentence }
4
+ content { Faker::Lorem.paragraph }
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ FactoryBot.define do
2
+ factory :tag do
3
+ name { Faker::Lorem.sentence }
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ FactoryBot.define do
2
+ factory :text do
3
+ content { Faker::Lorem.sentence }
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ factory :user do
3
+ name { Faker::Name.name }
4
+ role { 'visitor' }
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ FactoryBot.define do
2
+ factory :video do
3
+ title { Faker::Lorem.sentence }
4
+ end
5
+ 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
@@ -0,0 +1,3 @@
1
+ class Activity < ActiveRecord::Base
2
+ belongs_to :author
3
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'activity'
2
+
3
+ class ActivityBook < Activity
4
+ end