torque-postgresql 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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