137 lines
3.5 KiB
Ruby
137 lines
3.5 KiB
Ruby
module HasCustomFields
|
|
extend ActiveSupport::Concern
|
|
module Helpers
|
|
def self.append_field(target, key, value, types)
|
|
if target.has_key?(key)
|
|
target[key] = [target[key]] if !target[key].is_a? Array
|
|
target[key] << cast_custom_field(key, value, types)
|
|
else
|
|
target[key] = cast_custom_field(key, value, types)
|
|
end
|
|
end
|
|
|
|
CUSTOM_FIELD_TRUE = ['t','true', 'T', 'True', 'TRUE'].freeze unless defined? CUSTOM_FIELD_TRUE
|
|
|
|
def self.cast_custom_field(key, value, types)
|
|
return value unless types && type = types[key]
|
|
|
|
case type
|
|
when :boolean then !!CUSTOM_FIELD_TRUE.include?(value)
|
|
when :integer then value.to_i
|
|
else
|
|
value
|
|
end
|
|
end
|
|
end
|
|
|
|
included do
|
|
has_many :_custom_fields, dependent: :destroy, :class_name => "#{name}CustomField"
|
|
after_save :save_custom_fields
|
|
|
|
# To avoid n+1 queries, we have this function to retrieve lots of custom fields in one
|
|
# go and create a "sideloaded" version for easy querying by id.
|
|
def self.custom_fields_for_ids(ids, whitelisted_fields)
|
|
klass = "#{name}CustomField".constantize
|
|
foreign_key = "#{name.underscore}_id".to_sym
|
|
|
|
result = {}
|
|
|
|
return result if whitelisted_fields.blank?
|
|
klass.where(foreign_key => ids, :name => whitelisted_fields).pluck(foreign_key, :name, :value).each do |cf|
|
|
result[cf[0]] ||= {}
|
|
append_custom_field(result[cf[0]], cf[1], cf[2])
|
|
end
|
|
result
|
|
end
|
|
|
|
def self.append_custom_field(target, key, value)
|
|
HasCustomFields::Helpers.append_field(target,key,value,@custom_field_types)
|
|
end
|
|
|
|
|
|
def self.register_custom_field_type(name, type)
|
|
@custom_field_types ||= {}
|
|
@custom_field_types[name] = type
|
|
end
|
|
|
|
end
|
|
|
|
def reload(options = nil)
|
|
@custom_fields = nil
|
|
@custom_fields_orig = nil
|
|
super
|
|
end
|
|
|
|
|
|
def custom_fields
|
|
@custom_fields ||= refresh_custom_fields_from_db.dup
|
|
end
|
|
|
|
def custom_fields=(data)
|
|
custom_fields.replace(data)
|
|
end
|
|
|
|
def custom_fields_clean?
|
|
# Check whether the cached version has been
|
|
# changed on this model
|
|
!@custom_fields || @custom_fields_orig == @custom_fields
|
|
end
|
|
|
|
protected
|
|
|
|
def refresh_custom_fields_from_db
|
|
target = Hash.new
|
|
_custom_fields.pluck(:name,:value).each do |key, value|
|
|
self.class.append_custom_field(target, key, value)
|
|
end
|
|
@custom_fields_orig = target
|
|
@custom_fields = @custom_fields_orig.dup
|
|
end
|
|
|
|
def save_custom_fields
|
|
if !custom_fields_clean?
|
|
dup = @custom_fields.dup
|
|
|
|
array_fields = {}
|
|
|
|
_custom_fields.each do |f|
|
|
if dup[f.name].is_a? Array
|
|
# we need to collect Arrays fully before
|
|
# we can compare them
|
|
if !array_fields.has_key? f.name
|
|
array_fields[f.name] = [f]
|
|
else
|
|
array_fields[f.name] << f
|
|
end
|
|
else
|
|
if dup[f.name] != f.value
|
|
f.destroy
|
|
else
|
|
dup.delete(f.name)
|
|
end
|
|
end
|
|
end
|
|
|
|
# let's iterate through our arrays and compare them
|
|
array_fields.each do |field_name, fields|
|
|
if fields.length == dup[field_name].length &&
|
|
fields.map{|f| f.value} == dup[field_name]
|
|
dup.delete(f.name)
|
|
else
|
|
fields.each{|f| f.destroy }
|
|
end
|
|
end
|
|
|
|
dup.each do |k,v|
|
|
if v.is_a? Array
|
|
v.each {|subv| _custom_fields.create(name: k, value: subv)}
|
|
else
|
|
_custom_fields.create(name: k, value: v)
|
|
end
|
|
end
|
|
|
|
refresh_custom_fields_from_db
|
|
end
|
|
end
|
|
end
|