class JsonapiMapper::DocumentMapper
Attributes
data[RW]
document[RW]
included[RW]
renames[RW]
resources[RW]
types[RW]
unscoped[RW]
Public Class Methods
new(document, unscoped, rules, renames)
click to toggle source
# File lib/jsonapi_mapper.rb, line 23 def initialize(document, unscoped, rules, renames) self.document = document.deep_symbolize_keys self.renames = renames.deep_symbolize_keys self.unscoped = unscoped.map(&:to_sym) self.resources = {} setup_types(rules) main = if data = self.document[:data] if data.is_a?(Array) data.map{|r| build_resource(r) }.compact.collect(&:object) else build_resource(data).try(:object) end end rest = if included = self.document[:included] included.map{|r| build_resource(r) }.compact end resources.each{|_,r| assign_relationships(r) } self.data = main self.included = rest.try(:map, &:object) || [] end
Public Instance Methods
all()
click to toggle source
# File lib/jsonapi_mapper.rb, line 170 def all (data_mappable + included) end
all_errors()
click to toggle source
# File lib/jsonapi_mapper.rb, line 203 def all_errors errors = [] if collection? data.each_with_index do |resource, i| errors << serialize_errors_for("/data/#{i}", resource) end elsif data errors << serialize_errors_for("/data", data) end included.each_with_index do |resource, i| errors << serialize_errors_for("/included/#{i}", resource) end { errors: errors.flatten.compact } end
all_valid?()
click to toggle source
# File lib/jsonapi_mapper.rb, line 179 def all_valid? all.map(&:valid?).all? # This does not short-circuit, to get all errors. end
assign_relationships(resource)
click to toggle source
# File lib/jsonapi_mapper.rb, line 128 def assign_relationships(resource) resource.relationships.each do |name, ids| if ids.is_a?(Array) ids.each do |id| next unless other = find_resource_object(id) resource.object.send(name).push(other) end else next unless other = find_resource_object(ids) resource.object.send("#{name}=", other) end end end
build_id(json)
click to toggle source
# File lib/jsonapi_mapper.rb, line 124 def build_id(json) Id.new(json[:type].to_sym, json[:id]) end
build_resource(json)
click to toggle source
# File lib/jsonapi_mapper.rb, line 86 def build_resource(json) return unless json.is_a? Hash return unless json.fetch(:relationships, {}).is_a?(Hash) return unless json.fetch(:attributes, {}).is_a?(Hash) return unless type = types[json[:type].try(:to_sym)] object = if json[:id].nil? || json[:id].to_s.starts_with?("@") type.class.new.tap do |o| type.rule.scope.each do |k,v| o.send("#{k}=", v) end end else type.class.where(type.rule.scope).find(json[:id]) end relationships = {} json.fetch(:relationships, {}).each do |name, value| next unless type.rule.attributes.include?(name) next if value[:data].blank? relationships[renamed_attr(type.name, name)] = if value[:data].is_a?(Array) value[:data].map{|v| build_id(v) } else build_id(value[:data]) end end if new_values = json[:attributes] type.rule.attributes.each do |name| next unless new_values.has_key?(name) object.send("#{renamed_attr(type.name, name)}=", new_values[name]) end end resource = Resource.new(object, relationships, build_id(json)) resources[resource.id] = resource end
collection?()
click to toggle source
# File lib/jsonapi_mapper.rb, line 183 def collection? data.is_a?(Array) end
data_mappable()
click to toggle source
# File lib/jsonapi_mapper.rb, line 199 def data_mappable collection? ? data : [data].compact end
find_resource_object(id)
click to toggle source
# File lib/jsonapi_mapper.rb, line 142 def find_resource_object(id) return unless type = types[id.type] resources[id].try(:object) || type.class.where(type.rule.scope).find(id.raw) or raise ActiveRecord::RecordNotFound .new("Couldn't find #{id.type} with id=#{id.raw}") end
map_all(cls, &blk)
click to toggle source
# File lib/jsonapi_mapper.rb, line 195 def map_all(cls, &blk) all.select{|o| o.is_a?(cls)}.map(&blk) end
map_data(cls, &blk)
click to toggle source
# File lib/jsonapi_mapper.rb, line 191 def map_data(cls, &blk) data_mappable.select{|o| o.is_a?(cls)}.map(&blk) end
renamed_attr(type, attr)
click to toggle source
# File lib/jsonapi_mapper.rb, line 161 def renamed_attr(type, attr) renames.fetch(:attributes, {}).fetch(type, {}).fetch(attr, attr) end
renamed_type(type_name)
click to toggle source
# File lib/jsonapi_mapper.rb, line 151 def renamed_type(type_name) renames.fetch(:types, {})[type_name] || type_name.to_s.singularize.camelize.constantize end
save_all()
click to toggle source
# File lib/jsonapi_mapper.rb, line 174 def save_all return false unless all.all?(&:valid?) all.collect(&:save).all? end
setup_types(rules)
click to toggle source
# File lib/jsonapi_mapper.rb, line 48 def setup_types(rules) self.types = {} rules.each do |type_name, ruleset| type_name = type_name.to_sym attrs, scope = if ruleset.last.is_a?(Hash) [ruleset[0..-2], ruleset.last] else unless unscoped.map(&:to_sym).include?(type_name) raise RulesError.new("Missing Scope for #{type_name}") end [ruleset, {}] end unless attrs.all?{|v| v.is_a?(Symbol) || v.is_a?(String) } raise RulesError.new('Attributes must be Strings or Symbols') end attrs = attrs.map(&:to_sym) scope.symbolize_keys! danger = scope.keys.to_set & attrs.map{|a| renamed_attr(type_name, a) }.to_set if danger.count > 0 raise RulesError.new("Don't let user set the scope: #{danger.to_a}") end cls = renamed_type(type_name) attrs.map{|a| renamed_attr(type_name, a) }.each do |attr| unless cls.new.respond_to?(attr) raise NoMethodError.new("undefined method #{attr} for #{cls}") end end types[type_name] = Type.new(type_name, cls, Rule.new(attrs, scope)) end end
single?()
click to toggle source
# File lib/jsonapi_mapper.rb, line 187 def single? !collection? end
unrenamed_attr(type_name, attr)
click to toggle source
# File lib/jsonapi_mapper.rb, line 165 def unrenamed_attr(type_name, attr) renames.fetch(:attributes, {}).fetch(type_name, {}) .find{|k,v| v == attr }.try(:first) || attr end
unrenamed_type(type)
click to toggle source
# File lib/jsonapi_mapper.rb, line 156 def unrenamed_type(type) type_name = type.to_s.underscore.pluralize renames.fetch(:types, {}).find{|k,v| v == type }.try(:first) || type_name end
Private Instance Methods
serialize_errors_for(prefix, model)
click to toggle source
# File lib/jsonapi_mapper.rb, line 223 def serialize_errors_for(prefix, model) return if model.errors.empty? model.errors.collect do |attr, value| type_name = unrenamed_type(model.class) meta = { type: type_name.to_s } meta[:id] = model.id if model.id { status: 422, title: value, detail: value, code: value.parameterize.underscore, meta: meta, source: { pointer: "#{prefix}/attributes/#{unrenamed_attr(type_name, attr)}" } } end end