diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index 0850f7417..17e19b501 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -27,6 +27,7 @@ def get_serializer(resource, options = {}) end ActiveModel::SerializableResource.serialize(resource, options) do |serializable_resource| if serializable_resource.serializer? + serializable_resource.url_helper = self serializable_resource.serialization_scope ||= serialization_scope serializable_resource.serialization_scope_name = _serialization_scope begin diff --git a/lib/active_model/serializable_resource.rb b/lib/active_model/serializable_resource.rb index fa3fbe035..422d207eb 100644 --- a/lib/active_model/serializable_resource.rb +++ b/lib/active_model/serializable_resource.rb @@ -30,6 +30,10 @@ def serialization_scope=(scope) serializer_opts[:scope] = scope end + def url_helper=(url_helper) + adapter_opts[:url_helper] = url_helper + end + def serialization_scope serializer_opts[:scope] end diff --git a/lib/active_model/serializer/adapter/json_api.rb b/lib/active_model/serializer/adapter/json_api.rb index 1b55a8121..4053ac69b 100644 --- a/lib/active_model/serializer/adapter/json_api.rb +++ b/lib/active_model/serializer/adapter/json_api.rb @@ -8,7 +8,7 @@ class JsonApi < Adapter def initialize(serializer, options = {}) super @hash = { data: [] } - + @url_helper = options[:url_helper] if fields = options.delete(:fields) @fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key) else @@ -16,6 +16,8 @@ def initialize(serializer, options = {}) end end + attr_reader :url_helper + def serializable_hash(options = nil) options ||= {} if serializer.respond_to?(:each) @@ -44,10 +46,17 @@ def fragment_cache(cached_hash, non_cached_hash) private - def add_relationships(resource, name, serializers) + def add_relationships(resource, name, serializers, opts) resource[:relationships] ||= {} - resource[:relationships][name] ||= { data: [] } - resource[:relationships][name][:data] += serializers.map { |serializer| { type: serializer.json_api_type, id: serializer.id.to_s } } + + if opts.fetch(:data, true) + resource[:relationships][name] ||= { data: [] } + resource[:relationships][name][:data] += serializers.map { |serializer| { type: serializer.json_api_type, id: serializer.id.to_s } } + end + if opts.fetch(:links, false) + resource[:relationships][name] ||= {} + resource[:relationships][name][:links] = { related: url_helper.url_for([serializer.object, name]) } + end end def add_relationship(resource, name, serializer, val=nil) @@ -144,7 +153,7 @@ def add_resource_relationships(attrs, serializer, options = {}) attrs[:relationships] ||= {} if serializer.respond_to?(:each) - add_relationships(attrs, key, serializer) + add_relationships(attrs, key, serializer, opts) else if opts[:virtual_value] add_relationship(attrs, key, nil, opts[:virtual_value]) diff --git a/test/adapter/json_api/has_many_url_test.rb b/test/adapter/json_api/has_many_url_test.rb new file mode 100644 index 000000000..53ca44c9d --- /dev/null +++ b/test/adapter/json_api/has_many_url_test.rb @@ -0,0 +1,36 @@ +require 'test_helper' + +module ActionController + module Serialization + class JsonApiHasManyUrlTest < ActionController::TestCase + class MyController < ActionController::Base + + def render_resource_with_url_association + @tag = Tag.new(id: 1) + @tag.posts = [] + render json: @tag, adapter: :json_api, serializer: LinkTagSerializer + end + end + + tests MyController + + def test_render_resource_with_url_association + get :render_resource_with_url_association + expected = { + data: { + id: "1", + type: "tags", + relationships: { + posts: { + links: { + related: "http://test.host/tags/1/posts" + } + } + } + } + } + assert_equal expected.to_json, response.body + end + end + end +end \ No newline at end of file diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 2b281049c..571c4654a 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -1,10 +1,15 @@ class Model + extend ActiveModel::Naming FILE_DIGEST = Digest::MD5.hexdigest(File.open(__FILE__).read) def self.model_name @_model_name ||= ActiveModel::Name.new(self) end + def to_model + self + end + def initialize(hash={}) @attributes = hash end @@ -29,9 +34,14 @@ def id @attributes[:id] || @attributes['id'] || object_id end + def to_param + id.to_s + end + ### Helper methods, not required to be serializable # # Convenience for adding @attributes readers and writers + def method_missing(meth, *args) if meth.to_s =~ /^(.*)=$/ @attributes[$1.to_sym] = args[0] @@ -87,6 +97,7 @@ def cache_key "#{self.class.name.downcase}/#{self.id}" end end +Tag = Class.new(Model) module Spam; end Spam::UnrelatedLink = Class.new(Model) @@ -243,6 +254,10 @@ def self.root_name has_one :blog, key: :site end +LinkTagSerializer = Class.new(ActiveModel::Serializer) do + has_many :posts, links: true, data: false +end + VirtualValueSerializer = Class.new(ActiveModel::Serializer) do attributes :id diff --git a/test/test_helper.rb b/test/test_helper.rb index 68d844e69..0274b2bfb 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -86,6 +86,7 @@ class Foo < Rails::Application module TestHelper Routes = ActionDispatch::Routing::RouteSet.new Routes.draw do + get 'tags/:tag_id/posts', to: 'application#index', as: 'tag_posts' get ':controller(/:action(/:id))' get ':controller(/:action)' end