module Flapjack::Gateways::JSONAPI::Helpers::Serialiser
Public Instance Methods
as_jsonapi(klass, resources, resources_ids, options = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 11 def as_jsonapi(klass, resources, resources_ids, options = {}) included = options[:include] fields = options[:fields] query_type = options[:query_type] payload = {} unless included.nil? || included.empty? payload = collect_included_data(klass, resources, :include => included, :query_type => query_type) end ret = { :data => as_jsonapi_data(klass, resources, resources_ids, :primary => true, :payload => payload, :fields => fields, :unwrap => options[:unwrap]) } unless payload.nil? || payload.empty? ret[:included] = included_data_from_payload(payload, :fields => fields) end ret end
as_jsonapi_data(klass, resources, resources_ids, options = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 44 def as_jsonapi_data(klass, resources, resources_ids, options = {}) unwrap = options[:unwrap] payload = options[:payload] relationship_scopes = options[:relationship_scopes] fields = extract_fields(options[:fields], klass) whitelist = if klass.respond_to?(:jsonapi_methods) method_def = klass.jsonapi_methods[:get] || klass.jsonapi_methods[:post] method_def.nil? ? [] : method_def.attributes else [] end jsonapi_fields = if fields.nil? whitelist else Set.new(fields).keep_if {|f| whitelist.include?(f) }.to_a end links = jsonapi_linkages(klass, resources_ids, :primary => options[:primary], :payload => payload, :relationship_scopes => relationship_scopes) resources_as_json = resources.collect do |r| r_id = r.id l = links[r_id] data = { :type => klass.short_model_name.singular, :id => r_id } attrs = r.as_json(:only => jsonapi_fields) data[:attributes] = attrs unless attrs.empty? data[:relationships] = l unless l.nil? || l.empty? data end unless (resources_as_json.size == 1) && unwrap.is_a?(TrueClass) return resources_as_json end resources_as_json.first end
as_jsonapi_included(klass, resources, options = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 34 def as_jsonapi_included(klass, resources, options = {}) included = options[:include] fields = options[:fields] payload = collect_included_data(klass, resources, :include => included, :query_type => :association) included_data_from_payload(payload, :fields => fields) end
collect_included_data(klass, resources, options = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 130 def collect_included_data(klass, resources, options = {}) included = options[:include] query_type = options[:query_type] payload = {} init_node = [klass, nil => resources.ids] included_parts = included.map {|i| i.split('.')}.sort_by(&:length) included_parts.each do |incl_fragments| incl_todo = incl_fragments incl_done = [] last_node = init_node until incl_todo.empty? fragment = incl_todo.shift last_node = resolve_include_fragment(last_node, fragment, incl_todo, incl_done, :query_type => query_type) incl_done.push(fragment) # store new klass, resources as appropriate by joined incl_done k = incl_done.join(".") payload[k] ||= [last_node.first] payload[k] << last_node.last end end payload end
extract_fields(fields, klass)
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 252 def extract_fields(fields, klass) return if fields.nil? raise "Field limits must be passed as a Hash" unless fields.is_a?(Hash) resource_name = klass.short_model_name.singular return unless fields.has_key?(resource_name) fields[resource_name].split(',').map(&:to_sym) end
included_data_from_payload(payload, options = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 216 def included_data_from_payload(payload, options = {}) fields = options[:fields] scopes_by_klass = {} names_by_klass = {} payload.each do |name, pl| local_klass = pl.first scopes_by_klass[local_klass] ||= Set.new names_by_klass[local_klass] ||= [] names_by_klass[local_klass] << name data = pl[1..-1].map {|d| d.values }.flatten(1) if data.all? {|d| d.is_a?(Set) || d.is_a?(Array)} data.each do |sd| scopes_by_klass[local_klass] |= sd end else scopes_by_klass[local_klass] |= data end end result = scopes_by_klass.inject([]) do |memo, (local_klass, scopes)| memo += as_jsonapi_data(local_klass, local_klass.intersect(:id => scopes), scopes, :relationship_scopes => names_by_klass[local_klass], :payload => payload, :fields => fields) memo end result end
jsonapi_linkages(klass, resource_ids, opts = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 260 def jsonapi_linkages(klass, resource_ids, opts = {}) payload = opts[:payload] relationship_scopes = opts[:relationship_scopes] jsonapi_assocs = klass.jsonapi_associations.reject do |jl_name, jl_data| jl_data.link.is_a?(FalseClass) && jl_data.includable.is_a?(FalseClass) end resources_name = klass.short_model_name.plural resource_ids.each_with_object({}) do |r_id, memo| memo[r_id] = jsonapi_assocs.keys.each_with_object({}) do |jl_name, a_memo| linked = {} if opts[:primary] linked[:links] = { :self => "#{request.base_url}/#{resources_name}/#{r_id}/relationships/#{jl_name}", :related => "#{request.base_url}/#{resources_name}/#{r_id}/#{jl_name}" } end payload_scopes = if relationship_scopes.nil? || relationship_scopes.empty? opts[:primary] ? [jl_name.to_s] : [] else relationship_scopes.map {|rls| "#{rls}.#{jl_name}" } end payload_scopes.each do |pls| if payload.has_key?(pls) && payload[pls].last.has_key?(r_id) klass = payload[pls].first d = payload[pls].last[r_id] linked[:data] = case d when Set, Array d.map {|da| {:type => klass.short_model_name.singular, :id => da} } when String {:type => klass.short_model_name.singular, :id => d} end end end next if linked.empty? a_memo[jl_name] = linked end end end
lock_for_include_clause(klass, options = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 90 def lock_for_include_clause(klass, options = {}) included = options[:include] locks = Set.new included_parts = included.map {|i| i.split('.')}.sort_by(&:length) included_parts.each do |incl_fragments| incl_todo = incl_fragments incl_done = [] last_klass = klass until incl_todo.empty? fragment = incl_todo.shift last_klass, lock_k = resolve_include_locks(last_klass, fragment) incl_done.push(fragment) locks |= lock_k end end locks.to_a end
resolve_include_fragment(node, name, incl_todo, incl_done, options = {})
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 164 def resolve_include_fragment(node, name, incl_todo, incl_done, options = {}) query_type = options[:query_type] parent_klass = node.first nlv = node.last.values parent_resources = if nlv.all? {|v| v.is_a?(Set) || v.is_a?(Array)} nlv.reduce(Set.new, :|) else nlv end parent_links = if parent_klass.respond_to?(:jsonapi_associations) parent_klass.jsonapi_associations || {} else {} end assoc_data = parent_links[name.to_sym] return [] if assoc_data.nil? || (:resource.eql?(query_type) && !assoc_data.includable) || (:association.eql?(query_type) && !assoc_data.link) fragment_klass = assoc_data.data_klass # NB: if anything's limiting the returned data, it would need # to be included here scope = parent_klass.intersect(:id => parent_resources) data = if assoc_data.association_data.nil? scope.all.each_with_object({}) do |r, memo| d = r.send(name.to_sym) memo[r.id] = case d when nil nil when Zermelo::Filter, Zermelo::Associations::Multiple d.ids when Array, Set d.map(&:id) else d.id end end else scope.associated_ids_for(name.to_sym) end [fragment_klass, data] end
resolve_include_locks(parent_klass, name)
click to toggle source
# File lib/flapjack/gateways/jsonapi/helpers/serialiser.rb, line 114 def resolve_include_locks(parent_klass, name) links = if parent_klass.respond_to?(:jsonapi_associations) parent_klass.jsonapi_associations || {} else {} end assoc_data = links[name.to_sym] return if assoc_data.nil? || !assoc_data.link klass = assoc_data.data_klass [klass, assoc_data.lock_klasses] end