torque-postgresql 1.0.1 → 1.1.0
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 +4 -4
- data/lib/torque/postgresql/adapter/oid/enum.rb +10 -1
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +12 -5
- data/lib/torque/postgresql/adapter/oid/range.rb +7 -0
- data/lib/torque/postgresql/adapter/quoting.rb +1 -1
- data/lib/torque/postgresql/adapter/schema_statements.rb +4 -4
- data/lib/torque/postgresql/attributes.rb +0 -20
- data/lib/torque/postgresql/attributes/builder.rb +28 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +7 -6
- data/lib/torque/postgresql/attributes/builder/period.rb +365 -305
- data/lib/torque/postgresql/attributes/enum.rb +4 -17
- data/lib/torque/postgresql/attributes/enum_set.rb +4 -20
- data/lib/torque/postgresql/attributes/period.rb +3 -17
- data/lib/torque/postgresql/config.rb +42 -17
- data/lib/torque/postgresql/inheritance.rb +34 -4
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +8 -3
- data/lib/torque/postgresql/relation/inheritance.rb +10 -3
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/range.rb +0 -18
- metadata +2 -3
- data/lib/torque/postgresql/attributes/type_map.rb +0 -82
@@ -24,12 +24,10 @@ module Torque
|
|
24
24
|
|
25
25
|
# Provide a method on the given class to setup which enums will be
|
26
26
|
# manually initialized
|
27
|
-
def include_on(klass)
|
28
|
-
method_name
|
29
|
-
klass
|
30
|
-
|
31
|
-
Torque::PostgreSQL::Attributes::TypeMap.decorate(self, args, **options)
|
32
|
-
end
|
27
|
+
def include_on(klass, method_name = nil)
|
28
|
+
method_name ||= Torque::PostgreSQL.config.enum.base_method
|
29
|
+
Builder.include_on(klass, method_name, Builder::Enum) do |builder|
|
30
|
+
defined_enums[builder.attribute.to_sym] = builder.subtype
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -225,17 +223,6 @@ module Torque
|
|
225
223
|
raise EnumError, "Comparison of #{self.class.name} with #{self.inspect} failed"
|
226
224
|
end
|
227
225
|
end
|
228
|
-
|
229
|
-
# Create the methods related to the attribute to handle the enum type
|
230
|
-
TypeMap.register_type Adapter::OID::Enum do |subtype, attribute, options = nil|
|
231
|
-
# Generate methods on self class
|
232
|
-
builder = Builder::Enum.new(self, attribute, subtype, options || {})
|
233
|
-
break if builder.conflicting?
|
234
|
-
builder.build
|
235
|
-
|
236
|
-
# Mark the enum as defined
|
237
|
-
defined_enums[attribute] = subtype.klass
|
238
|
-
end
|
239
226
|
end
|
240
227
|
end
|
241
228
|
end
|
@@ -27,13 +27,8 @@ module Torque
|
|
27
27
|
|
28
28
|
# Provide a method on the given class to setup which enum sets will be
|
29
29
|
# manually initialized
|
30
|
-
def include_on(klass)
|
31
|
-
method_name
|
32
|
-
klass.singleton_class.class_eval do
|
33
|
-
define_method(method_name) do |*args, **options|
|
34
|
-
Torque::PostgreSQL::Attributes::TypeMap.decorate(self, args, **options)
|
35
|
-
end
|
36
|
-
end
|
30
|
+
def include_on(klass, method_name = nil)
|
31
|
+
Enum.include_on(klass, method_name || Torque::PostgreSQL.config.enum.set_method)
|
37
32
|
end
|
38
33
|
|
39
34
|
# The original Enum implementation, for individual values
|
@@ -146,7 +141,7 @@ module Torque
|
|
146
141
|
|
147
142
|
# Change the inspection to show the enum name
|
148
143
|
def inspect
|
149
|
-
map(&:inspect).
|
144
|
+
"[#{map(&:inspect).join(', ')}]"
|
150
145
|
end
|
151
146
|
|
152
147
|
# Replace the setter by instantiating the value
|
@@ -202,7 +197,7 @@ module Torque
|
|
202
197
|
# Turn all the values into their respective Enum representations
|
203
198
|
def transform_values(values)
|
204
199
|
values = values.first if values.size.eql?(1) && values.first.is_a?(::Enumerable)
|
205
|
-
values.map(&method(:instantiate))
|
200
|
+
values.map(&method(:instantiate)).reject(&:nil?)
|
206
201
|
end
|
207
202
|
|
208
203
|
# Check for valid '?' and '!' methods
|
@@ -240,17 +235,6 @@ module Torque
|
|
240
235
|
raise EnumSetError, "Comparison of #{self.class.name} with #{self.inspect} failed"
|
241
236
|
end
|
242
237
|
end
|
243
|
-
|
244
|
-
# Create the methods related to the attribute to handle the enum type
|
245
|
-
TypeMap.register_type Adapter::OID::EnumSet do |subtype, attribute, options = nil|
|
246
|
-
# Generate methods on self class
|
247
|
-
builder = Builder::Enum.new(self, attribute, subtype, options || {})
|
248
|
-
break if builder.conflicting?
|
249
|
-
builder.build
|
250
|
-
|
251
|
-
# Mark the enum as defined
|
252
|
-
defined_enums[attribute] = subtype.klass
|
253
|
-
end
|
254
238
|
end
|
255
239
|
end
|
256
240
|
end
|
@@ -4,28 +4,14 @@ module Torque
|
|
4
4
|
# For naw, period doesn't have it's own class
|
5
5
|
module Period
|
6
6
|
class << self
|
7
|
-
|
8
7
|
# Provide a method on the given class to setup which period columns
|
9
8
|
# will be manually initialized
|
10
|
-
def include_on(klass)
|
11
|
-
method_name
|
12
|
-
klass
|
13
|
-
define_method(method_name) do |*args, **options|
|
14
|
-
Torque::PostgreSQL::Attributes::TypeMap.decorate(self, args, **options)
|
15
|
-
end
|
16
|
-
end
|
9
|
+
def include_on(klass, method_name = nil)
|
10
|
+
method_name ||= Torque::PostgreSQL.config.period.base_method
|
11
|
+
Builder.include_on(klass, method_name, Builder::Period)
|
17
12
|
end
|
18
|
-
|
19
13
|
end
|
20
14
|
end
|
21
|
-
|
22
|
-
# Create the methods related to the attribute to handle the enum type
|
23
|
-
TypeMap.register_type Adapter::OID::Range do |subtype, attribute, options = nil|
|
24
|
-
# Generate methods on self class
|
25
|
-
builder = Builder::Period.new(self, attribute, subtype, options || {})
|
26
|
-
break if builder.conflicting?
|
27
|
-
builder.build
|
28
|
-
end
|
29
15
|
end
|
30
16
|
end
|
31
17
|
end
|
@@ -137,19 +137,23 @@ module Torque
|
|
137
137
|
# initialize model-based period features
|
138
138
|
period.base_method = :period_for
|
139
139
|
|
140
|
+
# The default name for a threshold attribute, which will automatically
|
141
|
+
# enable threshold features
|
142
|
+
period.auto_threshold = :threshold
|
143
|
+
|
140
144
|
# Define the list of methods that will be created by default while setting
|
141
145
|
# up a new period field
|
142
146
|
period.method_names = {
|
143
|
-
current_on: '%s_on', #
|
144
|
-
current: 'current_%s', #
|
145
|
-
not_current: 'not_current_%s', #
|
146
|
-
containing: '%s_containing', #
|
147
|
-
not_containing: '%s_not_containing', #
|
148
|
-
overlapping: '%s_overlapping', #
|
149
|
-
not_overlapping: '%s_not_overlapping', #
|
150
|
-
starting_after: '%s_starting_after', #
|
151
|
-
starting_before: '%s_starting_before', #
|
152
|
-
finishing_after: '%s_finishing_after', #
|
147
|
+
current_on: '%s_on', # 00
|
148
|
+
current: 'current_%s', # 01
|
149
|
+
not_current: 'not_current_%s', # 02
|
150
|
+
containing: '%s_containing', # 03
|
151
|
+
not_containing: '%s_not_containing', # 04
|
152
|
+
overlapping: '%s_overlapping', # 05
|
153
|
+
not_overlapping: '%s_not_overlapping', # 06
|
154
|
+
starting_after: '%s_starting_after', # 07
|
155
|
+
starting_before: '%s_starting_before', # 08
|
156
|
+
finishing_after: '%s_finishing_after', # 09
|
153
157
|
finishing_before: '%s_finishing_before', # 10
|
154
158
|
|
155
159
|
real_containing: '%s_real_containing', # 11
|
@@ -163,14 +167,35 @@ module Torque
|
|
163
167
|
not_containing_date: '%s_not_containing_date', # 18
|
164
168
|
overlapping_date: '%s_overlapping_date', # 19
|
165
169
|
not_overlapping_date: '%s_not_overlapping_date', # 20
|
170
|
+
real_containing_date: '%s_real_containing_date', # 21
|
171
|
+
real_overlapping_date: '%s_real_overlapping_date', # 22
|
172
|
+
|
173
|
+
current?: 'current_%s?', # 23
|
174
|
+
current_on?: 'current_%s_on?', # 24
|
175
|
+
start: '%s_start', # 25
|
176
|
+
finish: '%s_finish', # 26
|
177
|
+
real: 'real_%s', # 27
|
178
|
+
real_start: '%s_real_start', # 28
|
179
|
+
real_finish: '%s_real_finish', # 29
|
180
|
+
}
|
166
181
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
182
|
+
# If the period is marked as direct access, without the field name,
|
183
|
+
# then these method names will replace the default ones
|
184
|
+
period.direct_method_names = {
|
185
|
+
current_on: 'happening_in',
|
186
|
+
containing: 'during',
|
187
|
+
not_containing: 'not_during',
|
188
|
+
real_containing: 'real_during',
|
189
|
+
|
190
|
+
containing_date: 'during_date',
|
191
|
+
not_containing_date: 'not_during_date',
|
192
|
+
|
193
|
+
current_on?: 'happening_in?',
|
194
|
+
start: 'start_at',
|
195
|
+
finish: 'finish_at',
|
196
|
+
real: 'real_time',
|
197
|
+
real_start: 'real_start_at',
|
198
|
+
real_finish: 'real_finish_at',
|
174
199
|
}
|
175
200
|
|
176
201
|
end
|
@@ -33,7 +33,28 @@ module Torque
|
|
33
33
|
@inheritance_merged_attributes ||= begin
|
34
34
|
list = attribute_names
|
35
35
|
list += casted_dependents.values.map(&:attribute_names)
|
36
|
-
list.flatten.
|
36
|
+
list.flatten.uniq.freeze
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get the list of attributes that can be merged while querying because
|
41
|
+
# they all have the same type
|
42
|
+
def inheritance_mergeable_attributes
|
43
|
+
@inheritance_mergeable_attributes ||= begin
|
44
|
+
base = inheritance_merged_attributes - attribute_names
|
45
|
+
types = base.zip(base.size.times.map { [] }).to_h
|
46
|
+
|
47
|
+
casted_dependents.values.each do |klass|
|
48
|
+
klass.attribute_types.each do |column, type|
|
49
|
+
types[column]&.push(type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
result = types.select do
|
54
|
+
|_, types| types.each_with_object(types.shift).all?(&:==)
|
55
|
+
end.keys + attribute_names
|
56
|
+
|
57
|
+
result.freeze
|
37
58
|
end
|
38
59
|
end
|
39
60
|
|
@@ -128,7 +149,7 @@ module Torque
|
|
128
149
|
record_class = _record_class_attribute.to_s
|
129
150
|
|
130
151
|
return super unless record.key?(record_class) &&
|
131
|
-
record
|
152
|
+
record.delete(auto_cast) && record[record_class] != table_name
|
132
153
|
|
133
154
|
klass = casted_dependents[record[record_class]]
|
134
155
|
raise_unable_to_cast(record[record_class]) if klass.nil?
|
@@ -139,8 +160,17 @@ module Torque
|
|
139
160
|
# Filter the record attributes to be loaded to not included those from
|
140
161
|
# another inherited dependent
|
141
162
|
def filter_attributes_for_cast(record, klass)
|
142
|
-
|
143
|
-
|
163
|
+
new_record = record.slice(*klass.attribute_names)
|
164
|
+
table = new_record[_record_class_attribute.to_s] = klass.table_name
|
165
|
+
|
166
|
+
# Recover aliased attributes
|
167
|
+
(klass.attribute_names - inheritance_mergeable_attributes).each do |attribute|
|
168
|
+
new_record[attribute] = record["#{table}__#{attribute}"]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Add any additional columns and replace the record with the new record data
|
172
|
+
new_record.merge!(record.slice(*(record.keys - inheritance_merged_attributes)))
|
173
|
+
record.replace(new_record)
|
144
174
|
end
|
145
175
|
|
146
176
|
end
|
@@ -18,12 +18,16 @@ module Torque
|
|
18
18
|
Associations::BelongsToManyAssociation
|
19
19
|
end
|
20
20
|
|
21
|
+
def foreign_key
|
22
|
+
@foreign_key ||= options[:primary_key] || derive_foreign_key.freeze
|
23
|
+
end
|
24
|
+
|
21
25
|
def association_foreign_key
|
22
26
|
@association_foreign_key ||= foreign_key
|
23
27
|
end
|
24
28
|
|
25
29
|
def active_record_primary_key
|
26
|
-
@active_record_primary_key ||= options[:
|
30
|
+
@active_record_primary_key ||= options[:foreign_key] || derive_primary_key
|
27
31
|
end
|
28
32
|
|
29
33
|
private
|
@@ -33,12 +37,13 @@ module Torque
|
|
33
37
|
end
|
34
38
|
|
35
39
|
def derive_primary_key
|
36
|
-
|
40
|
+
"#{name.to_s.singularize}_ids"
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
::ActiveRecord::Reflection::AssociationReflection::VALID_AUTOMATIC_INVERSE_MACROS.push(:belongs_to_many)
|
41
44
|
::ActiveRecord::Reflection.const_set(:BelongsToManyReflection, BelongsToManyReflection)
|
45
|
+
::ActiveRecord::Reflection::AssociationReflection::VALID_AUTOMATIC_INVERSE_MACROS
|
46
|
+
.push(:belongs_to_many)
|
42
47
|
end
|
43
48
|
end
|
44
49
|
end
|
@@ -68,15 +68,22 @@ module Torque
|
|
68
68
|
def build_inheritances(arel)
|
69
69
|
return unless self.cast_records_value.present?
|
70
70
|
|
71
|
+
mergeable = inheritance_mergeable_attributes
|
72
|
+
|
71
73
|
columns = build_inheritances_joins(arel, self.cast_records_value)
|
72
74
|
columns = columns.map do |column, arel_tables|
|
73
75
|
next arel_tables.first[column] if arel_tables.size == 1
|
74
|
-
|
75
|
-
|
76
|
+
|
77
|
+
if mergeable.include?(column)
|
78
|
+
list = arel_tables.each_with_object(column).map(&:[])
|
79
|
+
::Arel::Nodes::NamedFunction.new('COALESCE', list).as(column)
|
80
|
+
else
|
81
|
+
arel_tables.map { |table| table[column].as("#{table.left.name}__#{column}") }
|
82
|
+
end
|
76
83
|
end
|
77
84
|
|
78
85
|
columns.push(build_auto_caster_marker(arel, self.cast_records_value))
|
79
|
-
self.select_extra_values += columns if columns.any?
|
86
|
+
self.select_extra_values += columns.flatten if columns.any?
|
80
87
|
end
|
81
88
|
|
82
89
|
# Build as many left outer join as necessary for each dependent table
|
data/lib/torque/range.rb
CHANGED
@@ -16,24 +16,6 @@ module Torque
|
|
16
16
|
([min, other.min].min)..([max, other.max].max)
|
17
17
|
end
|
18
18
|
alias_method :|, :union
|
19
|
-
|
20
|
-
def subtract(other)
|
21
|
-
raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
|
22
|
-
return if other.eql?(self)
|
23
|
-
|
24
|
-
other = intersection(other)
|
25
|
-
return self if other.nil?
|
26
|
-
|
27
|
-
min.eql?(other.min) ? other.max..max : min..other.min
|
28
|
-
end
|
29
|
-
alias_method :-, :subtract
|
30
|
-
|
31
|
-
def add(other)
|
32
|
-
raise ArgumentError, 'value must be a Range' unless other.kind_of?(Range)
|
33
|
-
|
34
|
-
intersection(other) ? union(other) : self
|
35
|
-
end
|
36
|
-
alias_method :+, :add
|
37
19
|
end
|
38
20
|
|
39
21
|
::Range.include(Range)
|
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.0
|
4
|
+
version: 1.1.0
|
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-
|
11
|
+
date: 2019-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -212,7 +212,6 @@ files:
|
|
212
212
|
- lib/torque/postgresql/attributes/enum_set.rb
|
213
213
|
- lib/torque/postgresql/attributes/lazy.rb
|
214
214
|
- lib/torque/postgresql/attributes/period.rb
|
215
|
-
- lib/torque/postgresql/attributes/type_map.rb
|
216
215
|
- lib/torque/postgresql/autosave_association.rb
|
217
216
|
- lib/torque/postgresql/auxiliary_statement.rb
|
218
217
|
- lib/torque/postgresql/auxiliary_statement/settings.rb
|
@@ -1,82 +0,0 @@
|
|
1
|
-
module Torque
|
2
|
-
module PostgreSQL
|
3
|
-
module Attributes
|
4
|
-
# Remove type map and add decorators direct to ActiveRecord::Base
|
5
|
-
module TypeMap
|
6
|
-
|
7
|
-
class << self
|
8
|
-
|
9
|
-
# Reader of the list of tyes
|
10
|
-
def types
|
11
|
-
@types ||= {}
|
12
|
-
end
|
13
|
-
|
14
|
-
# Store which elements should be initialized
|
15
|
-
def decorable
|
16
|
-
@decorable ||= Hash.new{ |h, k| h[k] = [] }
|
17
|
-
end
|
18
|
-
|
19
|
-
# List of options for each individual attribute on each klass
|
20
|
-
def options
|
21
|
-
@options ||= Hash.new{ |h, k| h[k] = {} }
|
22
|
-
end
|
23
|
-
|
24
|
-
# Mark the list of attributes on the given class that can be decorated
|
25
|
-
def decorate(klass, *attributes, **set_options)
|
26
|
-
attributes.flatten.each do |attribute|
|
27
|
-
decorable[klass] << attribute.to_s
|
28
|
-
options[klass][attribute.to_s] = set_options.deep_dup
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Force the list of attributes on the given class to be decorated by
|
33
|
-
# this type mapper
|
34
|
-
def decorate!(klass, *attributes, **options)
|
35
|
-
decorate(klass, *attributes, **options)
|
36
|
-
attributes.flatten.map do |attribute|
|
37
|
-
type = klass.attribute_types[attribute.to_s]
|
38
|
-
lookup(type, klass, attribute.to_s)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Register a type that can be processed by a given block
|
43
|
-
def register_type(key, &block)
|
44
|
-
raise_type_defined(key) if present?(key)
|
45
|
-
types[key] = block
|
46
|
-
end
|
47
|
-
|
48
|
-
# Search for a type match and process it if any
|
49
|
-
def lookup(key, klass, attribute, *args)
|
50
|
-
return unless present?(key) && decorable?(key, klass, attribute)
|
51
|
-
|
52
|
-
set_options = options[klass][attribute]
|
53
|
-
args.unshift(set_options) unless set_options.nil?
|
54
|
-
klass.instance_exec(key, attribute, *args, &types[key.class])
|
55
|
-
rescue LocalJumpError
|
56
|
-
# There's a bug or misbehavior that blocks being called through
|
57
|
-
# instance_exec don't accept neither return nor break
|
58
|
-
return false
|
59
|
-
end
|
60
|
-
|
61
|
-
# Check if the given type class is registered
|
62
|
-
def present?(key)
|
63
|
-
types.key?(key.class)
|
64
|
-
end
|
65
|
-
|
66
|
-
# Check whether the given attribute on the given klass is
|
67
|
-
# decorable by this type mapper
|
68
|
-
def decorable?(key, klass, attribute)
|
69
|
-
decorable.key?(klass) && decorable[klass].include?(attribute.to_s)
|
70
|
-
end
|
71
|
-
|
72
|
-
# Message when trying to define multiple types
|
73
|
-
def raise_type_defined(key)
|
74
|
-
raise ArgumentError, <<-MSG.squish
|
75
|
-
Type #{key} is already defined here: #{types[key].source_location.join(':')}
|
76
|
-
MSG
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|