Skip to content

Commit 80af763

Browse files
committed
Make test attributes explicit
- Organize test poros with associations and by serializer - Freeze derived attributes/associations against mutation - Cleanup PORO fixtures
1 parent 095ad9c commit 80af763

20 files changed

+372
-270
lines changed

lib/active_model_serializers.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ def self.default_include_directive
3838
@default_include_directive ||= JSONAPI::IncludeDirective.new(config.default_includes, allow_wildcard: true)
3939
end
4040

41+
def self.silence_warnings
42+
original_verbose = $VERBOSE
43+
$VERBOSE = nil
44+
yield
45+
ensure
46+
$VERBOSE = original_verbose
47+
end
48+
4149
require 'active_model/serializer/version'
4250
require 'active_model/serializer'
4351
require 'active_model/serializable_resource'

lib/active_model_serializers/model.rb

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,58 @@
33
# serializable non-activerecord objects.
44
module ActiveModelSerializers
55
class Model
6-
include ActiveModel::Model
76
include ActiveModel::Serializers::JSON
7+
include ActiveModel::Model
8+
9+
class_attribute :attribute_names
10+
# Initialize +attribute_names+ for all subclasses. The array is usually
11+
# mutated in the +attributes+ method, but can be set directly, as well.
12+
self.attribute_names = []
813

914
def self.attributes(*names)
10-
attr_accessor(*names)
15+
self.attribute_names |= names.map(&:to_sym)
16+
# Silence redefinition of methods warnings
17+
ActiveModelSerializers.silence_warnings do
18+
attr_accessor(*names)
19+
end
1120
end
1221

13-
attr_reader :attributes, :errors
22+
attr_reader :errors
23+
# NOTE that +updated_at+ isn't included in +attribute_names+,
24+
# which means it won't show up in +attributes+ unless a subclass has
25+
# either <tt>attributes :updated_at</tt> which will redefine the methods
26+
# or <tt>attribute_names << :updated_at</tt>.
27+
attr_writer :updated_at
28+
# NOTE that +id+ will always be in +attributes+.
29+
attributes :id
1430

1531
def initialize(attributes = {})
16-
@attributes = attributes && attributes.symbolize_keys
1732
@errors = ActiveModel::Errors.new(self)
1833
super
1934
end
2035

21-
# Defaults to the downcased model name.
22-
def id
23-
attributes.fetch(:id) { self.class.name.downcase }
36+
# The the fields in +attribute_names+ determines the returned hash.
37+
# +attributes+ are returned frozen to prevent any expectations that mutation affects
38+
# the actual values in the model.
39+
def attributes
40+
attribute_names.each_with_object({}) do |attribute_name, result|
41+
result[attribute_name] = public_send(attribute_name).freeze
42+
end.with_indifferent_access.freeze
2443
end
2544

26-
# Defaults to the downcased model name and updated_at
45+
# To customize model behavior, this method must be redefined. However,
46+
# there are other ways of setting the +cache_key+ a serializer uses.
2747
def cache_key
28-
attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}" }
48+
ActiveSupport::Cache.expand_cache_key([
49+
self.class.model_name.name.downcase,
50+
"#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}"
51+
].compact)
2952
end
3053

31-
# Defaults to the time the serializer file was modified.
54+
# When no set, defaults to the time the file was modified.
55+
# See NOTE by attr_writer :updated_at
3256
def updated_at
33-
attributes.fetch(:updated_at) { File.mtime(__FILE__) }
34-
end
35-
36-
def read_attribute_for_serialization(key)
37-
if key == :id || key == 'id'
38-
attributes.fetch(key) { id }
39-
else
40-
attributes[key]
41-
end
57+
defined?(@updated_at) ? @updated_at : File.mtime(__FILE__)
4258
end
4359

4460
# The following methods are needed to be minimally implemented for ActiveModel::Errors

test/action_controller/adapter_selector_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def render_using_adapter_override
1515
end
1616

1717
def render_skipping_adapter
18-
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
18+
@profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
1919
render json: @profile, adapter: false
2020
end
2121
end
@@ -46,7 +46,7 @@ def test_render_using_adapter_override
4646

4747
def test_render_skipping_adapter
4848
get :render_skipping_adapter
49-
assert_equal '{"name":"Name 1","description":"Description 1","comments":"Comments 1"}', response.body
49+
assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body
5050
end
5151
end
5252
end

test/action_controller/json_api/fields_test.rb

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@ module Serialization
55
class JsonApi
66
class FieldsTest < ActionController::TestCase
77
class FieldsTestController < ActionController::Base
8-
class PostSerializer < ActiveModel::Serializer
8+
class AuthorWithName < Author
9+
attributes :first_name, :last_name
10+
end
11+
class AuthorWithNameSerializer < AuthorSerializer
12+
type 'authors'
13+
end
14+
class PostWithPublishAt < Post
15+
attributes :publish_at
16+
end
17+
class PostWithPublishAtSerializer < ActiveModel::Serializer
918
type 'posts'
1019
attributes :title, :body, :publish_at
1120
belongs_to :author
@@ -14,19 +23,19 @@ class PostSerializer < ActiveModel::Serializer
1423

1524
def setup_post
1625
ActionController::Base.cache_store.clear
17-
@author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones')
26+
@author = AuthorWithName.new(id: 1, first_name: 'Bob', last_name: 'Jones')
1827
@comment1 = Comment.new(id: 7, body: 'cool', author: @author)
1928
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
20-
@post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
21-
author: @author, comments: [@comment1, @comment2],
22-
publish_at: '2020-03-16T03:55:25.291Z')
29+
@post = PostWithPublishAt.new(id: 1337, title: 'Title 1', body: 'Body 1',
30+
author: @author, comments: [@comment1, @comment2],
31+
publish_at: '2020-03-16T03:55:25.291Z')
2332
@comment1.post = @post
2433
@comment2.post = @post
2534
end
2635

2736
def render_fields_works_on_relationships
2837
setup_post
29-
render json: @post, serializer: PostSerializer, adapter: :json_api, fields: { posts: [:author] }
38+
render json: @post, serializer: PostWithPublishAtSerializer, adapter: :json_api, fields: { posts: [:author] }
3039
end
3140
end
3241

test/action_controller/json_api/transform_test.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@ module Serialization
55
class JsonApi
66
class KeyTransformTest < ActionController::TestCase
77
class KeyTransformTestController < ActionController::Base
8-
class Post < ::Model; end
9-
class Author < ::Model; end
10-
class TopComment < ::Model; end
8+
class Post < ::Model
9+
attributes :title, :body, :publish_at
10+
associations :author, :top_comments
11+
end
12+
class Author < ::Model
13+
attributes :first_name, :last_name
14+
end
15+
class TopComment < ::Model
16+
attributes :body
17+
associations :author, :post
18+
end
1119
class PostSerializer < ActiveModel::Serializer
1220
type 'posts'
1321
attributes :title, :body, :publish_at

test/action_controller/namespace_lookup_test.rb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
module ActionController
44
module Serialization
55
class NamespaceLookupTest < ActionController::TestCase
6-
class Book < ::Model; end
7-
class Page < ::Model; end
8-
class Chapter < ::Model; end
9-
class Writer < ::Model; end
6+
class Book < ::Model
7+
attributes :title, :body
8+
associations :writer, :chapters
9+
end
10+
class Chapter < ::Model
11+
attributes :title
12+
end
13+
class Writer < ::Model
14+
attributes :name
15+
end
1016

1117
module Api
1218
module V2
@@ -93,7 +99,7 @@ def explicit_namespace_as_symbol
9399
end
94100

95101
def invalid_namespace
96-
book = Book.new(title: 'New Post', body: 'Body')
102+
book = Book.new(id: 'invalid_namespace_book_id', title: 'New Post', body: 'Body')
97103

98104
render json: book, namespace: :api_v2
99105
end
@@ -205,7 +211,7 @@ def namespace_set_by_request_headers
205211

206212
assert_serializer ActiveModel::Serializer::Null
207213

208-
expected = { 'title' => 'New Post', 'body' => 'Body' }
214+
expected = { 'id' => 'invalid_namespace_book_id', 'title' => 'New Post', 'body' => 'Body' }
209215
actual = JSON.parse(@response.body)
210216

211217
assert_equal expected, actual

test/action_controller/serialization_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def render_fragment_changed_object_with_relationship
135135
like = Like.new(id: 1, likeable: comment, time: 3.days.ago)
136136

137137
generate_cached_serializer(like)
138-
like.likable = comment2
138+
like.likeable = comment2
139139
like.time = Time.zone.now.to_s
140140

141141
render json: like

test/adapter/json_api/fields_test.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@ module ActiveModelSerializers
44
module Adapter
55
class JsonApi
66
class FieldsTest < ActiveSupport::TestCase
7-
class Post < ::Model; end
8-
class Author < ::Model; end
9-
class Comment < ::Model; end
7+
class Post < ::Model
8+
attributes :title, :body
9+
associations :author, :comments
10+
end
11+
class Author < ::Model
12+
attributes :name, :birthday
13+
end
14+
class Comment < ::Model
15+
attributes :body
16+
associations :author, :post
17+
end
1018

1119
class PostSerializer < ActiveModel::Serializer
1220
type 'posts'

test/adapter/json_api/include_data_if_sideloaded_test.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ class Serializer
55
module Adapter
66
class JsonApi
77
class IncludeParamTest < ActiveSupport::TestCase
8-
IncludeParamAuthor = Class.new(::Model)
8+
IncludeParamAuthor = Class.new(::Model) do
9+
associations :tags, :posts
10+
end
911

1012
class CustomCommentLoader
1113
def all

test/adapter/json_api/linked_test.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require 'test_helper'
22

3-
class NestedPost < ::Model; end
3+
class NestedPost < ::Model; associations :nested_posts end
44
class NestedPostSerializer < ActiveModel::Serializer
55
has_many :nested_posts
66
end
@@ -301,8 +301,8 @@ def test_nil_link_with_specified_serializer
301301
end
302302

303303
class NoDuplicatesTest < ActiveSupport::TestCase
304-
class Post < ::Model; end
305-
class Author < ::Model; end
304+
class Post < ::Model; associations :author end
305+
class Author < ::Model; associations :posts, :roles, :bio end
306306

307307
class PostSerializer < ActiveModel::Serializer
308308
type 'posts'

0 commit comments

Comments
 (0)