active_model_serializers 0.10.0.rc5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -0
  3. data/.travis.yml +0 -8
  4. data/CHANGELOG.md +23 -0
  5. data/CONTRIBUTING.md +14 -4
  6. data/Gemfile +1 -2
  7. data/README.md +3 -3
  8. data/active_model_serializers.gemspec +1 -1
  9. data/appveyor.yml +6 -10
  10. data/docs/ARCHITECTURE.md +1 -1
  11. data/docs/README.md +1 -0
  12. data/docs/general/deserialization.md +19 -19
  13. data/docs/general/serializers.md +33 -0
  14. data/docs/howto/serialize_poro.md +32 -0
  15. data/lib/active_model/serializer.rb +2 -0
  16. data/lib/active_model/serializer/caching.rb +185 -3
  17. data/lib/active_model/serializer/field.rb +36 -2
  18. data/lib/active_model/serializer/lint.rb +8 -18
  19. data/lib/active_model/serializer/version.rb +1 -1
  20. data/lib/active_model_serializers.rb +0 -2
  21. data/lib/active_model_serializers/adapter/attributes.rb +20 -38
  22. data/lib/active_model_serializers/adapter/base.rb +8 -15
  23. data/lib/active_model_serializers/adapter/json.rb +12 -2
  24. data/lib/active_model_serializers/adapter/json_api.rb +33 -30
  25. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +10 -5
  26. data/lib/active_model_serializers/adapter/null.rb +1 -2
  27. data/lib/active_model_serializers/register_jsonapi_renderer.rb +3 -2
  28. data/lib/active_model_serializers/serializable_resource.rb +1 -1
  29. data/lib/active_model_serializers/test/schema.rb +44 -9
  30. data/test/action_controller/json_api/transform_test.rb +2 -1
  31. data/test/action_controller/serialization_test.rb +3 -1
  32. data/test/active_model_serializers/test/schema_test.rb +5 -3
  33. data/test/active_model_serializers/test/serializer_test.rb +1 -2
  34. data/test/adapter/json/transform_test.rb +14 -14
  35. data/test/adapter/json_api/has_many_test.rb +3 -2
  36. data/test/adapter/json_api/has_one_test.rb +3 -2
  37. data/test/adapter/json_api/pagination_links_test.rb +39 -21
  38. data/test/adapter/json_api/transform_test.rb +36 -34
  39. data/test/adapter/polymorphic_test.rb +111 -12
  40. data/test/adapter_test.rb +27 -0
  41. data/test/array_serializer_test.rb +10 -25
  42. data/test/benchmark/bm_caching.rb +17 -15
  43. data/test/benchmark/controllers.rb +9 -2
  44. data/test/benchmark/fixtures.rb +56 -4
  45. data/test/cache_test.rb +103 -6
  46. data/test/fixtures/active_record.rb +10 -0
  47. data/test/fixtures/poro.rb +31 -3
  48. data/test/serializers/associations_test.rb +43 -15
  49. data/test/serializers/attribute_test.rb +44 -16
  50. data/test/serializers/meta_test.rb +5 -7
  51. data/test/support/isolated_unit.rb +0 -1
  52. data/test/test_helper.rb +19 -21
  53. metadata +7 -12
  54. data/lib/active_model_serializers/cached_serializer.rb +0 -87
  55. data/lib/active_model_serializers/fragment_cache.rb +0 -118
  56. data/test/active_model_serializers/cached_serializer_test.rb +0 -80
  57. data/test/active_model_serializers/fragment_cache_test.rb +0 -34
@@ -1,12 +1,13 @@
1
1
  class PostController < ActionController::Base
2
2
  POST =
3
3
  begin
4
+ updated_at = Time.current
4
5
  if ENV['BENCH_STRESS']
5
6
  comments = (0..50).map do |i|
6
- Comment.new(id: i, body: 'ZOMG A COMMENT')
7
+ Comment.new(id: i, body: 'ZOMG A COMMENT', updated_at: updated_at + i)
7
8
  end
8
9
  else
9
- comments = [Comment.new(id: 1, body: 'ZOMG A COMMENT')]
10
+ comments = [Comment.new(id: 1, body: 'ZOMG A COMMENT', updated_at: updated_at)]
10
11
  end
11
12
  author = Author.new(id: 42, first_name: 'Joao', last_name: 'Moura')
12
13
  Post.new(id: 1337, title: 'New Post', blog: nil, body: 'Body', comments: comments, author: author)
@@ -17,6 +18,11 @@ class PostController < ActionController::Base
17
18
  render json: POST, serializer: CachingPostSerializer, adapter: :json, meta: { caching: perform_caching }
18
19
  end
19
20
 
21
+ def render_with_fragment_caching_serializer
22
+ toggle_cache_status
23
+ render json: POST, serializer: FragmentCachingPostSerializer, adapter: :json, meta: { caching: perform_caching }
24
+ end
25
+
20
26
  def render_with_non_caching_serializer
21
27
  toggle_cache_status
22
28
  render json: POST, adapter: :json, meta: { caching: perform_caching }
@@ -73,5 +79,6 @@ Rails.application.routes.draw do
73
79
  get '/status(/:on)' => 'post#render_cache_status'
74
80
  get '/clear' => 'post#clear'
75
81
  get '/caching(/:on)' => 'post#render_with_caching_serializer'
82
+ get '/fragment_caching(/:on)' => 'post#render_with_fragment_caching_serializer'
76
83
  get '/non_caching(/:on)' => 'post#render_with_non_caching_serializer'
77
84
  end
@@ -13,7 +13,7 @@ end
13
13
  Rails.configuration.serializers << BlogSerializer
14
14
 
15
15
  class CommentSerializer < ActiveModel::Serializer
16
- attributes :id, :body
16
+ attributes :id, :body, :updated_at
17
17
 
18
18
  belongs_to :post
19
19
  belongs_to :author
@@ -43,7 +43,7 @@ end
43
43
  Rails.configuration.serializers << PostSerializer
44
44
 
45
45
  class CachingAuthorSerializer < AuthorSerializer
46
- cache key: 'writer', only: [:first_name, :last_name], skip_digest: true
46
+ cache key: 'writer', skip_digest: true
47
47
  end
48
48
  Rails.configuration.serializers << CachingAuthorSerializer
49
49
 
@@ -52,14 +52,66 @@ class CachingCommentSerializer < CommentSerializer
52
52
  end
53
53
  Rails.configuration.serializers << CachingCommentSerializer
54
54
 
55
- class CachingPostSerializer < PostSerializer
55
+ # see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532
56
+ class CachingPostSerializer < ActiveModel::Serializer
56
57
  cache key: 'post', expires_in: 0.1, skip_digest: true
58
+
59
+ attributes :id, :title, :body
60
+
57
61
  belongs_to :blog, serializer: BlogSerializer
58
62
  belongs_to :author, serializer: CachingAuthorSerializer
59
63
  has_many :comments, serializer: CachingCommentSerializer
64
+
65
+ link(:post_authors) { 'https://example.com/post_authors' }
66
+
67
+ meta do
68
+ {
69
+ rating: 5,
70
+ favorite_count: 10
71
+ }
72
+ end
73
+
74
+ def blog
75
+ Blog.new(id: 999, name: 'Custom blog')
76
+ end
60
77
  end
61
78
  Rails.configuration.serializers << CachingPostSerializer
62
79
 
80
+ class FragmentCachingAuthorSerializer < AuthorSerializer
81
+ cache key: 'writer', only: [:first_name, :last_name], skip_digest: true
82
+ end
83
+ Rails.configuration.serializers << FragmentCachingAuthorSerializer
84
+
85
+ class FragmentCachingCommentSerializer < CommentSerializer
86
+ cache expires_in: 1.day, except: [:updated_at], skip_digest: true
87
+ end
88
+ Rails.configuration.serializers << CachingCommentSerializer
89
+
90
+ # see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532
91
+ class FragmentCachingPostSerializer < ActiveModel::Serializer
92
+ cache key: 'post', expires_in: 0.1, skip_digest: true
93
+
94
+ attributes :id, :title, :body
95
+
96
+ belongs_to :blog, serializer: BlogSerializer
97
+ belongs_to :author, serializer: FragmentCachingAuthorSerializer
98
+ has_many :comments, serializer: FragmentCachingCommentSerializer
99
+
100
+ link(:post_authors) { 'https://example.com/post_authors' }
101
+
102
+ meta do
103
+ {
104
+ rating: 5,
105
+ favorite_count: 10
106
+ }
107
+ end
108
+
109
+ def blog
110
+ Blog.new(id: 999, name: 'Custom blog')
111
+ end
112
+ end
113
+ Rails.configuration.serializers << FragmentCachingPostSerializer
114
+
63
115
  if ENV['ENABLE_ACTIVE_RECORD'] == 'true'
64
116
  require 'active_record'
65
117
 
@@ -150,7 +202,7 @@ else
150
202
  end
151
203
 
152
204
  class Comment < BenchmarkModel
153
- attr_accessor :id, :body
205
+ attr_accessor :id, :body, :updated_at
154
206
  end
155
207
 
156
208
  class Author < BenchmarkModel
@@ -114,7 +114,7 @@ module ActiveModelSerializers
114
114
 
115
115
  def test_error_is_raised_if_cache_key_is_not_defined_on_object_or_passed_as_cache_option
116
116
  article = Article.new(title: 'Must Read')
117
- e = assert_raises ActiveModelSerializers::CachedSerializer::UndefinedCacheKey do
117
+ e = assert_raises ActiveModel::Serializer::UndefinedCacheKey do
118
118
  render_object_with_cache(article)
119
119
  end
120
120
  assert_match(/ActiveModelSerializers::CacheTest::Article must define #cache_key, or the 'key:' option must be passed into 'CachedActiveModelSerializers_CacheTest_ArticleSerializer.cache'/, e.message)
@@ -252,11 +252,11 @@ module ActiveModelSerializers
252
252
  attributes_serialization = serializable_alert.as_json
253
253
  assert_equal expected_cached_attributes, alert.attributes
254
254
  assert_equal alert.attributes, attributes_serialization
255
- attributes_cache_key = CachedSerializer.new(serializable_alert.adapter.serializer).cache_key(serializable_alert.adapter)
255
+ attributes_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter)
256
256
  assert_equal attributes_serialization, cache_store.fetch(attributes_cache_key)
257
257
 
258
258
  serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :json_api)
259
- jsonapi_cache_key = CachedSerializer.new(serializable_alert.adapter.serializer).cache_key(serializable_alert.adapter)
259
+ jsonapi_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter)
260
260
  # Assert cache keys differ
261
261
  refute_equal attributes_cache_key, jsonapi_cache_key
262
262
  # Assert (cached) serializations differ
@@ -288,9 +288,9 @@ module ActiveModelSerializers
288
288
  serializable = ActiveModelSerializers::SerializableResource.new([@comment, @comment])
289
289
  include_tree = ActiveModel::Serializer::IncludeTree.from_include_args('*')
290
290
 
291
- actual = CachedSerializer.object_cache_keys(serializable.adapter.serializer, serializable.adapter, include_tree)
291
+ actual = ActiveModel::Serializer.object_cache_keys(serializable.adapter.serializer, serializable.adapter, include_tree)
292
292
 
293
- assert_equal actual.size, 3
293
+ assert_equal 3, actual.size
294
294
  assert actual.any? { |key| key == "comment/1/#{serializable.adapter.cached_name}" }
295
295
  assert actual.any? { |key| key =~ %r{post/post-\d+} }
296
296
  assert actual.any? { |key| key =~ %r{author/author-\d+} }
@@ -365,12 +365,109 @@ module ActiveModelSerializers
365
365
  assert called
366
366
  end
367
367
 
368
+ def test_cached_false_without_cache_store
369
+ cached_serializer = build_cached_serializer do |serializer|
370
+ serializer._cache = nil
371
+ end
372
+ refute cached_serializer.class.cache_enabled?
373
+ end
374
+
375
+ def test_cached_true_with_cache_store_and_without_cache_only_and_cache_except
376
+ cached_serializer = build_cached_serializer do |serializer|
377
+ serializer._cache = Object
378
+ end
379
+ assert cached_serializer.class.cache_enabled?
380
+ end
381
+
382
+ def test_cached_false_with_cache_store_and_with_cache_only
383
+ cached_serializer = build_cached_serializer do |serializer|
384
+ serializer._cache = Object
385
+ serializer._cache_only = [:name]
386
+ end
387
+ refute cached_serializer.class.cache_enabled?
388
+ end
389
+
390
+ def test_cached_false_with_cache_store_and_with_cache_except
391
+ cached_serializer = build_cached_serializer do |serializer|
392
+ serializer._cache = Object
393
+ serializer._cache_except = [:content]
394
+ end
395
+ refute cached_serializer.class.cache_enabled?
396
+ end
397
+
398
+ def test_fragment_cached_false_without_cache_store
399
+ cached_serializer = build_cached_serializer do |serializer|
400
+ serializer._cache = nil
401
+ serializer._cache_only = [:name]
402
+ end
403
+ refute cached_serializer.class.fragment_cache_enabled?
404
+ end
405
+
406
+ def test_fragment_cached_true_with_cache_store_and_cache_only
407
+ cached_serializer = build_cached_serializer do |serializer|
408
+ serializer._cache = Object
409
+ serializer._cache_only = [:name]
410
+ end
411
+ assert cached_serializer.class.fragment_cache_enabled?
412
+ end
413
+
414
+ def test_fragment_cached_true_with_cache_store_and_cache_except
415
+ cached_serializer = build_cached_serializer do |serializer|
416
+ serializer._cache = Object
417
+ serializer._cache_except = [:content]
418
+ end
419
+ assert cached_serializer.class.fragment_cache_enabled?
420
+ end
421
+
422
+ def test_fragment_cached_false_with_cache_store_and_cache_except_and_cache_only
423
+ cached_serializer = build_cached_serializer do |serializer|
424
+ serializer._cache = Object
425
+ serializer._cache_except = [:content]
426
+ serializer._cache_only = [:name]
427
+ end
428
+ refute cached_serializer.class.fragment_cache_enabled?
429
+ end
430
+
431
+ def test_fragment_fetch_with_virtual_attributes
432
+ @author = Author.new(name: 'Joao M. D. Moura')
433
+ @role = Role.new(name: 'Great Author', description: nil)
434
+ @role.author = [@author]
435
+ @role_serializer = RoleSerializer.new(@role)
436
+ @role_hash = @role_serializer.fetch_fragment_cache(ActiveModelSerializers::Adapter.configured_adapter.new(@role_serializer))
437
+
438
+ expected_result = {
439
+ id: @role.id,
440
+ description: @role.description,
441
+ slug: "#{@role.name}-#{@role.id}",
442
+ name: @role.name
443
+ }
444
+ assert_equal(@role_hash, expected_result)
445
+ end
446
+
447
+ def test_fragment_fetch_with_namespaced_object
448
+ @spam = Spam::UnrelatedLink.new(id: 'spam-id-1')
449
+ @spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam)
450
+ @spam_hash = @spam_serializer.fetch_fragment_cache(ActiveModelSerializers::Adapter.configured_adapter.new(@spam_serializer))
451
+ expected_result = {
452
+ id: @spam.id
453
+ }
454
+ assert_equal(@spam_hash, expected_result)
455
+ end
456
+
368
457
  private
369
458
 
370
459
  def cache_store
371
460
  ActiveModelSerializers.config.cache_store
372
461
  end
373
462
 
463
+ def build_cached_serializer
464
+ serializer = Class.new(ActiveModel::Serializer)
465
+ serializer._cache_key = nil
466
+ serializer._cache_options = nil
467
+ yield serializer if block_given?
468
+ serializer.new(Object)
469
+ end
470
+
374
471
  def render_object_with_cache(obj, options = {})
375
472
  @serializable_resource = serializable(obj, options)
376
473
  @serializable_resource.serializable_hash
@@ -381,7 +478,7 @@ module ActiveModelSerializers
381
478
  end
382
479
 
383
480
  def cached_serialization(serializer)
384
- cache_key = CachedSerializer.new(serializer).cache_key(adapter)
481
+ cache_key = serializer.cache_key(adapter)
385
482
  cache_store.fetch(cache_key)
386
483
  end
387
484
  end
@@ -24,6 +24,16 @@ ActiveRecord::Schema.define do
24
24
  t.string :email
25
25
  t.timestamp null: false
26
26
  end
27
+ create_table :object_tags, force: true do |t|
28
+ t.string :poly_tag_id
29
+ t.string :taggable_type
30
+ t.string :taggable_id
31
+ t.timestamp null: false
32
+ end
33
+ create_table :poly_tags, force: true do |t|
34
+ t.string :phrase
35
+ t.timestamp null: false
36
+ end
27
37
  create_table :pictures, force: true do |t|
28
38
  t.string :title
29
39
  t.string :imageable_type
@@ -70,10 +70,21 @@ end
70
70
 
71
71
  class Employee < ActiveRecord::Base
72
72
  has_many :pictures, as: :imageable
73
+ has_many :object_tags, as: :taggable
74
+ end
75
+
76
+ class ObjectTag < ActiveRecord::Base
77
+ belongs_to :poly_tag
78
+ belongs_to :taggable, polymorphic: true
73
79
  end
74
80
 
75
81
  class Picture < ActiveRecord::Base
76
82
  belongs_to :imageable, polymorphic: true
83
+ has_many :object_tags, as: :taggable
84
+ end
85
+
86
+ class PolyTag < ActiveRecord::Base
87
+ has_many :object_tags
77
88
  end
78
89
 
79
90
  module Spam; end
@@ -227,8 +238,9 @@ end
227
238
  VirtualValueSerializer = Class.new(ActiveModel::Serializer) do
228
239
  attributes :id
229
240
 
230
- has_many :reviews, virtual_value: [{ id: 1 }, { id: 2 }]
231
- has_one :maker, virtual_value: { id: 1 }
241
+ has_many :reviews, virtual_value: [{ type: 'reviews', id: '1' },
242
+ { type: 'reviews', id: '2' }]
243
+ has_one :maker, virtual_value: { type: 'makers', id: '1' }
232
244
 
233
245
  def reviews
234
246
  end
@@ -244,7 +256,23 @@ end
244
256
  PolymorphicBelongsToSerializer = Class.new(ActiveModel::Serializer) do
245
257
  attributes :id, :title
246
258
 
247
- has_one :imageable, serializer: PolymorphicHasManySerializer
259
+ has_one :imageable, serializer: PolymorphicHasManySerializer, polymorphic: true
260
+ end
261
+
262
+ PolymorphicSimpleSerializer = Class.new(ActiveModel::Serializer) do
263
+ attributes :id
264
+ end
265
+
266
+ PolymorphicObjectTagSerializer = Class.new(ActiveModel::Serializer) do
267
+ attributes :id
268
+
269
+ has_many :taggable, serializer: PolymorphicSimpleSerializer, polymorphic: true
270
+ end
271
+
272
+ PolymorphicTagSerializer = Class.new(ActiveModel::Serializer) do
273
+ attributes :id, :phrase
274
+
275
+ has_many :object_tags, serializer: PolymorphicObjectTagSerializer
248
276
  end
249
277
 
250
278
  Spam::UnrelatedLinkSerializer = Class.new(ActiveModel::Serializer) do
@@ -239,27 +239,55 @@ module ActiveModel
239
239
  end
240
240
  end
241
241
 
242
+ # rubocop:disable Metrics/AbcSize
242
243
  def test_conditional_associations
243
- serializer = Class.new(ActiveModel::Serializer) do
244
- belongs_to :if_assoc_included, if: :true
245
- belongs_to :if_assoc_excluded, if: :false
246
- belongs_to :unless_assoc_included, unless: :false
247
- belongs_to :unless_assoc_excluded, unless: :true
248
-
249
- def true
250
- true
251
- end
244
+ model = ::Model.new(true: true, false: false)
245
+
246
+ scenarios = [
247
+ { options: { if: :true }, included: true },
248
+ { options: { if: :false }, included: false },
249
+ { options: { unless: :false }, included: true },
250
+ { options: { unless: :true }, included: false },
251
+ { options: { if: 'object.true' }, included: true },
252
+ { options: { if: 'object.false' }, included: false },
253
+ { options: { unless: 'object.false' }, included: true },
254
+ { options: { unless: 'object.true' }, included: false },
255
+ { options: { if: -> { object.true } }, included: true },
256
+ { options: { if: -> { object.false } }, included: false },
257
+ { options: { unless: -> { object.false } }, included: true },
258
+ { options: { unless: -> { object.true } }, included: false },
259
+ { options: { if: -> (s) { s.object.true } }, included: true },
260
+ { options: { if: -> (s) { s.object.false } }, included: false },
261
+ { options: { unless: -> (s) { s.object.false } }, included: true },
262
+ { options: { unless: -> (s) { s.object.true } }, included: false }
263
+ ]
264
+
265
+ scenarios.each do |s|
266
+ serializer = Class.new(ActiveModel::Serializer) do
267
+ belongs_to :association, s[:options]
268
+
269
+ def true
270
+ true
271
+ end
252
272
 
253
- def false
254
- false
273
+ def false
274
+ false
275
+ end
255
276
  end
277
+
278
+ hash = serializable(model, serializer: serializer).serializable_hash
279
+ assert_equal(s[:included], hash.key?(:association), "Error with #{s[:options]}")
256
280
  end
281
+ end
257
282
 
258
- model = ::Model.new
259
- hash = serializable(model, serializer: serializer).serializable_hash
260
- expected = { if_assoc_included: nil, unless_assoc_included: nil }
283
+ def test_illegal_conditional_associations
284
+ exception = assert_raises(TypeError) do
285
+ Class.new(ActiveModel::Serializer) do
286
+ belongs_to :x, if: nil
287
+ end
288
+ end
261
289
 
262
- assert_equal(expected, hash)
290
+ assert_match(/:if should be a Symbol, String or Proc/, exception.message)
263
291
  end
264
292
  end
265
293
  end
@@ -96,27 +96,55 @@ module ActiveModel
96
96
  assert_equal(expected, hash)
97
97
  end
98
98
 
99
- def test_conditional_attributes
100
- serializer = Class.new(ActiveModel::Serializer) do
101
- attribute :if_attribute_included, if: :true
102
- attribute :if_attribute_excluded, if: :false
103
- attribute :unless_attribute_included, unless: :false
104
- attribute :unless_attribute_excluded, unless: :true
105
-
106
- def true
107
- true
99
+ # rubocop:disable Metrics/AbcSize
100
+ def test_conditional_associations
101
+ model = ::Model.new(true: true, false: false)
102
+
103
+ scenarios = [
104
+ { options: { if: :true }, included: true },
105
+ { options: { if: :false }, included: false },
106
+ { options: { unless: :false }, included: true },
107
+ { options: { unless: :true }, included: false },
108
+ { options: { if: 'object.true' }, included: true },
109
+ { options: { if: 'object.false' }, included: false },
110
+ { options: { unless: 'object.false' }, included: true },
111
+ { options: { unless: 'object.true' }, included: false },
112
+ { options: { if: -> { object.true } }, included: true },
113
+ { options: { if: -> { object.false } }, included: false },
114
+ { options: { unless: -> { object.false } }, included: true },
115
+ { options: { unless: -> { object.true } }, included: false },
116
+ { options: { if: -> (s) { s.object.true } }, included: true },
117
+ { options: { if: -> (s) { s.object.false } }, included: false },
118
+ { options: { unless: -> (s) { s.object.false } }, included: true },
119
+ { options: { unless: -> (s) { s.object.true } }, included: false }
120
+ ]
121
+
122
+ scenarios.each do |s|
123
+ serializer = Class.new(ActiveModel::Serializer) do
124
+ attribute :attribute, s[:options]
125
+
126
+ def true
127
+ true
128
+ end
129
+
130
+ def false
131
+ false
132
+ end
108
133
  end
109
134
 
110
- def false
111
- false
112
- end
135
+ hash = serializable(model, serializer: serializer).serializable_hash
136
+ assert_equal(s[:included], hash.key?(:attribute), "Error with #{s[:options]}")
113
137
  end
138
+ end
114
139
 
115
- model = ::Model.new
116
- hash = serializable(model, serializer: serializer).serializable_hash
117
- expected = { if_attribute_included: nil, unless_attribute_included: nil }
140
+ def test_illegal_conditional_attributes
141
+ exception = assert_raises(TypeError) do
142
+ Class.new(ActiveModel::Serializer) do
143
+ attribute :x, if: nil
144
+ end
145
+ end
118
146
 
119
- assert_equal(expected, hash)
147
+ assert_match(/:if should be a Symbol, String or Proc/, exception.message)
120
148
  end
121
149
  end
122
150
  end