class Torque::PostgreSQL::Attributes::Builder::Period
TODO: Allow documenting by building the methods outside and importing only the raw string
Constants
- CURRENT_GETTERS
- DIRECT_ACCESS_REGEX
- SUPPORTED_TYPES
- TYPE_CASTERS
Attributes
Public Class Methods
Start a new builder of methods for period values on ActiveRecord::Base
# File lib/torque/postgresql/attributes/builder/period.rb, line 29 def initialize(klass, attribute, options) @klass = klass @attribute = attribute.to_s @options = options @type = klass.attribute_types[@attribute].type raise ArgumentError, <<-MSG.squish unless SUPPORTED_TYPES.include?(type) Period cannot be generated for #{attribute} because its type #{type} is not supported. Only #{SUPPORTED_TYPES.join(', ')} are supported. MSG @current_getter = CURRENT_GETTERS[type] @type_caster = TYPE_CASTERS[type] @default = options[:pessimistic].blank? end
Public Instance Methods
Create all methods needed
# File lib/torque/postgresql/attributes/builder/period.rb, line 104 def build @klass_module = Module.new @instance_module = Module.new value_args = ['value'] left_right_args = ['left', 'right = nil'] ## Klass methods build_method_helper :klass, :current_on, value_args # 00 build_method_helper :klass, :current # 01 build_method_helper :klass, :not_current # 02 build_method_helper :klass, :containing, value_args # 03 build_method_helper :klass, :not_containing, value_args # 04 build_method_helper :klass, :overlapping, left_right_args # 05 build_method_helper :klass, :not_overlapping, left_right_args # 06 build_method_helper :klass, :starting_after, value_args # 07 build_method_helper :klass, :starting_before, value_args # 08 build_method_helper :klass, :finishing_after, value_args # 09 build_method_helper :klass, :finishing_before, value_args # 10 if threshold.present? build_method_helper :klass, :real_containing, value_args # 11 build_method_helper :klass, :real_overlapping, left_right_args # 12 build_method_helper :klass, :real_starting_after, value_args # 13 build_method_helper :klass, :real_starting_before, value_args # 14 build_method_helper :klass, :real_finishing_after, value_args # 15 build_method_helper :klass, :real_finishing_before, value_args # 16 end unless type.eql?(:daterange) build_method_helper :klass, :containing_date, value_args # 17 build_method_helper :klass, :not_containing_date, value_args # 18 build_method_helper :klass, :overlapping_date, left_right_args # 19 build_method_helper :klass, :not_overlapping_date, left_right_args # 20 if threshold.present? build_method_helper :klass, :real_containing_date, value_args # 21 build_method_helper :klass, :real_overlapping_date, left_right_args # 22 end end ## Instance methods build_method_helper :instance, :current? # 23 build_method_helper :instance, :current_on?, value_args # 24 build_method_helper :instance, :start # 25 build_method_helper :instance, :finish # 26 if threshold.present? build_method_helper :instance, :real # 27 build_method_helper :instance, :real_start # 28 build_method_helper :instance, :real_finish # 29 end klass.extend klass_module klass.include instance_module end
# File lib/torque/postgresql/attributes/builder/period.rb, line 161 def build_method_helper(type, key, args = []) method_name = method_names[key] return if method_name.nil? method_content = send("#{type}_#{key}") method_content = define_string_method(method_name, method_content, args) source_module = send("#{type}_module") source_module.module_eval(method_content) end
Check if any of the methods that will be created get in conflict with the base class methods
# File lib/torque/postgresql/attributes/builder/period.rb, line 90 def conflicting? return if options[:force] == true klass_method_names.values.each { |name| dangerous?(name, true) } instance_method_names.values.each { |name| dangerous?(name) } rescue Interrupt => err raise ArgumentError, <<-MSG.squish #{subtype.class.name} was not able to generate requested methods because the method #{err} already exists in #{klass.name}. MSG end
Get the list of methods associated withe the instances
# File lib/torque/postgresql/attributes/builder/period.rb, line 84 def instance_method_names @instance_method_names ||= method_names.to_a[23..29].to_h end
Get the list of methods associated withe the class
# File lib/torque/postgresql/attributes/builder/period.rb, line 79 def klass_method_names @klass_method_names ||= method_names.to_a[0..22].to_h end
Generate all the method names
# File lib/torque/postgresql/attributes/builder/period.rb, line 74 def method_names @method_names ||= default_method_names.merge(options.fetch(:methods, {})) end
Private Instance Methods
# File lib/torque/postgresql/attributes/builder/period.rb, line 206 def arel_attribute @arel_attribute ||= "arel_table[#{attribute.inspect}]" end
# File lib/torque/postgresql/attributes/builder/period.rb, line 311 def arel_check_condition(type) checker = arel_nullif(arel_real_attribute, arel_empty_value) checker << ".#{type}(value.cast(#{type_caster.inspect}))" arel_coalesce(checker, arel_default_sql) end
Create an arel version of coalesce
function
# File lib/torque/postgresql/attributes/builder/period.rb, line 292 def arel_coalesce(*args) arel_named_function('coalesce', *args) end
Create an arel version of the type with the following values
# File lib/torque/postgresql/attributes/builder/period.rb, line 275 def arel_convert_to_type(left, right = nil, set_type = nil) arel_named_function(set_type || type, left, right || left) end
Convert timestamp range to date range format
# File lib/torque/postgresql/attributes/builder/period.rb, line 302 def arel_daterange(real = false) arel_named_function( 'daterange', (real ? arel_real_start_at : arel_start_at) + '.cast(:date)', (real ? arel_real_finish_at : arel_finish_at) + '.cast(:date)', '::Arel.sql("\'[]\'")', ) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 210 def arel_default_sql @arel_default_sql ||= arel_sql_quote(@default.inspect) end
Create an arel version of an empty value for the range
# File lib/torque/postgresql/attributes/builder/period.rb, line 297 def arel_empty_value arel_convert_to_type('::Arel.sql(\'NULL\')') end
Finish at version of the value
# File lib/torque/postgresql/attributes/builder/period.rb, line 242 def arel_finish_at @arel_finish_at ||= arel_named_function('upper', arel_attribute) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 328 def arel_formatting_left_right(condition, set_type = nil, cast: nil) [ arel_formatting_value(nil, 'left', cast: cast), '', 'if right.present?', ' ' + arel_formatting_value(nil, 'right', cast: cast), " value = #{arel_convert_to_type('left', 'right', set_type)}", 'else', ' value = left', 'end', '', condition, ].join("\n") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 317 def arel_formatting_value(condition = nil, value = 'value', cast: nil) [ "#{value} = arel_table[#{value}] if #{value}.is_a?(Symbol)", "unless #{value}.respond_to?(:cast)", " #{value} = ::Arel.sql(connection.quote(#{value}))", (" #{value} = #{value}.cast(#{cast.inspect})" if cast), 'end', condition, ].compact.join("\n") end
Create an arel named function
# File lib/torque/postgresql/attributes/builder/period.rb, line 280 def arel_named_function(name, *args) result = +"::Arel::Nodes::NamedFunction.new(#{name.to_s.inspect}" result << ', [' << args.join(', ') << ']' if args.present? result << ')' end
Create an arel version of nullif
function
# File lib/torque/postgresql/attributes/builder/period.rb, line 287 def arel_nullif(*args) arel_named_function('nullif', *args) end
When the time has a threshold, then the real attribute is complex
# File lib/torque/postgresql/attributes/builder/period.rb, line 267 def arel_real_attribute return arel_attribute unless threshold.present? @arel_real_attribute ||= arel_named_function( type, arel_real_start_at, arel_real_finish_at, ) end
Finish at version of the value with threshold
# File lib/torque/postgresql/attributes/builder/period.rb, line 257 def arel_real_finish_at return arel_finish_at unless threshold.present? @arel_real_finish_at ||= begin result = +"(#{arel_finish_at} + #{arel_threshold_value})" result << '.cast(:date)' if type.eql?(:daterange) result end end
Start at version of the value with threshold
# File lib/torque/postgresql/attributes/builder/period.rb, line 247 def arel_real_start_at return arel_start_at unless threshold.present? @arel_real_start_at ||= begin result = +"(#{arel_start_at} - #{arel_threshold_value})" result << '.cast(:date)' if type.eql?(:daterange) result end end
# File lib/torque/postgresql/attributes/builder/period.rb, line 214 def arel_sql_quote(value) "::Arel.sql(connection.quote(#{value}))" end
Start at version of the value
# File lib/torque/postgresql/attributes/builder/period.rb, line 237 def arel_start_at @arel_start_at ||= arel_named_function('lower', arel_attribute) end
Check how to provide the threshold value
# File lib/torque/postgresql/attributes/builder/period.rb, line 219 def arel_threshold_value @arel_threshold_value ||= begin case threshold when Symbol, String "arel_attribute('#{threshold}')" when ActiveSupport::Duration value = "'#{threshold.to_i} seconds'" "::Arel.sql(\"#{value}\").cast(:interval)" when Numeric value = threshold.to_i.to_s value << type_caster.eql?(:date) ? ' days' : ' seconds' value = "'#{value}'" "::Arel.sql(\"#{value}\").cast(:interval)" end end end
Check if the method already exists in the reference class
# File lib/torque/postgresql/attributes/builder/period.rb, line 187 def dangerous?(method_name, class_method = false) if class_method if klass.dangerous_class_method?(method_name) raise Interrupt, method_name.to_s end else if klass.dangerous_attribute_method?(method_name) raise Interrupt, method_name.to_s end end end
Generates the default method names
# File lib/torque/postgresql/attributes/builder/period.rb, line 175 def default_method_names list = Torque::PostgreSQL.config.period.method_names.dup if options.fetch(:prefixed, true) list.transform_values { |value| format(value, attribute) } else list = list.merge(Torque::PostgreSQL.config.period.direct_method_names) list.transform_values { |value| value.gsub(DIRECT_ACCESS_REGEX, '') } end end
BUILDER HELPERS
# File lib/torque/postgresql/attributes/builder/period.rb, line 200 def define_string_method(name, body, args = []) headline = "def #{name}" headline += "(#{args.join(', ')})" [headline, body, 'end'].join("\n") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 448 def instance_current? "#{method_names[:current_on?]}(#{current_getter})" end
# File lib/torque/postgresql/attributes/builder/period.rb, line 452 def instance_current_on? attr_value = threshold.present? ? method_names[:real] : attribute default_value = default.inspect [ "return #{default_value} if #{attr_value}.nil?", "return #{default_value} if #{attr_value}.min.try(:infinite?)", "return #{default_value} if #{attr_value}.max.try(:infinite?)", "#{attr_value}.min < value && #{attr_value}.max > value", ].join("\n") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 467 def instance_finish "#{attribute}&.max" end
# File lib/torque/postgresql/attributes/builder/period.rb, line 471 def instance_real left = method_names[:real_start] right = method_names[:real_finish] [ "left = #{left}", "right = #{right}", 'return unless left || right', '((left || -::Float::INFINITY)..(right || ::Float::INFINITY))', ].join("\n") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 497 def instance_real_finish suffix = type.eql?(:daterange) ? '.to_date' : '' threshold_value = threshold.is_a?(Symbol) \ ? threshold.to_s \ : threshold.to_i.to_s + '.seconds' [ "return if #{method_names[:finish]}.nil?", "value = #{method_names[:finish]}", "value += (#{threshold_value} || 0)", "value#{suffix}" ].join("\n") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 483 def instance_real_start suffix = type.eql?(:daterange) ? '.to_date' : '' threshold_value = threshold.is_a?(Symbol) \ ? threshold.to_s \ : threshold.to_i.to_s + '.seconds' [ "return if #{method_names[:start]}.nil?", "value = #{method_names[:start]}", "value -= (#{threshold_value} || 0)", "value#{suffix}" ].join("\n") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 463 def instance_start "#{attribute}&.min" end
# File lib/torque/postgresql/attributes/builder/period.rb, line 362 def klass_containing arel_formatting_value("where(#{arel_attribute}.contains(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 418 def klass_containing_date arel_formatting_value("where(#{arel_daterange}.contains(value))", cast: :date) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 348 def klass_current [ "value = #{arel_sql_quote(current_getter)}", "where(#{arel_check_condition(:contains)})", ].join("\n") end
METHOD BUILDERS
# File lib/torque/postgresql/attributes/builder/period.rb, line 344 def klass_current_on arel_formatting_value("where(#{arel_check_condition(:contains)})") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 386 def klass_finishing_after arel_formatting_value("where((#{arel_finish_at}).gt(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 390 def klass_finishing_before arel_formatting_value("where((#{arel_finish_at}).lt(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 366 def klass_not_containing arel_formatting_value("where.not(#{arel_attribute}.contains(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 423 def klass_not_containing_date arel_formatting_value("where.not(#{arel_daterange}.contains(value))", cast: :date) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 355 def klass_not_current [ "value = #{arel_sql_quote(current_getter)}", "where.not(#{arel_check_condition(:contains)})", ].join("\n") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 374 def klass_not_overlapping arel_formatting_left_right("where.not(#{arel_attribute}.overlaps(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 433 def klass_not_overlapping_date arel_formatting_left_right("where.not(#{arel_daterange}.overlaps(value))", :daterange, cast: :date) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 370 def klass_overlapping arel_formatting_left_right("where(#{arel_attribute}.overlaps(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 428 def klass_overlapping_date arel_formatting_left_right("where(#{arel_daterange}.overlaps(value))", :daterange, cast: :date) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 394 def klass_real_containing arel_formatting_value("where(#{arel_real_attribute}.contains(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 438 def klass_real_containing_date arel_formatting_value("where(#{arel_daterange(true)}.contains(value))", cast: :date) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 410 def klass_real_finishing_after arel_formatting_value("where(#{arel_real_finish_at}.gt(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 414 def klass_real_finishing_before arel_formatting_value("where(#{arel_real_finish_at}.lt(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 398 def klass_real_overlapping arel_formatting_left_right("where(#{arel_real_attribute}.overlaps(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 443 def klass_real_overlapping_date arel_formatting_left_right("where(#{arel_daterange(true)}.overlaps(value))", :daterange, cast: :date) end
# File lib/torque/postgresql/attributes/builder/period.rb, line 402 def klass_real_starting_after arel_formatting_value("where(#{arel_real_start_at}.gt(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 406 def klass_real_starting_before arel_formatting_value("where(#{arel_real_start_at}.lt(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 378 def klass_starting_after arel_formatting_value("where((#{arel_start_at}).gt(value))") end
# File lib/torque/postgresql/attributes/builder/period.rb, line 382 def klass_starting_before arel_formatting_value("where((#{arel_start_at}).lt(value))") end