-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[0.10.0rc1] Odd results from #to_json
#878
Comments
👍 same here :( |
I think you need to run the serializer through the adapter. I'm not sure if this is the intended way to invoke the adapter, but this should produce the output you're expecting: let(:result) { JSON.parse(ActiveModel::Serializer.adapter.new(serializer).to_json) } |
Hmm, so either:
def to_json
self.class.adapter.new(self).to_json
end ? |
@nfm is right, this is how we have being testing it internally: adapter = ActiveModel::Serializer.adapter.new(serializer)
adapter.to_json But agree with @JustinAiken, would be nice to have thins implementation inside the serializer. |
Happy to submit it as a PR w/ tests if you you do want it 👍 |
Yeah, would be nice. FWIW, I've written this spec helper code begin
require "active_model_serializers"
module SerializerSupport
def serialize(resource)
adapter = serialization_adapter_for(resource)
yield adapter if block_given?
adapter.as_json
end
def serialization_adapter_for(resource)
serializer = serializer_for(resource)
# SERIALIZER_OPTS = :serializer, :each_serializer, etc
serializer_opts = { each_serializer: serializer }
object = serializer.new(resource, serializer_opts)
# ADAPTER_OPTION_KEYS = [:include, :fields, :root, :adapter]
adapter_opts = { root: resource_root(resource) }
ActiveModel::Serializer::Adapter.create(object, adapter_opts)
end
def resource_root(resource)
if resource.respond_to?(:first)
fail "Resource can't be empty #{resource}" if resource.empty?
resource.first.class.table_name
else
resource.class.table_name
end
end
def serializer_for(resource)
ActiveModel::Serializer.serializer_for(resource)
end
def serializer_attributes(serializer)
serializer._attributes
end
def serializer_associations(serializer)
serializer._associations
end
def serializer_association_names(serializer)
serializer_associations(serializer).keys
end
def serialized_resource_attributes(resource)
serialization_adapter_for(resource).serializable_hash
end
end
with_serializer_support = {
type: ->(v) { [:serializer, :controller].include?(v) }
}
RSpec.configure do |config|
config.include SerializerSupport, with_serializer_support
end
rescue LoadError
end and this shared helper # Usage:
#
# RSpec.describe UserSerializer, type: :serializer do
# it_behaves_like "a record serializer" do
# let(:attributes) { %i[id created_at] }
# let(:record) { create(:user, posts: [build(:post)]) }
# end
# end
#
# Note that the record will be written out as an
# example serialized resource, so you'll want to ensure
# you set attributes that don't change from run to run.
#
# Depends on spec/support/serializers.rb
RSpec.shared_examples "a record serializer" do
let(:serializer) { serializer_for(record) }
let(:resource_name) { record.class.name.underscore }
let(:nested_resources) { serializer_association_names(serializer) }
let(:serialized_attributes) do
serialized_resource_attributes(record).keys
end
it "with attributes" do
serialized_keys = attributes.dup
expect(serializer_attributes(serializer)).to match_array(serialized_keys)
end
it "serializes a record" do
serialized_resource = serialize(record)
serialized_keys = attributes.dup
serialized_keys += nested_resources unless nested_resources.empty?
expect(serialized_keys).to match_array(serialized_resource.keys)
writeout_resource(resource_name, serialized_resource)
end
it "serializes a record's nested resources" do
serialized_resource = serialize(record)
nested_resources.each do |nested_root|
# the nested resource isn't an attribute, so we have to call a method
# to get it. record[nested_root] doesn't work
nested_resource = record.public_send(nested_root)
called = false
# Get the adapter
# so we can get the serializer
# so we can get only the attributes of the nested resource
# and thereby exclude associations aren't serialized in a nested resource
serialize(nested_resource) do |adapter|
serializer = adapter.serializer
if serializer.respond_to?(:each)
serialized_nested_resource = serializer.map(&:attributes)
else
serialized_nested_resource = serializer.attributes
end
expect(serialized_resource[nested_root]).to eq(serialized_nested_resource)
called = true
end
# sanity check
expect(called).to eq(true)
end
end
end And I've written some other code to mimic the controller action opts = {
status: 200,
prefixes: %w[items application],
template: params["action"],
scope: nil,
scope_name: _serialization_scope
}
serializer_instance = ActiveModel::Serializer::ArraySerializer.new(resource, opts)
# or
serializer_instance = ItemSerializer.new(resource, opts)
opts = {root: "items"}
adapter = ActiveModel::Serializer::Adapter.create(serializer_instance, opts)
adapter.as_json
# https://github.com/rails-api/active_model_serializers/blob/master/lib/action_controller/serialization.rb
# https://github.com/rails-api/active_model_serializers/blob/1577969cb763/test/serializers/meta_test.rb
# https://github.com/rails-api/active_model_serializers/issues/870
# https://github.com/rails-api/active_model_serializers/issues/876 And use # In dev/test pretty print JSON
# ref
# https://github.com/rails/rails/blob/4-2-stable/actionpack/lib/action_controller/metal/renderers.rb#L66-L128
# https://github.com/rails/rails/blob/4-2-stable//actionview/lib/action_view/renderer/renderer.rb#L32
#
# Consider instead using: https://github.com/rack/rack/blob/master/lib/rack/deflater.rb
# or see
# https://github.com/brianhempel/stream_json_demo/commit/6bd580ea9bf3b1d508bb1ce9e48834bf67e313df
# http://collectiveidea.com/blog/archives/2015/03/13/optimizing-rails-for-memory-usage-part-4-lazy-json-generation-and-final-thoughts/
if Rails.env.development? || Rails.env.test?
gem "rails", "~> 4.2"
ActionController::Renderers.remove :json
ActionController::Renderers.add :json do |json, options|
if !json.is_a?(String)
# changed from
# json = json.to_json(options)
# changed to
json = json.as_json(options) if json.respond_to?(:as_json)
json = JSON.pretty_generate(json, options)
end
if options[:callback].present?
if content_type.nil? || content_type == Mime::JSON
self.content_type = Mime::JS
end
"/**/#{options[:callback]}(#{json})"
else
self.content_type ||= Mime::JSON
json
end
end
end |
So the current way (in master) to "get the JSON" is to do Closing this for now. Feel free to reopen if needed, or open a new issue with a feature request if you feel the interface should be different. |
@beauby How to pass fields argument to it? |
@williamweckl see https://github.com/rails-api/active_model_serializers/blob/v0.10.0.rc4/test/adapter/json_api/fields_test.rb#L50-L51
PR for docs https://github.com/rails-api/active_model_serializers/blob/master/docs/general/rendering.md#fields appreciated! If this didn't answer your question, please open a new issue. |
With this simple serializer:
and then this test:
I get this:
which seems a bit odd compared to what I was getting before in 0.8.x...
The text was updated successfully, but these errors were encountered: