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
# 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

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



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

def all
  (data_mappable + included)
end

#assign_relationships(resource) ⇒ Object



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

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



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

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

#build_resource(json) ⇒ Object



87
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
# File 'lib/jsonapi_mapper.rb', line 87

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)


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

def collection?
  data.is_a?(Array)
end

#data_mappableObject



182
183
184
# File 'lib/jsonapi_mapper.rb', line 182

def data_mappable
  collection? ? data : [data].compact
end

#find_resource_object(id) ⇒ Object



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

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



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

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

#map_data(cls, &blk) ⇒ Object



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

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

#renamed(type, attr) ⇒ Object



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

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

#save_allObject



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

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

#setup_types(rules) ⇒ Object



48
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
# 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(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)


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

def single?
  !collection?
end