module ActiveReporter module Dimension class Bin class Set class << self def from_hash(source) # Returns either a bin or nil, depending on whether the input is valid. case source when nil new(nil, nil) when Hash then min, max = source.symbolize_keys.values_at(:min, :max) new(min.presence, max.presence) unless min.blank? && max.blank? else nil end end def from_sql(value) case value when /^([^,]+),(.+)$/ then new($1, $2) when /^([^,]+),$/ then new($1, nil) when /^,(.+)$/ then new(nil, $1) when ',', nil then new(nil, nil) else raise "Unexpected SQL bin format #{value}" end end end def initialize(min, max) @min = min @max = max end def min @min && parse(@min) end def max @max && parse(@max) end def valid? (@min.nil? || parses?(@min)) && (@max.nil? || parses?(@max)) end def parses?(value) parse(value).present? rescue false end def parse(value) value end def quote(value) ActiveRecord::Base.connection.quote(value) end def cast(value) quote(value) end def bin_text "#{min},#{max}" end def cast_bin_text case ActiveReporter.database_type when :postgres, :sqlite "CAST(#{quote(bin_text)} AS text)" else quote(bin_text) end end def row_sql "SELECT #{cast(min)} AS min, #{cast(max)} AS max, #{cast_bin_text} AS bin_text" end def contains_sql(expr) case bin_edges when :min_and_max "(#{expr} >= #{quote(min)} AND #{expr} < #{quote(max)})" when :min "#{expr} >= #{quote(min)}" when :max "#{expr} < #{quote(max)}" else "#{expr} IS NULL" end end def as_json(*) @as_json ||= case bin_edges when :min_and_max { min: min, max: max } when :min { min: min } when :max { max: max } end end def [](key) case key.to_s when 'min' then min when 'max' then max end end def has_key?(key) %w[min max].include?(key.to_s) end alias key? has_key? def values_at(*keys) keys.map { |k| self[key] } end def inspect "" end def hash as_json.hash end def ==(other) if other.nil? min.nil? && max.nil? else min == other[:min] && max == other[:max] end end alias eql? == def bin_edges case when min_and_max? then :min_and_max when min? then :min when max? then :max end end private def min_and_max? min.present? && max.present? end def min? min.present? end def max? max.present? end end end end end