Skip to content

Commit 37f85b3

Browse files
committed
Separate out ActiveModelSerializers::Model caching concerns
1 parent fd2d302 commit 37f85b3

File tree

6 files changed

+110
-45
lines changed

6 files changed

+110
-45
lines changed

lib/active_model/serializer/lint.rb

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ module Lint
1717
# always return +{}+, and the tests would pass. It is up to you to ensure
1818
# that the values are semantically meaningful.
1919
module Tests
20+
extend ActiveSupport::Concern
21+
included do
22+
include SerializationTests
23+
include CacheTests
24+
end
25+
end
26+
module SerializationTests
2027
# Passes if the object responds to <tt>serializable_hash</tt> and if it takes
2128
# zero or one arguments.
2229
# Fails otherwise.
@@ -72,6 +79,27 @@ def test_to_json
7279
resource.to_json(nil)
7380
end
7481

82+
def test_active_model_errors
83+
assert_respond_to resource, :errors
84+
end
85+
86+
def test_active_model_errors_human_attribute_name
87+
assert_respond_to resource.class, :human_attribute_name
88+
assert_equal(-2, resource.class.method(:human_attribute_name).arity)
89+
end
90+
91+
def test_active_model_errors_lookup_ancestors
92+
assert_respond_to resource.class, :lookup_ancestors
93+
assert_equal 0, resource.class.method(:lookup_ancestors).arity
94+
end
95+
96+
private
97+
98+
def resource
99+
@resource or fail "'@resource' must be set as the linted object"
100+
end
101+
end
102+
module CacheTests
75103
# Passes if the object responds to <tt>cache_key</tt>
76104
# Fails otherwise.
77105
#
@@ -121,20 +149,6 @@ def test_model_name
121149
assert_instance_of resource_class.model_name, ActiveModel::Name
122150
end
123151

124-
def test_active_model_errors
125-
assert_respond_to resource, :errors
126-
end
127-
128-
def test_active_model_errors_human_attribute_name
129-
assert_respond_to resource.class, :human_attribute_name
130-
assert_equal(-2, resource.class.method(:human_attribute_name).arity)
131-
end
132-
133-
def test_active_model_errors_lookup_ancestors
134-
assert_respond_to resource.class, :lookup_ancestors
135-
assert_equal 0, resource.class.method(:lookup_ancestors).arity
136-
end
137-
138152
private
139153

140154
def resource

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: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
# ActiveModelSerializers::Model is a convenient
22
# serializable class to inherit from when making
33
# serializable non-activerecord objects.
4+
#
5+
# Caching support is by-default mixed in when subclassing.
6+
# To disable mixing in any caching support,
7+
# set ActiveModelSerializers::Model.add_caching_support = false
48
module ActiveModelSerializers
59
class Model
610
include ActiveModel::Serializers::JSON
711
include ActiveModel::Model
12+
extend ActiveSupport::Autoload
13+
autoload :Caching
814

15+
class_attribute :add_caching_support
16+
self.add_caching_support = true
917
class_attribute :attribute_names
1018
self.attribute_names = []
1119

1220
def self.attributes(*names)
13-
attr_accessor(*names)
14-
self.attribute_names = attribute_names | names.map(&:to_sym)
21+
self.attribute_names |= names.map(&:to_sym)
22+
# Silence redefinition of methods warnings
23+
ActiveModelSerializers.silence_warnings do
24+
attr_accessor(*names)
25+
end
1526
end
1627

17-
attributes :id
18-
attr_writer :updated_at
28+
def self.inherited(host)
29+
if add_caching_support && !host.included_modules.include?(ActiveModelSerializers::Model::Caching)
30+
host.include(ActiveModelSerializers::Model::Caching)
31+
end
32+
super
33+
end
1934

2035
attr_reader :errors
2136

@@ -24,24 +39,6 @@ def initialize(attributes = {})
2439
super
2540
end
2641

27-
# Defaults to the downcased model name.
28-
def id
29-
@id ||= self.class.name && self.class.name.downcase
30-
end
31-
32-
# Defaults to the downcased model name and updated_at
33-
def cache_key
34-
ActiveSupport::Cache.expand_cache_key([
35-
self.class.name && self.class.name.downcase,
36-
"#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}"
37-
].compact)
38-
end
39-
40-
# Defaults to the time the serializer file was modified.
41-
def updated_at
42-
defined?(@updated_at) ? @updated_at : File.mtime(__FILE__)
43-
end
44-
4542
def attributes
4643
attribute_names.each_with_object({}) do |attribute_name, result|
4744
result[attribute_name] = public_send(attribute_name)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module ActiveModelSerializers
2+
class Model
3+
module Caching
4+
extend ActiveSupport::Concern
5+
6+
included do
7+
attr_writer :updated_at
8+
attributes :id
9+
end
10+
11+
# Defaults to the downcased model name and updated_at
12+
def cache_key
13+
ActiveSupport::Cache.expand_cache_key([
14+
self.class.model_name.name.downcase,
15+
"#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}"
16+
].compact)
17+
end
18+
19+
# Defaults to the time the serializer file was modified.
20+
def updated_at
21+
defined?(@updated_at) ? @updated_at : File.mtime(__FILE__)
22+
end
23+
end
24+
end
25+
end

test/active_model_serializers/model_test.rb

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
require 'test_helper'
22

33
module ActiveModelSerializers
4-
class ModelTest < ActiveSupport::TestCase
5-
include ActiveModel::Serializer::Lint::Tests
4+
class CachingModelTest < ActiveSupport::TestCase
5+
include ActiveModel::Serializer::Lint::CacheTests
6+
class TestModel < ActiveModelSerializers::Model; end
7+
8+
setup do
9+
@resource = TestModel.new
10+
end
11+
end
12+
13+
class BasicModelTest < ActiveSupport::TestCase
14+
include ActiveModel::Serializer::Lint::SerializationTests
615

716
setup do
817
@resource = ActiveModelSerializers::Model.new
918
end
1019

1120
def test_initialization_with_string_keys
12-
klass = Class.new(ActiveModelSerializers::Model) do
21+
klass = subclass_without_caching_support do
1322
attributes :key
1423
end
1524
value = 'value'
@@ -20,32 +29,31 @@ def test_initialization_with_string_keys
2029
end
2130

2231
def test_attributes_can_be_read_for_serialization
23-
klass = Class.new(ActiveModelSerializers::Model) do
32+
klass = subclass_without_caching_support do
2433
attributes :one, :two, :three
2534
end
2635
original_attributes = { one: 1, two: 2, three: 3 }
2736
instance = klass.new(original_attributes)
2837

2938
# Initial value
30-
expected_attributes = { id: nil, one: 1, two: 2, three: 3 }.with_indifferent_access
39+
expected_attributes = { one: 1, two: 2, three: 3 }.with_indifferent_access
3140
assert_equal expected_attributes, instance.attributes
3241
assert_equal 1, instance.one
3342
assert_equal 1, instance.read_attribute_for_serialization(:one)
3443

3544
# Change via accessor
3645
instance.one = :not_one
3746

38-
expected_attributes = { id: nil, one: :not_one, two: 2, three: 3 }.with_indifferent_access
47+
expected_attributes = { one: :not_one, two: 2, three: 3 }.with_indifferent_access
3948
assert_equal expected_attributes, instance.attributes
4049
assert_equal :not_one, instance.one
4150
assert_equal :not_one, instance.read_attribute_for_serialization(:one)
4251
end
4352

4453
def test_id_attribute_can_be_read_for_serialization
45-
klass = Class.new(ActiveModelSerializers::Model) do
54+
klass = subclass_without_caching_support do
4655
attributes :id, :one, :two, :three
4756
end
48-
self.class.const_set(:SomeTestModel, klass)
4957
original_attributes = { id: :ego, one: 1, two: 2, three: 3 }
5058
instance = klass.new(original_attributes)
5159

@@ -62,8 +70,16 @@ def test_id_attribute_can_be_read_for_serialization
6270
assert_equal expected_attributes, instance.attributes
6371
assert_equal :superego, instance.id
6472
assert_equal :superego, instance.read_attribute_for_serialization(:id)
73+
end
74+
75+
def subclass_without_caching_support(&block)
76+
original_caching_support = ActiveModelSerializers::Model.add_caching_support
77+
ActiveModelSerializers::Model.add_caching_support = false
78+
Class.new(ActiveModelSerializers::Model) do
79+
instance_exec(&block)
80+
end
6581
ensure
66-
self.class.send(:remove_const, :SomeTestModel)
82+
ActiveModelSerializers::Model.add_caching_support = original_caching_support
6783
end
6884
end
6985
end

test/fixtures/poro.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
class Model < ActiveModelSerializers::Model
44
FILE_DIGEST = Digest::MD5.hexdigest(File.open(__FILE__).read)
55

6+
# Defaults to the downcased model name.
7+
def id
8+
@id ||= self.class.model_name.name.downcase
9+
end
10+
611
# At this time, just for organization of intent
712
class_attribute :association_names
813
self.association_names = []

0 commit comments

Comments
 (0)