torque-postgresql 0.2.13 → 0.2.14

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: 6a19c0e5178591e3b9ea84d11df0b21d34f135df
4
- data.tar.gz: 06c0b78395434ae948854222ce3ad015ce55cd61
3
+ metadata.gz: d0ed00788be18944446039d37828775e220ab9c8
4
+ data.tar.gz: 1c7d9ab51f7715931944c9442a84d1798a73068a
5
5
  SHA512:
6
- metadata.gz: 90146d035616baa734f5a068028b32bf5a9605539d1f58a76f8ce107b65eeeaed8e88776d6bde29460ab340667b96a14fbf0e18f196c98d805f479511a5db167
7
- data.tar.gz: f5f126cf74c95a4c3ccb4d6aab8f69c82a5e37215ea79928d2bc261f819d00db8c6721e059e954c9dd353182a74794c31668630ce58dac49f6274c5a113d919f
6
+ metadata.gz: 993b2f4cb92a254f801db65f8ec97730e260a427e62d4cde9ea986cc6a63b813e28c4179da1ec84e6516889a0bf4e7591774468e67afaea267495a257e779533
7
+ data.tar.gz: a9f78472309cd8604d692f9b2128027b41e16a6d1768f4b02dbf3fd00bf80a471c0cb550566e3960da4b2be43a8cfb8ac4be725c47dddd2d25c66f9b95f2ba88
@@ -10,6 +10,10 @@ module Torque
10
10
  new(row['typname'])
11
11
  end
12
12
 
13
+ def self.auto_initialize?
14
+ Torque::PostgreSQL.config.enum.initializer
15
+ end
16
+
13
17
  def initialize(name)
14
18
  @name = name
15
19
  @klass = Attributes::Enum.lookup(name)
@@ -49,8 +49,7 @@ module Torque
49
49
  # The value must be Integer when no precision is given
50
50
  def deserialize(value)
51
51
  return if value.blank?
52
- parts = ActiveSupport::Duration::ISO8601Parser.new(value).parse!
53
- parts_to_duration(parts)
52
+ ActiveSupport::Duration.parse(value)
54
53
  end
55
54
 
56
55
  # Uses the ActiveSupport::Duration::ISO8601Serializer
@@ -58,6 +57,7 @@ module Torque
58
57
  def serialize(value)
59
58
  return if value.blank?
60
59
  value = cast(value) unless value.is_a?(ActiveSupport::Duration)
60
+ value = remove_weeks(value) if value.parts.to_h.key?(:weeks)
61
61
  value.iso8601(precision: @scale)
62
62
  end
63
63
 
@@ -73,20 +73,31 @@ module Torque
73
73
 
74
74
  # Transform a list of parts into a duration object
75
75
  def parts_to_duration(parts)
76
- parts = parts.to_h.with_indifferent_access.slice(*CAST_PARTS)
76
+ parts = parts.to_h.slice(*CAST_PARTS)
77
77
  return 0.seconds if parts.blank?
78
78
 
79
79
  seconds = 0
80
80
  parts = parts.map do |part, num|
81
81
  num = num.to_i unless num.is_a?(Numeric)
82
- if num > 0
83
- seconds += num.send(part).value
84
- [part.to_sym, num]
85
- end
82
+ next if num <= 0
83
+
84
+ seconds += num.send(part).value
85
+ [part.to_sym, num]
86
86
  end
87
+
87
88
  ActiveSupport::Duration.new(seconds, parts.compact)
88
89
  end
89
90
 
91
+ # As PostgreSQL converts weeks in duration to days, intercept duration
92
+ # values with weeks and turn them into days before serializing so it
93
+ # won't break because the following issues
94
+ # https://github.com/crashtech/torque-postgresql/issues/26
95
+ # https://github.com/rails/rails/issues/34655
96
+ def remove_weeks(value)
97
+ parts = value.parts.dup
98
+ parts[:days] += parts.delete(:weeks) * 7
99
+ ActiveSupport::Duration.new(value.seconds.to_i, parts)
100
+ end
90
101
  end
91
102
  end
92
103
  end
@@ -29,7 +29,7 @@ module Torque
29
29
  # Use local type map to identify attribute decorator
30
30
  def define_attribute_method(attribute)
31
31
  type = attribute_types[attribute]
32
- super unless TypeMap.lookup(type, self, attribute, true)
32
+ super unless TypeMap.lookup(type, self, attribute)
33
33
  end
34
34
 
35
35
  end
@@ -4,15 +4,14 @@ module Torque
4
4
  module Builder
5
5
  class Enum
6
6
 
7
- attr_accessor :klass, :attribute, :subtype, :initial, :options, :values
7
+ attr_accessor :klass, :attribute, :subtype, :options, :values
8
8
 
9
9
  # Start a new builder of methods for composite values on
10
10
  # ActiveRecord::Base
11
- def initialize(klass, attribute, subtype, initial, options)
11
+ def initialize(klass, attribute, subtype, options)
12
12
  @klass = klass
13
13
  @attribute = attribute.to_s
14
14
  @subtype = subtype
15
- @initial = initial
16
15
  @options = options
17
16
 
18
17
  @values = subtype.klass.values
@@ -53,9 +52,12 @@ module Torque
53
52
  # with the base class methods
54
53
  def conflicting?
55
54
  return false if options[:force] == true
55
+ attributes = attribute.pluralize
56
56
 
57
- dangerous?(attribute.pluralize, true)
58
- dangerous?(attribute + '_text')
57
+ dangerous?(attributes, true)
58
+ dangerous?("#{attributes}_options", true)
59
+ dangerous?("#{attributes}_texts", true)
60
+ dangerous?("#{attribute}_text")
59
61
 
60
62
  values_methods.each do |attr, list|
61
63
  list.map(&method(:dangerous?))
@@ -63,19 +65,11 @@ module Torque
63
65
 
64
66
  return false
65
67
  rescue Interrupt => err
66
- if !initial
67
- raise ArgumentError, <<-MSG.strip.gsub(/\n +/, ' ')
68
- #{subtype.class.name} was not able to generate requested
69
- methods because the method #{err} already exists in
70
- #{klass.name}.
71
- MSG
72
- else
73
- warn <<-MSG.strip.gsub(/\n +/, ' ')
74
- #{subtype.class.name} was not able to autoload on
75
- #{klass.name} because the method #{err} already exists.
76
- MSG
77
- return true
78
- end
68
+ raise ArgumentError, <<-MSG.strip.gsub(/\n +/, ' ')
69
+ #{subtype.class.name} was not able to generate requested
70
+ methods because the method #{err} already exists in
71
+ #{klass.name}.
72
+ MSG
79
73
  end
80
74
 
81
75
  # Create all methods needed
@@ -102,45 +96,60 @@ module Torque
102
96
 
103
97
  # Create the method that allow access to the list of values
104
98
  def plural
105
- klass.singleton_class.module_eval <<-STR, __FILE__, __LINE__ + 1
106
- def #{attribute.pluralize} # def statuses
107
- ::#{subtype.klass.name}.values # ::Enum::Status.values
108
- end # end
109
- STR
99
+ attr = attribute
100
+ enum_klass = subtype.klass
101
+ klass.singleton_class.module_eval do
102
+ # def self.statuses() statuses end
103
+ define_method(attr.pluralize) do
104
+ enum_klass.values
105
+ end
106
+
107
+ # def self.statuses_texts() members.map(&:text) end
108
+ define_method(attr.pluralize + '_texts') do
109
+ enum_klass.members.map do |member|
110
+ member.text(attr, self)
111
+ end
112
+ end
113
+
114
+ # def self.statuses_options() statuses_texts.zip(statuses) end
115
+ define_method(attr.pluralize + '_options') do
116
+ enum_klass.values
117
+ end
118
+ end
110
119
  end
111
120
 
112
121
  # Create the method that turn the attribute value into text using
113
122
  # the model scope
114
123
  def text
115
- klass.module_eval <<-STR, __FILE__, __LINE__ + 1
116
- def #{attribute}_text # def status_text
117
- #{attribute}.text('#{attribute}', self) # status.text('status', self)
118
- end # end
119
- STR
124
+ attr = attribute
125
+ klass.module_eval do
126
+ # def status_text() status.text('status', self) end
127
+ define_method("#{attr}_text") { send(attr).text(attr, self) }
128
+ end
120
129
  end
121
130
 
122
131
  # Create all the methods that represent actions related to the
123
132
  # attribute value
124
133
  def all_values
125
- values_methods.each do |val, list|
126
- klass.module_eval <<-STR, __FILE__, __LINE__ + 1
127
- scope :#{list[0]}, -> do # scope :disabled, -> do
128
- where(#{attribute}: '#{val}') # where(status: 'disabled')
129
- end # end
130
- STR
131
- klass.module_eval <<-STR, __FILE__, __LINE__ + 1
132
- def #{list[1]} # def disabled?
133
- #{attribute}.#{val}? # status.disabled?
134
- end # end
135
-
136
- def #{list[2]} # def disabled!
137
- if enum_save_on_bang # if enum_save_on_bang
138
- update!(#{attribute}: '#{val}') # update!(status: 'disabled')
139
- else # else
140
- #{attribute}.#{val}! # status.disabled!
141
- end # end
142
- end # end
143
- STR
134
+ attr = attribute
135
+ vals = values_methods
136
+ klass.module_eval do
137
+ vals.each do |val, list|
138
+ # scope :disabled, -> { where(status: 'disabled') }
139
+ scope list[0], -> { where(attr => val) }
140
+
141
+ # def disabled? status.disabled? end
142
+ define_method(list[1]) { send(attr).public_send("#{val}?") }
143
+
144
+ # def disabled! enum_save_on_bang ? update!(status: 'disabled') : status.disabled! end
145
+ define_method(list[2]) do
146
+ if enum_save_on_bang
147
+ update!(attr => val)
148
+ else
149
+ send(attr).public_send("#{val}!")
150
+ end
151
+ end
152
+ end
144
153
  end
145
154
  end
146
155
 
@@ -22,6 +22,17 @@ module Torque
22
22
  namespace.const_set(const, Class.new(Enum))
23
23
  end
24
24
 
25
+ # Provide a method on the given class to setup which enums will be
26
+ # manually initialized
27
+ def include_on(klass)
28
+ method_name = Torque::PostgreSQL.config.enum.base_method
29
+ klass.singleton_class.class_eval do
30
+ define_method(method_name) do |*args, **options|
31
+ Torque::PostgreSQL::Attributes::TypeMap.decorate(self, args, **options)
32
+ end
33
+ end
34
+ end
35
+
25
36
  # You can specify the connection name for each enum
26
37
  def connection_specification_name
27
38
  return self == Enum ? 'primary' : superclass.connection_specification_name
@@ -47,6 +58,16 @@ module Torque
47
58
  values.dup.map(&method(:new))
48
59
  end
49
60
 
61
+ # Get the list of the values translated by I18n
62
+ def texts
63
+ members.map(&:text)
64
+ end
65
+
66
+ # Get a list of values translated and ready for select
67
+ def to_options
68
+ texts.zip(values)
69
+ end
70
+
50
71
  # Fetch a value from the list
51
72
  # see https://github.com/rails/rails/blob/v5.0.0/activerecord/lib/active_record/fixtures.rb#L656
52
73
  # see https://github.com/rails/rails/blob/v5.0.0/activerecord/lib/active_record/validations/uniqueness.rb#L101
@@ -88,21 +109,6 @@ module Torque
88
109
 
89
110
  end
90
111
 
91
- # Extension of the ActiveRecord::Base to initiate the enum features
92
- module Base
93
-
94
- method_name = Torque::PostgreSQL.config.enum.base_method
95
- module_eval <<-STR, __FILE__, __LINE__ + 1
96
- def #{method_name}(*args, **options)
97
- args.each do |attribute|
98
- type = attribute_types[attribute.to_s]
99
- TypeMap.lookup(type, self, attribute.to_s, false, options)
100
- end
101
- end
102
- STR
103
-
104
- end
105
-
106
112
  # Override string initializer to check for a valid value
107
113
  def initialize(value)
108
114
  str_value = value.is_a?(Numeric) ? self.class.values[value.to_i] : value.to_s
@@ -171,7 +177,7 @@ module Torque
171
177
 
172
178
  if attr && model
173
179
  values[:attr] = attr
174
- values[:model] = model.class.model_name.i18n_key
180
+ values[:model] = model.model_name.i18n_key
175
181
  list_from = :i18n_scopes
176
182
  end
177
183
 
@@ -217,32 +223,16 @@ module Torque
217
223
 
218
224
  end
219
225
 
220
- # Extend ActiveRecord::Base so it can have the initializer
221
- ActiveRecord::Base.extend Enum::Base
222
-
223
226
  # Create the methods related to the attribute to handle the enum type
224
- TypeMap.register_type Adapter::OID::Enum do |subtype, attribute, initial = false, options = nil|
225
- break if initial && !Torque::PostgreSQL.config.enum.initializer
226
- options = {} if options.nil?
227
-
227
+ TypeMap.register_type Adapter::OID::Enum do |subtype, attribute, options = nil|
228
228
  # Generate methods on self class
229
- builder = Builder::Enum.new(self, attribute, subtype, initial, options)
229
+ builder = Builder::Enum.new(self, attribute, subtype, options || {})
230
230
  break if builder.conflicting?
231
231
  builder.build
232
232
 
233
233
  # Mark the enum as defined
234
234
  defined_enums[attribute] = subtype.klass
235
235
  end
236
-
237
- # Define a method to find yet to define constants
238
- Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:const_missing) do |name|
239
- Enum.lookup(name)
240
- end
241
-
242
- # Define a helper method to get a sample value
243
- Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:sample) do |name|
244
- Enum.lookup(name).sample
245
- end
246
236
  end
247
237
  end
248
238
  end
@@ -10,6 +10,34 @@ module Torque
10
10
  @types ||= {}
11
11
  end
12
12
 
13
+ # Store which elements should be initialized
14
+ def decorable
15
+ @decorable ||= Hash.new{ |h, k| h[k] = [] }
16
+ end
17
+
18
+ # List of options for each individual attribute on each klass
19
+ def options
20
+ @options ||= Hash.new{ |h, k| h[k] = {} }
21
+ end
22
+
23
+ # Mark the list of attributes on the given class that can be decorated
24
+ def decorate(klass, *attributes, **set_options)
25
+ attributes.flatten.each do |attribute|
26
+ decorable[klass] << attribute.to_s
27
+ options[klass][attribute.to_s] = set_options.deep_dup
28
+ end
29
+ end
30
+
31
+ # Force the list of attributes on the given class to be decorated by
32
+ # this type mapper
33
+ def decorate!(klass, *attributes, **options)
34
+ decorate(klass, *attributes, **options)
35
+ attributes.flatten.map do |attribute|
36
+ type = klass.attribute_types[attribute.to_s]
37
+ lookup(type, klass, attribute.to_s)
38
+ end
39
+ end
40
+
13
41
  # Register a type that can be processed by a given block
14
42
  def register_type(key, &block)
15
43
  raise_type_defined(key) if present?(key)
@@ -17,9 +45,12 @@ module Torque
17
45
  end
18
46
 
19
47
  # Search for a type match and process it if any
20
- def lookup(key, klass, *args)
21
- return unless present?(key)
22
- klass.instance_exec(key, *args, &types[key.class])
48
+ def lookup(key, klass, attribute, *args)
49
+ return unless present?(key) && decorable?(key, klass, attribute)
50
+
51
+ set_options = options[klass][attribute]
52
+ args.unshift(set_options) unless set_options.nil?
53
+ klass.instance_exec(key, attribute, *args, &types[key.class])
23
54
  rescue LocalJumpError
24
55
  # There's a bug or misbehavior that blocks being called through
25
56
  # instance_exec don't accept neither return nor break
@@ -31,6 +62,13 @@ module Torque
31
62
  types.key?(key.class)
32
63
  end
33
64
 
65
+ # Check whether the given attribute on the given klass is
66
+ # decorable by this type mapper
67
+ def decorable?(key, klass, attribute)
68
+ key.class.auto_initialize? ||
69
+ (decorable.key?(klass) && decorable[klass].include?(attribute.to_s))
70
+ end
71
+
34
72
  # Message when trying to define multiple types
35
73
  def raise_type_defined(key)
36
74
  raise ArgumentError, <<-MSG.strip
@@ -87,6 +87,20 @@ module Torque
87
87
  "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
88
88
  end
89
89
 
90
+ # For all main purposes, physical inherited classes should have
91
+ # base_class as their own
92
+ def base_class
93
+ return super unless physically_inherited?
94
+ self
95
+ end
96
+
97
+ # Primary key is one exception when getting information about the class,
98
+ # it must returns the superclass PK
99
+ def primary_key
100
+ return super unless physically_inherited?
101
+ superclass.primary_key
102
+ end
103
+
90
104
  # Add an additional check to return the name of the table even when the
91
105
  # class is inherited, but only if it is a physical inheritance
92
106
  def compute_table_name
@@ -6,6 +6,20 @@ module Torque
6
6
  # Get information from the running rails app
7
7
  initializer 'torque-postgresql' do |app|
8
8
  Torque::PostgreSQL.config.eager_load = app.config.eager_load
9
+
10
+ # Include enum on ActiveRecord::Base so it can have the correct enum
11
+ # initializer
12
+ Torque::PostgreSQL::Attributes::Enum.include_on(ActiveRecord::Base)
13
+
14
+ # Define a method to find yet to define constants
15
+ Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:const_missing) do |name|
16
+ Torque::PostgreSQL::Attributes::Enum.lookup(name)
17
+ end
18
+
19
+ # Define a helper method to get a sample value
20
+ Torque::PostgreSQL.config.enum.namespace.define_singleton_method(:sample) do |name|
21
+ Torque::PostgreSQL::Attributes::Enum.lookup(name).sample
22
+ end
9
23
  end
10
24
 
11
25
  end
@@ -112,11 +112,24 @@ module Torque
112
112
  .inheritance.auto_cast_column_name.to_sym
113
113
  end
114
114
  end
115
+
116
+ # When a relation is created, force the attributes to be defined,
117
+ # because the type mapper may add new methods to the model. This happens
118
+ # for the given model Klass and its inheritances
119
+ module Initializer
120
+ def initialize(klass, *)
121
+ super
122
+
123
+ klass.superclass.send(:relation) if klass.define_attribute_methods &&
124
+ klass.superclass != ActiveRecord::Base && !klass.superclass.abstract_class?
125
+ end
126
+ end
115
127
  end
116
128
 
117
129
  # Include the methos here provided and then change the constants to ensure
118
130
  # the operation of ActiveRecord Relation
119
131
  ActiveRecord::Relation.include Relation
132
+ ActiveRecord::Relation.prepend Relation::Initializer
120
133
 
121
134
  warn_level = $VERBOSE
122
135
  $VERBOSE = nil
@@ -11,7 +11,6 @@ module Torque
11
11
  @data_sources_model_names = {}
12
12
  @inheritance_dependencies = {}
13
13
  @inheritance_associations = {}
14
- @cached_data_sources_size = 0
15
14
  end
16
15
 
17
16
  def initialize_dup(*) # :nodoc:
@@ -19,7 +18,30 @@ module Torque
19
18
  @data_sources_model_names = @data_sources_model_names.dup
20
19
  @inheritance_dependencies = @inheritance_dependencies.dup
21
20
  @inheritance_associations = @inheritance_associations.dup
22
- @cached_data_sources_size = @cached_data_sources_size.dup
21
+ end
22
+
23
+ def encode_with(coder) # :nodoc:
24
+ super
25
+ coder["data_sources_model_names"] = @data_sources_model_names
26
+ coder["inheritance_dependencies"] = @inheritance_dependencies
27
+ coder["inheritance_associations"] = @inheritance_associations
28
+ end
29
+
30
+ def init_with(coder) # :nodoc:
31
+ super
32
+ @data_sources_model_names = coder["data_sources_model_names"]
33
+ @inheritance_dependencies = coder["inheritance_dependencies"]
34
+ @inheritance_associations = coder["inheritance_associations"]
35
+ end
36
+
37
+ def add(table_name, *) # :nodoc:
38
+ super
39
+
40
+ # Reset inheritance information when a table is added
41
+ if @data_sources.key?(table_name)
42
+ @inheritance_dependencies.clear
43
+ @inheritance_associations.clear
44
+ end
23
45
  end
24
46
 
25
47
  def clear! # :nodoc:
@@ -27,7 +49,6 @@ module Torque
27
49
  @data_sources_model_names.clear
28
50
  @inheritance_dependencies.clear
29
51
  @inheritance_associations.clear
30
- @cached_data_sources_size = nil
31
52
  end
32
53
 
33
54
  def size # :nodoc:
@@ -43,12 +64,10 @@ module Torque
43
64
  @data_sources_model_names.delete name
44
65
  @inheritance_dependencies.delete name
45
66
  @inheritance_associations.delete name
46
- @inheritance_cache = inheritance_cache_key
47
67
  end
48
68
 
49
69
  def marshal_dump # :nodoc:
50
70
  super + [
51
- @inheritance_cache,
52
71
  @inheritance_dependencies,
53
72
  @inheritance_associations,
54
73
  @data_sources_model_names,
@@ -59,7 +78,6 @@ module Torque
59
78
  @data_sources_model_names = array.pop
60
79
  @inheritance_associations = array.pop
61
80
  @inheritance_dependencies = array.pop
62
- @inheritance_cache = array.pop
63
81
  super
64
82
  end
65
83
 
@@ -143,19 +161,10 @@ module Torque
143
161
  end
144
162
  end
145
163
 
146
- # Generates the cache key for inheitance information
147
- def inheritance_cache_key
148
- @data_sources.keys.compact.sort.join(',')
149
- end
150
-
151
164
  # Reload information about tables inheritance and dependencies, uses a
152
165
  # cache to not perform additional checkes
153
166
  def reload_inheritance_data!
154
- cache_key = inheritance_cache_key
155
-
156
- return unless @inheritance_cache != cache_key
157
- @inheritance_cache = cache_key
158
-
167
+ return if @inheritance_dependencies.present?
159
168
  @inheritance_dependencies = connection.inherited_tables
160
169
  @inheritance_associations = generate_associations
161
170
  end
@@ -1,5 +1,5 @@
1
1
  module Torque
2
2
  module PostgreSQL
3
- VERSION = '0.2.13'
3
+ VERSION = '0.2.14'
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: 0.2.13
4
+ version: 0.2.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-22 00:00:00.000000000 Z
11
+ date: 2018-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails