torque-postgresql 0.2.16 → 1.0.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/README.rdoc +76 -3
- data/lib/torque-postgresql.rb +1 -0
- data/lib/torque/postgresql.rb +6 -0
- data/lib/torque/postgresql/adapter.rb +2 -4
- data/lib/torque/postgresql/adapter/database_statements.rb +23 -9
- data/lib/torque/postgresql/adapter/oid.rb +12 -1
- data/lib/torque/postgresql/adapter/oid/box.rb +28 -0
- data/lib/torque/postgresql/adapter/oid/circle.rb +37 -0
- data/lib/torque/postgresql/adapter/oid/enum.rb +9 -5
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +44 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +59 -0
- data/lib/torque/postgresql/adapter/oid/range.rb +52 -0
- data/lib/torque/postgresql/adapter/oid/segment.rb +73 -0
- data/lib/torque/postgresql/adapter/quoting.rb +21 -0
- data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +10 -1
- data/lib/torque/postgresql/arel.rb +3 -0
- data/lib/torque/postgresql/arel/infix_operation.rb +42 -0
- data/lib/torque/postgresql/arel/nodes.rb +32 -0
- data/lib/torque/postgresql/arel/operations.rb +18 -0
- data/lib/torque/postgresql/arel/visitors.rb +28 -2
- data/lib/torque/postgresql/associations.rb +8 -0
- data/lib/torque/postgresql/associations/association.rb +30 -0
- data/lib/torque/postgresql/associations/association_scope.rb +116 -0
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +117 -0
- data/lib/torque/postgresql/associations/builder.rb +2 -0
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +121 -0
- data/lib/torque/postgresql/associations/builder/has_many.rb +15 -0
- data/lib/torque/postgresql/associations/join_dependency/join_association.rb +15 -0
- data/lib/torque/postgresql/associations/preloader.rb +25 -0
- data/lib/torque/postgresql/associations/preloader/association.rb +64 -0
- data/lib/torque/postgresql/attributes.rb +2 -0
- data/lib/torque/postgresql/attributes/builder.rb +1 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +23 -15
- data/lib/torque/postgresql/attributes/builder/period.rb +452 -0
- data/lib/torque/postgresql/attributes/enum.rb +11 -8
- data/lib/torque/postgresql/attributes/enum_set.rb +256 -0
- data/lib/torque/postgresql/attributes/lazy.rb +1 -1
- data/lib/torque/postgresql/attributes/period.rb +31 -0
- data/lib/torque/postgresql/attributes/type_map.rb +3 -5
- data/lib/torque/postgresql/autosave_association.rb +40 -0
- data/lib/torque/postgresql/auxiliary_statement.rb +201 -198
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +20 -12
- data/lib/torque/postgresql/base.rb +161 -2
- data/lib/torque/postgresql/config.rb +91 -9
- data/lib/torque/postgresql/geometry_builder.rb +92 -0
- data/lib/torque/postgresql/i18n.rb +1 -1
- data/lib/torque/postgresql/railtie.rb +18 -5
- data/lib/torque/postgresql/reflection.rb +21 -0
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +109 -0
- data/lib/torque/postgresql/reflection/association_reflection.rb +30 -0
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +44 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +13 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +12 -0
- data/lib/torque/postgresql/reflection/through_reflection.rb +11 -0
- data/lib/torque/postgresql/relation.rb +11 -10
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +11 -18
- data/lib/torque/postgresql/relation/inheritance.rb +2 -2
- data/lib/torque/postgresql/relation/merger.rb +11 -7
- data/lib/torque/postgresql/schema_cache.rb +1 -1
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/range.rb +40 -0
- metadata +41 -9
@@ -0,0 +1,452 @@
|
|
1
|
+
module Torque
|
2
|
+
module PostgreSQL
|
3
|
+
module Attributes
|
4
|
+
module Builder
|
5
|
+
# TODO: Allow methods to have nil in order to not include that specific method
|
6
|
+
class Period
|
7
|
+
SUPPORTED_TYPES = %i[daterange tsrange tstzrange].freeze
|
8
|
+
CURRENT_GETTERS = {
|
9
|
+
daterange: 'Date.today',
|
10
|
+
tsrange: 'Time.zone.now',
|
11
|
+
tstzrange: 'Time.zone.now',
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
TYPE_CASTERS = {
|
15
|
+
daterange: :date,
|
16
|
+
tsrange: :timestamp,
|
17
|
+
tstzrange: :timestamp,
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
attr_accessor :klass, :attribute, :options, :type, :arel_attribute, :default,
|
21
|
+
:current_getter, :type_caster, :default_sql, :threshold, :dynamic_threshold,
|
22
|
+
:period_module
|
23
|
+
|
24
|
+
# Start a new builder of methods for period values on
|
25
|
+
# ActiveRecord::Base
|
26
|
+
def initialize(klass, attribute, _, options)
|
27
|
+
@klass = klass
|
28
|
+
@attribute = attribute.to_s
|
29
|
+
@options = options
|
30
|
+
@type = klass.attribute_types[@attribute].type
|
31
|
+
|
32
|
+
@arel_attribute = klass.arel_table[@attribute]
|
33
|
+
@current_getter = CURRENT_GETTERS[type]
|
34
|
+
@type_caster = TYPE_CASTERS[type]
|
35
|
+
|
36
|
+
@default = options[:pessimistic].blank?
|
37
|
+
@default_sql = ::Arel.sql(klass.connection.quote(@default))
|
38
|
+
|
39
|
+
@threshold = options[:threshold].presence
|
40
|
+
|
41
|
+
raise ArgumentError, <<-MSG.squish unless SUPPORTED_TYPES.include?(type)
|
42
|
+
Period cannot be generated for #{attribute} because its type
|
43
|
+
#{type} is not supported. Only #{SUPPORTED_TYPES.join(', ')} are supported.
|
44
|
+
MSG
|
45
|
+
end
|
46
|
+
|
47
|
+
# Generate all the method names
|
48
|
+
def method_names
|
49
|
+
@method_names ||= options.fetch(:methods, {}).symbolize_keys
|
50
|
+
.reverse_merge(default_method_names)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get the list of methods associated withe the class
|
54
|
+
def klass_method_names
|
55
|
+
@klass_method_names ||= method_names.to_a[0..20].to_h
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get the list of methods associated withe the instances
|
59
|
+
def instance_method_names
|
60
|
+
@instance_method_names ||= method_names.to_a[21..27].to_h
|
61
|
+
end
|
62
|
+
|
63
|
+
# Check if any of the methods that will be created get in conflict
|
64
|
+
# with the base class methods
|
65
|
+
def conflicting?
|
66
|
+
return false if options[:force] == true
|
67
|
+
|
68
|
+
klass_method_names.values.each { |name| dangerous?(name, true) }
|
69
|
+
instance_method_names.values.each { |name| dangerous?(name) }
|
70
|
+
|
71
|
+
return false
|
72
|
+
rescue Interrupt => err
|
73
|
+
raise ArgumentError, <<-MSG.squish
|
74
|
+
#{subtype.class.name} was not able to generate requested
|
75
|
+
methods because the method #{err} already exists in
|
76
|
+
#{klass.name}.
|
77
|
+
MSG
|
78
|
+
end
|
79
|
+
|
80
|
+
# Create all methods needed
|
81
|
+
def build
|
82
|
+
@period_module = Module.new
|
83
|
+
|
84
|
+
build_singleton_methods
|
85
|
+
build_instance_methods
|
86
|
+
|
87
|
+
klass.include period_module
|
88
|
+
end
|
89
|
+
|
90
|
+
# When the time has a threshold, then the real attribute is complex
|
91
|
+
def real_arel_attribute
|
92
|
+
return arel_attribute unless threshold.present?
|
93
|
+
|
94
|
+
left = named_function(:lower, arel_attribute) - threshold_value
|
95
|
+
right = named_function(:upper, arel_attribute) + threshold_value
|
96
|
+
|
97
|
+
if type.eql?(:daterange)
|
98
|
+
left = left.cast(:date)
|
99
|
+
right = right.cast(:date)
|
100
|
+
end
|
101
|
+
|
102
|
+
@real_arel_attribute ||= named_function(type, left, right)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Create an arel named function
|
106
|
+
def named_function(name, *args)
|
107
|
+
::Arel::Nodes::NamedFunction.new(name.to_s, args)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Create an arel version of +nullif+ function
|
111
|
+
def arel_nullif(*args)
|
112
|
+
named_function('nullif', args)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Create an arel version of +coalesce+ function
|
116
|
+
def arel_coalesce(*args)
|
117
|
+
named_function('coalesce', args)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Create an arel version of the type with the following values
|
121
|
+
def arel_convert_to_type(left, right = nil, set_type = nil)
|
122
|
+
named_function(set_type || type, [left, right || left])
|
123
|
+
end
|
124
|
+
|
125
|
+
# Convert timestamp range to date range format
|
126
|
+
def arel_daterange
|
127
|
+
named_function(
|
128
|
+
:daterange,
|
129
|
+
named_function(:lower, real_arel_attribute).cast(:date),
|
130
|
+
named_function(:upper, real_arel_attribute).cast(:date),
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Create an arel version of an empty value for the range
|
135
|
+
def arel_empty_value
|
136
|
+
arel_convert_to_type(::Arel.sql('NULL'))
|
137
|
+
end
|
138
|
+
|
139
|
+
# Create an arel not condition
|
140
|
+
def arel_not(value)
|
141
|
+
named_function(:not, value)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the main arel condition to check the value
|
145
|
+
def arel_check_condition(type, value)
|
146
|
+
value = ::Arel.sql(klass.connection.quote(value))
|
147
|
+
|
148
|
+
checker = arel_nullif(real_arel_attribute, arel_empty_value)
|
149
|
+
checker = checker.public_send(type, value.cast(type_caster))
|
150
|
+
arel_coalesce(checker, default_sql)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Check how to provide the threshold value
|
154
|
+
def threshold_value
|
155
|
+
@threshold_value ||= begin
|
156
|
+
case threshold
|
157
|
+
when Symbol, String
|
158
|
+
klass.arel_table[threshold]
|
159
|
+
when ActiveSupport::Duration
|
160
|
+
::Arel.sql("'#{threshold.to_i} seconds'").cast(:interval)
|
161
|
+
when Numeric
|
162
|
+
value = threshold.to_i.to_s
|
163
|
+
value << type_caster.eql?(:date) ? ' days' : ' seconds'
|
164
|
+
::Arel.sql("'#{value}'").cast(:interval)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# Generates the default method names
|
172
|
+
def default_method_names
|
173
|
+
Torque::PostgreSQL.config.period.method_names.transform_values do |value|
|
174
|
+
format(value, attribute)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Check if the method already exists in the reference class
|
179
|
+
def dangerous?(method_name, class_method = false)
|
180
|
+
if class_method
|
181
|
+
if klass.dangerous_class_method?(method_name)
|
182
|
+
raise Interrupt, method_name.to_s
|
183
|
+
end
|
184
|
+
else
|
185
|
+
if klass.dangerous_attribute_method?(method_name)
|
186
|
+
raise Interrupt, method_name.to_s
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Build model methods
|
192
|
+
def build_singleton_methods
|
193
|
+
attr = attribute
|
194
|
+
builder = self
|
195
|
+
|
196
|
+
# TODO: Rewrite these as string
|
197
|
+
klass.scope method_names[:current_on], ->(value) do
|
198
|
+
where(builder.arel_check_condition(:contains, value))
|
199
|
+
end
|
200
|
+
|
201
|
+
klass.scope method_names[:current], -> do
|
202
|
+
public_send(builder.method_names[:current_on], eval(builder.current_getter))
|
203
|
+
end
|
204
|
+
|
205
|
+
klass.scope method_names[:not_current], -> do
|
206
|
+
current_value = eval(builder.current_getter)
|
207
|
+
where.not(builder.arel_check_condition(:contains, current_value))
|
208
|
+
end
|
209
|
+
|
210
|
+
klass.scope method_names[:containing], ->(value) do
|
211
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
212
|
+
value = ::Arel.sql(connection.quote(value)) unless value.respond_to?(:cast)
|
213
|
+
where(builder.arel_attribute.contains(value))
|
214
|
+
end
|
215
|
+
|
216
|
+
klass.scope method_names[:not_containing], ->(value) do
|
217
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
218
|
+
value = ::Arel.sql(connection.quote(value)) unless value.respond_to?(:cast)
|
219
|
+
where.not(builder.arel_attribute.contains(value))
|
220
|
+
end
|
221
|
+
|
222
|
+
klass.scope method_names[:overlapping], ->(value, right = nil) do
|
223
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
224
|
+
|
225
|
+
if right.present?
|
226
|
+
value = ::Arel.sql(connection.quote(value))
|
227
|
+
right = ::Arel.sql(connection.quote(right))
|
228
|
+
value = builder.arel_convert_to_type(value, right)
|
229
|
+
elsif !value.respond_to?(:cast)
|
230
|
+
value = ::Arel.sql(connection.quote(value))
|
231
|
+
end
|
232
|
+
|
233
|
+
where(builder.arel_attribute.overlaps(value))
|
234
|
+
end
|
235
|
+
|
236
|
+
klass.scope method_names[:not_overlapping], ->(value, right = nil) do
|
237
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
238
|
+
|
239
|
+
if right.present?
|
240
|
+
value = ::Arel.sql(connection.quote(value))
|
241
|
+
right = ::Arel.sql(connection.quote(right))
|
242
|
+
value = builder.arel_convert_to_type(value, right)
|
243
|
+
elsif !value.respond_to?(:cast)
|
244
|
+
value = ::Arel.sql(connection.quote(value))
|
245
|
+
end
|
246
|
+
|
247
|
+
where.not(builder.arel_attribute.overlaps(value))
|
248
|
+
end
|
249
|
+
|
250
|
+
klass.scope method_names[:starting_after], ->(value) do
|
251
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
252
|
+
value = ::Arel.sql(connection.quote(value)) \
|
253
|
+
unless value.is_a?(::Arel::Attributes::Attribute)
|
254
|
+
|
255
|
+
where(builder.named_function(:lower, builder.arel_attribute).gt(value))
|
256
|
+
end
|
257
|
+
|
258
|
+
klass.scope method_names[:starting_before], ->(value) do
|
259
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
260
|
+
value = ::Arel.sql(connection.quote(value)) \
|
261
|
+
unless value.is_a?(::Arel::Attributes::Attribute)
|
262
|
+
|
263
|
+
where(builder.named_function(:lower, builder.arel_attribute).lt(value))
|
264
|
+
end
|
265
|
+
|
266
|
+
klass.scope method_names[:finishing_after], ->(value) do
|
267
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
268
|
+
value = ::Arel.sql(connection.quote(value)) \
|
269
|
+
unless value.is_a?(::Arel::Attributes::Attribute)
|
270
|
+
|
271
|
+
where(builder.named_function(:upper, builder.arel_attribute).gt(value))
|
272
|
+
end
|
273
|
+
|
274
|
+
klass.scope method_names[:finishing_before], ->(value) do
|
275
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
276
|
+
value = ::Arel.sql(connection.quote(value)) \
|
277
|
+
unless value.is_a?(::Arel::Attributes::Attribute)
|
278
|
+
|
279
|
+
where(builder.named_function(:upper, builder.arel_attribute).lt(value))
|
280
|
+
end
|
281
|
+
|
282
|
+
if threshold.present?
|
283
|
+
klass.scope method_names[:real_containing], ->(value) do
|
284
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
285
|
+
value = ::Arel.sql(connection.quote(value)) unless value.respond_to?(:cast)
|
286
|
+
where(builder.real_arel_attribute.contains(value))
|
287
|
+
end
|
288
|
+
|
289
|
+
klass.scope method_names[:real_overlapping], ->(value, right = nil) do
|
290
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
291
|
+
|
292
|
+
if right.present?
|
293
|
+
value = ::Arel.sql(connection.quote(value))
|
294
|
+
right = ::Arel.sql(connection.quote(right))
|
295
|
+
value = builder.arel_convert_to_type(value, right)
|
296
|
+
elsif !value.respond_to?(:cast)
|
297
|
+
value = ::Arel.sql(connection.quote(value))
|
298
|
+
end
|
299
|
+
|
300
|
+
where(builder.real_arel_attribute.overlaps(value))
|
301
|
+
end
|
302
|
+
|
303
|
+
klass.scope method_names[:real_starting_after], ->(value) do
|
304
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
305
|
+
condition = builder.named_function(:lower, builder.arel_attribute)
|
306
|
+
condition -= builder.threshold_value
|
307
|
+
condition = condition.cast(:date) if builder.type.eql?(:daterange)
|
308
|
+
where(condition.gt(value))
|
309
|
+
end
|
310
|
+
|
311
|
+
klass.scope method_names[:real_starting_before], ->(value) do
|
312
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
313
|
+
condition = builder.named_function(:lower, builder.arel_attribute)
|
314
|
+
condition -= builder.threshold_value
|
315
|
+
condition = condition.cast(:date) if builder.type.eql?(:daterange)
|
316
|
+
where(condition.lt(value))
|
317
|
+
end
|
318
|
+
|
319
|
+
klass.scope method_names[:real_finishing_after], ->(value) do
|
320
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
321
|
+
condition = builder.named_function(:upper, builder.arel_attribute)
|
322
|
+
condition += builder.threshold_value
|
323
|
+
condition = condition.cast(:date) if builder.type.eql?(:daterange)
|
324
|
+
where(condition.gt(value))
|
325
|
+
end
|
326
|
+
|
327
|
+
klass.scope method_names[:real_finishing_before], ->(value) do
|
328
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
329
|
+
condition = builder.named_function(:upper, builder.arel_attribute)
|
330
|
+
condition += builder.threshold_value
|
331
|
+
condition = condition.cast(:date) if builder.type.eql?(:daterange)
|
332
|
+
where(condition.lt(value))
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
unless type.eql?(:daterange)
|
337
|
+
klass.scope method_names[:containing_date], ->(value) do
|
338
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
339
|
+
value = ::Arel.sql(connection.quote(value)) unless value.respond_to?(:cast)
|
340
|
+
where(builder.arel_daterange.contains(value))
|
341
|
+
end
|
342
|
+
|
343
|
+
klass.scope method_names[:not_containing_date], ->(value) do
|
344
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
345
|
+
value = ::Arel.sql(connection.quote(value)) unless value.respond_to?(:cast)
|
346
|
+
where.not(builder.arel_daterange.contains(value))
|
347
|
+
end
|
348
|
+
|
349
|
+
klass.scope method_names[:overlapping_date], ->(value, right = nil) do
|
350
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
351
|
+
|
352
|
+
if right.present?
|
353
|
+
value = ::Arel.sql(connection.quote(value))
|
354
|
+
right = ::Arel.sql(connection.quote(right))
|
355
|
+
value = builder.arel_convert_to_type(value, right, :daterange)
|
356
|
+
elsif !value.respond_to?(:cast)
|
357
|
+
value = ::Arel.sql(connection.quote(value))
|
358
|
+
end
|
359
|
+
|
360
|
+
where(builder.arel_daterange.overlaps(value))
|
361
|
+
end
|
362
|
+
|
363
|
+
klass.scope method_names[:not_overlapping_date], ->(value, right = nil) do
|
364
|
+
value = arel_table[value] if value.is_a?(Symbol)
|
365
|
+
|
366
|
+
if right.present?
|
367
|
+
value = ::Arel.sql(connection.quote(value))
|
368
|
+
right = ::Arel.sql(connection.quote(right))
|
369
|
+
value = builder.arel_convert_to_type(value, right, :daterange)
|
370
|
+
elsif !value.respond_to?(:cast)
|
371
|
+
value = ::Arel.sql(connection.quote(value))
|
372
|
+
end
|
373
|
+
|
374
|
+
where.not(builder.arel_daterange.overlaps(value))
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
# Build model instance methods
|
380
|
+
def build_instance_methods
|
381
|
+
attr = attribute
|
382
|
+
builder = self
|
383
|
+
|
384
|
+
attr_threshold = threshold
|
385
|
+
attr_threshold = attr_threshold.to_sym if attr_threshold.is_a?(String)
|
386
|
+
attr_threshold = attr_threshold.seconds if attr_threshold.is_a?(Numeric)
|
387
|
+
|
388
|
+
# TODO: Rewrite these as string
|
389
|
+
period_module.module_eval do
|
390
|
+
define_method(builder.method_names[:current?]) do
|
391
|
+
public_send(builder.method_names[:current_on?], eval(builder.current_getter))
|
392
|
+
end
|
393
|
+
|
394
|
+
define_method(builder.method_names[:current_on?]) do |value|
|
395
|
+
attr_value = builder.threshold ? builder.method_names[:real] : attr
|
396
|
+
attr_value = public_send(attr_value)
|
397
|
+
|
398
|
+
return builder.default if attr_value.nil? ||
|
399
|
+
(attr_value.min.try(:infinite?) && attr_value.max.try(:infinite?))
|
400
|
+
|
401
|
+
attr_value.min < value && attr_value.max > value
|
402
|
+
end
|
403
|
+
|
404
|
+
define_method(builder.method_names[:start]) do
|
405
|
+
public_send(attr)&.min
|
406
|
+
end
|
407
|
+
|
408
|
+
define_method(builder.method_names[:finish]) do
|
409
|
+
public_send(attr)&.max
|
410
|
+
end
|
411
|
+
|
412
|
+
if attr_threshold.present?
|
413
|
+
define_method(builder.method_names[:start]) do
|
414
|
+
public_send(attr)&.min
|
415
|
+
end
|
416
|
+
|
417
|
+
define_method(builder.method_names[:finish]) do
|
418
|
+
public_send(attr)&.max
|
419
|
+
end
|
420
|
+
|
421
|
+
define_method(builder.method_names[:real]) do
|
422
|
+
left = public_send(builder.method_names[:real_start])
|
423
|
+
right = public_send(builder.method_names[:real_finish])
|
424
|
+
return unless left || right
|
425
|
+
|
426
|
+
left ||= -::Float::INFINITY
|
427
|
+
right ||= ::Float::INFINITY
|
428
|
+
|
429
|
+
(left..right)
|
430
|
+
end
|
431
|
+
|
432
|
+
define_method(builder.method_names[:real_start]) do
|
433
|
+
threshold = attr_threshold
|
434
|
+
threshold = public_send(threshold) if threshold.is_a?(Symbol)
|
435
|
+
result = public_send(attr)&.min.try(:-, threshold)
|
436
|
+
builder.type.eql?(:daterange) ? result&.to_date : result
|
437
|
+
end
|
438
|
+
|
439
|
+
define_method(builder.method_names[:real_finish]) do
|
440
|
+
threshold = attr_threshold
|
441
|
+
threshold = public_send(threshold) if threshold.is_a?(Symbol)
|
442
|
+
result = public_send(attr)&.max.try(:+, threshold)
|
443
|
+
builder.type.eql?(:daterange) ? result&.to_date : result
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
@@ -11,7 +11,7 @@ module Torque
|
|
11
11
|
class << self
|
12
12
|
include Enumerable
|
13
13
|
|
14
|
-
delegate :each, :sample, to: :members
|
14
|
+
delegate :each, :sample, :size, :length, to: :members
|
15
15
|
|
16
16
|
# Find or create the class that will handle the value
|
17
17
|
def lookup(name)
|
@@ -72,8 +72,7 @@ module Torque
|
|
72
72
|
# see https://github.com/rails/rails/blob/v5.0.0/activerecord/lib/active_record/fixtures.rb#L656
|
73
73
|
# see https://github.com/rails/rails/blob/v5.0.0/activerecord/lib/active_record/validations/uniqueness.rb#L101
|
74
74
|
def fetch(value, *)
|
75
|
-
|
76
|
-
send(value)
|
75
|
+
new(value.to_s) if values.include?(value)
|
77
76
|
end
|
78
77
|
alias [] fetch
|
79
78
|
|
@@ -89,11 +88,16 @@ module Torque
|
|
89
88
|
self.values.include?(value.to_s)
|
90
89
|
end
|
91
90
|
|
91
|
+
# Build an active record scope for a given atribute agains a value
|
92
|
+
def scope(attribute, value)
|
93
|
+
attribute.eq(value)
|
94
|
+
end
|
95
|
+
|
92
96
|
private
|
93
97
|
|
94
98
|
# Allows checking value existance
|
95
99
|
def respond_to_missing?(method_name, include_private = false)
|
96
|
-
valid?(method_name)
|
100
|
+
valid?(method_name) || super
|
97
101
|
end
|
98
102
|
|
99
103
|
# Allow fast creation of values
|
@@ -165,7 +169,7 @@ module Torque
|
|
165
169
|
|
166
170
|
# Change the inspection to show the enum name
|
167
171
|
def inspect
|
168
|
-
nil? ? 'nil' : "
|
172
|
+
nil? ? 'nil' : ":#{to_s}"
|
169
173
|
end
|
170
174
|
|
171
175
|
private
|
@@ -199,9 +203,9 @@ module Torque
|
|
199
203
|
name = method_name.to_s
|
200
204
|
|
201
205
|
if name.chomp!('?')
|
202
|
-
self == name
|
206
|
+
self == name
|
203
207
|
elsif name.chomp!('!')
|
204
|
-
replace(name)
|
208
|
+
replace(name) unless self == name
|
205
209
|
else
|
206
210
|
super
|
207
211
|
end
|
@@ -220,7 +224,6 @@ module Torque
|
|
220
224
|
def raise_comparison(other)
|
221
225
|
raise EnumError, "Comparison of #{self.class.name} with #{self.inspect} failed"
|
222
226
|
end
|
223
|
-
|
224
227
|
end
|
225
228
|
|
226
229
|
# Create the methods related to the attribute to handle the enum type
|