Class: JsonapiMapper::DocumentMapper

Inherits:
Object
  • Object
show all
Defined in:
lib/jsonapi_mapper.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document, unscoped, rules, renames) ⇒ DocumentMapper

Returns a new instance of DocumentMapper.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 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
    else
      [ build_resource(data) ].compact
    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.try(:map, &:object) || []
  self.included = rest.try(:map, &:object) || []
end

Instance Attribute Details

#dataObject

Returns the value of attribute data.



20
21
22
# File 'lib/jsonapi_mapper.rb', line 20

def data
  @data
end

#documentObject

Returns the value of attribute document.



20
21
22
# File 'lib/jsonapi_mapper.rb', line 20

def document
  @document
end

#includedObject

Returns the value of attribute included.



20
21
22
# File 'lib/jsonapi_mapper.rb', line 20

def included
  @included
end

#renamesObject

Returns the value of attribute renames.



20
21
22
# File 'lib/jsonapi_mapper.rb', line 20

def renames
  @renames
end

#resourcesObject

Returns the value of attribute resources.



20
21
22
# File 'lib/jsonapi_mapper.rb', line 20

def resources
  @resources
end

#typesObject

Returns the value of attribute types.



20
21
22
# File 'lib/jsonapi_mapper.rb', line 20

def types
  @types
end

#unscopedObject

Returns the value of attribute unscoped.



20
21
22
# File 'lib/jsonapi_mapper.rb', line 20

def unscoped
  @unscoped
end

Instance Method Details

#allObject



157
158
159
# File 'lib/jsonapi_mapper.rb', line 157

def all
  (data + included)
end

#assign_relationships(resource) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/jsonapi_mapper.rb', line 130

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) ⇒ Object



126
127
128
# File 'lib/jsonapi_mapper.rb', line 126

def build_id(json)
  Id.new(json[:type].to_sym, json[:id])
end

#build_resource(json) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/jsonapi_mapper.rb', line 88

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)
    relationships[renamed(type.name, name)] = if value[:data].is_a?(Array)
      value[:data].map{|v| build_id(v) }
    else
      build_id(value[:data])
    end
  end

  if attributes_to_be_set = json[:attributes]
    type.rule.attributes.each do |name|
      if value = attributes_to_be_set[name]
        object.send("#{renamed(type.name, name)}=", value) 
      end
    end
  end

  resource = Resource.new(object, relationships, build_id(json))
  resources[resource.id] = resource
end

#collection?Boolean

Returns:

  • (Boolean)


167
168
169
# File 'lib/jsonapi_mapper.rb', line 167

def collection?
  data.size > 1
end

#find_resource_object(id) ⇒ Object



144
145
146
147
148
149
150
151
# File 'lib/jsonapi_mapper.rb', line 144

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) ⇒ Object



179
180
181
# File 'lib/jsonapi_mapper.rb', line 179

def map_all(cls, &blk)
  all.select{|o| o.is_a?(cls)}.map(&blk)
end

#map_data(cls, &blk) ⇒ Object



175
176
177
# File 'lib/jsonapi_mapper.rb', line 175

def map_data(cls, &blk)
  data.select{|o| o.is_a?(cls)}.map(&blk)
end

#renamed(type, attr) ⇒ Object



153
154
155
# File 'lib/jsonapi_mapper.rb', line 153

def renamed(type, attr)
  renames.fetch(:attributes, {}).fetch(type, {}).fetch(attr, attr)
end

#save_allObject



161
162
163
164
165
# File 'lib/jsonapi_mapper.rb', line 161

def save_all
  return false unless all.all?(&:valid?)
  all.each(&:save)
  true
end

#setup_types(rules) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/jsonapi_mapper.rb', line 49

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(type_name, a) }.to_set
    if danger.count > 0
      raise RulesError.new("Don't let user set the scope: #{danger.to_a}")
    end

    cls = renames.fetch(:types, {})[type_name] ||
      type_name.to_s.singularize.camelize.constantize

    attrs.map{|a| renamed(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?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/jsonapi_mapper.rb', line 171

def single?
  !collection?
end