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.


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

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

Class Attribute Details

._allowed_filtersObject


900
901
902
# File 'lib/jsonapi/basic_resource.rb', line 900

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

._allowed_sortObject


904
905
906
# File 'lib/jsonapi/basic_resource.rb', line 904

def _allowed_sort
  @_allowed_sort ||= {}
end

._attributesObject

Returns the value of attribute _attributes


505
506
507
# File 'lib/jsonapi/basic_resource.rb', line 505

def _attributes
  @_attributes
end

._model_hintsObject

Returns the value of attribute _model_hints


505
506
507
# File 'lib/jsonapi/basic_resource.rb', line 505

def _model_hints
  @_model_hints
end

._paginatorObject


908
909
910
# File 'lib/jsonapi/basic_resource.rb', line 908

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

._relationshipsObject

Returns the value of attribute _relationships


505
506
507
# File 'lib/jsonapi/basic_resource.rb', line 505

def _relationships
  @_relationships
end

._routedObject

Returns the value of attribute _routed


505
506
507
# File 'lib/jsonapi/basic_resource.rb', line 505

def _routed
  @_routed
end

._typeObject

Returns the value of attribute _type


505
506
507
# File 'lib/jsonapi/basic_resource.rb', line 505

def _type
  @_type
end

._warned_missing_routeObject

Returns the value of attribute _warned_missing_route


505
506
507
# File 'lib/jsonapi/basic_resource.rb', line 505

def _warned_missing_route
  @_warned_missing_route
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context


12
13
14
# File 'lib/jsonapi/basic_resource.rb', line 12

def context
  @context
end

Class Method Details

._abstractObject


958
959
960
# File 'lib/jsonapi/basic_resource.rb', line 958

def _abstract
  @abstract
end

._add_relationship(klass, *attrs) ⇒ Object


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

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)

1040
1041
1042
# File 'lib/jsonapi/basic_resource.rb', line 1040

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

._as_parent_keyObject


896
897
898
# File 'lib/jsonapi/basic_resource.rb', line 896

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

._attribute_delegated_name(attr) ⇒ Object


838
839
840
# File 'lib/jsonapi/basic_resource.rb', line 838

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

._attribute_options(attr) ⇒ Object

quasi private class methods


834
835
836
# File 'lib/jsonapi/basic_resource.rb', line 834

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

._cache_fieldObject


888
889
890
# File 'lib/jsonapi/basic_resource.rb', line 888

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

._cachingObject


1003
1004
1005
# File 'lib/jsonapi/basic_resource.rb', line 1003

def _caching
  @caching
end

._clear_cached_attribute_optionsObject


1118
1119
1120
# File 'lib/jsonapi/basic_resource.rb', line 1118

def _clear_cached_attribute_options
  @_cached_attribute_options = {}
end

._clear_fields_cacheObject


1122
1123
1124
# File 'lib/jsonapi/basic_resource.rb', line 1122

def _clear_fields_cache
  @_fields_cache = nil
end

._default_primary_keyObject


884
885
886
# File 'lib/jsonapi/basic_resource.rb', line 884

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

991
992
993
# File 'lib/jsonapi/basic_resource.rb', line 991

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

._has_attribute?(attr) ⇒ Boolean

Returns:

  • (Boolean)

842
843
844
# File 'lib/jsonapi/basic_resource.rb', line 842

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

._has_sort?(sorting) ⇒ Boolean

Returns:

  • (Boolean)

1044
1045
1046
# File 'lib/jsonapi/basic_resource.rb', line 1044

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

._immutableObject


966
967
968
# File 'lib/jsonapi/basic_resource.rb', line 966

def _immutable
  @immutable
end

._model_classObject


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

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


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

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


916
917
918
# File 'lib/jsonapi/basic_resource.rb', line 916

def _polymorphic
  @_polymorphic
end

._polymorphic_nameObject


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

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

._polymorphic_resource_klassesObject


938
939
940
941
942
# File 'lib/jsonapi/basic_resource.rb', line 938

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

._polymorphic_typesObject


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

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


880
881
882
# File 'lib/jsonapi/basic_resource.rb', line 880

def _primary_key
  @_primary_key ||= _default_primary_key
end

._relationship(type) ⇒ Object


854
855
856
857
858
# File 'lib/jsonapi/basic_resource.rb', line 854

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

._resource_name_from_type(type) ⇒ Object


492
493
494
# File 'lib/jsonapi/basic_resource.rb', line 492

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

._singleton_optionsObject


649
650
651
# File 'lib/jsonapi/basic_resource.rb', line 649

def _singleton_options
  @_singleton_options ||= {}
end

._table_nameObject


892
893
894
# File 'lib/jsonapi/basic_resource.rb', line 892

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

._updatable_attributesObject


846
847
848
# File 'lib/jsonapi/basic_resource.rb', line 846

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

._updatable_relationshipsObject


850
851
852
# File 'lib/jsonapi/basic_resource.rb', line 850

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

.abstract(val = true) ⇒ Object


954
955
956
# File 'lib/jsonapi/basic_resource.rb', line 954

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

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


532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/jsonapi/basic_resource.rb', line 532

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?)
    ActiveSupport::Deprecation.warn('Id without format is no longer supported. Please remove ids from attributes, or specify a format.')
  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


1015
1016
1017
# File 'lib/jsonapi/basic_resource.rb', line 1015

def attribute_caching_context(_context)
  nil
end

.attribute_to_model_field(attribute) ⇒ Object


561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
# File 'lib/jsonapi/basic_resource.rb', line 561

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
  if Rails::VERSION::MAJOR >= 5
    attribute_type = _model_class.attribute_types[field_name.to_s]
  else
    attribute_type = _model_class.column_types[field_name.to_s]
  end
  { name: field_name, type: attribute_type}
end

.attributes(*attrs) ⇒ Object

Methods used in defining a resource class


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

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

.belongs_to(*attrs) ⇒ Object


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

def belongs_to(*attrs)
  ActiveSupport::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


678
679
680
# File 'lib/jsonapi/basic_resource.rb', line 678

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

.caching(val = true) ⇒ Object


999
1000
1001
# File 'lib/jsonapi/basic_resource.rb', line 999

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

.caching?Boolean

Returns:

  • (Boolean)

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

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


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

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


579
580
581
582
583
584
585
# File 'lib/jsonapi/basic_resource.rb', line 579

def cast_to_attribute_type(value, type)
  if Rails::VERSION::MAJOR >= 5
    return type.cast(value)
  else
    return type.type_cast_from_database(value)
  end
end

.construct_order_options(sort_params) ⇒ Object


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

def construct_order_options(sort_params)
  sort_params ||= default_sort

  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


688
689
690
# File 'lib/jsonapi/basic_resource.rb', line 688

def creatable_fields(_context = nil)
  _updatable_relationships | _updatable_attributes
end

.create(context) ⇒ Object


508
509
510
# File 'lib/jsonapi/basic_resource.rb', line 508

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

.create_modelObject


512
513
514
# File 'lib/jsonapi/basic_resource.rb', line 512

def create_model
  _model_class.new
end

.default_attribute_optionsObject


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

def default_attribute_options
  { format: :default }
end

.default_sortObject


1056
1057
1058
# File 'lib/jsonapi/basic_resource.rb', line 1056

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

.define_foreign_key_setter(relationship) ⇒ Object


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

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


1109
1110
1111
1112
# File 'lib/jsonapi/basic_resource.rb', line 1109

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


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

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)

995
996
997
# File 'lib/jsonapi/basic_resource.rb', line 995

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

987
988
989
# File 'lib/jsonapi/basic_resource.rb', line 987

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

.fieldsObject


701
702
703
# File 'lib/jsonapi/basic_resource.rb', line 701

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

.filter(attr, *args) ⇒ Object


661
662
663
# File 'lib/jsonapi/basic_resource.rb', line 661

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

.filters(*attrs) ⇒ Object


657
658
659
# File 'lib/jsonapi/basic_resource.rb', line 657

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

.has_many(*attrs) ⇒ Object


619
620
621
# File 'lib/jsonapi/basic_resource.rb', line 619

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

.has_one(*attrs) ⇒ Object


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

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


1020
1021
1022
# File 'lib/jsonapi/basic_resource.rb', line 1020

def hash_cache_field(value)
  value.hash
end

.immutable(val = true) ⇒ Object


962
963
964
# File 'lib/jsonapi/basic_resource.rb', line 962

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

.inherited(subclass) ⇒ Object


421
422
423
424
425
426
427
428
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
# File 'lib/jsonapi/basic_resource.rb', line 421

def inherited(subclass)
  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)

725
726
727
# File 'lib/jsonapi/basic_resource.rb', line 725

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

.key_type(key_type) ⇒ Object


761
762
763
# File 'lib/jsonapi/basic_resource.rb', line 761

def key_type(key_type)
  @_resource_key_type = key_type
end

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


638
639
640
641
642
# File 'lib/jsonapi/basic_resource.rb', line 638

def model_hint(model: _model_name, resource: _type)
  resource_type = ((resource.is_a?(Class)) && (resource < JSONAPI::Resource)) ? 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.


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

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


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

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

.mutable?Boolean

Returns:

  • (Boolean)

970
971
972
# File 'lib/jsonapi/basic_resource.rb', line 970

def mutable?
  !@immutable
end

.paginator(paginator) ⇒ Object


912
913
914
# File 'lib/jsonapi/basic_resource.rb', line 912

def paginator(paginator)
  @_paginator = paginator
end

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

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


920
921
922
# File 'lib/jsonapi/basic_resource.rb', line 920

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

.primary_key(key) ⇒ Object


674
675
676
# File 'lib/jsonapi/basic_resource.rb', line 674

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

.rebuild_relationships(relationships) ⇒ Object


461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/jsonapi/basic_resource.rb', line 461

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


1114
1115
1116
# File 'lib/jsonapi/basic_resource.rb', line 1114

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

.relationship(*attrs) ⇒ Object


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

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


711
712
713
714
# File 'lib/jsonapi/basic_resource.rb', line 711

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

.resource_key_typeObject


765
766
767
# File 'lib/jsonapi/basic_resource.rb', line 765

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

.resource_klass_for(type) ⇒ Object


476
477
478
479
480
481
482
483
484
485
486
# File 'lib/jsonapi/basic_resource.rb', line 476

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


488
489
490
# File 'lib/jsonapi/basic_resource.rb', line 488

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

.resource_type_for(model) ⇒ Object


496
497
498
499
500
501
502
503
# File 'lib/jsonapi/basic_resource.rb', line 496

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


705
706
707
708
709
# File 'lib/jsonapi/basic_resource.rb', line 705

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

.root?Boolean

Returns:

  • (Boolean)

950
951
952
# File 'lib/jsonapi/basic_resource.rb', line 950

def root?
  @root
end

.root_resourceObject


944
945
946
947
948
# File 'lib/jsonapi/basic_resource.rb', line 944

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

.routing_options(options) ⇒ Object


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

def routing_options(options)
  @_routing_resource_options = options
end

.routing_resource_optionsObject


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

def routing_resource_options
  @_routing_resource_options ||= {}
end

.singleton(*attrs) ⇒ Object


644
645
646
647
# File 'lib/jsonapi/basic_resource.rb', line 644

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

.singleton?Boolean

Returns:

  • (Boolean)

653
654
655
# File 'lib/jsonapi/basic_resource.rb', line 653

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`


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

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


665
666
667
# File 'lib/jsonapi/basic_resource.rb', line 665

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

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

Returns:

  • (Boolean)

697
698
699
# File 'lib/jsonapi/basic_resource.rb', line 697

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


693
694
695
# File 'lib/jsonapi/basic_resource.rb', line 693

def sortable_fields(_context = nil)
  _allowed_sort.keys
end

.sorts(*args) ⇒ Object


669
670
671
672
# File 'lib/jsonapi/basic_resource.rb', line 669

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


683
684
685
# File 'lib/jsonapi/basic_resource.rb', line 683

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


823
824
825
# File 'lib/jsonapi/basic_resource.rb', line 823

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

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


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

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


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

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


787
788
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
# File 'lib/jsonapi/basic_resource.rb', line 787

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


816
817
818
819
820
# File 'lib/jsonapi/basic_resource.rb', line 816

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


829
830
831
# File 'lib/jsonapi/basic_resource.rb', line 829

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

Instance Method Details

#_modelObject


35
36
37
# File 'lib/jsonapi/basic_resource.rb', line 35

def _model
  @model
end

#cache_idObject


47
48
49
# File 'lib/jsonapi/basic_resource.rb', line 47

def cache_id
  [id, self.class.hash_cache_field(_model.public_send(self.class._cache_field))]
end

#change(callback) ⇒ Object


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/jsonapi/basic_resource.rb', line 55

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

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

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


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

def custom_links(_options)
  {}
end

#fetchable_fieldsObject

Override this on a resource instance to override the fetchable keys


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

def fetchable_fields
  self.class.fields
end

#idObject


39
40
41
# File 'lib/jsonapi/basic_resource.rb', line 39

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

#identityObject


43
44
45
# File 'lib/jsonapi/basic_resource.rb', line 43

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

#is_new?Boolean

Returns:

  • (Boolean)

51
52
53
# File 'lib/jsonapi/basic_resource.rb', line 51

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


161
162
163
# File 'lib/jsonapi/basic_resource.rb', line 161

def meta(_options)
  {}
end

#model_error_messagesObject


129
130
131
# File 'lib/jsonapi/basic_resource.rb', line 129

def model_error_messages
  _model.errors.messages
end

#removeObject


76
77
78
79
80
# File 'lib/jsonapi/basic_resource.rb', line 76

def remove
  run_callbacks :remove do
    _remove
  end
end

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

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

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

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


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

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

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

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

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

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

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

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.


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

def 
  {}
end