# require "eitil_wrapper/records/default_calculators" # require "eitil_wrapper/railtie" to run the dynamic dispatch as an init hook during boot require "eitil_wrapper/railtie" module EitilWrapper module Records module DefaultCalculators Eitil::ApplicationRecordModules << self SharableNumberCalculators = -> (_class, column) { _class.eitil_calculator :"#{column}_max", -> { _class.maximum(column) } _class.eitil_calculator :"#{column}_min", -> { _class.minimum(column) } _class.eitil_calculator :"#{column}_sum", -> { _class.sum(column) } _class.eitil_calculator :"#{column}_avg", -> { _class.average(column) } } SharableIterableCalculators = -> (_class, column) { _class.eitil_calculator :"#{column}_sum", -> { _class.pluck(column).flatten!.uniq! } } def inherited(subclass) super return if Eitil.skip_default_calculators_for_models.include?(subclass.to_s.to_sym) # Set the proper table_names for namespaced models. Without setting this, # Rails run into problems due to the fact that the first call to the model's # constant triggers this initializer first, and only thereafter the model file # which sets the correct table_name through a macro. namespaced_class = subclass.to_s.include?('::') subclass.table_name = subclass.to_s.gsub('::', '_').downcase.pluralize if namespaced_class subclass.use_eitil_calculators rescue => e puts "default calculators failed for class '#{subclass}' with expected table '#{subclass.table_name}' because of #{e.class} and '#{e.to_s.split(' ').first}'" end def use_eitil_calculators return if abstract_class? # text[] is postgresql's datatype for serialized arrays # numeric is postgresql's datatype for decimals # double precision is postgresql's datatype for floats %w[integer bigint double\ precision numeric].each do |_type| send :"create_eitil_#{_type.gsub(' ','_')}_calculators" end create_array_calculators end def eitil_calculator(_name, _proc) # skip calculator methods for primary and foreign key columns return if _name.to_s =~ /^id_[a-z]{1,}$/ || _name.to_s =~ /_id_[a-z]{1,}$/ define_singleton_method(_name) { _proc.call } unless respond_to? _name end def calculator_columns_of_type(data_type) columns_hash.select { |column,v| v.sql_type == data_type } end def create_eitil_integer_calculators calculator_columns_of_type("integer")&.map do |column, object| SharableNumberCalculators.call self, column end end def create_eitil_bigint_calculators calculator_columns_of_type("bigint")&.map do |column, object| SharableNumberCalculators.call self, column end end def create_eitil_double_precision_calculators calculator_columns_of_type("double\ precision")&.map do |column, object| SharableNumberCalculators.call self, column end end def create_eitil_numeric_calculators calculator_columns_of_type("numeric")&.map do |column, object| SharableNumberCalculators.call self, column end end def create_array_calculators columns = columns_hash.select { |column,v| v.sql_type == "text" && v.default == "{}" } columns&.map do |column, object| SharableIterableCalculators.call self, column end end end end end