cheap_ams 0.10.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.travis.yml +26 -0
  4. data/CHANGELOG.md +13 -0
  5. data/CONTRIBUTING.md +31 -0
  6. data/Gemfile +35 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +348 -0
  9. data/Rakefile +12 -0
  10. data/appveyor.yml +25 -0
  11. data/cheap_ams.gemspec +49 -0
  12. data/docs/README.md +27 -0
  13. data/docs/general/adapters.md +51 -0
  14. data/docs/general/getting_started.md +73 -0
  15. data/docs/howto/add_pagination_links.md +112 -0
  16. data/docs/howto/add_root_key.md +51 -0
  17. data/lib/action_controller/serialization.rb +62 -0
  18. data/lib/active_model/serializable_resource.rb +84 -0
  19. data/lib/active_model/serializer/adapter/flatten_json.rb +19 -0
  20. data/lib/active_model/serializer/adapter/fragment_cache.rb +82 -0
  21. data/lib/active_model/serializer/adapter/json/fragment_cache.rb +16 -0
  22. data/lib/active_model/serializer/adapter/json.rb +53 -0
  23. data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +24 -0
  24. data/lib/active_model/serializer/adapter/json_api/pagination_links.rb +58 -0
  25. data/lib/active_model/serializer/adapter/json_api.rb +183 -0
  26. data/lib/active_model/serializer/adapter/null.rb +11 -0
  27. data/lib/active_model/serializer/adapter.rb +98 -0
  28. data/lib/active_model/serializer/array_serializer.rb +35 -0
  29. data/lib/active_model/serializer/association.rb +21 -0
  30. data/lib/active_model/serializer/associations.rb +97 -0
  31. data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
  32. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  33. data/lib/active_model/serializer/configuration.rb +14 -0
  34. data/lib/active_model/serializer/fieldset.rb +42 -0
  35. data/lib/active_model/serializer/has_many_reflection.rb +10 -0
  36. data/lib/active_model/serializer/has_one_reflection.rb +10 -0
  37. data/lib/active_model/serializer/lint.rb +131 -0
  38. data/lib/active_model/serializer/railtie.rb +9 -0
  39. data/lib/active_model/serializer/reflection.rb +74 -0
  40. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  41. data/lib/active_model/serializer/version.rb +5 -0
  42. data/lib/active_model/serializer.rb +205 -0
  43. data/lib/active_model_serializers.rb +29 -0
  44. data/lib/generators/serializer/USAGE +6 -0
  45. data/lib/generators/serializer/resource_override.rb +12 -0
  46. data/lib/generators/serializer/serializer_generator.rb +37 -0
  47. data/lib/generators/serializer/templates/serializer.rb +8 -0
  48. data/test/action_controller/adapter_selector_test.rb +53 -0
  49. data/test/action_controller/explicit_serializer_test.rb +134 -0
  50. data/test/action_controller/json_api/linked_test.rb +180 -0
  51. data/test/action_controller/json_api/pagination_test.rb +116 -0
  52. data/test/action_controller/serialization_scope_name_test.rb +67 -0
  53. data/test/action_controller/serialization_test.rb +426 -0
  54. data/test/adapter/fragment_cache_test.rb +37 -0
  55. data/test/adapter/json/belongs_to_test.rb +47 -0
  56. data/test/adapter/json/collection_test.rb +82 -0
  57. data/test/adapter/json/has_many_test.rb +47 -0
  58. data/test/adapter/json_api/belongs_to_test.rb +157 -0
  59. data/test/adapter/json_api/collection_test.rb +96 -0
  60. data/test/adapter/json_api/has_many_embed_ids_test.rb +45 -0
  61. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
  62. data/test/adapter/json_api/has_many_test.rb +145 -0
  63. data/test/adapter/json_api/has_one_test.rb +81 -0
  64. data/test/adapter/json_api/json_api_test.rb +38 -0
  65. data/test/adapter/json_api/linked_test.rb +283 -0
  66. data/test/adapter/json_api/pagination_links_test.rb +115 -0
  67. data/test/adapter/json_api/resource_type_config_test.rb +59 -0
  68. data/test/adapter/json_test.rb +47 -0
  69. data/test/adapter/null_test.rb +25 -0
  70. data/test/adapter_test.rb +52 -0
  71. data/test/array_serializer_test.rb +97 -0
  72. data/test/capture_warnings.rb +57 -0
  73. data/test/fixtures/active_record.rb +57 -0
  74. data/test/fixtures/poro.rb +266 -0
  75. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  76. data/test/generators/serializer_generator_test.rb +56 -0
  77. data/test/lint_test.rb +44 -0
  78. data/test/poro_test.rb +9 -0
  79. data/test/serializable_resource_test.rb +27 -0
  80. data/test/serializers/adapter_for_test.rb +50 -0
  81. data/test/serializers/association_macros_test.rb +36 -0
  82. data/test/serializers/associations_test.rb +150 -0
  83. data/test/serializers/attribute_test.rb +62 -0
  84. data/test/serializers/attributes_test.rb +63 -0
  85. data/test/serializers/cache_test.rb +164 -0
  86. data/test/serializers/configuration_test.rb +15 -0
  87. data/test/serializers/fieldset_test.rb +26 -0
  88. data/test/serializers/meta_test.rb +121 -0
  89. data/test/serializers/options_test.rb +21 -0
  90. data/test/serializers/root_test.rb +23 -0
  91. data/test/serializers/serializer_for_test.rb +65 -0
  92. data/test/serializers/urls_test.rb +26 -0
  93. data/test/support/rails_app.rb +21 -0
  94. data/test/support/stream_capture.rb +49 -0
  95. data/test/support/test_case.rb +5 -0
  96. data/test/test_helper.rb +41 -0
  97. metadata +287 -0
@@ -0,0 +1,183 @@
1
+ require 'active_model/serializer/adapter/json_api/fragment_cache'
2
+ require 'active_model/serializer/adapter/json_api/pagination_links'
3
+
4
+ module ActiveModel
5
+ class Serializer
6
+ class Adapter
7
+ class JsonApi < Adapter
8
+ def initialize(serializer, options = {})
9
+ super
10
+ @hash = { data: [] }
11
+
12
+ if fields = options.delete(:fields)
13
+ @fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key)
14
+ else
15
+ @fieldset = options[:fieldset]
16
+ end
17
+ end
18
+
19
+ def serializable_hash(options = nil)
20
+ options ||= {}
21
+ if serializer.respond_to?(:each)
22
+ serializer.each do |s|
23
+ result = self.class.new(s, @options.merge(fieldset: @fieldset)).serializable_hash(options)
24
+ @hash[:data] << result[:data]
25
+
26
+ if result[:included]
27
+ @hash[:included] ||= []
28
+ @hash[:included] |= result[:included]
29
+ end
30
+ end
31
+
32
+ add_links(options)
33
+ else
34
+ @hash[:data] = attributes_for_serializer(serializer, options)
35
+ add_resource_relationships(@hash[:data], serializer)
36
+ end
37
+ @hash
38
+ end
39
+
40
+ def fragment_cache(cached_hash, non_cached_hash)
41
+ root = false if @options.include?(:include)
42
+ JsonApi::FragmentCache.new().fragment_cache(root, cached_hash, non_cached_hash)
43
+ end
44
+
45
+ private
46
+
47
+ def add_relationships(resource, name, serializers)
48
+ resource[:relationships] ||= {}
49
+ resource[:relationships][name] ||= { data: [] }
50
+ resource[:relationships][name][:data] += serializers.map { |serializer| { type: serializer.json_api_type, id: serializer.id.to_s } }
51
+ end
52
+
53
+ def add_relationship(resource, name, serializer, val=nil)
54
+ resource[:relationships] ||= {}
55
+ resource[:relationships][name] = { data: val }
56
+
57
+ if serializer && serializer.object
58
+ resource[:relationships][name][:data] = { type: serializer.json_api_type, id: serializer.id.to_s }
59
+ end
60
+ end
61
+
62
+ def add_included(resource_name, serializers, parent = nil)
63
+ unless serializers.respond_to?(:each)
64
+ return unless serializers.object
65
+ serializers = Array(serializers)
66
+ end
67
+ resource_path = [parent, resource_name].compact.join('.')
68
+ if include_assoc?(resource_path)
69
+ @hash[:included] ||= []
70
+
71
+ serializers.each do |serializer|
72
+ attrs = attributes_for_serializer(serializer, @options)
73
+
74
+ add_resource_relationships(attrs, serializer, add_included: false)
75
+
76
+ @hash[:included].push(attrs) unless @hash[:included].include?(attrs)
77
+ end
78
+ end
79
+
80
+ serializers.each do |serializer|
81
+ serializer.associations.each do |association|
82
+ serializer = association.serializer
83
+
84
+ add_included(association.key, serializer, resource_path) if serializer
85
+ end if include_nested_assoc? resource_path
86
+ end
87
+ end
88
+
89
+ def attributes_for_serializer(serializer, options)
90
+ if serializer.respond_to?(:each)
91
+ result = []
92
+ serializer.each do |object|
93
+ result << resource_object_for(object, options)
94
+ end
95
+ else
96
+ result = resource_object_for(serializer, options)
97
+ end
98
+ result
99
+ end
100
+
101
+ def resource_object_for(serializer, options)
102
+ options[:fields] = @fieldset && @fieldset.fields_for(serializer)
103
+ options[:required_fields] = [:id, :json_api_type]
104
+
105
+ cache_check(serializer) do
106
+ attributes = serializer.attributes(options)
107
+
108
+ result = {
109
+ id: attributes.delete(:id).to_s,
110
+ type: attributes.delete(:json_api_type)
111
+ }
112
+
113
+ result[:attributes] = attributes if attributes.any?
114
+ result
115
+ end
116
+ end
117
+
118
+ def include_assoc?(assoc)
119
+ return false unless @options[:include]
120
+ check_assoc("#{assoc}$")
121
+ end
122
+
123
+ def include_nested_assoc?(assoc)
124
+ return false unless @options[:include]
125
+ check_assoc("#{assoc}.")
126
+ end
127
+
128
+ def check_assoc(assoc)
129
+ include_opt = @options[:include]
130
+ include_opt = include_opt.split(',') if include_opt.is_a?(String)
131
+ include_opt.any? do |s|
132
+ s.match(/^#{assoc.gsub('.', '\.')}/)
133
+ end
134
+ end
135
+
136
+ def add_resource_relationships(attrs, serializer, options = {})
137
+ options[:add_included] = options.fetch(:add_included, true)
138
+
139
+ serializer.associations.each do |association|
140
+ key = association.key
141
+ serializer = association.serializer
142
+ opts = association.options
143
+
144
+ attrs[:relationships] ||= {}
145
+
146
+ if serializer.respond_to?(:each)
147
+ add_relationships(attrs, key, serializer)
148
+ else
149
+ if opts[:virtual_value]
150
+ add_relationship(attrs, key, nil, opts[:virtual_value])
151
+ else
152
+ add_relationship(attrs, key, serializer)
153
+ end
154
+ end
155
+
156
+ if options[:add_included]
157
+ Array(serializer).each do |s|
158
+ add_included(key, s)
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ def add_links(options)
165
+ links = @hash.fetch(:links) { {} }
166
+ resources = serializer.instance_variable_get(:@resource)
167
+ @hash[:links] = add_pagination_links(links, resources, options) if is_paginated?(resources)
168
+ end
169
+
170
+ def add_pagination_links(links, resources, options)
171
+ pagination_links = JsonApi::PaginationLinks.new(resources, options[:context]).serializable_hash(options)
172
+ links.update(pagination_links)
173
+ end
174
+
175
+ def is_paginated?(resource)
176
+ resource.respond_to?(:current_page) &&
177
+ resource.respond_to?(:total_pages) &&
178
+ resource.respond_to?(:size)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Adapter
4
+ class Null < Adapter
5
+ def serializable_hash(options = nil)
6
+ {}
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,98 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Adapter
4
+ extend ActiveSupport::Autoload
5
+ require 'active_model/serializer/adapter/json'
6
+ require 'active_model/serializer/adapter/json_api'
7
+ autoload :FlattenJson
8
+ autoload :Null
9
+ autoload :FragmentCache
10
+
11
+ def self.create(resource, options = {})
12
+ override = options.delete(:adapter)
13
+ klass = override ? adapter_class(override) : ActiveModel::Serializer.adapter
14
+ klass.new(resource, options)
15
+ end
16
+
17
+ def self.adapter_class(adapter)
18
+ adapter_name = adapter.to_s.classify.sub("API", "Api")
19
+ "ActiveModel::Serializer::Adapter::#{adapter_name}".safe_constantize
20
+ end
21
+
22
+ attr_reader :serializer
23
+
24
+ def initialize(serializer, options = {})
25
+ @serializer = serializer
26
+ @options = options
27
+ end
28
+
29
+ def serializable_hash(options = nil)
30
+ raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
31
+ end
32
+
33
+ def as_json(options = nil)
34
+ hash = serializable_hash(options)
35
+ include_meta(hash)
36
+ hash
37
+ end
38
+
39
+ def fragment_cache(*args)
40
+ raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
41
+ end
42
+
43
+ private
44
+
45
+ def cache_check(serializer)
46
+ @cached_serializer = serializer
47
+ @klass = @cached_serializer.class
48
+ if is_cached?
49
+ @klass._cache.fetch(cache_key, @klass._cache_options) do
50
+ yield
51
+ end
52
+ elsif is_fragment_cached?
53
+ FragmentCache.new(self, @cached_serializer, @options).fetch
54
+ else
55
+ yield
56
+ end
57
+ end
58
+
59
+ def is_cached?
60
+ @klass._cache && !@klass._cache_only && !@klass._cache_except
61
+ end
62
+
63
+ def is_fragment_cached?
64
+ @klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except
65
+ end
66
+
67
+ def cache_key
68
+ parts = []
69
+ parts << object_cache_key
70
+ parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
71
+ parts.join("/")
72
+ end
73
+
74
+ def object_cache_key
75
+ object_time_safe = @cached_serializer.object.updated_at
76
+ object_time_safe = object_time_safe.strftime("%Y%m%d%H%M%S%9N") if object_time_safe.respond_to?(:strftime)
77
+ (@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}" : @cached_serializer.object.cache_key
78
+ end
79
+
80
+ def meta
81
+ serializer.meta if serializer.respond_to?(:meta)
82
+ end
83
+
84
+ def meta_key
85
+ serializer.meta_key || "meta"
86
+ end
87
+
88
+ def root
89
+ serializer.json_key.to_sym if serializer.json_key
90
+ end
91
+
92
+ def include_meta(json)
93
+ json[meta_key] = meta if meta
94
+ json
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,35 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class ArraySerializer
4
+ NoSerializerError = Class.new(StandardError)
5
+ include Enumerable
6
+ delegate :each, to: :@objects
7
+
8
+ attr_reader :root, :meta, :meta_key
9
+
10
+ def initialize(objects, options = {})
11
+ @root = options[:root]
12
+ @resource = objects
13
+ @objects = objects.map do |object|
14
+ serializer_class = options.fetch(
15
+ :serializer,
16
+ ActiveModel::Serializer.serializer_for(object)
17
+ )
18
+
19
+ if serializer_class.nil?
20
+ fail NoSerializerError, "No serializer found for object: #{object.inspect}"
21
+ else
22
+ serializer_class.new(object, options.except(:serializer))
23
+ end
24
+ end
25
+ @meta = options[:meta]
26
+ @meta_key = options[:meta_key]
27
+ end
28
+
29
+ def json_key
30
+ key = root || @objects.first.try(:json_key) || @resource.try(:name).try(:underscore)
31
+ key.try(:pluralize)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # This class hold all information about serializer's association.
4
+ #
5
+ # @param [Symbol] name
6
+ # @param [ActiveModel::Serializer] serializer
7
+ # @param [Hash{Symbol => Object}] options
8
+ #
9
+ # @example
10
+ # Association.new(:comments, CommentSummarySerializer)
11
+ #
12
+ Association = Struct.new(:name, :serializer, :options) do
13
+
14
+ # @return [Symbol]
15
+ #
16
+ def key
17
+ options.fetch(:key, name)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,97 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # Defines an association in the object should be rendered.
4
+ #
5
+ # The serializer object should implement the association name
6
+ # as a method which should return an array when invoked. If a method
7
+ # with the association name does not exist, the association name is
8
+ # dispatched to the serialized object.
9
+ #
10
+ module Associations
11
+ extend ActiveSupport::Concern
12
+
13
+ included do |base|
14
+ class << base
15
+ attr_accessor :_reflections
16
+ end
17
+
18
+ autoload :Association
19
+ autoload :Reflection
20
+ autoload :SingularReflection
21
+ autoload :CollectionReflection
22
+ autoload :BelongsToReflection
23
+ autoload :HasOneReflection
24
+ autoload :HasManyReflection
25
+ end
26
+
27
+ module ClassMethods
28
+ def inherited(base)
29
+ base._reflections = self._reflections.try(:dup) || []
30
+ end
31
+
32
+ # @param [Symbol] name of the association
33
+ # @param [Hash<Symbol => any>] options for the reflection
34
+ # @return [void]
35
+ #
36
+ # @example
37
+ # has_many :comments, serializer: CommentSummarySerializer
38
+ #
39
+ def has_many(name, options = {})
40
+ associate HasManyReflection.new(name, options)
41
+ end
42
+
43
+ # @param [Symbol] name of the association
44
+ # @param [Hash<Symbol => any>] options for the reflection
45
+ # @return [void]
46
+ #
47
+ # @example
48
+ # belongs_to :author, serializer: AuthorSerializer
49
+ #
50
+ def belongs_to(name, options = {})
51
+ associate BelongsToReflection.new(name, options)
52
+ end
53
+
54
+ # @param [Symbol] name of the association
55
+ # @param [Hash<Symbol => any>] options for the reflection
56
+ # @return [void]
57
+ #
58
+ # @example
59
+ # has_one :author, serializer: AuthorSerializer
60
+ #
61
+ def has_one(name, options = {})
62
+ associate HasOneReflection.new(name, options)
63
+ end
64
+
65
+ private
66
+
67
+ # Add reflection and define {name} accessor.
68
+ # @param [ActiveModel::Serializer::Reflection] reflection
69
+ # @return [void]
70
+ #
71
+ # @api private
72
+ #
73
+ def associate(reflection)
74
+ self._reflections = _reflections.dup
75
+
76
+ define_method reflection.name do
77
+ object.send reflection.name
78
+ end unless method_defined?(reflection.name)
79
+
80
+ self._reflections << reflection
81
+ end
82
+ end
83
+
84
+ # @return [Enumerator<Association>]
85
+ #
86
+ def associations
87
+ return unless object
88
+
89
+ Enumerator.new do |y|
90
+ self.class._reflections.each do |reflection|
91
+ y.yield reflection.build_association(self, options)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class BelongsToReflection < SingularReflection
5
+ def macro
6
+ :belongs_to
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class CollectionReflection < Reflection
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Configuration
4
+ include ActiveSupport::Configurable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do |base|
8
+ base.config.array_serializer = ActiveModel::Serializer::ArraySerializer
9
+ base.config.adapter = :flatten_json
10
+ base.config.jsonapi_resource_type = :plural
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Fieldset
4
+
5
+ def initialize(fields, root = nil)
6
+ @root = root
7
+ @raw_fields = fields
8
+ end
9
+
10
+ def fields
11
+ @fields ||= parsed_fields
12
+ end
13
+
14
+ def fields_for(serializer)
15
+ key = serializer.json_key
16
+ fields[key.to_sym] || fields[key.pluralize.to_sym]
17
+ end
18
+
19
+ private
20
+
21
+ ActiveModelSerializers.silence_warnings do
22
+ attr_reader :raw_fields, :root
23
+ end
24
+
25
+ def parsed_fields
26
+ if raw_fields.is_a?(Hash)
27
+ raw_fields.inject({}) { |h,(k,v)| h[k.to_sym] = v.map(&:to_sym); h}
28
+ elsif raw_fields.is_a?(Array)
29
+ if root.nil?
30
+ raise ArgumentError, 'The root argument must be specified if the fileds argument is an array.'
31
+ end
32
+ hash = {}
33
+ hash[root.to_sym] = raw_fields.map(&:to_sym)
34
+ hash
35
+ else
36
+ {}
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class HasManyReflection < CollectionReflection
5
+ def macro
6
+ :has_many
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class HasOneReflection < SingularReflection
5
+ def macro
6
+ :has_one
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,131 @@
1
+ module ActiveModel::Serializer::Lint
2
+ # == Active \Model \Serializer \Lint \Tests
3
+ #
4
+ # You can test whether an object is compliant with the Active \Model \Serializers
5
+ # API by including <tt>ActiveModel::Serializer::Lint::Tests</tt> in your TestCase.
6
+ # It will include tests that tell you whether your object is fully compliant,
7
+ # or if not, which aspects of the API are not implemented.
8
+ #
9
+ # Note an object is not required to implement all APIs in order to work
10
+ # with Active \Model \Serializers. This module only intends to provide guidance in case
11
+ # you want all features out of the box.
12
+ #
13
+ # These tests do not attempt to determine the semantic correctness of the
14
+ # returned values. For instance, you could implement <tt>serializable_hash</tt> to
15
+ # always return +{}+, and the tests would pass. It is up to you to ensure
16
+ # that the values are semantically meaningful.
17
+ module Tests
18
+
19
+ # Passes if the object responds to <tt>serializable_hash</tt> and if it takes
20
+ # zero or one arguments.
21
+ # Fails otherwise.
22
+ #
23
+ # <tt>serializable_hash</tt> returns a hash representation of a object's attributes.
24
+ # Typically, it is implemented by including ActiveModel::Serialization.
25
+ def test_serializable_hash
26
+ assert_respond_to resource, :serializable_hash, "The resource should respond to serializable_hash"
27
+ resource.serializable_hash
28
+ resource.serializable_hash(nil)
29
+ end
30
+
31
+ # Passes if the object responds to <tt>read_attribute_for_serialization</tt>
32
+ # and if it requires one argument (the attribute to be read).
33
+ # Fails otherwise.
34
+ #
35
+ # <tt>read_attribute_for_serialization</tt> gets the attribute value for serialization
36
+ # Typically, it is implemented by including ActiveModel::Serialization.
37
+ def test_read_attribute_for_serialization
38
+ assert_respond_to resource, :read_attribute_for_serialization, "The resource should respond to read_attribute_for_serialization"
39
+ actual_arity = resource.method(:read_attribute_for_serialization).arity
40
+ if defined?(::Rubinius)
41
+ # 1 for def read_attribute_for_serialization(name); end
42
+ # -2 for alias :read_attribute_for_serialization :send for rbx because :shrug:
43
+ assert_includes [1, -2], actual_arity, "expected #{actual_arity.inspect} to be 1 or -2"
44
+ else
45
+ # using absolute value since arity is:
46
+ # 1 for def read_attribute_for_serialization(name); end
47
+ # -1 for alias :read_attribute_for_serialization :send
48
+ assert_includes [1, -1], actual_arity, "expected #{actual_arity.inspect} to be 1 or -1"
49
+ end
50
+ end
51
+
52
+ # Passes if the object responds to <tt>as_json</tt> and if it takes
53
+ # zero or one arguments.
54
+ # Fails otherwise.
55
+ #
56
+ # <tt>as_json</tt> returns a hash representation of a serialized object.
57
+ # It may delegate to <tt>serializable_hash</tt>
58
+ # Typically, it is implemented either by including ActiveModel::Serialization
59
+ # which includes ActiveModel::Serializers::JSON.
60
+ # or by the JSON gem when required.
61
+ def test_as_json
62
+ assert_respond_to resource, :as_json
63
+ resource.as_json
64
+ resource.as_json(nil)
65
+ end
66
+
67
+ # Passes if the object responds to <tt>to_json</tt> and if it takes
68
+ # zero or one arguments.
69
+ # Fails otherwise.
70
+ #
71
+ # <tt>to_json</tt> returns a string representation (JSON) of a serialized object.
72
+ # It may be called on the result of <tt>as_json</tt>.
73
+ # Typically, it is implemented on all objects when the JSON gem is required.
74
+ def test_to_json
75
+ assert_respond_to resource, :to_json
76
+ resource.to_json
77
+ resource.to_json(nil)
78
+ end
79
+
80
+ # Passes if the object responds to <tt>cache_key</tt> and if it takes no
81
+ # arguments (Rails 4.0) or a splat (Rails 4.1+).
82
+ # Fails otherwise.
83
+ #
84
+ # <tt>cache_key</tt> returns a (self-expiring) unique key for the object,
85
+ # which is used by the adapter.
86
+ # It is not required unless caching is enabled.
87
+ def test_cache_key
88
+ assert_respond_to resource, :cache_key
89
+ actual_arity = resource.method(:cache_key).arity
90
+ # using absolute value since arity is:
91
+ # 0 for Rails 4.1+, *timestamp_names
92
+ # -1 for Rails 4.0, no arguments
93
+ assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1"
94
+ end
95
+
96
+ # Passes if the object responds to <tt>id</tt> and if it takes no
97
+ # arguments.
98
+ # Fails otherwise.
99
+ #
100
+ # <tt>id</tt> returns a unique identifier for the object.
101
+ # It is not required unless caching is enabled.
102
+ def test_id
103
+ assert_respond_to resource, :id
104
+ assert_equal resource.method(:id).arity, 0
105
+ end
106
+
107
+ # Passes if the object's class responds to <tt>model_name</tt> and if it
108
+ # is in an instance of +ActiveModel::Name+.
109
+ # Fails otherwise.
110
+ #
111
+ # <tt>model_name</tt> returns an ActiveModel::Name instance.
112
+ # It is used by the serializer to identify the object's type.
113
+ # It is not required unless caching is enabled.
114
+ def test_model_name
115
+ resource_class = resource.class
116
+ assert_respond_to resource_class, :model_name
117
+ assert_instance_of resource_class.model_name, ActiveModel::Name
118
+ end
119
+
120
+ private
121
+
122
+ def resource
123
+ @resource or fail "'@resource' must be set as the linted object"
124
+ end
125
+
126
+ def assert_instance_of(result, name)
127
+ assert result.instance_of?(name), "#{result} should be an instance of #{name}"
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,9 @@
1
+ require 'rails/railtie'
2
+ module ActiveModel
3
+ class Railtie < Rails::Railtie
4
+ initializer 'generators' do |app|
5
+ app.load_generators
6
+ require 'generators/serializer/resource_override'
7
+ end
8
+ end
9
+ end