torque-postgresql 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a161d5be06adc9e93a5888b4b16b1a07b6f4a613
4
- data.tar.gz: 51c1aae61c09bfa681e02daeb523551da9ef1139
3
+ metadata.gz: b95a55ebfe82e5b96582da09307f1d45d62ba922
4
+ data.tar.gz: 38a068ec3fa7cc5aab2425a31e8ed37a49b15132
5
5
  SHA512:
6
- metadata.gz: 99892a8ca40bf830e81accb0d4f15d87fbcb44697b95b4f9854a81b2e5d733c80c1a23f7312009407741ea11e7a6cb4d29acae8b03284226d618fadfbaa6a9da
7
- data.tar.gz: 53e5888be0f1c89d2e30c85acc2ad350e34ad5efe6b6d899315a4cd18affe9366978d5cfd7e4ddad7843c0856ca11f7243f106d5d4ad2f538a86e2c11d5551ce
6
+ metadata.gz: b708c73cfcac1798474e35099570e4def16a6a51b2448ef34ce2ec5e97dd4c8d57f48fa51f18182f0405afa7eed1884b81846cb3b2e132b638c8711ee80dd72c
7
+ data.tar.gz: 7bab6bd30e0b3898826b20cce99ff629192ea62d772864e10a3fc141e86e5f3f27a0f9e459addc29a8a27bcbb7df17920831d0f7e7b01ac823d65f600b699530
@@ -20,6 +20,7 @@ module Torque
20
20
  # 5.2 reflection, owner
21
21
 
22
22
  reflection = args.size.eql?(2) ? args[0] : args[1]
23
+ puts reflection.connected_through_array?.inspect
23
24
  return super unless reflection.connected_through_array?
24
25
 
25
26
  table = args[0] if args.size > 2
@@ -4,19 +4,3 @@ require_relative 'attributes/builder'
4
4
  require_relative 'attributes/enum'
5
5
  require_relative 'attributes/enum_set'
6
6
  require_relative 'attributes/period'
7
-
8
- module Torque
9
- module PostgreSQL
10
- module Attributes
11
- extend ActiveSupport::Concern
12
-
13
- # Configure enum_save_on_bang behavior
14
- included do
15
- class_attribute :enum_save_on_bang, instance_accessor: true
16
- self.enum_save_on_bang = Torque::PostgreSQL.config.enum.save_on_bang
17
- end
18
- end
19
-
20
- ActiveRecord::Base.include Attributes
21
- end
22
- end
@@ -5,14 +5,14 @@ module Torque
5
5
  module PostgreSQL
6
6
  module Attributes
7
7
  module Builder
8
- def self.include_on(klass, method_name, builder_klass, &block)
8
+ def self.include_on(klass, method_name, builder_klass, **extra, &block)
9
9
  klass.define_singleton_method(method_name) do |*args, **options|
10
10
  return unless connection.table_exists?(table_name)
11
11
 
12
12
  args.each do |attribute|
13
13
  begin
14
14
  # Generate methods on self class
15
- builder = builder_klass.new(self, attribute, options)
15
+ builder = builder_klass.new(self, attribute, extra.merge(options))
16
16
  builder.conflicting?
17
17
  builder.build
18
18
 
@@ -5,7 +5,8 @@ module Torque
5
5
  class Enum
6
6
  VALID_TYPES = %i[enum enum_set].freeze
7
7
 
8
- attr_accessor :klass, :attribute, :subtype, :options, :values, :enum_module
8
+ attr_accessor :klass, :attribute, :subtype, :options, :values,
9
+ :klass_module, :instance_module
9
10
 
10
11
  # Start a new builder of methods for enum values on ActiveRecord::Base
11
12
  def initialize(klass, attribute, options)
@@ -40,15 +41,20 @@ module Torque
40
41
 
41
42
  @values_methods = begin
42
43
  values.map do |val|
43
- val = val.tr('-', '_')
44
- scope = base % val
44
+ key = val.downcase.tr('- ', '__')
45
+ scope = base % key
45
46
  ask = scope + '?'
46
47
  bang = scope + '!'
47
- [val, [scope, ask, bang]]
48
+ [key, [scope, ask, bang, val]]
48
49
  end.to_h
49
50
  end
50
51
  end
51
52
 
53
+ # Check if it's building the methods for sets
54
+ def set_features?
55
+ options[:set_features].present?
56
+ end
57
+
52
58
  # Check if any of the methods that will be created get in conflict
53
59
  # with the base class methods
54
60
  def conflicting?
@@ -56,12 +62,20 @@ module Torque
56
62
  attributes = attribute.pluralize
57
63
 
58
64
  dangerous?(attributes, true)
59
- dangerous?("#{attributes}_options", true)
65
+ dangerous?("#{attributes}_keys", true)
60
66
  dangerous?("#{attributes}_texts", true)
67
+ dangerous?("#{attributes}_options", true)
61
68
  dangerous?("#{attribute}_text")
62
69
 
63
- values_methods.each do |attr, list|
64
- list.map(&method(:dangerous?))
70
+ if set_features?
71
+ dangerous?("has_#{attributes}", true)
72
+ dangerous?("has_any_#{attributes}", true)
73
+ end
74
+
75
+ values_methods.each do |attr, (scope, ask, bang, *)|
76
+ dangerous?(scope, true)
77
+ dangerous?(bang)
78
+ dangerous?(ask)
65
79
  end
66
80
  rescue Interrupt => err
67
81
  raise ArgumentError, <<-MSG.squish
@@ -73,14 +87,16 @@ module Torque
73
87
 
74
88
  # Create all methods needed
75
89
  def build
76
- @enum_module = Module.new
90
+ @klass_module = Module.new
91
+ @instance_module = Module.new
77
92
 
78
93
  plural
79
94
  stringify
80
95
  all_values
96
+ set_scopes if set_features?
81
97
 
82
- klass.include enum_module
83
- klass.extend enum_module::ClassMethods
98
+ klass.extend klass_module
99
+ klass.include instance_module
84
100
  end
85
101
 
86
102
  private
@@ -96,78 +112,99 @@ module Torque
96
112
  raise Interrupt, method_name.to_s
97
113
  end
98
114
  end
115
+ rescue Interrupt => e
116
+ raise e if Torque::PostgreSQL.config.enum.raise_conflicting
117
+ type = class_method ? 'class method' : 'instance method'
118
+ indicator = class_method ? '.' : '#'
119
+
120
+ Torque::PostgreSQL.logger.info(<<~MSG.squish)
121
+ Creating #{class_method} :#{method_name} for enum.
122
+ Overwriting existing method #{klass.name}#{indicator}#{method_name}.
123
+ MSG
99
124
  end
100
125
 
101
126
  # Create the method that allow access to the list of values
102
127
  def plural
103
- attr = attribute
104
- enum_klass = subtype.klass
105
-
106
- # TODO: Rewrite these as string
107
- enum_module.const_set('ClassMethods', Module.new)
108
- enum_module::ClassMethods.module_eval do
109
- # def self.statuses() statuses end
110
- define_method(attr.pluralize) do
111
- enum_klass.values
112
- end
113
-
114
- # def self.statuses_texts() members.map(&:text) end
115
- define_method(attr.pluralize + '_texts') do
116
- enum_klass.members.map do |member|
117
- member.text(attr, self)
118
- end
119
- end
128
+ enum_klass = subtype.klass.name
129
+ klass_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
130
+ def #{attribute.pluralize} # def roles
131
+ ::#{enum_klass}.values # Enum::Roles.values
132
+ end # end
133
+
134
+ def #{attribute.pluralize}_keys # def roles_keys
135
+ ::#{enum_klass}.keys # Enum::Roles.keys
136
+ end # end
137
+
138
+ def #{attribute.pluralize}_texts # def roles_texts
139
+ ::#{enum_klass}.members.map do |member| # Enum::Roles.members do |member|
140
+ member.text('#{attribute}', self) # member.text('role', self)
141
+ end # end
142
+ end # end
143
+
144
+ def #{attribute.pluralize}_options # def roles_options
145
+ #{attribute.pluralize}_texts.zip(::#{enum_klass}.values) # roles_texts.zip(Enum::Roles.values)
146
+ end # end
147
+ RUBY
148
+ end
120
149
 
121
- # def self.statuses_options() statuses_texts.zip(statuses) end
122
- define_method(attr.pluralize + '_options') do
123
- public_send(attr.pluralize + '_texts').zip(enum_klass.values)
124
- end
125
- end
150
+ # Create additional methods when the enum is a set, which needs
151
+ # better ways to check if values are present or not
152
+ def set_scopes
153
+ cast_type = subtype.name.chomp('[]')
154
+ klass_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
155
+ def has_#{attribute.pluralize}(*values) # def has_roles(*values)
156
+ attr = arel_attribute('#{attribute}') # attr = arel_attribute('role')
157
+ where(attr.contains(::Arel.array(values, cast: '#{cast_type}'))) # where(attr.contains(::Arel.array(values, cast: 'roles')))
158
+ end # end
159
+
160
+ def has_any_#{attribute.pluralize}(*values) # def has_roles(*values)
161
+ attr = arel_attribute('#{attribute}') # attr = arel_attribute('role')
162
+ where(attr.overlaps(::Arel.array(values, cast: '#{cast_type}'))) # where(attr.overlaps(::Arel.array(values, cast: 'roles')))
163
+ end # end
164
+ RUBY
126
165
  end
127
166
 
128
167
  # Create the method that turn the attribute value into text using
129
168
  # the model scope
130
169
  def stringify
131
- attr = attribute
132
-
133
- # TODO: Rewrite these as string
134
- enum_module.module_eval do
135
- # def status_text() status.text('status', self) end
136
- define_method("#{attr}_text") { send(attr)&.text(attr, self) }
137
- end
170
+ instance_module.module_eval <<-RUBY, __FILE__, __LINE__ + 1
171
+ def #{attribute}_text # def role_text
172
+ #{attribute}.text('#{attribute}', self) # role.text('role', self)
173
+ end # end
174
+ RUBY
138
175
  end
139
176
 
140
177
  # Create all the methods that represent actions related to the
141
178
  # attribute value
142
179
  def all_values
143
- attr = attribute
144
- vals = values_methods
145
-
146
- enum_klass = subtype.klass
147
- model_klass = klass
148
-
149
- # TODO: Rewrite these as string
150
- enum_module.module_eval do
151
- vals.each do |val, list|
152
- # scope :disabled, -> { where(status: 'disabled') }
153
- model_klass.scope list[0], -> do
154
- where(enum_klass.scope(arel_table[attr], val))
155
- end
156
-
157
- # def disabled? status.disabled? end
158
- define_method(list[1]) { send(attr).public_send("#{val}?") }
159
-
160
- # def disabled!
161
- # changed = send(attr).public_send("#{val}!")
162
- # save! if changed && enum_save_on_bang
163
- # true
164
- define_method(list[2]) do
165
- changed = send(attr).public_send("#{val}!")
166
- return save! if changed && enum_save_on_bang
167
- true
168
- end
169
- end
180
+ klass_content = ''
181
+ instance_content = ''
182
+ enum_klass = subtype.klass.name
183
+
184
+ values_methods.each do |key, (scope, ask, bang, val)|
185
+ klass_content += <<-RUBY
186
+ def #{scope} # def admin
187
+ attr = arel_attribute('#{attribute}') # attr = arel_attribute('role')
188
+ where(::#{enum_klass}.scope(attr, '#{val}')) # where(Enum::Roles.scope(attr, 'admin'))
189
+ end # end
190
+ RUBY
191
+
192
+ instance_content += <<-RUBY
193
+ def #{ask} # def admin?
194
+ #{attribute}.#{key}? # role.admin?
195
+ end # end
196
+
197
+ def #{bang} # admin!
198
+ self.#{attribute} = '#{val}' # self.role = 'admin'
199
+ return unless #{attribute}_changed? # return unless role_changed?
200
+ return save! if Torque::PostgreSQL.config.enum.save_on_bang
201
+ true # true
202
+ end # end
203
+ RUBY
170
204
  end
205
+
206
+ klass_module.module_eval(klass_content)
207
+ instance_module.module_eval(instance_content)
171
208
  end
172
209
  end
173
210
  end
@@ -164,7 +164,7 @@ module Torque
164
164
  method_content = define_string_method(method_name, method_content, args)
165
165
 
166
166
  source_module = send("#{type}_module")
167
- source_module.class_eval(method_content)
167
+ source_module.module_eval(method_content)
168
168
  end
169
169
 
170
170
  private
@@ -51,9 +51,14 @@ module Torque
51
51
  end
52
52
  end
53
53
 
54
+ # List of valus as symbols
55
+ def keys
56
+ values.map(&:to_sym)
57
+ end
58
+
54
59
  # Different from values, it returns the list of items already casted
55
60
  def members
56
- values.dup.map(&method(:new))
61
+ values.map(&method(:new))
57
62
  end
58
63
 
59
64
  # Get the list of the values translated by I18n
@@ -10,7 +10,7 @@ module Torque
10
10
  include Enumerable
11
11
 
12
12
  delegate :each, to: :members
13
- delegate :values, :members, :texts, :to_options, :valid?, :size,
13
+ delegate :values, :keys, :members, :texts, :to_options, :valid?, :size,
14
14
  :length, :connection_specification_name, to: :enum_source
15
15
 
16
16
  # Find or create the class that will handle the value
@@ -28,7 +28,10 @@ module Torque
28
28
  # Provide a method on the given class to setup which enum sets will be
29
29
  # manually initialized
30
30
  def include_on(klass, method_name = nil)
31
- Enum.include_on(klass, method_name || Torque::PostgreSQL.config.enum.set_method)
31
+ method_name ||= Torque::PostgreSQL.config.enum.set_method
32
+ Builder.include_on(klass, method_name, Builder::Enum, set_features: true) do |builder|
33
+ defined_enums[builder.attribute.to_sym] = builder.subtype
34
+ end
32
35
  end
33
36
 
34
37
  # The original Enum implementation, for individual values
@@ -73,7 +76,7 @@ module Torque
73
76
 
74
77
  # Build an active record scope for a given atribute agains a value
75
78
  def scope(attribute, value)
76
- attribute.contains(Array.wrap(value))
79
+ attribute.contains(::Arel.array(value, cast: enum_source.type_name))
77
80
  end
78
81
 
79
82
  private
@@ -205,9 +205,16 @@ module Torque
205
205
  end
206
206
 
207
207
  # Add the scopes defined by the reflection
208
+ # Possibilities:
209
+ # table
210
+ # table, foreign_klass
211
+ # table, foreign_table, foreign_klass
208
212
  if association.respond_to?(:join_scope)
209
- args = [@query.arel_table]
210
- args << base if association.method(:join_scope).arity.eql?(2)
213
+ arity = association.method(:join_scope).arity
214
+ args = [@query.arel_table, foreign_table, base]
215
+ args.delete_at(1) if arity <= 2 # Delete foreign_table
216
+ args.delete_at(1) if arity <= 1 # Delete base (foreign_klass)
217
+
211
218
  @query.merge(association.join_scope(*args))
212
219
  end
213
220
 
@@ -4,6 +4,12 @@ module Torque
4
4
 
5
5
  # Stores a version check for compatibility purposes
6
6
  AR521 = (ActiveRecord.gem_version >= Gem::Version.new('5.2.1'))
7
+ AR523 = (ActiveRecord.gem_version >= Gem::Version.new('5.2.3'))
8
+
9
+ # Use the same logger as the Active Record one
10
+ def self.logger
11
+ ActiveRecord::Base.logger
12
+ end
7
13
 
8
14
  # Allow nested configurations
9
15
  # :TODO: Rely on +inheritable_copy+ to make nested configurations
@@ -63,6 +69,10 @@ module Torque
63
69
  # database or not
64
70
  enum.save_on_bang = true
65
71
 
72
+ # Indicates if it should raise errors when a generated method would
73
+ # conflict with an existing one
74
+ enum.raise_conflicting = false
75
+
66
76
  # Specify the namespace of each enum type of value
67
77
  enum.namespace = ::Object.const_set('Enum', Module.new)
68
78
 
@@ -17,12 +17,28 @@ module Torque
17
17
  method(:join_keys).arity.eql?(0) ? join_keys : join_keys(klass)
18
18
  end
19
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?
34
+
35
+ scope_chain_items.inject(klass_scope, &:merge!)
36
+ end
37
+ end
38
+
20
39
  # Manually build the join constraint
21
40
  def build_join_constraint(table, foreign_table)
22
- klass_attr = table[torque_join_keys.key.to_s]
23
- source_attr = foreign_table[torque_join_keys.foreign_key.to_s]
24
-
25
- result = build_id_constraint(klass_attr, source_attr)
41
+ result = build_id_constraint_between(table, foreign_table)
26
42
  result = table.create_and([result, klass.send(:type_condition, table)]) \
27
43
  if klass.finder_needs_type_condition?
28
44
 
@@ -61,6 +77,13 @@ module Torque
61
77
 
62
78
  private
63
79
 
80
+ 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]
83
+
84
+ build_id_constraint(klass_attr, source_attr)
85
+ end
86
+
64
87
  # Prepare a value for an array constraint overlap condition
65
88
  def cast_constraint_to_array(type, value, should_cast)
66
89
  base_ready = type.try(:array) && value.is_a?(AREL_ATTR)
@@ -1,5 +1,5 @@
1
1
  module Torque
2
2
  module PostgreSQL
3
- VERSION = '1.1.0'
3
+ VERSION = '1.1.1'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torque-postgresql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-03 00:00:00.000000000 Z
11
+ date: 2019-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails