lib/her/model/parse.rb
module Her
module Model
# This module handles resource data parsing at the model level (after the parsing middleware)
module Parse
extend ActiveSupport::Concern
# Convert into a hash of request parameters, based on `include_root_in_json`.
#
# @example
# @user.to_params
# # => { :id => 1, :name => 'John Smith' }
def to_params
self.class.to_params(attributes, changes)
end
module ClassMethods
# Parse data before assigning it to a resource, based on `parse_root_in_json`.
#
# @param [Hash] data
# @private
def parse(data)
if parse_root_in_json? && root_element_included?(data)
if json_api_format?
data.fetch(parsed_root_element).first
else
data.fetch(parsed_root_element) { data }
end
else
data
end
end
# @private
def to_params(attributes, changes = {})
filtered_attributes = attributes.each_with_object({}) do |(key, value), memo|
case value
when Her::Model
when ActiveModel::Serialization
value = value.serializable_hash.symbolize_keys
end
memo[key.to_sym] = value
end
filtered_attributes.merge!(embeded_params(attributes))
if her_api.options[:send_only_modified_attributes]
filtered_attributes.slice! *changes.keys.map(&:to_sym)
end
if include_root_in_json?
if json_api_format?
{ included_root_element => [filtered_attributes] }
else
{ included_root_element => filtered_attributes }
end
else
filtered_attributes
end
end
# @private
def embeded_params(attributes)
associations.values.flatten.each_with_object({}) do |definition, hash|
value = case association = attributes[definition[:name]]
when Her::Collection, Array
association.map { |a| a.to_params }.reject(&:empty?)
when Her::Model
association.to_params
end
hash[definition[:data_key]] = value if value.present?
end
end
# Return or change the value of `include_root_in_json`
#
# @example
# class User
# include Her::Model
# include_root_in_json true
# end
def include_root_in_json(value, options = {})
@_her_include_root_in_json = value
@_her_include_root_in_json_format = options[:format]
end
# Return or change the value of `parse_root_in_json`
#
# @example
# class User
# include Her::Model
# parse_root_in_json true
# end
#
# class User
# include Her::Model
# parse_root_in_json true, format: :active_model_serializers
# end
#
# class User
# include Her::Model
# parse_root_in_json true, format: :json_api
# end
def parse_root_in_json(value, options = {})
@_her_parse_root_in_json = value
@_her_parse_root_in_json_format = options[:format]
end
# Return or change the value of `request_new_object_on_build`
#
# @example
# class User
# include Her::Model
# request_new_object_on_build true
# end
def request_new_object_on_build(value = nil)
@_her_request_new_object_on_build = value
end
# Return or change the value of `root_element`. Always defaults to the base name of the class.
#
# @example
# class User
# include Her::Model
# parse_root_in_json true
# root_element :huh
# end
#
# user = User.find(1) # { :huh => { :id => 1, :name => "Tobias" } }
# user.name # => "Tobias"
def root_element(value = nil)
if value.nil?
@_her_root_element ||= if json_api_format?
name.split("::").last.pluralize.underscore.to_sym
else
name.split("::").last.underscore.to_sym
end
else
@_her_root_element = value.to_sym
end
end
# @private
def root_element_included?(data)
element = data[parsed_root_element]
element.is_a?(Hash) || element.is_a?(Array)
end
# @private
def included_root_element
include_root_in_json? == true ? root_element : include_root_in_json?
end
# Extract an array from the request data
#
# @example
# # with parse_root_in_json true, :format => :active_model_serializers
# class User
# include Her::Model
# parse_root_in_json true, :format => :active_model_serializers
# end
#
# users = User.all # { :users => [ { :id => 1, :name => "Tobias" } ] }
# users.first.name # => "Tobias"
#
# # without parse_root_in_json
# class User
# include Her::Model
# end
#
# users = User.all # [ { :id => 1, :name => "Tobias" } ]
# users.first.name # => "Tobias"
#
# @private
def extract_array(request_data)
if request_data[:data].is_a?(Hash) && (active_model_serializers_format? || json_api_format?)
request_data[:data][pluralized_parsed_root_element]
else
request_data[:data]
end
end
# @private
def pluralized_parsed_root_element
parsed_root_element.to_s.pluralize.to_sym
end
# @private
def parsed_root_element
parse_root_in_json? == true ? root_element : parse_root_in_json?
end
# @private
def active_model_serializers_format?
@_her_parse_root_in_json_format == :active_model_serializers || (superclass.respond_to?(:active_model_serializers_format?) && superclass.active_model_serializers_format?)
end
# @private
def json_api_format?
@_her_parse_root_in_json_format == :json_api || (superclass.respond_to?(:json_api_format?) && superclass.json_api_format?)
end
# @private
def request_new_object_on_build?
return @_her_request_new_object_on_build unless @_her_request_new_object_on_build.nil?
superclass.respond_to?(:request_new_object_on_build?) && superclass.request_new_object_on_build?
end
# @private
def include_root_in_json?
return @_her_include_root_in_json unless @_her_include_root_in_json.nil?
superclass.respond_to?(:include_root_in_json?) && superclass.include_root_in_json?
end
# @private
def parse_root_in_json?
return @_her_parse_root_in_json unless @_her_parse_root_in_json.nil?
superclass.respond_to?(:parse_root_in_json?) && superclass.parse_root_in_json?
end
end
end
end
end