From 7bb395904181798c25630bf84ca3a3ccbdddc44a Mon Sep 17 00:00:00 2001 From: Denis Talakevich Date: Tue, 15 Jan 2019 13:57:33 +0200 Subject: [PATCH] allow custom type for models fix #288 --- README.md | 16 +++++++ lib/json_api_client/resource.rb | 7 +++ lib/json_api_client/utils.rb | 1 + test/test_helper.rb | 15 ++++++ test/unit/association_test.rb | 85 +++++++++++++++++++++++++++++++++ test/unit/creation_test.rb | 27 +++++++++++ test/unit/finding_test.rb | 22 +++++++++ 7 files changed, 173 insertions(+) diff --git a/README.md b/README.md index 4c4c9b22..d1425d5f 100644 --- a/README.md +++ b/README.md @@ -553,6 +553,22 @@ class MyApi::Base < JsonApiClient::Resource end ``` +### Custom type + +If your model must be named differently from classified type of resource you can easily customize it. +It will work both for defined and not defined relationships + +```ruby +class MyApi::Base < JsonApiClient::Resource + resolve_custom_type 'document--files', 'File' +end + +class MyApi::File < MyApi::Base + def self.resource_name + 'document--files' + end +end +``` ### Type Casting diff --git a/lib/json_api_client/resource.rb b/lib/json_api_client/resource.rb index 0c44f9d4..69ccf48b 100644 --- a/lib/json_api_client/resource.rb +++ b/lib/json_api_client/resource.rb @@ -34,6 +34,7 @@ class Resource :route_format, :request_params_class, :keep_request_params, + :custom_type_to_class, instance_accessor: false class_attribute :add_defaults_to_changes, instance_writer: false @@ -50,6 +51,7 @@ class Resource self.request_params_class = RequestParams self.keep_request_params = false self.add_defaults_to_changes = false + self.custom_type_to_class = {} #:underscored_key, :camelized_key, :dasherized_key, or custom self.json_key_format = :underscored_key @@ -61,6 +63,11 @@ class << self extend Forwardable def_delegators :_new_scope, :where, :order, :includes, :select, :all, :paginate, :page, :with_params, :first, :find, :last + def resolve_custom_type(type_name, class_name) + classified_type = key_formatter.unformat(type_name.to_s).singularize.classify + self.custom_type_to_class = custom_type_to_class.merge(classified_type => class_name.to_s) + end + # The table name for this resource. i.e. Article -> articles, Person -> people # # @return [String] The table name for this resource diff --git a/lib/json_api_client/utils.rb b/lib/json_api_client/utils.rb index 73fd2dd8..e91e566b 100644 --- a/lib/json_api_client/utils.rb +++ b/lib/json_api_client/utils.rb @@ -2,6 +2,7 @@ module JsonApiClient module Utils def self.compute_type(klass, type_name) + return klass.custom_type_to_class.fetch(type_name).constantize if klass.custom_type_to_class.key?(type_name) # If the type is prefixed with a scope operator then we assume that # the type_name is an absolute reference. return type_name.constantize if type_name.match(/^::/) diff --git a/test/test_helper.rb b/test/test_helper.rb index b580ca38..35ae039b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -35,6 +35,21 @@ class UserPreference < TestResource self.primary_key = :user_id end +class DocumentUser < TestResource + resolve_custom_type 'document--files', 'DocumentFile' +end + +class DocumentStore < TestResource + resolve_custom_type 'document--files', 'DocumentFile' + has_many :files, class_name: 'DocumentFile' +end + +class DocumentFile < TestResource + def self.resource_name + 'document--files' + end +end + def with_altered_config(resource_class, changes) # remember and overwrite config old_config_values = {} diff --git a/test/unit/association_test.rb b/test/unit/association_test.rb index 6031f226..bbdc7d36 100644 --- a/test/unit/association_test.rb +++ b/test/unit/association_test.rb @@ -765,4 +765,89 @@ def test_nested_create_from_scope Specified.where(foo_id: 1).create end + def test_get_with_relationship_for_model_with_custom_type + stub_request(:get, "http://example.com/document_users/1?include=file") + .to_return(headers: {content_type: "application/vnd.api+json"}, body: { + data: [ + { + id: '1', + type: 'document_users', + attributes: { + name: 'John Doe' + }, + relationships: { + file: { + links: { + self: 'http://example.com/document_users/1/relationships/file', + related: 'http://example.com/document_users/1/file' + }, + data: { + id: '2', + type: 'document--files' + } + } + } + } + ], + included: [ + { + id: '2', + type: 'document--files', + attributes: { + url: 'http://example.com/downloads/2.pdf' + } + } + ] + }.to_json) + + user = DocumentUser.includes('file').find(1).first + + assert_equal 'document--files', user.file.type + assert user.file.is_a?(DocumentFile) + end + + def test_get_with_defined_relationship_for_model_with_custom_type + stub_request(:get, "http://example.com/document_stores/1?include=files") + .to_return(headers: {content_type: "application/vnd.api+json"}, body: { + data: [ + { + id: '1', + type: 'document_stores', + attributes: { + name: 'store #1' + }, + relationships: { + files: { + links: { + self: 'http://example.com/document_stores/1/relationships/files', + related: 'http://example.com/document_stores/1/files' + }, + data: [ + { + id: '2', + type: 'document--files' + } + ] + } + } + } + ], + included: [ + { + id: '2', + type: 'document--files', + attributes: { + url: 'http://example.com/downloads/2.pdf' + } + } + ] + }.to_json) + + user = DocumentStore.includes('files').find(1).first + + assert_equal 1, user.files.size + assert_equal 'document--files', user.files.first.type + assert user.files.first.is_a?(DocumentFile) + end + end diff --git a/test/unit/creation_test.rb b/test/unit/creation_test.rb index 9932469f..a793a926 100644 --- a/test/unit/creation_test.rb +++ b/test/unit/creation_test.rb @@ -314,4 +314,31 @@ def test_create_with_relationships_in_payload assert_equal "1", article.id end + def test_create_with_custom_type + stub_request(:post, 'http://example.com/document--files') + .with(headers: {content_type: 'application/vnd.api+json', accept: 'application/vnd.api+json'}, body: { + data: { + type: 'document--files', + attributes: { + url: 'http://example.com/downloads/1.pdf' + } + } + }.to_json) + .to_return(headers: {content_type: 'application/vnd.api+json'}, body: { + data: { + type: 'document--files', + id: '1', + attributes: { + url: 'http://example.com/downloads/1.pdf' + } + } + }.to_json) + + file = DocumentFile.new(url: 'http://example.com/downloads/1.pdf') + + assert file.save + assert file.persisted? + assert_equal '1', file.id + end + end diff --git a/test/unit/finding_test.rb b/test/unit/finding_test.rb index e1e20c97..34675aab 100644 --- a/test/unit/finding_test.rb +++ b/test/unit/finding_test.rb @@ -77,4 +77,26 @@ def test_find_all assert_equal ["2", "3"], articles.map(&:id) end + def test_find_by_id_with_custom_type + stub_request(:get, "http://example.com/document--files/1") + .to_return(headers: {content_type: "application/vnd.api+json"}, body: { + data: { + type: "document--files", + id: "1", + attributes: { + url: 'http://example.com/downloads/1.pdf' + } + } + }.to_json) + + articles = DocumentFile.find(1) + + assert articles.is_a?(JsonApiClient::ResultSet) + assert_equal 1, articles.length + + article = articles.first + assert_equal '1', article.id + assert_equal 'http://example.com/downloads/1.pdf', article.url + end + end