Class: JSONAPI::Resource
- Inherits:
-
Object
- Object
- JSONAPI::Resource
- Includes:
- Callbacks
- Defined in:
- lib/jsonapi/resource.rb
Constant Summary collapse
- DEFAULT_ATTRIBUTE_OPTIONS =
{ format: :default }.freeze
- MODULE_PATH_REGEXP =
/::[^:]+\Z/.freeze
Class Attribute Summary collapse
- ._allowed_filters ⇒ Object
-
._attributes ⇒ Object
Returns the value of attribute _attributes.
-
._model_hints ⇒ Object
Returns the value of attribute _model_hints.
- ._paginator ⇒ Object
-
._relationships ⇒ Object
Returns the value of attribute _relationships.
-
._routed ⇒ Object
Returns the value of attribute _routed.
-
._type ⇒ Object
Returns the value of attribute _type.
-
._warned_missing_route ⇒ Object
Returns the value of attribute _warned_missing_route.
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
Class Method Summary collapse
- ._abstract ⇒ Object
- ._add_relationship(klass, *attrs) ⇒ Object
- ._allowed_filter?(filter) ⇒ Boolean
- ._as_parent_key ⇒ Object
-
._attribute_options(attr) ⇒ Object
quasi private class methods.
- ._build_joins(associations) ⇒ Object
- ._cache_field ⇒ Object
- ._caching ⇒ Object
- ._exclude_links ⇒ Object
- ._has_attribute?(attr) ⇒ Boolean
- ._immutable ⇒ Object
- ._lookup_association_chain(model_names) ⇒ Object
- ._model_class ⇒ Object
- ._model_name ⇒ Object
- ._primary_key ⇒ Object
- ._relationship(type) ⇒ Object
- ._resolve_exclude_links(exclude) ⇒ Object
- ._resource_name_from_type(type) ⇒ Object
- ._singleton_options ⇒ Object
- ._table_name ⇒ Object
- ._updatable_relationships ⇒ Object
- .abstract(val = true) ⇒ Object
- .apply_filter(records, filter, value, options = {}) ⇒ Object
- .apply_filters(records, filters, options = {}) ⇒ Object
- .apply_included_resources_filters(records, options = {}) ⇒ Object
- .apply_includes(records, options = {}) ⇒ Object
- .apply_pagination(records, paginator, order_options) ⇒ Object
- .apply_sort(records, order_options, _context = {}) ⇒ Object
- .attribute(attribute_name, options = {}) ⇒ Object
- .attribute_caching_context(context) ⇒ Object
-
.attributes(*attrs) ⇒ Object
Methods used in defining a resource class.
- .belongs_to(*attrs) ⇒ Object
- .cache_field(field) ⇒ Object
- .caching(val = true) ⇒ Object
- .caching? ⇒ Boolean
- .construct_order_options(sort_params) ⇒ Object
-
.count_records(records) ⇒ Object
Assumes ActiveRecord’s counting.
-
.creatable_fields(_context = nil) ⇒ Object
Override in your resource to filter the creatable keys.
- .create(context) ⇒ Object
- .create_model ⇒ Object
- .default_attribute_options ⇒ Object
- .default_sort ⇒ Object
- .exclude_link?(link) ⇒ Boolean
- .exclude_links(exclude) ⇒ Object
- .fields ⇒ Object
- .filter(attr, *args) ⇒ Object
- .filter_records(filters, options, records = records(options)) ⇒ Object
- .filters(*attrs) ⇒ Object
- .find(filters, options = {}) ⇒ Object
- .find_by_key(key, options = {}) ⇒ Object
- .find_by_key_serialized_with_caching(key, serializer, options = {}) ⇒ Object
- .find_by_keys(keys, options = {}) ⇒ Object
- .find_count(filters, options = {}) ⇒ Object
- .find_serialized_with_caching(filters_or_source, serializer, options = {}) ⇒ Object
- .has_many(*attrs) ⇒ Object
- .has_one(*attrs) ⇒ Object
- .immutable(val = true) ⇒ Object
- .inherited(subclass) ⇒ Object
-
.inject_method_definition(name, body) ⇒ Object
Allows JSONAPI::RelationshipBuilder to access metaprogramming hooks.
- .is_filter_relationship?(filter) ⇒ Boolean
- .key_type(key_type) ⇒ Object
- .model_hint(model: _model_name, resource: _type) ⇒ Object
-
.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.
- .module_path ⇒ Object
- .mutable? ⇒ Boolean
- .paginator(paginator) ⇒ Object
- .primary_key(key) ⇒ Object
- .rebuild_relationships(relationships) ⇒ Object
-
.records(_options = {}) ⇒ Object
Override this method if you want to customize the relation for finder methods (find, find_by_key, find_serialized_with_caching).
- .register_relationship(name, relationship_object) ⇒ Object
- .relationship(*attrs) ⇒ Object
- .resolve_relationship_names_to_relations(resource_klass, model_includes, options = {}) ⇒ Object
- .resource_for(type) ⇒ Object
- .resource_for_model(model) ⇒ Object
- .resource_key_type ⇒ Object
- .resource_type_for(model) ⇒ Object
- .resources_for(records, context) ⇒ Object
- .routing_options(options) ⇒ Object
- .routing_resource_options ⇒ Object
- .singleton(*attrs) ⇒ Object
- .singleton? ⇒ Boolean
-
.singleton_key(context) ⇒ Object
override to all resolution of masked ids to actual ids.
- .sort_records(records, order_options, context = {}) ⇒ Object
-
.sortable_fields(_context = nil) ⇒ Object
Override in your resource to filter the sortable keys.
-
.updatable_fields(_context = nil) ⇒ Object
Override in your resource to filter the updatable keys.
-
.verify_custom_filter(filter, value, _context = nil) ⇒ Object
Either add a custom :verify labmda or override verify_custom_filter to allow for custom filters.
- .verify_filter(filter, raw, context = nil) ⇒ Object
- .verify_filters(filters, context = nil) ⇒ Object
- .verify_key(key, context = nil) ⇒ Object
-
.verify_keys(keys, context = nil) ⇒ Object
override to allow for key processing and checking.
-
.verify_relationship_filter(filter, raw, _context = nil) ⇒ Object
Either add a custom :verify labmda or override verify_relationship_filter to allow for custom relationship logic, such as uuids, multiple keys or permission checks on keys.
Instance Method Summary collapse
- #_model ⇒ Object
- #cache_id ⇒ Object
- #change(callback) ⇒ Object
- #create_to_many_links(relationship_type, relationship_key_values, options = {}) ⇒ Object
-
#custom_links(_options) ⇒ Object
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.
-
#fetchable_fields ⇒ Object
Override this on a resource instance to override the fetchable keys.
- #id ⇒ Object
-
#initialize(model, context) ⇒ Resource
constructor
A new instance of Resource.
- #is_new? ⇒ Boolean
-
#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.
- #model_error_messages ⇒ Object
- #preloaded_fragments ⇒ Object
-
#records_for(relation_name) ⇒ Object
Override this on a resource to customize how the associated records are fetched for a model.
- #remove ⇒ Object
- #remove_to_many_link(relationship_type, key, options = {}) ⇒ Object
- #remove_to_one_link(relationship_type, options = {}) ⇒ Object
- #replace_fields(field_data) ⇒ Object
- #replace_polymorphic_to_one_link(relationship_type, relationship_key_value, relationship_key_type, options = {}) ⇒ Object
- #replace_to_many_links(relationship_type, relationship_key_values, options = {}) ⇒ Object
- #replace_to_one_link(relationship_type, relationship_key_value, options = {}) ⇒ Object
-
#validation_error_metadata ⇒ Object
Add metadata to validation error objects.
Methods included from Callbacks
Constructor Details
#initialize(model, context) ⇒ Resource
Returns a new instance of Resource.
26 27 28 29 30 31 32 |
# File 'lib/jsonapi/resource.rb', line 26 def initialize(model, context) @model = model @context = context @reload_needed = false @changing = false @save_needed = false end |
Class Attribute Details
._allowed_filters ⇒ Object
1042 1043 1044 |
# File 'lib/jsonapi/resource.rb', line 1042 def _allowed_filters defined?(@_allowed_filters) ? @_allowed_filters : { id: {} } end |
._attributes ⇒ Object
Returns the value of attribute _attributes.
511 512 513 |
# File 'lib/jsonapi/resource.rb', line 511 def _attributes @_attributes end |
._model_hints ⇒ Object
Returns the value of attribute _model_hints.
511 512 513 |
# File 'lib/jsonapi/resource.rb', line 511 def _model_hints @_model_hints end |
._paginator ⇒ Object
1046 1047 1048 |
# File 'lib/jsonapi/resource.rb', line 1046 def _paginator @_paginator ||= JSONAPI.configuration.default_paginator end |
._relationships ⇒ Object
Returns the value of attribute _relationships.
511 512 513 |
# File 'lib/jsonapi/resource.rb', line 511 def _relationships @_relationships end |
._routed ⇒ Object
Returns the value of attribute _routed.
511 512 513 |
# File 'lib/jsonapi/resource.rb', line 511 def _routed @_routed end |
._type ⇒ Object
Returns the value of attribute _type.
511 512 513 |
# File 'lib/jsonapi/resource.rb', line 511 def _type @_type end |
._warned_missing_route ⇒ Object
Returns the value of attribute _warned_missing_route.
511 512 513 |
# File 'lib/jsonapi/resource.rb', line 511 def _warned_missing_route @_warned_missing_route end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
11 12 13 |
# File 'lib/jsonapi/resource.rb', line 11 def context @context end |
Class Method Details
._abstract ⇒ Object
1058 1059 1060 |
# File 'lib/jsonapi/resource.rb', line 1058 def _abstract @abstract end |
._add_relationship(klass, *attrs) ⇒ Object
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 |
# File 'lib/jsonapi/resource.rb', line 1158 def _add_relationship(klass, *attrs) = attrs. [:parent_resource] = self attrs.each do |name| relationship_name = name.to_sym check_reserved_relationship_name(relationship_name) check_duplicate_relationship_name(relationship_name) JSONAPI::RelationshipBuilder.new(klass, _model_class, ) .define_relationship_methods(relationship_name.to_sym) end end |
._allowed_filter?(filter) ⇒ Boolean
1131 1132 1133 |
# File 'lib/jsonapi/resource.rb', line 1131 def _allowed_filter?(filter) !_allowed_filters[filter].nil? end |
._as_parent_key ⇒ Object
1038 1039 1040 |
# File 'lib/jsonapi/resource.rb', line 1038 def _as_parent_key @_as_parent_key ||= "#{_type.to_s.singularize}_id" end |
._attribute_options(attr) ⇒ Object
quasi private class methods
997 998 999 |
# File 'lib/jsonapi/resource.rb', line 997 def (attr) .merge(@_attributes[attr]) end |
._build_joins(associations) ⇒ Object
734 735 736 737 738 739 740 741 742 |
# File 'lib/jsonapi/resource.rb', line 734 def _build_joins(associations) joins = [] associations.inject do |prev, current| joins << "LEFT JOIN #{current.table_name} AS #{current.name}_sorting ON #{current.name}_sorting.id = #{prev.table_name}.#{current.foreign_key}" current end joins.join("\n") end |
._cache_field ⇒ Object
1030 1031 1032 |
# File 'lib/jsonapi/resource.rb', line 1030 def _cache_field @_cache_field ||= JSONAPI.configuration.default_resource_cache_field end |
._caching ⇒ Object
1103 1104 1105 |
# File 'lib/jsonapi/resource.rb', line 1103 def _caching @caching end |
._exclude_links ⇒ Object
1078 1079 1080 |
# File 'lib/jsonapi/resource.rb', line 1078 def _exclude_links @_exclude_links ||= _resolve_exclude_links(JSONAPI.configuration.default_exclude_links) end |
._has_attribute?(attr) ⇒ Boolean
1001 1002 1003 |
# File 'lib/jsonapi/resource.rb', line 1001 def _has_attribute?(attr) @_attributes.keys.include?(attr.to_sym) end |
._immutable ⇒ Object
1066 1067 1068 |
# File 'lib/jsonapi/resource.rb', line 1066 def _immutable @immutable end |
._lookup_association_chain(model_names) ⇒ Object
721 722 723 724 725 726 727 728 729 730 731 732 |
# File 'lib/jsonapi/resource.rb', line 721 def _lookup_association_chain(model_names) associations = [] model_names.inject do |prev, current| association = prev.classify.constantize.reflect_on_all_associations.detect do |assoc| assoc.name.to_s.downcase == current.downcase end associations << association association.class_name end associations end |
._model_class ⇒ Object
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 |
# File 'lib/jsonapi/resource.rb', line 1115 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_name ⇒ Object
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 |
# File 'lib/jsonapi/resource.rb', line 1014 def _model_name if _abstract return '' 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$/, '') return @_model_name.to_s end end |
._primary_key ⇒ Object
1026 1027 1028 |
# File 'lib/jsonapi/resource.rb', line 1026 def _primary_key @_primary_key ||= _model_class.respond_to?(:primary_key) ? _model_class.primary_key : :id end |
._relationship(type) ⇒ Object
1009 1010 1011 1012 |
# File 'lib/jsonapi/resource.rb', line 1009 def _relationship(type) type = type.to_sym @_relationships[type] end |
._resolve_exclude_links(exclude) ⇒ Object
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 |
# File 'lib/jsonapi/resource.rb', line 1086 def _resolve_exclude_links(exclude) case exclude when :default, "default" @_exclude_links = [:self] when :none, "none" @_exclude_links = [] when Array @_exclude_links = exclude.collect {|link| link.to_sym} else fail "Invalid exclude_links" end end |
._resource_name_from_type(type) ⇒ Object
498 499 500 |
# File 'lib/jsonapi/resource.rb', line 498 def _resource_name_from_type(type) "#{type.to_s.underscore.singularize}_resource".camelize end |
._singleton_options ⇒ Object
622 623 624 |
# File 'lib/jsonapi/resource.rb', line 622 def ||= {} end |
._table_name ⇒ Object
1034 1035 1036 |
# File 'lib/jsonapi/resource.rb', line 1034 def _table_name @_table_name ||= _model_class.respond_to?(:table_name) ? _model_class.table_name : _model_name.tableize end |
._updatable_relationships ⇒ Object
1005 1006 1007 |
# File 'lib/jsonapi/resource.rb', line 1005 def _updatable_relationships @_relationships.map { |key, _relationship| key } end |
.abstract(val = true) ⇒ Object
1054 1055 1056 |
# File 'lib/jsonapi/resource.rb', line 1054 def abstract(val = true) @abstract = val end |
.apply_filter(records, filter, value, options = {}) ⇒ Object
744 745 746 747 748 749 750 751 752 753 754 755 756 |
# File 'lib/jsonapi/resource.rb', line 744 def apply_filter(records, filter, value, = {}) strategy = _allowed_filters.fetch(filter.to_sym, Hash.new)[:apply] if strategy if strategy.is_a?(Symbol) || strategy.is_a?(String) send(strategy, records, value, ) else strategy.call(records, value, ) end else records.where(filter => value) end end |
.apply_filters(records, filters, options = {}) ⇒ Object
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
# File 'lib/jsonapi/resource.rb', line 758 def apply_filters(records, filters, = {}) required_includes = [] if filters filters.each do |filter, value| if _relationships.include?(filter) if _relationships[filter].belongs_to? records = apply_filter(records, _relationships[filter].foreign_key, value, ) else required_includes.push(filter.to_s) records = apply_filter(records, "#{_relationships[filter].table_name}.#{_relationships[filter].primary_key}", value, ) end else records = apply_filter(records, filter, value, ) end end end if required_includes.any? records = apply_includes(records, .merge(include_directives: IncludeDirectives.new(self, required_includes, force_eager_load: true))) end records end |
.apply_included_resources_filters(records, options = {}) ⇒ Object
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 |
# File 'lib/jsonapi/resource.rb', line 783 def apply_included_resources_filters(records, = {}) include_directives = [:include_directives] return records unless include_directives = include_directives.include_directives.fetch(:include_related) .reduce(records) do |memo, (relationship_name, config)| relationship = _relationship(relationship_name) next memo unless relationship && relationship.is_a?(JSONAPI::Relationship::ToMany) filtering_resource = relationship.resource_klass # Don't try to merge where clauses when relation isn't already being joined to query. next memo unless config[:include_in_join] filters = config[:include_filters] next memo unless filters rel_records = filtering_resource.apply_filters(filtering_resource.records(), filters, ).references(relationship_name) memo.merge(rel_records) end end |
.apply_includes(records, options = {}) ⇒ Object
685 686 687 688 689 690 691 692 693 |
# File 'lib/jsonapi/resource.rb', line 685 def apply_includes(records, = {}) include_directives = [:include_directives] if include_directives model_includes = resolve_relationship_names_to_relations(self, include_directives.model_includes, ) records = records.includes(model_includes) if model_includes.present? end records end |
.apply_pagination(records, paginator, order_options) ⇒ Object
695 696 697 698 |
# File 'lib/jsonapi/resource.rb', line 695 def apply_pagination(records, paginator, ) records = paginator.apply(records, ) if paginator records end |
.apply_sort(records, order_options, _context = {}) ⇒ Object
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 |
# File 'lib/jsonapi/resource.rb', line 700 def apply_sort(records, , _context = {}) if .any? .each_pair do |field, direction| if field.to_s.include?(".") *model_names, column_name = field.split(".") associations = _lookup_association_chain([records.model.to_s, *model_names]) joins_query = _build_joins([records.model, *associations]) # _sorting is appended to avoid name clashes with manual joins eg. overridden filters order_by_query = "#{associations.last.name}_sorting.#{column_name} #{direction}" records = records.joins(joins_query).order(order_by_query) else records = records.order(field => direction) end end end records end |
.attribute(attribute_name, options = {}) ⇒ Object
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 |
# File 'lib/jsonapi/resource.rb', line 538 def attribute(attribute_name, = {}) attr = attribute_name.to_sym check_reserved_attribute_name(attr) if (attr == :id) && ([: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 [:format].nil? @_attributes ||= {} @_attributes[attr] = define_method attr do @model.public_send([:delegate] ? [: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}=") end |
.attribute_caching_context(context) ⇒ Object
1111 1112 1113 |
# File 'lib/jsonapi/resource.rb', line 1111 def attribute_caching_context(context) nil end |
.attributes(*attrs) ⇒ Object
Methods used in defining a resource class
531 532 533 534 535 536 |
# File 'lib/jsonapi/resource.rb', line 531 def attributes(*attrs) = attrs..dup attrs.each do |attr| attribute(attr, ) end end |
.belongs_to(*attrs) ⇒ Object
583 584 585 586 587 588 589 590 |
# File 'lib/jsonapi/resource.rb', line 583 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
642 643 644 |
# File 'lib/jsonapi/resource.rb', line 642 def cache_field(field) @_cache_field = field.to_sym end |
.caching(val = true) ⇒ Object
1099 1100 1101 |
# File 'lib/jsonapi/resource.rb', line 1099 def caching(val = true) @caching = val end |
.caching? ⇒ Boolean
1107 1108 1109 |
# File 'lib/jsonapi/resource.rb', line 1107 def caching? @caching && !JSONAPI.configuration.resource_cache.nil? end |
.construct_order_options(sort_params) ⇒ Object
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 |
# File 'lib/jsonapi/resource.rb', line 1147 def (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 |
.count_records(records) ⇒ Object
Assumes ActiveRecord’s counting. Override if you need a different counting method
814 815 816 |
# File 'lib/jsonapi/resource.rb', line 814 def count_records(records) records.count(:all) end |
.creatable_fields(_context = nil) ⇒ Object
Override in your resource to filter the creatable keys
652 653 654 |
# File 'lib/jsonapi/resource.rb', line 652 def creatable_fields(_context = nil) _updatable_relationships | _attributes.keys - [:id] end |
.create(context) ⇒ Object
514 515 516 |
# File 'lib/jsonapi/resource.rb', line 514 def create(context) new(create_model, context) end |
.create_model ⇒ Object
518 519 520 |
# File 'lib/jsonapi/resource.rb', line 518 def create_model _model_class.new end |
.default_attribute_options ⇒ Object
560 561 562 |
# File 'lib/jsonapi/resource.rb', line 560 def DEFAULT_ATTRIBUTE_OPTIONS end |
.default_sort ⇒ Object
1143 1144 1145 |
# File 'lib/jsonapi/resource.rb', line 1143 def default_sort [{field: 'id', direction: :asc}] end |
.exclude_link?(link) ⇒ Boolean
1082 1083 1084 |
# File 'lib/jsonapi/resource.rb', line 1082 def exclude_link?(link) _exclude_links.include?(link.to_sym) end |
.exclude_links(exclude) ⇒ Object
1074 1075 1076 |
# File 'lib/jsonapi/resource.rb', line 1074 def exclude_links(exclude) _resolve_exclude_links(exclude) end |
.fields ⇒ Object
661 662 663 |
# File 'lib/jsonapi/resource.rb', line 661 def fields _relationships.keys | _attributes.keys end |
.filter(attr, *args) ⇒ Object
634 635 636 |
# File 'lib/jsonapi/resource.rb', line 634 def filter(attr, *args) @_allowed_filters[attr.to_sym] = args. end |
.filter_records(filters, options, records = records(options)) ⇒ Object
803 804 805 806 807 |
# File 'lib/jsonapi/resource.rb', line 803 def filter_records(filters, , records = records()) records = apply_filters(records, filters, ) records = apply_includes(records, ) apply_included_resources_filters(records, ) end |
.filters(*attrs) ⇒ Object
630 631 632 |
# File 'lib/jsonapi/resource.rb', line 630 def filters(*attrs) @_allowed_filters.merge!(attrs.inject({}) { |h, attr| h[attr] = {}; h }) end |
.find(filters, options = {}) ⇒ Object
822 823 824 |
# File 'lib/jsonapi/resource.rb', line 822 def find(filters, = {}) resources_for(find_records(filters, ), [:context]) end |
.find_by_key(key, options = {}) ⇒ Object
854 855 856 857 858 859 860 |
# File 'lib/jsonapi/resource.rb', line 854 def find_by_key(key, = {}) context = [:context] records = find_records({_primary_key => key}, .except(:paginator, :sort_criteria)) model = records.first fail JSONAPI::Exceptions::RecordNotFound.new(key) if model.nil? self.resource_for_model(model).new(model, context) end |
.find_by_key_serialized_with_caching(key, serializer, options = {}) ⇒ Object
862 863 864 865 866 867 868 869 870 871 872 |
# File 'lib/jsonapi/resource.rb', line 862 def find_by_key_serialized_with_caching(key, serializer, = {}) if _model_class.respond_to?(:all) && _model_class.respond_to?(:arel_table) results = find_serialized_with_caching({_primary_key => key}, serializer, ) result = results.first fail JSONAPI::Exceptions::RecordNotFound.new(key) if result.nil? return result else resource = find_by_key(key, ) return cached_resources_for([resource], serializer, ).first end end |
.find_by_keys(keys, options = {}) ⇒ Object
833 834 835 836 837 838 839 840 841 |
# File 'lib/jsonapi/resource.rb', line 833 def find_by_keys(keys, = {}) context = [:context] records = records() records = apply_includes(records, ) models = records.where({_primary_key => keys}) models.collect do |model| self.resource_for_model(model).new(model, context) end end |
.find_count(filters, options = {}) ⇒ Object
818 819 820 |
# File 'lib/jsonapi/resource.rb', line 818 def find_count(filters, = {}) count_records(filter_records(filters, )) end |
.find_serialized_with_caching(filters_or_source, serializer, options = {}) ⇒ Object
843 844 845 846 847 848 849 850 851 852 |
# File 'lib/jsonapi/resource.rb', line 843 def find_serialized_with_caching(filters_or_source, serializer, = {}) if filters_or_source.is_a?(ActiveRecord::Relation) records = filters_or_source elsif _model_class.respond_to?(:all) && _model_class.respond_to?(:arel_table) records = find_records(filters_or_source, .except(:include_directives)) else records = find(filters_or_source, ) end cached_resources_for(records, serializer, ) end |
.has_many(*attrs) ⇒ Object
592 593 594 |
# File 'lib/jsonapi/resource.rb', line 592 def has_many(*attrs) _add_relationship(Relationship::ToMany, *attrs) end |
.has_one(*attrs) ⇒ Object
579 580 581 |
# File 'lib/jsonapi/resource.rb', line 579 def has_one(*attrs) _add_relationship(Relationship::ToOne, *attrs) end |
.immutable(val = true) ⇒ Object
1062 1063 1064 |
# File 'lib/jsonapi/resource.rb', line 1062 def immutable(val = true) @immutable = val end |
.inherited(subclass) ⇒ Object
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 |
# File 'lib/jsonapi/resource.rb', line 437 def inherited(subclass) subclass.abstract(false) subclass.immutable(false) subclass.caching(false) subclass.singleton(singleton?, (.dup || {})) subclass.exclude_links(_exclude_links) subclass._attributes = (_attributes || {}).dup subclass._model_hints = (_model_hints || {}).dup unless _model_name.empty? 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 type = subclass.name.demodulize.sub(/Resource$/, '').underscore subclass._type = type.pluralize.to_sym unless subclass._attributes[:id] subclass.attribute :id, format: :id end check_reserved_resource_name(subclass._type, subclass.name) subclass._routed = false subclass._warned_missing_route = false end |
.inject_method_definition(name, body) ⇒ Object
Allows JSONAPI::RelationshipBuilder to access metaprogramming hooks
1173 1174 1175 |
# File 'lib/jsonapi/resource.rb', line 1173 def inject_method_definition(name, body) define_method(name, body) end |
.is_filter_relationship?(filter) ⇒ Boolean
892 893 894 |
# File 'lib/jsonapi/resource.rb', line 892 def is_filter_relationship?(filter) filter == _type || _relationships.include?(filter) end |
.key_type(key_type) ⇒ Object
924 925 926 |
# File 'lib/jsonapi/resource.rb', line 924 def key_type(key_type) @_resource_key_type = key_type end |
.model_hint(model: _model_name, resource: _type) ⇒ Object
611 612 613 614 615 |
# File 'lib/jsonapi/resource.rb', line 611 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.
602 603 604 605 606 607 608 609 |
# File 'lib/jsonapi/resource.rb', line 602 def model_name(model, = {}) @model_class = nil @_model_name = model.to_sym model_hint(model: @_model_name, resource: self) unless [:add_model_hint] == false rebuild_relationships(_relationships) end |
.module_path ⇒ Object
1135 1136 1137 1138 1139 1140 1141 |
# File 'lib/jsonapi/resource.rb', line 1135 def module_path if name == 'JSONAPI::Resource' '' else name =~ MODULE_PATH_REGEXP ? ($`.freeze.gsub('::', '/') + '/').underscore : '' end end |
.mutable? ⇒ Boolean
1070 1071 1072 |
# File 'lib/jsonapi/resource.rb', line 1070 def mutable? !@immutable end |
.paginator(paginator) ⇒ Object
1050 1051 1052 |
# File 'lib/jsonapi/resource.rb', line 1050 def paginator(paginator) @_paginator = paginator end |
.primary_key(key) ⇒ Object
638 639 640 |
# File 'lib/jsonapi/resource.rb', line 638 def primary_key(key) @_primary_key = key.to_sym end |
.rebuild_relationships(relationships) ⇒ Object
468 469 470 471 472 473 474 475 476 477 478 479 480 |
# File 'lib/jsonapi/resource.rb', line 468 def rebuild_relationships(relationships) original_relationships = relationships.deep_dup @_relationships = {} if original_relationships.is_a?(Hash) original_relationships.each_value do |relationship| = relationship..dup [:parent_resource] = self _add_relationship(relationship.class, relationship.name, ) end end end |
.records(_options = {}) ⇒ Object
Override this method if you want to customize the relation for finder methods (find, find_by_key, find_serialized_with_caching)
876 877 878 |
# File 'lib/jsonapi/resource.rb', line 876 def records( = {}) _model_class.all end |
.register_relationship(name, relationship_object) ⇒ Object
1177 1178 1179 |
# File 'lib/jsonapi/resource.rb', line 1177 def register_relationship(name, relationship_object) @_relationships[name] = relationship_object end |
.relationship(*attrs) ⇒ Object
564 565 566 567 568 569 570 571 572 573 574 575 576 577 |
# File 'lib/jsonapi/resource.rb', line 564 def relationship(*attrs) = attrs. klass = case [: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, .except(:to)) end |
.resolve_relationship_names_to_relations(resource_klass, model_includes, options = {}) ⇒ Object
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 |
# File 'lib/jsonapi/resource.rb', line 665 def resolve_relationship_names_to_relations(resource_klass, model_includes, = {}) case model_includes when Array return model_includes.map do |value| resolve_relationship_names_to_relations(resource_klass, value, ) end when Hash model_includes.keys.each do |key| relationship = resource_klass._relationships[key] value = model_includes[key] model_includes.delete(key) model_includes[relationship.relation_name()] = resolve_relationship_names_to_relations(relationship.resource_klass, value, ) end return model_includes when Symbol relationship = resource_klass._relationships[model_includes] return relationship.relation_name() end end |
.resource_for(type) ⇒ Object
482 483 484 485 486 487 488 489 490 491 492 |
# File 'lib/jsonapi/resource.rb', line 482 def resource_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_for_model(model) ⇒ Object
494 495 496 |
# File 'lib/jsonapi/resource.rb', line 494 def resource_for_model(model) resource_for(resource_type_for(model)) end |
.resource_key_type ⇒ Object
928 929 930 |
# File 'lib/jsonapi/resource.rb', line 928 def resource_key_type @_resource_key_type ||= JSONAPI.configuration.resource_key_type end |
.resource_type_for(model) ⇒ Object
502 503 504 505 506 507 508 509 |
# File 'lib/jsonapi/resource.rb', line 502 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
826 827 828 829 830 831 |
# File 'lib/jsonapi/resource.rb', line 826 def resources_for(records, context) records.collect do |model| resource_class = self.resource_for_model(model) resource_class.new(model, context) end end |
.routing_options(options) ⇒ Object
522 523 524 |
# File 'lib/jsonapi/resource.rb', line 522 def () = end |
.routing_resource_options ⇒ Object
526 527 528 |
# File 'lib/jsonapi/resource.rb', line 526 def ||= {} end |
.singleton(*attrs) ⇒ Object
617 618 619 620 |
# File 'lib/jsonapi/resource.rb', line 617 def singleton(*attrs) @_singleton = (!!attrs[0] == attrs[0]) ? attrs[0] : true = attrs. end |
.singleton? ⇒ Boolean
626 627 628 |
# File 'lib/jsonapi/resource.rb', line 626 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`
935 936 937 938 939 940 941 942 943 944 945 946 947 948 |
# File 'lib/jsonapi/resource.rb', line 935 def singleton_key(context) if && [:singleton_key] strategy = [: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_records(records, order_options, context = {}) ⇒ Object
809 810 811 |
# File 'lib/jsonapi/resource.rb', line 809 def sort_records(records, , context = {}) apply_sort(records, , context) end |
.sortable_fields(_context = nil) ⇒ Object
Override in your resource to filter the sortable keys
657 658 659 |
# File 'lib/jsonapi/resource.rb', line 657 def sortable_fields(_context = nil) _attributes.keys end |
.updatable_fields(_context = nil) ⇒ Object
Override in your resource to filter the updatable keys
647 648 649 |
# File 'lib/jsonapi/resource.rb', line 647 def updatable_fields(_context = nil) _updatable_relationships | _attributes.keys - [:id] end |
.verify_custom_filter(filter, value, _context = nil) ⇒ Object
Either add a custom :verify labmda or override verify_custom_filter to allow for custom filters
986 987 988 |
# File 'lib/jsonapi/resource.rb', line 986 def verify_custom_filter(filter, value, _context = nil) [filter, value] end |
.verify_filter(filter, raw, context = nil) ⇒ Object
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 |
# File 'lib/jsonapi/resource.rb', line 896 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 if strategy.is_a?(Symbol) || strategy.is_a?(String) values = send(strategy, filter_values, context) else values = strategy.call(filter_values, context) end [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
880 881 882 883 884 885 886 887 888 889 890 |
# File 'lib/jsonapi/resource.rb', line 880 def verify_filters(filters, context = nil) verified_filters = {} return verified_filters if filters.nil? 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
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 |
# File 'lib/jsonapi/resource.rb', line 950 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
979 980 981 982 983 |
# File 'lib/jsonapi/resource.rb', line 979 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 labmda or override verify_relationship_filter to allow for custom relationship logic, such as uuids, multiple keys or permission checks on keys
992 993 994 |
# File 'lib/jsonapi/resource.rb', line 992 def verify_relationship_filter(filter, raw, _context = nil) [filter, raw] end |
Instance Method Details
#_model ⇒ Object
34 35 36 |
# File 'lib/jsonapi/resource.rb', line 34 def _model @model end |
#cache_id ⇒ Object
42 43 44 |
# File 'lib/jsonapi/resource.rb', line 42 def cache_id [id, _model.public_send(self.class._cache_field)] end |
#change(callback) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/jsonapi/resource.rb', line 50 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 |
#create_to_many_links(relationship_type, relationship_key_values, options = {}) ⇒ Object
77 78 79 80 81 |
# File 'lib/jsonapi/resource.rb', line 77 def create_to_many_links(relationship_type, relationship_key_values, = {}) change :create_to_many_link do _create_to_many_links(relationship_type, relationship_key_values, ) end end |
#custom_links(_options) ⇒ Object
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
171 172 173 |
# File 'lib/jsonapi/resource.rb', line 171 def custom_links() {} end |
#fetchable_fields ⇒ Object
Override this on a resource instance to override the fetchable keys
120 121 122 |
# File 'lib/jsonapi/resource.rb', line 120 def fetchable_fields self.class.fields end |
#id ⇒ Object
38 39 40 |
# File 'lib/jsonapi/resource.rb', line 38 def id _model.public_send(self.class._primary_key) end |
#is_new? ⇒ Boolean
46 47 48 |
# File 'lib/jsonapi/resource.rb', line 46 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
162 163 164 |
# File 'lib/jsonapi/resource.rb', line 162 def () {} end |
#model_error_messages ⇒ Object
130 131 132 |
# File 'lib/jsonapi/resource.rb', line 130 def _model.errors. end |
#preloaded_fragments ⇒ Object
175 176 177 178 |
# File 'lib/jsonapi/resource.rb', line 175 def preloaded_fragments # A hash of hashes @preloaded_fragments ||= Hash.new end |
#records_for(relation_name) ⇒ Object
Override this on a resource to customize how the associated records are fetched for a model. Particularly helpful for authorization.
126 127 128 |
# File 'lib/jsonapi/resource.rb', line 126 def records_for(relation_name) _model.public_send relation_name end |
#remove ⇒ Object
71 72 73 74 75 |
# File 'lib/jsonapi/resource.rb', line 71 def remove run_callbacks :remove do _remove end end |
#remove_to_many_link(relationship_type, key, options = {}) ⇒ Object
101 102 103 104 105 |
# File 'lib/jsonapi/resource.rb', line 101 def remove_to_many_link(relationship_type, key, = {}) change :remove_to_many_link do _remove_to_many_link(relationship_type, key, ) end end |
#remove_to_one_link(relationship_type, options = {}) ⇒ Object
107 108 109 110 111 |
# File 'lib/jsonapi/resource.rb', line 107 def remove_to_one_link(relationship_type, = {}) change :remove_to_one_link do _remove_to_one_link(relationship_type, ) end end |
#replace_fields(field_data) ⇒ Object
113 114 115 116 117 |
# File 'lib/jsonapi/resource.rb', line 113 def replace_fields(field_data) change :replace_fields do _replace_fields(field_data) end end |
#replace_polymorphic_to_one_link(relationship_type, relationship_key_value, relationship_key_type, options = {}) ⇒ Object
95 96 97 98 99 |
# File 'lib/jsonapi/resource.rb', line 95 def replace_polymorphic_to_one_link(relationship_type, relationship_key_value, relationship_key_type, = {}) change :replace_polymorphic_to_one_link do _replace_polymorphic_to_one_link(relationship_type, relationship_key_value, relationship_key_type, ) end end |
#replace_to_many_links(relationship_type, relationship_key_values, options = {}) ⇒ Object
83 84 85 86 87 |
# File 'lib/jsonapi/resource.rb', line 83 def replace_to_many_links(relationship_type, relationship_key_values, = {}) change :replace_to_many_links do _replace_to_many_links(relationship_type, relationship_key_values, ) end end |
#replace_to_one_link(relationship_type, relationship_key_value, options = {}) ⇒ Object
89 90 91 92 93 |
# File 'lib/jsonapi/resource.rb', line 89 def replace_to_one_link(relationship_type, relationship_key_value, = {}) change :replace_to_one_link do _replace_to_one_link(relationship_type, relationship_key_value, ) end end |
#validation_error_metadata ⇒ Object
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.
153 154 155 |
# File 'lib/jsonapi/resource.rb', line 153 def {} end |