Websites/app/models/application_record.rb

159 lines
4.2 KiB
Ruby

# frozen_string_literal: true
class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
module ApiMethods
extend ActiveSupport::Concern
def as_json(options = {})
options ||= {}
options[:except] ||= []
options[:except] += hidden_attributes
options[:methods] ||= []
options[:methods] += method_attributes
super(options)
end
def serializable_hash(*args)
hash = super(*args)
hash.transform_keys { |key| key.delete("?") }
end
protected
def hidden_attributes
%i[uploader_ip_addr updater_ip_addr creator_ip_addr user_ip_addr ip_addr]
end
def method_attributes
[]
end
end
module UserMethods
def belongs_to_creator(options = {})
field = options.delete(:field) || :creator
class_eval do
belongs_to(field, **options.merge(class_name: "APIUser"))
before_validation(on: :create) do |rec|
rec.send("#{field}_id=", CurrentUser.id) if rec.send("#{field}_id").nil?
rec.send("#{field}_ip_addr=", CurrentUser.ip_addr) if rec.respond_to?(:"#{field}_ip_addr=") && rec.send("#{field}_ip_addr").nil?
end
end
end
end
module SearchMethods
def attribute_matches(attribute, value, **)
return all if value.nil?
column = column_for_attribute(attribute)
case column.sql_type_metadata.type
when :boolean
boolean_attribute_matches(attribute, value, **)
when :integer, :datetime
numeric_attribute_matches(attribute, value, **)
when :string, :text
text_attribute_matches(attribute, value, **)
when :uuid
where(attribute => value)
else
raise(ArgumentError, "unhandled attribute type: #{column.sql_type_metadata.type}")
end
end
def boolean_attribute_matches(attribute, value)
if value.to_s.truthy?
value = true
elsif value.to_s.falsy?
value = false
else
raise(ArgumentError, "value must be truthy or falsy")
end
where(attribute => value)
end
# range: "5", ">5", "<5", ">=5", "<=5", "5..10", "5,6,7"
def numeric_attribute_matches(attribute, range)
column = column_for_attribute(attribute)
qualified_column = "#{table_name}.#{column.name}"
parsed_range = ParseValue.range(range, column.type)
add_range_relation(parsed_range, qualified_column)
end
def add_range_relation(arr, field)
return all if arr.nil?
case arr[0]
when :eq
if arr[1].is_a?(Time)
where("#{field} between ? and ?", arr[1].beginning_of_day, arr[1].end_of_day)
else
where(["#{field} = ?", arr[1]])
end
when :gt
where(["#{field} > ?", arr[1]])
when :gte
where(["#{field} >= ?", arr[1]])
when :lt
where(["#{field} < ?", arr[1]])
when :lte
where(["#{field} <= ?", arr[1]])
when :in
where(["#{field} in (?)", arr[1]])
when :between
where(["#{field} BETWEEN ? AND ?", arr[1], arr[2]])
else
all
end
end
def text_attribute_matches(attribute, value, convert_to_wildcard: false)
column = column_for_attribute(attribute)
qualified_column = "#{table_name}.#{column.name}"
value = "*#{value}*" if convert_to_wildcard && value.exclude?("*")
if value =~ /\*/
where("lower(#{qualified_column}) LIKE :value ESCAPE E'\\\\'", value: value.downcase.to_escaped_for_sql_like)
else
where("to_tsvector(:ts_config, #{qualified_column}) @@ plainto_tsquery(:ts_config, :value)", ts_config: "english", value: value)
end
end
def apply_basic_order(params)
case params[:order]
when "id_asc"
order(id: :asc)
when "id_desc"
order(id: :desc)
else
default_order
end
end
def default_order
order(id: :desc)
end
def search(params)
params ||= {}
q = all
q = q.attribute_matches(:id, params[:id])
q = q.attribute_matches(:created_at, params[:created_at]) if attribute_names.include?("created_at")
q = q.attribute_matches(:updated_at, params[:updated_at]) if attribute_names.include?("updated_at")
q
end
end
include ApiMethods
extend SearchMethods
extend UserMethods
end