Class: JSONAPI::Resource
- Inherits:
-
Object
- Object
- JSONAPI::Resource
- Includes:
- Callbacks
- Defined in:
- lib/jsonapi/resource.rb
Constant Summary collapse
- @@resource_types =
{}
Class Attribute Summary collapse
-
._allowed_filters ⇒ Object
Returns the value of attribute _allowed_filters.
-
._associations ⇒ Object
Returns the value of attribute _associations.
-
._attributes ⇒ Object
Returns the value of attribute _attributes.
-
._paginator ⇒ Object
Returns the value of attribute _paginator.
-
._type ⇒ Object
Returns the value of attribute _type.
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
Class Method Summary collapse
- ._allowed_filter?(filter) ⇒ Boolean
- ._as_parent_key ⇒ Object
- ._association(type) ⇒ Object
-
._attribute_options(attr) ⇒ Object
quasi private class methods.
- ._has_association?(type) ⇒ Boolean
- ._model_class ⇒ Object
- ._model_name ⇒ Object
- ._primary_key ⇒ Object
- ._resource_name_from_type(type) ⇒ Object
- ._updatable_associations ⇒ Object
- .apply_filter(records, filter, value, _options = {}) ⇒ Object
- .apply_filters(records, filters, options = {}) ⇒ Object
- .apply_includes(records, options = {}) ⇒ Object
- .apply_pagination(records, paginator, order_options) ⇒ Object
- .apply_sort(records, order_options) ⇒ Object
- .attribute(attr, options = {}) ⇒ Object
-
.attributes(*attrs) ⇒ Object
Methods used in defining a resource class.
- .construct_order_options(sort_params) ⇒ Object
-
.creatable_fields(_context = nil) ⇒ Object
Override in your resource to filter the creatable keys.
- .create(context) ⇒ Object
- .create_model ⇒ Object
- .default_attribute_options ⇒ Object
- .fields ⇒ Object
- .filter(attr, *args) ⇒ Object
- .filter_records(filters, options) ⇒ Object
- .filters(*attrs) ⇒ Object
-
.find(filters, options = {}) ⇒ Object
Override this method if you have more complex requirements than this basic find method provides.
- .find_by_key(key, options = {}) ⇒ Object
- .find_count(filters, options = {}) ⇒ Object
- .has_many(*attrs) ⇒ Object
- .has_one(*attrs) ⇒ Object
- .inherited(base) ⇒ Object
- .is_filter_association?(filter) ⇒ Boolean
-
.method_missing(method, *args) ⇒ Object
TODO: remove this after the createable_fields and updateable_fields are phased out :nocov:.
- .model_name(model) ⇒ Object
- .module_path ⇒ Object
- .paginator(paginator) ⇒ Object
- .primary_key(key) ⇒ Object
-
.records(_options = {}) ⇒ Object
Override this method if you want to customize the relation for finder methods (find, find_by_key).
- .resolve_association_names_to_relations(resource_klass, model_includes, options = {}) ⇒ Object
- .resource_for(type) ⇒ Object
- .routing_options(options) ⇒ Object
- .routing_resource_options ⇒ Object
- .sort_records(records, order_options) ⇒ 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_association_filter(filter, raw, _context = nil) ⇒ Object
override to allow for custom association logic, such as uuids, multiple keys or permission checks on keys.
-
.verify_custom_filter(filter, value, _context = nil) ⇒ Object
override to allow for custom filters.
- .verify_filter(filter, raw, context = nil) ⇒ Object
- .verify_filters(filters, context = nil) ⇒ Object
-
.verify_key(key, _context = nil) ⇒ Object
override to allow for key processing and checking.
-
.verify_keys(keys, context = nil) ⇒ Object
override to allow for key processing and checking.
Instance Method Summary collapse
- #change(callback) ⇒ Object
- #create_has_many_links(association_type, association_key_values) ⇒ Object
-
#fetchable_fields ⇒ Object
Override this on a resource instance to override the fetchable keys.
- #id ⇒ Object
-
#initialize(model, context = nil) ⇒ Resource
constructor
A new instance of Resource.
- #is_new? ⇒ Boolean
-
#records_for(association_name, _options = {}) ⇒ Object
Override this on a resource to customize how the associated records are fetched for a model.
- #remove ⇒ Object
- #remove_has_many_link(association_type, key) ⇒ Object
- #remove_has_one_link(association_type) ⇒ Object
- #replace_fields(field_data) ⇒ Object
- #replace_has_many_links(association_type, association_key_values) ⇒ Object
- #replace_has_one_link(association_type, association_key_value) ⇒ Object
- #replace_polymorphic_has_one_link(association_type, association_key_value, association_key_type) ⇒ Object
Methods included from Callbacks
Constructor Details
#initialize(model, context = nil) ⇒ Resource
25 26 27 28 |
# File 'lib/jsonapi/resource.rb', line 25 def initialize(model, context = nil) @model = model @context = context end |
Class Attribute Details
._allowed_filters ⇒ Object
Returns the value of attribute _allowed_filters.
281 282 283 |
# File 'lib/jsonapi/resource.rb', line 281 def _allowed_filters @_allowed_filters end |
._associations ⇒ Object
Returns the value of attribute _associations.
281 282 283 |
# File 'lib/jsonapi/resource.rb', line 281 def _associations @_associations end |
._attributes ⇒ Object
Returns the value of attribute _attributes.
281 282 283 |
# File 'lib/jsonapi/resource.rb', line 281 def _attributes @_attributes end |
._paginator ⇒ Object
Returns the value of attribute _paginator.
281 282 283 |
# File 'lib/jsonapi/resource.rb', line 281 def _paginator @_paginator end |
._type ⇒ Object
Returns the value of attribute _type.
281 282 283 |
# File 'lib/jsonapi/resource.rb', line 281 def _type @_type end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
9 10 11 |
# File 'lib/jsonapi/resource.rb', line 9 def context @context end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
10 11 12 |
# File 'lib/jsonapi/resource.rb', line 10 def model @model end |
Class Method Details
._allowed_filter?(filter) ⇒ Boolean
612 613 614 |
# File 'lib/jsonapi/resource.rb', line 612 def _allowed_filter?(filter) !_allowed_filters[filter].nil? end |
._as_parent_key ⇒ Object
583 584 585 |
# File 'lib/jsonapi/resource.rb', line 583 def _as_parent_key @_as_parent_key ||= "#{_type.to_s.singularize}_#{_primary_key}" end |
._association(type) ⇒ Object
570 571 572 573 |
# File 'lib/jsonapi/resource.rb', line 570 def _association(type) type = type.to_sym @_associations[type] end |
._attribute_options(attr) ⇒ Object
quasi private class methods
557 558 559 |
# File 'lib/jsonapi/resource.rb', line 557 def (attr) .merge(@_attributes[attr]) end |
._has_association?(type) ⇒ Boolean
565 566 567 568 |
# File 'lib/jsonapi/resource.rb', line 565 def _has_association?(type) type = type.to_s @_associations.key?(type.singularize.to_sym) || @_associations.key?(type.pluralize.to_sym) end |
._model_class ⇒ Object
608 609 610 |
# File 'lib/jsonapi/resource.rb', line 608 def _model_class @model ||= _model_name.to_s.safe_constantize end |
._model_name ⇒ Object
575 576 577 |
# File 'lib/jsonapi/resource.rb', line 575 def _model_name @_model_name ||= name.demodulize.sub(/Resource$/, '') end |
._primary_key ⇒ Object
579 580 581 |
# File 'lib/jsonapi/resource.rb', line 579 def _primary_key @_primary_key ||= :id end |
._resource_name_from_type(type) ⇒ Object
591 592 593 594 595 596 597 598 |
# File 'lib/jsonapi/resource.rb', line 591 def _resource_name_from_type(type) class_name = @@resource_types[type] if class_name.nil? class_name = "#{type.to_s.underscore.singularize}_resource".camelize @@resource_types[type] = class_name end return class_name end |
._updatable_associations ⇒ Object
561 562 563 |
# File 'lib/jsonapi/resource.rb', line 561 def _updatable_associations @_associations.map { |key, _association| key } end |
.apply_filter(records, filter, value, _options = {}) ⇒ Object
430 431 432 |
# File 'lib/jsonapi/resource.rb', line 430 def apply_filter(records, filter, value, = {}) records.where(filter => value) end |
.apply_filters(records, filters, options = {}) ⇒ Object
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'lib/jsonapi/resource.rb', line 434 def apply_filters(records, filters, = {}) required_includes = [] if filters filters.each do |filter, value| if _associations.include?(filter) if _associations[filter].is_a?(JSONAPI::Association::HasMany) required_includes.push(filter.to_s) records = apply_filter(records, "#{filter}.#{_associations[filter].primary_key}", value, ) else records = apply_filter(records, "#{_associations[filter].foreign_key}", value, ) end else records = apply_filter(records, filter, value, ) end end end if required_includes.any? records = apply_includes(records, .merge(include_directives: IncludeDirectives.new(required_includes))) end records end |
.apply_includes(records, options = {}) ⇒ Object
407 408 409 410 411 412 413 414 415 |
# File 'lib/jsonapi/resource.rb', line 407 def apply_includes(records, = {}) include_directives = [:include_directives] if include_directives model_includes = resolve_association_names_to_relations(self, include_directives.model_includes, ) records = records.includes(model_includes) end records end |
.apply_pagination(records, paginator, order_options) ⇒ Object
417 418 419 420 |
# File 'lib/jsonapi/resource.rb', line 417 def apply_pagination(records, paginator, ) records = paginator.apply(records, ) if paginator records end |
.apply_sort(records, order_options) ⇒ Object
422 423 424 425 426 427 428 |
# File 'lib/jsonapi/resource.rb', line 422 def apply_sort(records, ) if .any? records.order() else records end end |
.attribute(attr, options = {}) ⇒ Object
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/jsonapi/resource.rb', line 307 def attribute(attr, = {}) check_reserved_attribute_name(attr) if (attr.to_sym == :id) && ([:format].nil?) ActiveSupport::Deprecation.warn('Id without format is no longer supported. Please remove ids from attributes, or specify a format.') end @_attributes ||= {} @_attributes[attr] = define_method attr do @model.send(attr) end unless method_defined?(attr) define_method "#{attr}=" do |value| @model.send "#{attr}=", value end unless method_defined?("#{attr}=") end |
.attributes(*attrs) ⇒ Object
Methods used in defining a resource class
300 301 302 303 304 305 |
# File 'lib/jsonapi/resource.rb', line 300 def attributes(*attrs) = attrs..dup attrs.each do |attr| attribute(attr, ) end end |
.construct_order_options(sort_params) ⇒ Object
620 621 622 623 624 625 626 627 |
# File 'lib/jsonapi/resource.rb', line 620 def (sort_params) return {} unless sort_params sort_params.each_with_object({}) do |sort, order_hash| field = sort[:field] == 'id' ? _primary_key : sort[:field] order_hash[field] = sort[:direction] end end |
.creatable_fields(_context = nil) ⇒ Object
Override in your resource to filter the creatable keys
374 375 376 |
# File 'lib/jsonapi/resource.rb', line 374 def creatable_fields(_context = nil) _updatable_associations | _attributes.keys end |
.create(context) ⇒ Object
283 284 285 |
# File 'lib/jsonapi/resource.rb', line 283 def create(context) new(create_model, context) end |
.create_model ⇒ Object
287 288 289 |
# File 'lib/jsonapi/resource.rb', line 287 def create_model _model_class.new end |
.default_attribute_options ⇒ Object
325 326 327 |
# File 'lib/jsonapi/resource.rb', line 325 def { format: :default } end |
.fields ⇒ Object
383 384 385 |
# File 'lib/jsonapi/resource.rb', line 383 def fields _associations.keys | _attributes.keys end |
.filter(attr, *args) ⇒ Object
345 346 347 |
# File 'lib/jsonapi/resource.rb', line 345 def filter(attr, *args) @_allowed_filters[attr.to_sym] = args. end |
.filter_records(filters, options) ⇒ Object
459 460 461 462 463 |
# File 'lib/jsonapi/resource.rb', line 459 def filter_records(filters, ) records = records() records = apply_filters(records, filters, ) apply_includes(records, ) end |
.filters(*attrs) ⇒ Object
341 342 343 |
# File 'lib/jsonapi/resource.rb', line 341 def filters(*attrs) @_allowed_filters.merge!(attrs.inject({}) { |h, attr| h[attr] = {}; h }) end |
.find(filters, options = {}) ⇒ Object
Override this method if you have more complex requirements than this basic find method provides
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/jsonapi/resource.rb', line 474 def find(filters, = {}) context = [:context] records = filter_records(filters, ) sort_criteria = .fetch(:sort_criteria) { [] } = (sort_criteria) records = sort_records(records, ) records = apply_pagination(records, [:paginator], ) resources = [] records.each do |model| resources.push new(model, context) end resources end |
.find_by_key(key, options = {}) ⇒ Object
493 494 495 496 497 498 499 500 |
# File 'lib/jsonapi/resource.rb', line 493 def find_by_key(key, = {}) context = [:context] records = records() records = apply_includes(records, ) model = records.where({_primary_key => key}).first fail JSONAPI::Exceptions::RecordNotFound.new(key) if model.nil? new(model, context) end |
.find_count(filters, options = {}) ⇒ Object
469 470 471 |
# File 'lib/jsonapi/resource.rb', line 469 def find_count(filters, = {}) filter_records(filters, ).count end |
.has_many(*attrs) ⇒ Object
333 334 335 |
# File 'lib/jsonapi/resource.rb', line 333 def has_many(*attrs) _associate(Association::HasMany, *attrs) end |
.has_one(*attrs) ⇒ Object
329 330 331 |
# File 'lib/jsonapi/resource.rb', line 329 def has_one(*attrs) _associate(Association::HasOne, *attrs) end |
.inherited(base) ⇒ Object
259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/jsonapi/resource.rb', line 259 def inherited(base) base._attributes = (_attributes || {}).dup base._associations = (_associations || {}).dup base._allowed_filters = (_allowed_filters || Set.new).dup type = base.name.demodulize.sub(/Resource$/, '').underscore base._type = type.pluralize.to_sym base.attribute :id, format: :id check_reserved_resource_name(base._type, base.name) end |
.is_filter_association?(filter) ⇒ Boolean
517 518 519 |
# File 'lib/jsonapi/resource.rb', line 517 def is_filter_association?(filter) filter == _type || _associations.include?(filter) end |
.method_missing(method, *args) ⇒ Object
TODO: remove this after the createable_fields and updateable_fields are phased out :nocov:
355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/jsonapi/resource.rb', line 355 def method_missing(method, *args) if method.to_s.match /createable_fields/ ActiveSupport::Deprecation.warn('`createable_fields` is deprecated, please use `creatable_fields` instead') creatable_fields(*args) elsif method.to_s.match /updateable_fields/ ActiveSupport::Deprecation.warn('`updateable_fields` is deprecated, please use `updatable_fields` instead') updatable_fields(*args) else super end end |
.model_name(model) ⇒ Object
337 338 339 |
# File 'lib/jsonapi/resource.rb', line 337 def model_name(model) @_model_name = model.to_sym end |
.module_path ⇒ Object
616 617 618 |
# File 'lib/jsonapi/resource.rb', line 616 def module_path @module_path ||= name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').downcase : '' end |
.paginator(paginator) ⇒ Object
604 605 606 |
# File 'lib/jsonapi/resource.rb', line 604 def paginator(paginator) @_paginator = paginator end |
.primary_key(key) ⇒ Object
349 350 351 |
# File 'lib/jsonapi/resource.rb', line 349 def primary_key(key) @_primary_key = key.to_sym end |
.records(_options = {}) ⇒ Object
Override this method if you want to customize the relation for finder methods (find, find_by_key)
504 505 506 |
# File 'lib/jsonapi/resource.rb', line 504 def records( = {}) _model_class end |
.resolve_association_names_to_relations(resource_klass, model_includes, options = {}) ⇒ Object
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'lib/jsonapi/resource.rb', line 387 def resolve_association_names_to_relations(resource_klass, model_includes, = {}) case model_includes when Array return model_includes.map do |value| resolve_association_names_to_relations(resource_klass, value, ) end when Hash model_includes.keys.each do |key| association = resource_klass._associations[key] value = model_includes[key] model_includes.delete(key) model_includes[association.relation_name()] = resolve_association_names_to_relations(association.resource_klass, value, ) end return model_includes when Symbol association = resource_klass._associations[model_includes] return association.relation_name() end end |
.resource_for(type) ⇒ Object
272 273 274 275 276 277 278 279 |
# File 'lib/jsonapi/resource.rb', line 272 def resource_for(type) resource_name = JSONAPI::Resource._resource_name_from_type(type) 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 |
.routing_options(options) ⇒ Object
291 292 293 |
# File 'lib/jsonapi/resource.rb', line 291 def () @_routing_resource_options = end |
.routing_resource_options ⇒ Object
295 296 297 |
# File 'lib/jsonapi/resource.rb', line 295 def @_routing_resource_options ||= {} end |
.sort_records(records, order_options) ⇒ Object
465 466 467 |
# File 'lib/jsonapi/resource.rb', line 465 def sort_records(records, ) apply_sort(records, ) end |
.sortable_fields(_context = nil) ⇒ Object
Override in your resource to filter the sortable keys
379 380 381 |
# File 'lib/jsonapi/resource.rb', line 379 def sortable_fields(_context = nil) _attributes.keys end |
.updatable_fields(_context = nil) ⇒ Object
Override in your resource to filter the updatable keys
369 370 371 |
# File 'lib/jsonapi/resource.rb', line 369 def updatable_fields(_context = nil) _updatable_associations | _attributes.keys - [:id] end |
.verify_association_filter(filter, raw, _context = nil) ⇒ Object
override to allow for custom association logic, such as uuids, multiple keys or permission checks on keys
552 553 554 |
# File 'lib/jsonapi/resource.rb', line 552 def verify_association_filter(filter, raw, _context = nil) [filter, raw] end |
.verify_custom_filter(filter, value, _context = nil) ⇒ Object
override to allow for custom filters
547 548 549 |
# File 'lib/jsonapi/resource.rb', line 547 def verify_custom_filter(filter, value, _context = nil) [filter, value] end |
.verify_filter(filter, raw, context = nil) ⇒ Object
521 522 523 524 525 526 527 528 529 530 |
# File 'lib/jsonapi/resource.rb', line 521 def verify_filter(filter, raw, context = nil) filter_values = [] filter_values += CSV.parse_line(raw) unless raw.nil? || raw.empty? if is_filter_association?(filter) verify_association_filter(filter, filter_values, context) else verify_custom_filter(filter, filter_values, context) end end |
.verify_filters(filters, context = nil) ⇒ Object
508 509 510 511 512 513 514 515 |
# File 'lib/jsonapi/resource.rb', line 508 def verify_filters(filters, context = nil) verified_filters = {} filters.each do |filter, raw_value| verified_filter = verify_filter(filter, raw_value, context) verified_filters[verified_filter[0]] = verified_filter[1] end verified_filters end |
.verify_key(key, _context = nil) ⇒ Object
override to allow for key processing and checking
533 534 535 536 537 |
# File 'lib/jsonapi/resource.rb', line 533 def verify_key(key, _context = nil) key && Integer(key) rescue raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key) end |
.verify_keys(keys, context = nil) ⇒ Object
override to allow for key processing and checking
540 541 542 543 544 |
# File 'lib/jsonapi/resource.rb', line 540 def verify_keys(keys, context = nil) return keys.collect do |key| verify_key(key, context) end end |
Instance Method Details
#change(callback) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/jsonapi/resource.rb', line 38 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_has_many_links(association_type, association_key_values) ⇒ Object
65 66 67 68 69 |
# File 'lib/jsonapi/resource.rb', line 65 def create_has_many_links(association_type, association_key_values) change :create_has_many_link do _create_has_many_links(association_type, association_key_values) end end |
#fetchable_fields ⇒ Object
Override this on a resource instance to override the fetchable keys
108 109 110 |
# File 'lib/jsonapi/resource.rb', line 108 def fetchable_fields self.class.fields end |
#id ⇒ Object
30 31 32 |
# File 'lib/jsonapi/resource.rb', line 30 def id model.send(self.class._primary_key) end |
#is_new? ⇒ Boolean
34 35 36 |
# File 'lib/jsonapi/resource.rb', line 34 def is_new? id.nil? end |
#records_for(association_name, _options = {}) ⇒ Object
Override this on a resource to customize how the associated records are fetched for a model. Particularly helpful for authorization.
114 115 116 |
# File 'lib/jsonapi/resource.rb', line 114 def records_for(association_name, = {}) model.send association_name end |
#remove ⇒ Object
59 60 61 62 63 |
# File 'lib/jsonapi/resource.rb', line 59 def remove run_callbacks :remove do _remove end end |
#remove_has_many_link(association_type, key) ⇒ Object
89 90 91 92 93 |
# File 'lib/jsonapi/resource.rb', line 89 def remove_has_many_link(association_type, key) change :remove_has_many_link do _remove_has_many_link(association_type, key) end end |
#remove_has_one_link(association_type) ⇒ Object
95 96 97 98 99 |
# File 'lib/jsonapi/resource.rb', line 95 def remove_has_one_link(association_type) change :remove_has_one_link do _remove_has_one_link(association_type) end end |
#replace_fields(field_data) ⇒ Object
101 102 103 104 105 |
# File 'lib/jsonapi/resource.rb', line 101 def replace_fields(field_data) change :replace_fields do _replace_fields(field_data) end end |
#replace_has_many_links(association_type, association_key_values) ⇒ Object
71 72 73 74 75 |
# File 'lib/jsonapi/resource.rb', line 71 def replace_has_many_links(association_type, association_key_values) change :replace_has_many_links do _replace_has_many_links(association_type, association_key_values) end end |
#replace_has_one_link(association_type, association_key_value) ⇒ Object
77 78 79 80 81 |
# File 'lib/jsonapi/resource.rb', line 77 def replace_has_one_link(association_type, association_key_value) change :replace_has_one_link do _replace_has_one_link(association_type, association_key_value) end end |
#replace_polymorphic_has_one_link(association_type, association_key_value, association_key_type) ⇒ Object
83 84 85 86 87 |
# File 'lib/jsonapi/resource.rb', line 83 def replace_polymorphic_has_one_link(association_type, association_key_value, association_key_type) change :replace_polymorphic_has_one_link do _replace_polymorphic_has_one_link(association_type, association_key_value, association_key_type) end end |