Class: JSONAPI::Resource

Inherits:
Object
  • Object
show all
Includes:
Callbacks, ResourceFor
Defined in:
lib/jsonapi/resource.rb

Constant Summary collapse

@@resource_types =
{}

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Callbacks

included

Methods included from ResourceFor

included

Constructor Details

#initialize(model, context = nil) ⇒ Resource

Returns a new instance of Resource.



28
29
30
31
# File 'lib/jsonapi/resource.rb', line 28

def initialize(model, context = nil)
  @model = model
  @context = context
end

Class Attribute Details

._allowed_filtersObject

Returns the value of attribute _allowed_filters.



226
227
228
# File 'lib/jsonapi/resource.rb', line 226

def _allowed_filters
  @_allowed_filters
end

._associationsObject

Returns the value of attribute _associations.



226
227
228
# File 'lib/jsonapi/resource.rb', line 226

def _associations
  @_associations
end

._attributesObject

Returns the value of attribute _attributes.



226
227
228
# File 'lib/jsonapi/resource.rb', line 226

def _attributes
  @_attributes
end

._typeObject

Returns the value of attribute _type.



226
227
228
# File 'lib/jsonapi/resource.rb', line 226

def _type
  @_type
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



13
14
15
# File 'lib/jsonapi/resource.rb', line 13

def context
  @context
end

#modelObject (readonly)

Returns the value of attribute model.



14
15
16
# File 'lib/jsonapi/resource.rb', line 14

def model
  @model
end

Class Method Details

._allowed_filter?(filter) ⇒ Boolean

:nocov:

Returns:

  • (Boolean)


495
496
497
# File 'lib/jsonapi/resource.rb', line 495

def _allowed_filter?(filter)
  _allowed_filters.include?(filter)
end

._as_parent_keyObject



466
467
468
# File 'lib/jsonapi/resource.rb', line 466

def _as_parent_key
  @_as_parent_key ||= "#{_type.to_s.singularize}_#{_primary_key}"
end

._association(type) ⇒ Object



446
447
448
449
# File 'lib/jsonapi/resource.rb', line 446

def _association(type)
  type = type.to_sym
  @_associations[type]
end

._attribute_options(attr) ⇒ Object

quasi private class methods



426
427
428
# File 'lib/jsonapi/resource.rb', line 426

def _attribute_options(attr)
  default_attribute_options.merge(@_attributes[attr])
end

._has_association?(type) ⇒ Boolean

Returns:

  • (Boolean)


441
442
443
444
# File 'lib/jsonapi/resource.rb', line 441

def _has_association?(type)
  type = type.to_s
  @_associations.has_key?(type.singularize.to_sym) || @_associations.has_key?(type.pluralize.to_sym)
end

._keyObject



455
456
457
458
459
460
# File 'lib/jsonapi/resource.rb', line 455

def _key
  # :nocov:
  warn '[DEPRECATION] `_key` is deprecated.  Please use `_primary_key` instead.'
  _primary_key
  # :nocov:
end

._model_nameObject



451
452
453
# File 'lib/jsonapi/resource.rb', line 451

def _model_name
  @_model_name ||= self.name.demodulize.sub(/Resource$/, '')
end

._primary_keyObject



462
463
464
# File 'lib/jsonapi/resource.rb', line 462

def _primary_key
  @_primary_key ||= :id
end

._resource_name_from_type(type) ⇒ Object



474
475
476
477
478
479
480
481
# File 'lib/jsonapi/resource.rb', line 474

def _resource_name_from_type(type)
  class_name = @@resource_types[type]
  if class_name.nil?
    class_name = type.to_s.singularize.camelize + 'Resource'
    @@resource_types[type] = class_name
  end
  return class_name
end

._updateable_associationsObject



430
431
432
433
434
435
436
437
438
439
# File 'lib/jsonapi/resource.rb', line 430

def _updateable_associations
  associations = []

  @_associations.each do |key, association|
    if association.is_a?(JSONAPI::Association::HasOne) || association.acts_as_set
      associations.push(key)
    end
  end
  associations
end

.apply_filter(records, filter, value) ⇒ Object



318
319
320
# File 'lib/jsonapi/resource.rb', line 318

def apply_filter(records, filter, value)
  records.where(filter => value)
end

.attribute(attr, options = {}) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/jsonapi/resource.rb', line 251

def attribute(attr, options = {})
  check_reserved_attribute_name(attr)

  @_attributes[attr] = options
  define_method attr do
    @model.send(attr)
  end unless method_defined?(attr)

  define_method "#{attr}=" do |value|
    @model.send "#{attr}=", value
  end unless method_defined?("#{attr}=")
end

.attributes(*attrs) ⇒ Object

Methods used in defining a resource class



245
246
247
248
249
# File 'lib/jsonapi/resource.rb', line 245

def attributes(*attrs)
  attrs.each do |attr|
    attribute(attr)
  end
end

.create(context) ⇒ Object



228
229
230
# File 'lib/jsonapi/resource.rb', line 228

def create(context)
  self.new(self.create_model, context)
end

.create_modelObject



232
233
234
# File 'lib/jsonapi/resource.rb', line 232

def create_model
  _model_class.new
end

.createable_fields(context = nil) ⇒ Object

Override in your resource to filter the createable keys



305
306
307
# File 'lib/jsonapi/resource.rb', line 305

def createable_fields(context = nil)
  _updateable_associations | _attributes.keys
end

.default_attribute_optionsObject



264
265
266
# File 'lib/jsonapi/resource.rb', line 264

def default_attribute_options
  {format: :default}
end

.fieldsObject



314
315
316
# File 'lib/jsonapi/resource.rb', line 314

def fields
  _associations.keys | _attributes.keys
end

.filter(attr) ⇒ Object



284
285
286
# File 'lib/jsonapi/resource.rb', line 284

def filter(attr)
  @_allowed_filters.add(attr.to_sym)
end

.filters(*attrs) ⇒ Object



280
281
282
# File 'lib/jsonapi/resource.rb', line 280

def filters(*attrs)
  @_allowed_filters.merge(attrs)
end

.find(filters, options = {}) ⇒ Object

Override this method if you have more complex requirements than this basic find method provides



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/jsonapi/resource.rb', line 323

def find(filters, options = {})
  context = options[:context]
  sort_params = options.fetch(:sort_params) { [] }
  includes = []

  records = records(options)

  filters.each do |filter, value|
    if _associations.include?(filter)
      if _associations[filter].is_a?(JSONAPI::Association::HasMany)
        includes.push(filter)
        records = apply_filter(records, "#{filter}.#{_associations[filter].primary_key}", value)
      else
        records = apply_filter(records, "#{_associations[filter].foreign_key}", value)
      end
    else
      records = apply_filter(records, filter, value)
    end
  end

  resources = []
  order_options = construct_order_options(sort_params)
  records.order(order_options).includes(includes).each do |model|
    resources.push self.new(model, context)
  end

  return resources
end

.find_by_key(key, options = {}) ⇒ Object



352
353
354
355
356
357
358
359
# File 'lib/jsonapi/resource.rb', line 352

def find_by_key(key, options = {})
  context = options[:context]
  model = records(options).where({_primary_key => key}).first
  if model.nil?
    raise JSONAPI::Exceptions::RecordNotFound.new(key)
  end
  self.new(model, context)
end

.find_by_keys(keys, options = {}) ⇒ Object



361
362
363
364
365
366
367
368
369
370
371
# File 'lib/jsonapi/resource.rb', line 361

def find_by_keys(keys, options = {})
  context = options[:context]
  _models = records(options).where({_primary_key => keys})

  unless _models.length == keys.length
    key = (keys - _models.pluck(_primary_key).map(&:to_s)).first
    raise JSONAPI::Exceptions::RecordNotFound.new(key)
  end

  _models.map { |model| self.new(model, context) }
end

.has_many(*attrs) ⇒ Object



272
273
274
# File 'lib/jsonapi/resource.rb', line 272

def has_many(*attrs)
  _associate(Association::HasMany, *attrs)
end

.has_one(*attrs) ⇒ Object



268
269
270
# File 'lib/jsonapi/resource.rb', line 268

def has_one(*attrs)
  _associate(Association::HasOne, *attrs)
end

.inherited(base) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/jsonapi/resource.rb', line 210

def inherited(base)
  base._attributes = (_attributes || {}).dup
  base._associations = (_associations || {}).dup
  base._allowed_filters = (_allowed_filters || Set.new).dup

  type = base.name.demodulize.sub(/Resource$/, '').underscore
  base._type = type.pluralize.to_sym

  check_reserved_resource_name(base._type, base.name)

  # If eager loading is on this is how all the resource types are setup
  # If eager loading is off some resource types will be initialized in
  # _resource_name_from_type
  @@resource_types[base._type] ||= base.name.demodulize
end

.is_filter_association?(filter) ⇒ Boolean

Returns:

  • (Boolean)


388
389
390
# File 'lib/jsonapi/resource.rb', line 388

def is_filter_association?(filter)
  filter == _type || _associations.include?(filter)
end

.key(key) ⇒ Object



288
289
290
291
292
293
# File 'lib/jsonapi/resource.rb', line 288

def key(key)
  # :nocov:
  warn '[DEPRECATION] `key` is deprecated.  Please use `primary_key` instead.'
  @_primary_key = key.to_sym
  # :nocov:
end

.model_name(model) ⇒ Object



276
277
278
# File 'lib/jsonapi/resource.rb', line 276

def model_name(model)
  @_model_name = model.to_sym
end

.module_pathObject



499
500
501
# File 'lib/jsonapi/resource.rb', line 499

def module_path
  @module_path ||= self.name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').downcase : ''
end

.primary_key(key) ⇒ Object



295
296
297
# File 'lib/jsonapi/resource.rb', line 295

def primary_key(key)
  @_primary_key = key.to_sym
end

.records(options = {}) ⇒ Object

Override this method if you want to customize the relation for finder methods (find, find_by_key, find_by_keys)



375
376
377
# File 'lib/jsonapi/resource.rb', line 375

def records(options = {})
  _model_class
end

.routing_options(options) ⇒ Object



236
237
238
# File 'lib/jsonapi/resource.rb', line 236

def routing_options(options)
  @_routing_resource_options = options
end

.routing_resource_optionsObject



240
241
242
# File 'lib/jsonapi/resource.rb', line 240

def routing_resource_options
  @_routing_resource_options ||= {}
end

.sortable_fields(context = nil) ⇒ Object

Override in your resource to filter the sortable keys



310
311
312
# File 'lib/jsonapi/resource.rb', line 310

def sortable_fields(context = nil)
  _attributes.keys
end

.updateable_fields(context = nil) ⇒ Object

Override in your resource to filter the updateable keys



300
301
302
# File 'lib/jsonapi/resource.rb', line 300

def updateable_fields(context = nil)
  _updateable_associations | _attributes.keys
end

.verify_association_filter(filter, raw, context = nil) ⇒ Object

override to allow for custom association logic, such as uuids, multiple keys or permission checks on keys



421
422
423
# File 'lib/jsonapi/resource.rb', line 421

def verify_association_filter(filter, raw, context = nil)
  return filter, raw
end

.verify_custom_filter(filter, value, context = nil) ⇒ Object

override to allow for custom filters



416
417
418
# File 'lib/jsonapi/resource.rb', line 416

def verify_custom_filter(filter, value, context = nil)
  return filter, value
end

.verify_filter(filter, raw, context = nil) ⇒ Object



392
393
394
395
396
397
398
399
400
401
# File 'lib/jsonapi/resource.rb', line 392

def verify_filter(filter, raw, context = nil)
  filter_values = []
  filter_values += CSV.parse_line(raw) unless raw.nil? || raw.empty?

  if is_filter_association?(filter)
    verify_association_filter(filter, filter_values, context)
  else
    verify_custom_filter(filter, filter_values, context)
  end
end

.verify_filters(filters, context = nil) ⇒ Object



379
380
381
382
383
384
385
386
# File 'lib/jsonapi/resource.rb', line 379

def verify_filters(filters, context = nil)
  verified_filters = {}
  filters.each do |filter, raw_value|
    verified_filter = verify_filter(filter, raw_value, context)
    verified_filters[verified_filter[0]] = verified_filter[1]
  end
  verified_filters
end

.verify_key(key, context = nil) ⇒ Object

override to allow for key processing and checking



404
405
406
# File 'lib/jsonapi/resource.rb', line 404

def verify_key(key, context = nil)
  return key
end

.verify_keys(keys, context = nil) ⇒ Object

override to allow for key processing and checking



409
410
411
412
413
# File 'lib/jsonapi/resource.rb', line 409

def verify_keys(keys, context = nil)
  return keys.collect do |key|
    verify_key(key, context)
  end
end

Instance Method Details

#_model_classObject



485
486
487
# File 'lib/jsonapi/resource.rb', line 485

def _model_class
  @model ||= Object.const_get(_model_name.to_s)
end

#change(callback) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/jsonapi/resource.rb', line 41

def change(callback)
  if @changing
    run_callbacks callback do
      yield
    end
  else
    run_callbacks is_new? ? :create : :update do
      @changing = true
      run_callbacks callback do
        yield
        save if @save_needed
      end
    end
  end
end


63
64
65
66
67
# File 'lib/jsonapi/resource.rb', line 63

def create_has_many_links(association_type, association_key_values)
  change :create_has_many_link do
    _create_has_many_links(association_type, association_key_values)
  end
end


75
76
77
78
79
# File 'lib/jsonapi/resource.rb', line 75

def create_has_one_link(association_type, association_key_value)
  change :create_has_one_link do
    _create_has_one_link(association_type, association_key_value)
  end
end

#fetchable_fieldsObject

Override this on a resource instance to override the fetchable keys



106
107
108
# File 'lib/jsonapi/resource.rb', line 106

def fetchable_fields
  self.class.fields
end

#idObject



33
34
35
# File 'lib/jsonapi/resource.rb', line 33

def id
  model.send(self.class._primary_key)
end

#is_new?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/jsonapi/resource.rb', line 37

def is_new?
  id.nil?
end

#removeObject



57
58
59
60
61
# File 'lib/jsonapi/resource.rb', line 57

def remove
  run_callbacks :remove do
    _remove
  end
end


87
88
89
90
91
# File 'lib/jsonapi/resource.rb', line 87

def remove_has_many_link(association_type, key)
  change :remove_has_many_link do
    _remove_has_many_link(association_type, key)
  end
end


93
94
95
96
97
# File 'lib/jsonapi/resource.rb', line 93

def remove_has_one_link(association_type)
  change :remove_has_one_link do
    _remove_has_one_link(association_type)
  end
end

#replace_fields(field_data) ⇒ Object



99
100
101
102
103
# File 'lib/jsonapi/resource.rb', line 99

def replace_fields(field_data)
  change :replace_fields do
    _replace_fields(field_data)
  end
end


69
70
71
72
73
# File 'lib/jsonapi/resource.rb', line 69

def replace_has_many_links(association_type, association_key_values)
  change :replace_has_many_links do
    _replace_has_many_links(association_type, association_key_values)
  end
end


81
82
83
84
85
# File 'lib/jsonapi/resource.rb', line 81

def replace_has_one_link(association_type, association_key_value)
  change :replace_has_one_link do
    _replace_has_one_link(association_type, association_key_value)
  end
end