-
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
Make test attributes explicit #1984
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,42 +3,58 @@ | |
# serializable non-activerecord objects. | ||
module ActiveModelSerializers | ||
class Model | ||
include ActiveModel::Model | ||
include ActiveModel::Serializers::JSON | ||
include ActiveModel::Model | ||
|
||
class_attribute :attribute_names | ||
# Initialize +attribute_names+ for all subclasses. The array is usually | ||
# mutated in the +attributes+ method, but can be set directly, as well. | ||
self.attribute_names = [] | ||
|
||
def self.attributes(*names) | ||
attr_accessor(*names) | ||
self.attribute_names |= names.map(&:to_sym) | ||
# Silence redefinition of methods warnings | ||
ActiveModelSerializers.silence_warnings do | ||
attr_accessor(*names) | ||
end | ||
end | ||
|
||
attr_reader :attributes, :errors | ||
attr_reader :errors | ||
# NOTE that +updated_at+ isn't included in +attribute_names+, | ||
# which means it won't show up in +attributes+ unless a subclass has | ||
# either <tt>attributes :updated_at</tt> which will redefine the methods | ||
# or <tt>attribute_names << :updated_at</tt>. | ||
attr_writer :updated_at | ||
# NOTE that +id+ will always be in +attributes+. | ||
attributes :id | ||
|
||
def initialize(attributes = {}) | ||
@attributes = attributes && attributes.symbolize_keys | ||
@errors = ActiveModel::Errors.new(self) | ||
super | ||
end | ||
|
||
# Defaults to the downcased model name. | ||
def id | ||
attributes.fetch(:id) { self.class.name.downcase } | ||
# The the fields in +attribute_names+ determines the returned hash. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo |
||
# +attributes+ are returned frozen to prevent any expectations that mutation affects | ||
# the actual values in the model. | ||
def attributes | ||
attribute_names.each_with_object({}) do |attribute_name, result| | ||
result[attribute_name] = public_send(attribute_name).freeze | ||
end.with_indifferent_access.freeze | ||
end | ||
|
||
# Defaults to the downcased model name and updated_at | ||
# To customize model behavior, this method must be redefined. However, | ||
# there are other ways of setting the +cache_key+ a serializer uses. | ||
def cache_key | ||
attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}" } | ||
ActiveSupport::Cache.expand_cache_key([ | ||
self.class.model_name.name.downcase, | ||
"#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}" | ||
].compact) | ||
end | ||
|
||
# Defaults to the time the serializer file was modified. | ||
# When no set, defaults to the time the file was modified. | ||
# See NOTE by attr_writer :updated_at | ||
def updated_at | ||
attributes.fetch(:updated_at) { File.mtime(__FILE__) } | ||
end | ||
|
||
def read_attribute_for_serialization(key) | ||
if key == :id || key == 'id' | ||
attributes.fetch(key) { id } | ||
else | ||
attributes[key] | ||
end | ||
defined?(@updated_at) ? @updated_at : File.mtime(__FILE__) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is safe to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @beauby yeah, but give ruby warning undefined ivar |
||
end | ||
|
||
# The following methods are needed to be minimally implemented for ActiveModel::Errors | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ def render_using_adapter_override | |
end | ||
|
||
def render_skipping_adapter | ||
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1') | ||
@profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably a good thing that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
render json: @profile, adapter: false | ||
end | ||
end | ||
|
@@ -46,7 +46,7 @@ def test_render_using_adapter_override | |
|
||
def test_render_skipping_adapter | ||
get :render_skipping_adapter | ||
assert_equal '{"name":"Name 1","description":"Description 1","comments":"Comments 1"}', response.body | ||
assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body | ||
end | ||
end | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,16 @@ module Serialization | |
class JsonApi | ||
class FieldsTest < ActionController::TestCase | ||
class FieldsTestController < ActionController::Base | ||
class PostSerializer < ActiveModel::Serializer | ||
class AuthorWithName < Author | ||
attributes :first_name, :last_name | ||
end | ||
class AuthorWithNameSerializer < AuthorSerializer | ||
type 'authors' | ||
end | ||
class PostWithPublishAt < Post | ||
attributes :publish_at | ||
end | ||
class PostWithPublishAtSerializer < ActiveModel::Serializer | ||
type 'posts' | ||
attributes :title, :body, :publish_at | ||
belongs_to :author | ||
|
@@ -14,19 +23,19 @@ class PostSerializer < ActiveModel::Serializer | |
|
||
def setup_post | ||
ActionController::Base.cache_store.clear | ||
@author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones') | ||
@author = AuthorWithName.new(id: 1, first_name: 'Bob', last_name: 'Jones') | ||
@comment1 = Comment.new(id: 7, body: 'cool', author: @author) | ||
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author) | ||
@post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1', | ||
author: @author, comments: [@comment1, @comment2], | ||
publish_at: '2020-03-16T03:55:25.291Z') | ||
@post = PostWithPublishAt.new(id: 1337, title: 'Title 1', body: 'Body 1', | ||
author: @author, comments: [@comment1, @comment2], | ||
publish_at: '2020-03-16T03:55:25.291Z') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
@comment1.post = @post | ||
@comment2.post = @post | ||
end | ||
|
||
def render_fields_works_on_relationships | ||
setup_post | ||
render json: @post, serializer: PostSerializer, adapter: :json_api, fields: { posts: [:author] } | ||
render json: @post, serializer: PostWithPublishAtSerializer, adapter: :json_api, fields: { posts: [:author] } | ||
end | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,9 +5,17 @@ module Serialization | |
class JsonApi | ||
class KeyTransformTest < ActionController::TestCase | ||
class KeyTransformTestController < ActionController::Base | ||
class Post < ::Model; end | ||
class Author < ::Model; end | ||
class TopComment < ::Model; end | ||
class Post < ::Model | ||
attributes :title, :body, :publish_at | ||
associations :author, :top_comments | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm certainly a fan of this being more explicit |
||
end | ||
class Author < ::Model | ||
attributes :first_name, :last_name | ||
end | ||
class TopComment < ::Model | ||
attributes :body | ||
associations :author, :post | ||
end | ||
class PostSerializer < ActiveModel::Serializer | ||
type 'posts' | ||
attributes :title, :body, :publish_at | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -135,7 +135,7 @@ def render_fragment_changed_object_with_relationship | |
like = Like.new(id: 1, likeable: comment, time: 3.days.ago) | ||
|
||
generate_cached_serializer(like) | ||
like.likable = comment2 | ||
like.likeable = comment2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, caught it because it raised an error, undefined attribute.. makes me wonder if it was being used... |
||
like.time = Time.zone.now.to_s | ||
|
||
render json: like | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ module ActiveModelSerializers | |
class ModelTest < ActiveSupport::TestCase | ||
include ActiveModel::Serializer::Lint::Tests | ||
|
||
def setup | ||
setup do | ||
@resource = ActiveModelSerializers::Model.new | ||
end | ||
|
||
|
@@ -18,5 +18,52 @@ def test_initialization_with_string_keys | |
|
||
assert_equal model_instance.read_attribute_for_serialization(:key), value | ||
end | ||
|
||
def test_attributes_can_be_read_for_serialization | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should new tests be using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, I guess so. I guess I haven't completely switch to activesupport form. I mostly just wanted the stable interface vs. minitest |
||
klass = Class.new(ActiveModelSerializers::Model) do | ||
attributes :one, :two, :three | ||
end | ||
original_attributes = { one: 1, two: 2, three: 3 } | ||
instance = klass.new(original_attributes) | ||
|
||
# Initial value | ||
expected_attributes = { id: nil, one: 1, two: 2, three: 3 }.with_indifferent_access | ||
assert_equal expected_attributes, instance.attributes | ||
assert_equal 1, instance.one | ||
assert_equal 1, instance.read_attribute_for_serialization(:one) | ||
|
||
# Change via accessor | ||
instance.one = :not_one | ||
|
||
expected_attributes = { id: nil, one: :not_one, two: 2, three: 3 }.with_indifferent_access | ||
assert_equal expected_attributes, instance.attributes | ||
assert_equal :not_one, instance.one | ||
assert_equal :not_one, instance.read_attribute_for_serialization(:one) | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bug ref #1857 (comment) |
||
|
||
def test_id_attribute_can_be_read_for_serialization | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no longer necessary now that the |
||
klass = Class.new(ActiveModelSerializers::Model) do | ||
attributes :id, :one, :two, :three | ||
end | ||
self.class.const_set(:SomeTestModel, klass) | ||
original_attributes = { id: :ego, one: 1, two: 2, three: 3 } | ||
instance = klass.new(original_attributes) | ||
|
||
# Initial value | ||
expected_attributes = { id: :ego, one: 1, two: 2, three: 3 }.with_indifferent_access | ||
assert_equal expected_attributes, instance.attributes | ||
assert_equal 1, instance.one | ||
assert_equal 1, instance.read_attribute_for_serialization(:one) | ||
|
||
# Change via accessor | ||
instance.id = :superego | ||
|
||
expected_attributes = { id: :superego, one: 1, two: 2, three: 3 }.with_indifferent_access | ||
assert_equal expected_attributes, instance.attributes | ||
assert_equal :superego, instance.id | ||
assert_equal :superego, instance.read_attribute_for_serialization(:id) | ||
ensure | ||
self.class.send(:remove_const, :SomeTestModel) | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this actually needed? Which method redefinition are you actually trying to silence?
Wouldn't
work instead of messing up with $VERBOSE?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the
attr_accessor
. It still yields warnings when, e.g. redefiningupdated_at
, which isn't anattribute
on the model by default