Class: JSONAPI::BasicResource

Inherits:
Object
  • Object
show all
Includes:
Callbacks
Defined in:
lib/jsonapi/basic_resource.rb

Direct Known Subclasses

ActiveRelationResource

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Callbacks

included

Constructor Details

#initialize(model, context) ⇒ BasicResource

Returns a new instance of BasicResource.



29
30
31
32
33
34
35
# File 'lib/jsonapi/basic_resource.rb', line 29

def initialize(model, context)
  @model = model
  @context = context
  @reload_needed = false
  @changing = false
  @save_needed = false
end

Class Attribute Details

._allowed_filtersObject



902
903
904
# File 'lib/jsonapi/basic_resource.rb', line 902

def _allowed_filters
  defined?(@_allowed_filters) ? @_allowed_filters : { id: {} }
end

._allowed_sortObject



906
907
908
# File 'lib/jsonapi/basic_resource.rb', line 906

def _allowed_sort
  @_allowed_sort ||= {}
end

._attributesObject

Returns the value of attribute _attributes.



514
515
516
# File 'lib/jsonapi/basic_resource.rb', line 514

def _attributes
  @_attributes
end

._model_hintsObject

Returns the value of attribute _model_hints.



514
515
516
# File 'lib/jsonapi/basic_resource.rb', line 514

def _model_hints
  @_model_hints
end

._paginatorObject



910
911
912
# File 'lib/jsonapi/basic_resource.rb', line 910

def _paginator
  @_paginator || JSONAPI.configuration.default_paginator
end

._relationshipsObject

Returns the value of attribute _relationships.



514
515
516
# File 'lib/jsonapi/basic_resource.rb', line 514

def _relationships
  @_relationships
end

._routedObject

Returns the value of attribute _routed.



514
515
516
# File 'lib/jsonapi/basic_resource.rb', line 514

def _routed
  @_routed
end

._typeObject

Returns the value of attribute _type.



514
515
516
# File 'lib/jsonapi/basic_resource.rb', line 514

def _type
  @_type
end

._warned_missing_routeObject

Returns the value of attribute _warned_missing_route.



514
515
516
# File 'lib/jsonapi/basic_resource.rb', line 514

def _warned_missing_route
  @_warned_missing_route
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



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

def context
  @context
end

Class Method Details

._abstractObject



960
961
962
# File 'lib/jsonapi/basic_resource.rb', line 960

def _abstract
  @abstract
end

._add_relationship(klass, *attrs) ⇒ Object



1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
# File 'lib/jsonapi/basic_resource.rb', line 1073

def _add_relationship(klass, *attrs)
  _clear_fields_cache

  options = attrs.extract_options!
  options[:parent_resource] = self

  attrs.each do |name|
    relationship_name = name.to_sym
    check_reserved_relationship_name(relationship_name)
    check_duplicate_relationship_name(relationship_name)

    define_relationship_methods(relationship_name.to_sym, klass, options)
  end
end

._allowed_filter?(filter) ⇒ Boolean

Returns:

  • (Boolean)


1042
1043
1044
# File 'lib/jsonapi/basic_resource.rb', line 1042

def _allowed_filter?(filter)
  !_allowed_filters[filter].nil?
end

._as_parent_keyObject



898
899
900
# File 'lib/jsonapi/basic_resource.rb', line 898

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

._attribute_delegated_name(attr) ⇒ Object



840
841
842
# File 'lib/jsonapi/basic_resource.rb', line 840

def _attribute_delegated_name(attr)
  @_attributes.fetch(attr.to_sym, {}).fetch(:delegate, attr)
end

._attribute_options(attr) ⇒ Object

quasi private class methods



836
837
838
# File 'lib/jsonapi/basic_resource.rb', line 836

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

._cache_fieldObject



890
891
892
# File 'lib/jsonapi/basic_resource.rb', line 890

def _cache_field
  @_cache_field || JSONAPI.configuration.default_resource_cache_field
end

._cachingObject



1005
1006
1007
# File 'lib/jsonapi/basic_resource.rb', line 1005

def _caching
  @caching
end

._clear_cached_attribute_optionsObject



1120
1121
1122
# File 'lib/jsonapi/basic_resource.rb', line 1120

def _clear_cached_attribute_options
  @_cached_attribute_options = {}
end

._clear_fields_cacheObject



1124
1125
1126
# File 'lib/jsonapi/basic_resource.rb', line 1124

def _clear_fields_cache
  @_fields_cache = nil
end

._default_primary_keyObject



886
887
888
# File 'lib/jsonapi/basic_resource.rb', line 886

def _default_primary_key
  @_default_primary_key ||=_model_class.respond_to?(:primary_key) ? _model_class.primary_key : :id
end


993
994
995
# File 'lib/jsonapi/basic_resource.rb', line 993

def _exclude_links
  @_exclude_links ||= parse_exclude_links(JSONAPI.configuration.default_exclude_links)
end

._has_attribute?(attr) ⇒ Boolean

Returns:

  • (Boolean)


844
845
846
# File 'lib/jsonapi/basic_resource.rb', line 844

def _has_attribute?(attr)
  @_attributes.keys.include?(attr.to_sym)
end

._has_sort?(sorting) ⇒ Boolean

Returns:

  • (Boolean)


1046
1047
1048
# File 'lib/jsonapi/basic_resource.rb', line 1046

def _has_sort?(sorting)
  !_allowed_sort[sorting.to_sym].nil?
end

._immutableObject



968
969
970
# File 'lib/jsonapi/basic_resource.rb', line 968

def _immutable
  @immutable
end

._model_classObject



1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
# File 'lib/jsonapi/basic_resource.rb', line 1026

def _model_class
  return nil if _abstract

  return @model_class if @model_class

  model_name = _model_name
  return nil if model_name.to_s.blank?

  @model_class = model_name.to_s.safe_constantize
  if @model_class.nil?
    warn "[MODEL NOT FOUND] Model could not be found for #{self.name}. If this is a base Resource declare it as abstract."
  end

  @model_class
end

._model_nameObject



862
863
864
865
866
867
868
869
870
871
872
# File 'lib/jsonapi/basic_resource.rb', line 862

def _model_name
  if _abstract
     ''
  else
    return @_model_name.to_s if defined?(@_model_name)
    class_name = self.name
    return '' if class_name.nil?
    @_model_name = class_name.demodulize.sub(/Resource$/, '')
    @_model_name.to_s
  end
end

._polymorphicObject



918
919
920
# File 'lib/jsonapi/basic_resource.rb', line 918

def _polymorphic
  @_polymorphic
end

._polymorphic_nameObject



874
875
876
877
878
879
880
# File 'lib/jsonapi/basic_resource.rb', line 874

def _polymorphic_name
  if !_polymorphic
    ''
  else
    @_polymorphic_name ||= _model_name.to_s.downcase
  end
end

._polymorphic_resource_klassesObject



940
941
942
943
944
# File 'lib/jsonapi/basic_resource.rb', line 940

def _polymorphic_resource_klasses
  @_polymorphic_resource_klasses ||= _polymorphic_types.collect do |type|
    resource_klass_for(type)
  end
end

._polymorphic_typesObject



926
927
928
929
930
931
932
933
934
935
936
937
938
# File 'lib/jsonapi/basic_resource.rb', line 926

def _polymorphic_types
  @poly_hash ||= {}.tap do |hash|
    ObjectSpace.each_object do |klass|
      next unless Module === klass
      if klass < ActiveRecord::Base
        klass.reflect_on_all_associations(:has_many).select{|r| r.options[:as] }.each do |reflection|
          (hash[reflection.options[:as]] ||= []) << klass.name.downcase
        end
      end
    end
  end
  @poly_hash[_polymorphic_name.to_sym]
end

._primary_keyObject



882
883
884
# File 'lib/jsonapi/basic_resource.rb', line 882

def _primary_key
  @_primary_key ||= _default_primary_key
end

._relationship(type) ⇒ Object



856
857
858
859
860
# File 'lib/jsonapi/basic_resource.rb', line 856

def _relationship(type)
  return nil unless type
  type = type.to_sym
  @_relationships[type]
end

._resource_name_from_type(type) ⇒ Object



501
502
503
# File 'lib/jsonapi/basic_resource.rb', line 501

def _resource_name_from_type(type)
  "#{type.to_s.underscore.singularize}_resource".camelize
end

._singleton_optionsObject



651
652
653
# File 'lib/jsonapi/basic_resource.rb', line 651

def _singleton_options
  @_singleton_options ||= {}
end

._table_nameObject



894
895
896
# File 'lib/jsonapi/basic_resource.rb', line 894

def _table_name
  @_table_name ||= _model_class.respond_to?(:table_name) ? _model_class.table_name : _model_name.tableize
end

._updatable_attributesObject



848
849
850
# File 'lib/jsonapi/basic_resource.rb', line 848

def _updatable_attributes
  _attributes.map { |key, options| key unless options[:readonly] }.compact
end

._updatable_relationshipsObject



852
853
854
# File 'lib/jsonapi/basic_resource.rb', line 852

def _updatable_relationships
  @_relationships.map { |key, relationship| key unless relationship.readonly? }.compact
end

.abstract(val = true) ⇒ Object



956
957
958
# File 'lib/jsonapi/basic_resource.rb', line 956

def abstract(val = true)
  @abstract = val
end

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



541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
# File 'lib/jsonapi/basic_resource.rb', line 541

def attribute(attribute_name, options = {})
  _clear_cached_attribute_options
  _clear_fields_cache

  attr = attribute_name.to_sym

  check_reserved_attribute_name(attr)

  if (attr == :id) && (options[:format].nil?)
    JSONAPI::CompatibilityHelper.deprecation_warn('Id without format is deprecated. Please specify a format for the id attribute.')
  end

  check_duplicate_attribute_name(attr) if options[:format].nil?

  @_attributes ||= {}
  @_attributes[attr] = options
  define_method attr do
    @model.public_send(options[:delegate] ? options[:delegate].to_sym : attr)
  end unless method_defined?(attr)

  define_method "#{attr}=" do |value|
    @model.public_send("#{options[:delegate] ? options[:delegate].to_sym : attr}=", value)
  end unless method_defined?("#{attr}=")

  if options.fetch(:sortable, true) && !_has_sort?(attr)
    sort attr
  end
end

.attribute_caching_context(_context) ⇒ Object



1017
1018
1019
# File 'lib/jsonapi/basic_resource.rb', line 1017

def attribute_caching_context(_context)
  nil
end

.attribute_to_model_field(attribute) ⇒ Object



570
571
572
573
574
575
576
577
578
579
580
581
582
# File 'lib/jsonapi/basic_resource.rb', line 570

def attribute_to_model_field(attribute)
  field_name = if attribute == :_cache_field
                 _cache_field
               else
                 # Note: this will allow the returning of model attributes without a corresponding
                 # resource attribute, for example a belongs_to id such as `author_id` or bypassing
                 # the delegate.
                 attr = @_attributes[attribute]
                 attr && attr[:delegate] ? attr[:delegate].to_sym : attribute
               end

  { name: field_name, type: _model_class.attribute_types[field_name.to_s]}
end

.attributes(*attrs) ⇒ Object

Methods used in defining a resource class



534
535
536
537
538
539
# File 'lib/jsonapi/basic_resource.rb', line 534

def attributes(*attrs)
  options = attrs.extract_options!.dup
  attrs.each do |attr|
    attribute(attr, options)
  end
end

.belongs_to(*attrs) ⇒ Object



611
612
613
614
615
616
617
618
619
# File 'lib/jsonapi/basic_resource.rb', line 611

def belongs_to(*attrs)

  JSONAPI::CompatibilityHelper.deprecation_warn( "In #{name} you exposed a `has_one` relationship "\
                                  " using the `belongs_to` class method. We think `has_one`" \
                                  " is more appropriate. If you know what you're doing," \
                                  " and don't want to see this warning again, override the" \
                                  " `belongs_to` class method on your resource.")
  _add_relationship(Relationship::ToOne, *attrs)
end

.cache_field(field) ⇒ Object



680
681
682
# File 'lib/jsonapi/basic_resource.rb', line 680

def cache_field(field)
  @_cache_field = field.to_sym
end

.caching(val = true) ⇒ Object



1001
1002
1003
# File 'lib/jsonapi/basic_resource.rb', line 1001

def caching(val = true)
  @caching = val
end

.caching?Boolean

Returns:

  • (Boolean)


1009
1010
1011
1012
1013
1014
1015
# File 'lib/jsonapi/basic_resource.rb', line 1009

def caching?
  if @caching.nil?
    !JSONAPI.configuration.resource_cache.nil? && JSONAPI.configuration.default_caching
  else
    @caching && !JSONAPI.configuration.resource_cache.nil?
  end
end

.call_method_or_proc(strategy, *args) ⇒ Object



755
756
757
758
759
760
761
# File 'lib/jsonapi/basic_resource.rb', line 755

def call_method_or_proc(strategy, *args)
  if strategy.is_a?(Symbol) || strategy.is_a?(String)
    send(strategy, *args)
  else
    strategy.call(*args)
  end
end

.cast_to_attribute_type(value, type) ⇒ Object



584
585
586
# File 'lib/jsonapi/basic_resource.rb', line 584

def cast_to_attribute_type(value, type)
  type.cast(value)
end

.construct_order_options(sort_params) ⇒ Object



1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
# File 'lib/jsonapi/basic_resource.rb', line 1062

def construct_order_options(sort_params)
  sort_params = default_sort if sort_params.blank?

  return {} unless sort_params

  sort_params.each_with_object({}) do |sort, order_hash|
    field = sort[:field].to_s == 'id' ? _primary_key : sort[:field].to_s
    order_hash[field] = sort[:direction]
  end
end

.creatable_fields(_context = nil) ⇒ Object

Override in your resource to filter the creatable keys



690
691
692
# File 'lib/jsonapi/basic_resource.rb', line 690

def creatable_fields(_context = nil)
  _updatable_relationships | _updatable_attributes
end

.create(context) ⇒ Object



517
518
519
# File 'lib/jsonapi/basic_resource.rb', line 517

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

.create_modelObject



521
522
523
# File 'lib/jsonapi/basic_resource.rb', line 521

def create_model
  _model_class.new
end

.default_attribute_optionsObject



588
589
590
# File 'lib/jsonapi/basic_resource.rb', line 588

def default_attribute_options
  { format: :default }
end

.default_sortObject



1058
1059
1060
# File 'lib/jsonapi/basic_resource.rb', line 1058

def default_sort
  [{field: 'id', direction: :asc}]
end

.define_foreign_key_setter(relationship) ⇒ Object



1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
# File 'lib/jsonapi/basic_resource.rb', line 1098

def define_foreign_key_setter(relationship)
  if relationship.polymorphic?
    define_on_resource "#{relationship.foreign_key}=" do |v|
      _model.method("#{relationship.foreign_key}=").call(v[:id])
      _model.public_send("#{relationship.polymorphic_type}=", v[:type])
    end
  else
    define_on_resource "#{relationship.foreign_key}=" do |value|
      _model.method("#{relationship.foreign_key}=").call(value)
    end
  end
end

.define_on_resource(method_name, &block) ⇒ Object



1111
1112
1113
1114
# File 'lib/jsonapi/basic_resource.rb', line 1111

def define_on_resource(method_name, &block)
  return if method_defined?(method_name)
  define_method(method_name, block)
end

.define_relationship_methods(relationship_name, relationship_klass, options) ⇒ Object

ResourceBuilder methods



1089
1090
1091
1092
1093
1094
1095
1096
# File 'lib/jsonapi/basic_resource.rb', line 1089

def define_relationship_methods(relationship_name, relationship_klass, options)
  relationship = register_relationship(
      relationship_name,
      relationship_klass.new(relationship_name, options)
  )

  define_foreign_key_setter(relationship)
end

.exclude_link?(link) ⇒ Boolean

Returns:

  • (Boolean)


997
998
999
# File 'lib/jsonapi/basic_resource.rb', line 997

def exclude_link?(link)
  _exclude_links.include?(link.to_sym)
end


989
990
991
# File 'lib/jsonapi/basic_resource.rb', line 989

def exclude_links(exclude)
  @_exclude_links = parse_exclude_links(exclude)
end

.fieldsObject



703
704
705
# File 'lib/jsonapi/basic_resource.rb', line 703

def fields
  @_fields_cache ||= _relationships.keys | _attributes.keys
end

.filter(attr, *args) ⇒ Object



663
664
665
# File 'lib/jsonapi/basic_resource.rb', line 663

def filter(attr, *args)
  @_allowed_filters[attr.to_sym] = args.extract_options!
end

.filters(*attrs) ⇒ Object



659
660
661
# File 'lib/jsonapi/basic_resource.rb', line 659

def filters(*attrs)
  @_allowed_filters.merge!(attrs.inject({}) { |h, attr| h[attr] = {}; h })
end

.has_many(*attrs) ⇒ Object



621
622
623
# File 'lib/jsonapi/basic_resource.rb', line 621

def has_many(*attrs)
  _add_relationship(Relationship::ToMany, *attrs)
end

.has_one(*attrs) ⇒ Object



607
608
609
# File 'lib/jsonapi/basic_resource.rb', line 607

def has_one(*attrs)
  _add_relationship(Relationship::ToOne, *attrs)
end

.hash_cache_field(value) ⇒ Object

Generate a hashcode from the value to be used as part of the cache lookup



1022
1023
1024
# File 'lib/jsonapi/basic_resource.rb', line 1022

def hash_cache_field(value)
  value.hash
end

.immutable(val = true) ⇒ Object



964
965
966
# File 'lib/jsonapi/basic_resource.rb', line 964

def immutable(val = true)
  @immutable = val
end

.inherited(subclass) ⇒ Object



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/jsonapi/basic_resource.rb', line 429

def inherited(subclass)
  super
  subclass.abstract(false)
  subclass.immutable(false)
  subclass.caching(_caching)
  subclass.cache_field(_cache_field) if @_cache_field
  subclass.singleton(singleton?, (_singleton_options.dup || {}))
  subclass.exclude_links(_exclude_links)
  subclass.paginator(@_paginator)
  subclass._attributes = (_attributes || {}).dup
  subclass.polymorphic(false)
  subclass.key_type(@_resource_key_type)

  subclass._model_hints = (_model_hints || {}).dup

  unless _model_name.empty? || _immutable
    subclass.model_name(_model_name, add_model_hint: (_model_hints && !_model_hints[_model_name].nil?) == true)
  end

  subclass.rebuild_relationships(_relationships || {})

  subclass._allowed_filters = (_allowed_filters || Set.new).dup

  subclass._allowed_sort = _allowed_sort.dup

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

  unless subclass._attributes[:id]
    subclass.attribute :id, format: :id, readonly: true
  end

  check_reserved_resource_name(subclass._type, subclass.name)

  subclass._routed = false
  subclass._warned_missing_route = false

  subclass._clear_cached_attribute_options
  subclass._clear_fields_cache
end

.is_filter_relationship?(filter) ⇒ Boolean

Returns:

  • (Boolean)


727
728
729
# File 'lib/jsonapi/basic_resource.rb', line 727

def is_filter_relationship?(filter)
  filter == _type || _relationships.include?(filter)
end

.key_type(key_type) ⇒ Object



763
764
765
# File 'lib/jsonapi/basic_resource.rb', line 763

def key_type(key_type)
  @_resource_key_type = key_type
end

.model_hint(model: _model_name, resource: _type) ⇒ Object



640
641
642
643
644
# File 'lib/jsonapi/basic_resource.rb', line 640

def model_hint(model: _model_name, resource: _type)
  resource_type = ((resource.is_a?(Class)) && (resource < JSONAPI::BasicResource)) ? resource._type : resource.to_s

  _model_hints[model.to_s.gsub('::', '/').underscore] = resource_type.to_s
end

.model_name(model, options = {}) ⇒ Object

“‘ CarResource._model_class #=> Vehicle # it should be Car “` so in order to invoke the right class from subclasses, we should call this method to override it.



631
632
633
634
635
636
637
638
# File 'lib/jsonapi/basic_resource.rb', line 631

def model_name(model, options = {})
  @model_class = nil
  @_model_name = model.to_sym

  model_hint(model: @_model_name, resource: self) unless options[:add_model_hint] == false

  rebuild_relationships(_relationships)
end

.module_pathObject



1050
1051
1052
1053
1054
1055
1056
# File 'lib/jsonapi/basic_resource.rb', line 1050

def module_path
  if name == 'JSONAPI::Resource'
    ''
  else
    name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').underscore : ''
  end
end

.mutable?Boolean

Returns:

  • (Boolean)


972
973
974
# File 'lib/jsonapi/basic_resource.rb', line 972

def mutable?
  !@immutable
end

.paginator(paginator) ⇒ Object



914
915
916
# File 'lib/jsonapi/basic_resource.rb', line 914

def paginator(paginator)
  @_paginator = paginator
end


976
977
978
979
980
981
982
983
984
985
986
987
# File 'lib/jsonapi/basic_resource.rb', line 976

def parse_exclude_links(exclude)
  case exclude
    when :default, "default"
      [:self]
    when :none, "none"
      []
    when Array
      exclude.collect {|link| link.to_sym}
    else
      fail "Invalid exclude_links"
  end
end

.polymorphic(polymorphic = true) ⇒ Object



922
923
924
# File 'lib/jsonapi/basic_resource.rb', line 922

def polymorphic(polymorphic = true)
  @_polymorphic = polymorphic
end

.primary_key(key) ⇒ Object



676
677
678
# File 'lib/jsonapi/basic_resource.rb', line 676

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

.rebuild_relationships(relationships) ⇒ Object



470
471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'lib/jsonapi/basic_resource.rb', line 470

def rebuild_relationships(relationships)
  original_relationships = relationships.deep_dup

  @_relationships = {}

  if original_relationships.is_a?(Hash)
    original_relationships.each_value do |relationship|
      options = relationship.options.dup
      options[:parent_resource] = self
      options[:inverse_relationship] = relationship.inverse_relationship
      _add_relationship(relationship.class, relationship.name, options)
    end
  end
end

.register_relationship(name, relationship_object) ⇒ Object



1116
1117
1118
# File 'lib/jsonapi/basic_resource.rb', line 1116

def register_relationship(name, relationship_object)
  @_relationships[name] = relationship_object
end

.relationship(*attrs) ⇒ Object



592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/jsonapi/basic_resource.rb', line 592

def relationship(*attrs)
  options = attrs.extract_options!
  klass = case options[:to]
            when :one
              Relationship::ToOne
            when :many
              Relationship::ToMany
            else
              #:nocov:#
              fail ArgumentError.new('to: must be either :one or :many')
              #:nocov:#
          end
  _add_relationship(klass, *attrs, options.except(:to))
end

.resource_for(model_record, context) ⇒ Object



713
714
715
716
# File 'lib/jsonapi/basic_resource.rb', line 713

def resource_for(model_record, context)
  resource_klass = resource_klass_for_model(model_record)
  resource_klass.new(model_record, context)
end

.resource_key_typeObject



767
768
769
# File 'lib/jsonapi/basic_resource.rb', line 767

def resource_key_type
  @_resource_key_type || JSONAPI.configuration.resource_key_type
end

.resource_klass_for(type) ⇒ Object



485
486
487
488
489
490
491
492
493
494
495
# File 'lib/jsonapi/basic_resource.rb', line 485

def resource_klass_for(type)
  type = type.underscore
  type_with_module = type.start_with?(module_path) ? type : module_path + type

  resource_name = _resource_name_from_type(type_with_module)
  resource = resource_name.safe_constantize if resource_name
  if resource.nil?
    fail NameError, "JSONAPI: Could not find resource '#{type}'. (Class #{resource_name} not found)"
  end
  resource
end

.resource_klass_for_model(model) ⇒ Object



497
498
499
# File 'lib/jsonapi/basic_resource.rb', line 497

def resource_klass_for_model(model)
  resource_klass_for(resource_type_for(model))
end

.resource_type_for(model) ⇒ Object



505
506
507
508
509
510
511
512
# File 'lib/jsonapi/basic_resource.rb', line 505

def resource_type_for(model)
  model_name = model.class.to_s.underscore
  if _model_hints[model_name]
    _model_hints[model_name]
  else
    model_name.rpartition('/').last
  end
end

.resources_for(records, context) ⇒ Object



707
708
709
710
711
# File 'lib/jsonapi/basic_resource.rb', line 707

def resources_for(records, context)
  records.collect do |record|
    resource_for(record, context)
  end
end

.root?Boolean

Returns:

  • (Boolean)


952
953
954
# File 'lib/jsonapi/basic_resource.rb', line 952

def root?
  @root
end

.root_resourceObject



946
947
948
949
950
# File 'lib/jsonapi/basic_resource.rb', line 946

def root_resource
  @abstract = true
  @immutable = true
  @root = true
end

.routing_options(options) ⇒ Object



525
526
527
# File 'lib/jsonapi/basic_resource.rb', line 525

def routing_options(options)
  @_routing_resource_options = options
end

.routing_resource_optionsObject



529
530
531
# File 'lib/jsonapi/basic_resource.rb', line 529

def routing_resource_options
  @_routing_resource_options ||= {}
end

.singleton(*attrs) ⇒ Object



646
647
648
649
# File 'lib/jsonapi/basic_resource.rb', line 646

def singleton(*attrs)
  @_singleton = (!!attrs[0] == attrs[0]) ? attrs[0] : true
  @_singleton_options = attrs.extract_options!
end

.singleton?Boolean

Returns:

  • (Boolean)


655
656
657
# File 'lib/jsonapi/basic_resource.rb', line 655

def singleton?
  @_singleton ||= false
end

.singleton_key(context) ⇒ Object

override to all resolution of masked ids to actual ids. Because singleton routes do not specify the id this will be needed to allow lookup of singleton resources. Alternately singleton resources can override ‘verify_key`



774
775
776
777
778
779
780
781
782
783
784
785
786
787
# File 'lib/jsonapi/basic_resource.rb', line 774

def singleton_key(context)
  if @_singleton_options && @_singleton_options[:singleton_key]
    strategy = @_singleton_options[:singleton_key]
    case strategy
      when Proc
        key = strategy.call(context)
      when Symbol, String
        key = send(strategy, context)
      else
        raise "singleton_key must be a proc or function name"
    end
  end
  key
end

.sort(sorting, options = {}) ⇒ Object



667
668
669
# File 'lib/jsonapi/basic_resource.rb', line 667

def sort(sorting, options = {})
  self._allowed_sort[sorting.to_sym] = options
end

.sortable_field?(key, context = nil) ⇒ Boolean

Returns:

  • (Boolean)


699
700
701
# File 'lib/jsonapi/basic_resource.rb', line 699

def sortable_field?(key, context = nil)
  sortable_fields(context).include? key.to_sym
end

.sortable_fields(_context = nil) ⇒ Object

Override in your resource to filter the sortable keys



695
696
697
# File 'lib/jsonapi/basic_resource.rb', line 695

def sortable_fields(_context = nil)
  _allowed_sort.keys
end

.sorts(*args) ⇒ Object



671
672
673
674
# File 'lib/jsonapi/basic_resource.rb', line 671

def sorts(*args)
  options = args.extract_options!
  _allowed_sort.merge!(args.inject({}) { |h, sorting| h[sorting.to_sym] = options.dup; h })
end

.updatable_fields(_context = nil) ⇒ Object

Override in your resource to filter the updatable keys



685
686
687
# File 'lib/jsonapi/basic_resource.rb', line 685

def updatable_fields(_context = nil)
  _updatable_relationships | _updatable_attributes - [:id]
end

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

Either add a custom :verify lambda or override verify_custom_filter to allow for custom filters



825
826
827
# File 'lib/jsonapi/basic_resource.rb', line 825

def verify_custom_filter(filter, value, _context = nil)
  [filter, value]
end

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



731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
# File 'lib/jsonapi/basic_resource.rb', line 731

def verify_filter(filter, raw, context = nil)
  filter_values = []
  if raw.present?
    begin
      filter_values += raw.is_a?(String) ? CSV.parse_line(raw) : [raw]
    rescue CSV::MalformedCSVError
      filter_values << raw
    end
  end

  strategy = _allowed_filters.fetch(filter, Hash.new)[:verify]

  if strategy
    values = call_method_or_proc(strategy, filter_values, context)
    [filter, values]
  else
    if is_filter_relationship?(filter)
      verify_relationship_filter(filter, filter_values, context)
    else
      verify_custom_filter(filter, filter_values, context)
    end
  end
end

.verify_filters(filters, context = nil) ⇒ Object



718
719
720
721
722
723
724
725
# File 'lib/jsonapi/basic_resource.rb', line 718

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



789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
# File 'lib/jsonapi/basic_resource.rb', line 789

def verify_key(key, context = nil)
  key_type = resource_key_type

  case key_type
  when :integer
    return if key.nil?
    Integer(key)
  when :string
    return if key.nil?
    if key.to_s.include?(',')
      raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
    else
      key
    end
  when :uuid
    return if key.nil?
    if key.to_s.match(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/)
      key
    else
      raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
    end
  else
    key_type.call(key, context)
  end
rescue
  raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
end

.verify_keys(keys, context = nil) ⇒ Object

override to allow for key processing and checking



818
819
820
821
822
# File 'lib/jsonapi/basic_resource.rb', line 818

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

.verify_relationship_filter(filter, raw, _context = nil) ⇒ Object

Either add a custom :verify lambda or override verify_relationship_filter to allow for custom relationship logic, such as uuids, multiple keys or permission checks on keys



831
832
833
# File 'lib/jsonapi/basic_resource.rb', line 831

def verify_relationship_filter(filter, raw, _context = nil)
  [filter, raw]
end

Instance Method Details

#_modelObject



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

def _model
  @model
end

#cache_field_valueObject



49
50
51
# File 'lib/jsonapi/basic_resource.rb', line 49

def cache_field_value
  _model.public_send(self.class._cache_field)
end

#cache_idObject



53
54
55
# File 'lib/jsonapi/basic_resource.rb', line 53

def cache_id
  [id, self.class.hash_cache_field(cache_field_value)]
end

#change(callback) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/jsonapi/basic_resource.rb', line 61

def change(callback)
  completed = false

  if @changing
    run_callbacks callback do
      completed = (yield == :completed)
    end
  else
    run_callbacks is_new? ? :create : :update do
      @changing = true
      run_callbacks callback do
        completed = (yield == :completed)
      end

      completed = (save == :completed) if @save_needed || is_new?
    end
  end

  return completed ? :completed : :accepted
end


88
89
90
91
92
# File 'lib/jsonapi/basic_resource.rb', line 88

def create_to_many_links(relationship_type, relationship_key_values, options = {})
  change :create_to_many_link do
    _create_to_many_links(relationship_type, relationship_key_values, options)
  end
end

Override this to return custom links must return a hash, which will be merged with the default { self: ‘self-url’ } links hash links keys will be not be formatted with the key formatter for the serializer by default. They can however use the serializer’s format_key and format_value methods if desired the _options hash will contain the serializer and the serialization_options



176
177
178
# File 'lib/jsonapi/basic_resource.rb', line 176

def custom_links(_options)
  {}
end

#fetchable_fieldsObject

Override this on a resource instance to override the fetchable keys



131
132
133
# File 'lib/jsonapi/basic_resource.rb', line 131

def fetchable_fields
  self.class.fields
end

#idObject



41
42
43
# File 'lib/jsonapi/basic_resource.rb', line 41

def id
  _model.public_send(self.class._primary_key)
end

#identityObject



45
46
47
# File 'lib/jsonapi/basic_resource.rb', line 45

def identity
  JSONAPI::ResourceIdentity.new(self.class, id)
end

#is_new?Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/jsonapi/basic_resource.rb', line 57

def is_new?
  id.nil?
end

#meta(_options) ⇒ Object

Override this to return resource level meta data must return a hash, and if the hash is empty the meta section will not be serialized with the resource meta keys will be not be formatted with the key formatter for the serializer by default. They can however use the serializer’s format_key and format_value methods if desired the _options hash will contain the serializer and the serialization_options



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

def meta(_options)
  {}
end

#model_error_messagesObject



135
136
137
# File 'lib/jsonapi/basic_resource.rb', line 135

def model_error_messages
  _model.errors.messages
end

#removeObject



82
83
84
85
86
# File 'lib/jsonapi/basic_resource.rb', line 82

def remove
  run_callbacks :remove do
    _remove
  end
end


112
113
114
115
116
# File 'lib/jsonapi/basic_resource.rb', line 112

def remove_to_many_link(relationship_type, key, options = {})
  change :remove_to_many_link do
    _remove_to_many_link(relationship_type, key, options)
  end
end


118
119
120
121
122
# File 'lib/jsonapi/basic_resource.rb', line 118

def remove_to_one_link(relationship_type, options = {})
  change :remove_to_one_link do
    _remove_to_one_link(relationship_type, options)
  end
end

#replace_fields(field_data) ⇒ Object



124
125
126
127
128
# File 'lib/jsonapi/basic_resource.rb', line 124

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


106
107
108
109
110
# File 'lib/jsonapi/basic_resource.rb', line 106

def replace_polymorphic_to_one_link(relationship_type, relationship_key_value, relationship_key_type, options = {})
  change :replace_polymorphic_to_one_link do
    _replace_polymorphic_to_one_link(relationship_type, relationship_key_value, relationship_key_type, options)
  end
end


94
95
96
97
98
# File 'lib/jsonapi/basic_resource.rb', line 94

def replace_to_many_links(relationship_type, relationship_key_values, options = {})
  change :replace_to_many_links do
    _replace_to_many_links(relationship_type, relationship_key_values, options)
  end
end


100
101
102
103
104
# File 'lib/jsonapi/basic_resource.rb', line 100

def replace_to_one_link(relationship_type, relationship_key_value, options = {})
  change :replace_to_one_link do
    _replace_to_one_link(relationship_type, relationship_key_value, options)
  end
end

#validation_error_metadataObject

Add metadata to validation error objects.

Suppose ‘model_error_messages` returned the following error messages hash:

{password: ["too_short", "format"]}

Then to add data to the validation error ‘validation_error_metadata` could return:

{
  password: {
    "too_short": {"minimum_length" => 6},
    "format": {"requirement" => "must contain letters and numbers"}
  }
}

The specified metadata is then be merged into the validation error object.



158
159
160
# File 'lib/jsonapi/basic_resource.rb', line 158

def 
  {}
end