cheap_ams 0.10.0.rc2

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 (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