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