torque-postgresql 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|