active_model_serializers 0.10.0.rc5 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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