app/deserializers/forest_liana/resource_deserializer.rb
require_relative '../../../lib/forest_liana/base64_string_io'
module ForestLiana
class ResourceDeserializer
def initialize(resource, params, with_relationships)
@params = params.permit! if params.respond_to?(:permit!)
@resource = resource
@with_relationships = with_relationships
end
def perform
@attributes = extract_attributes
extract_attributes_serialize
extract_relationships if @with_relationships
extract_paperclip
extract_carrierwave
extract_acts_as_taggable
extract_smart_fields_values
extract_devise
@attributes
end
def extract_attributes
if @params[:data][:attributes]
@params['data']['attributes'].select { |attribute| column?(attribute) }
else
ActionController::Parameters.new()
end
end
def extract_attributes_serialize
attributes_serialized.each do |attribute, serializer|
value = @params[:data][:attributes][attribute]
begin
if @attributes.has_key?(attribute)
@attributes[attribute] = value.nil? ? nil : JSON::parse(value)
end
rescue
message = "Bad format for '#{attribute}' attribute value."
raise ForestLiana::Errors::SerializeAttributeBadFormat.new(message)
end
end
end
def extract_relationships
if @params['data']['relationships']
@params['data']['relationships'].each do |name, relationship|
data = relationship['data']
# Rails 3 requires a :sym argument for the reflect_on_association
# call.
association = @resource.reflect_on_association(name.try(:to_sym))
if [:has_one, :belongs_to].include?(association.try(:macro))
# TODO: refactor like this?
#if data.blank?
#@attributes[name] = nil
#else
#@attributes[name] = association.klass.find(data[:id])
#end
# ActionController::Parameters do not inherit from Hash anymore
# since Rails 5.
if (data.is_a?(Hash) || data.is_a?(ActionController::Parameters)) && data[:id]
if (SchemaUtils.polymorphic?(association))
@attributes[association.foreign_key] = data[:id]
@attributes[association.foreign_type] = data[:type]
else
@attributes[name] = association.klass.find(data[:id])
end
elsif data.blank?
@attributes[name] = nil
end
end
end
# Strong parameter permit all new relationships attributes.
@attributes = @attributes.permit! if @attributes.respond_to?(:permit!)
end
end
def extract_paperclip
return if @params['data']['attributes'].blank?
return unless @resource.respond_to?(:attachment_definitions)
paperclip_attr = @params['data']['attributes']
.select do |attr|
!column?(attr) &&
paperclip_handler?(@params['data']['attributes'][attr])
end
if paperclip_attr
# NOTICE: Force to set the file_name attribute to support
# validates_attachment_file_name option
array_keys = paperclip_attr.keys
array_keys.each do |key|
extension = /\Adata:image\/([a-z]+);base64/.match(paperclip_attr[key])
.try(:[], 1)
paperclip_attr["#{key}_file_name"] = "image.#{extension}"
end
@attributes.merge!(paperclip_attr)
end
end
def extract_carrierwave
return if @params['data']['attributes'].blank?
return unless @resource.respond_to?(:uploaders)
@params['data']['attributes'].each do |key, value|
if value && carrierwave_attribute?(key)
if value.match(/\Adata:\w+\/.+;base64,.+/)
@attributes[key] = ForestLiana::Base64StringIO.new(value)
else
@attributes.delete(key)
end
end
end
end
def extract_acts_as_taggable
return if @params['data']['attributes'].blank?
return unless has_acts_as_taggable?
@params['data']['attributes'].each do |key, value|
if acts_as_taggable_attribute?(key)
@attributes["#{key.singularize}_list"] = value
@attributes.delete(key)
end
end
end
def extract_smart_fields_values
# NOTICE: Look for some Smart Field setters and apply them if any.
ForestLiana.schema_for_resource(@resource).fields.each do |field|
if field.try(:[], :set) &&
@params['data']['attributes'].has_key?(field[:field])
# WARNING: The Smart Fields setters may override other changes.
@attributes = field[:set].call(@attributes,
@params['data']['attributes'][field[:field]])
end
end
end
def extract_devise
return if @params['data']['attributes'].blank?
return unless has_devise?
if @params['data']['attributes']['password'] == '**********'
@params['data']['attributes'].delete('password')
elsif @params['data']['attributes']['password'].present?
@attributes['password'] = @params['data']['attributes']['password']
end
end
def paperclip_handler?(attr)
begin
Paperclip.io_adapters.handler_for(attr)
true
rescue Paperclip::AdapterRegistry::NoHandlerError
false
end
end
def column?(attribute)
@resource.columns.find { |column| column.name == attribute }.present?
end
def attributes_serialized
if Rails::VERSION::MAJOR >= 5
@attributes.select do |attribute|
@resource.type_for_attribute(attribute).class ==
::ActiveRecord::Type::Serialized
end
else
# NOTICE: Silent deprecation warnings for removed
# "serialized_attributes" in Rails 5
ActiveSupport::Deprecation.silence do
@resource.serialized_attributes
end
end
end
def carrierwave_attribute?(attr)
@resource.uploaders.include?(attr.try(:to_sym))
end
def acts_as_taggable_attribute?(attr)
@resource.acts_as_taggable.to_a.include?(attr)
end
def has_acts_as_taggable?
@resource.try(:taggable?)
end
def has_devise?
@resource.respond_to?(:devise_modules?)
end
end
end