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.
- checksums.yaml +4 -4
- data/.gitignore +10 -0
- data/.travis.yml +0 -8
- data/CHANGELOG.md +23 -0
- data/CONTRIBUTING.md +14 -4
- data/Gemfile +1 -2
- data/README.md +3 -3
- data/active_model_serializers.gemspec +1 -1
- data/appveyor.yml +6 -10
- data/docs/ARCHITECTURE.md +1 -1
- data/docs/README.md +1 -0
- data/docs/general/deserialization.md +19 -19
- data/docs/general/serializers.md +33 -0
- data/docs/howto/serialize_poro.md +32 -0
- data/lib/active_model/serializer.rb +2 -0
- data/lib/active_model/serializer/caching.rb +185 -3
- data/lib/active_model/serializer/field.rb +36 -2
- data/lib/active_model/serializer/lint.rb +8 -18
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers.rb +0 -2
- data/lib/active_model_serializers/adapter/attributes.rb +20 -38
- data/lib/active_model_serializers/adapter/base.rb +8 -15
- data/lib/active_model_serializers/adapter/json.rb +12 -2
- data/lib/active_model_serializers/adapter/json_api.rb +33 -30
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +10 -5
- data/lib/active_model_serializers/adapter/null.rb +1 -2
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +3 -2
- data/lib/active_model_serializers/serializable_resource.rb +1 -1
- data/lib/active_model_serializers/test/schema.rb +44 -9
- data/test/action_controller/json_api/transform_test.rb +2 -1
- data/test/action_controller/serialization_test.rb +3 -1
- data/test/active_model_serializers/test/schema_test.rb +5 -3
- data/test/active_model_serializers/test/serializer_test.rb +1 -2
- data/test/adapter/json/transform_test.rb +14 -14
- data/test/adapter/json_api/has_many_test.rb +3 -2
- data/test/adapter/json_api/has_one_test.rb +3 -2
- data/test/adapter/json_api/pagination_links_test.rb +39 -21
- data/test/adapter/json_api/transform_test.rb +36 -34
- data/test/adapter/polymorphic_test.rb +111 -12
- data/test/adapter_test.rb +27 -0
- data/test/array_serializer_test.rb +10 -25
- data/test/benchmark/bm_caching.rb +17 -15
- data/test/benchmark/controllers.rb +9 -2
- data/test/benchmark/fixtures.rb +56 -4
- data/test/cache_test.rb +103 -6
- data/test/fixtures/active_record.rb +10 -0
- data/test/fixtures/poro.rb +31 -3
- data/test/serializers/associations_test.rb +43 -15
- data/test/serializers/attribute_test.rb +44 -16
- data/test/serializers/meta_test.rb +5 -7
- data/test/support/isolated_unit.rb +0 -1
- data/test/test_helper.rb +19 -21
- metadata +7 -12
- data/lib/active_model_serializers/cached_serializer.rb +0 -87
- data/lib/active_model_serializers/fragment_cache.rb +0 -118
- data/test/active_model_serializers/cached_serializer_test.rb +0 -80
- data/test/active_model_serializers/fragment_cache_test.rb +0 -34
@@ -6,24 +6,29 @@ module ActiveModelSerializers
|
|
6
6
|
|
7
7
|
attr_reader :collection, :context
|
8
8
|
|
9
|
-
def initialize(collection,
|
9
|
+
def initialize(collection, adapter_options)
|
10
10
|
@collection = collection
|
11
|
-
@
|
11
|
+
@adapter_options = adapter_options
|
12
|
+
@context = adapter_options.fetch(:serialization_context)
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
+
def as_json
|
15
16
|
per_page = collection.try(:per_page) || collection.try(:limit_value) || collection.size
|
16
17
|
pages_from.each_with_object({}) do |(key, value), hash|
|
17
18
|
params = query_parameters.merge(page: { number: value, size: per_page }).to_query
|
18
19
|
|
19
|
-
hash[key] = "#{url(
|
20
|
+
hash[key] = "#{url(adapter_options)}?#{params}"
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
protected
|
25
|
+
|
26
|
+
attr_reader :adapter_options
|
27
|
+
|
23
28
|
private
|
24
29
|
|
25
30
|
def pages_from
|
26
|
-
return {} if collection.total_pages
|
31
|
+
return {} if collection.total_pages <= FIRST_PAGE
|
27
32
|
|
28
33
|
{}.tap do |pages|
|
29
34
|
pages[:self] = collection.current_page
|
@@ -57,8 +57,9 @@ end
|
|
57
57
|
ActionController::Renderers.add :jsonapi do |json, options|
|
58
58
|
json = serialize_jsonapi(json, options).to_json(options) unless json.is_a?(String)
|
59
59
|
self.content_type ||= media_type
|
60
|
-
headers.merge! ActiveModelSerializers::Jsonapi::HEADERS[:response]
|
61
60
|
self.response_body = json
|
62
61
|
end
|
63
62
|
|
64
|
-
|
63
|
+
ActiveSupport.on_load(:action_controller) do
|
64
|
+
include ActiveModelSerializers::Jsonapi::ControllerSupport
|
65
|
+
end
|
@@ -2,7 +2,7 @@ require 'set'
|
|
2
2
|
|
3
3
|
module ActiveModelSerializers
|
4
4
|
class SerializableResource
|
5
|
-
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links])
|
5
|
+
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context, :key_transform])
|
6
6
|
include ActiveModelSerializers::Logging
|
7
7
|
|
8
8
|
delegate :serializable_hash, :as_json, :to_json, to: :adapter
|
@@ -10,19 +10,38 @@ module ActiveModelSerializers
|
|
10
10
|
# get :index
|
11
11
|
# assert_response_schema
|
12
12
|
def assert_response_schema(schema_path = nil, message = nil)
|
13
|
-
matcher = AssertResponseSchema.new(schema_path, response, message)
|
13
|
+
matcher = AssertResponseSchema.new(schema_path, request, response, message)
|
14
14
|
assert(matcher.call, matcher.message)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
def assert_request_schema(schema_path = nil, message = nil)
|
18
|
+
matcher = AssertRequestSchema.new(schema_path, request, response, message)
|
19
|
+
assert(matcher.call, matcher.message)
|
20
|
+
end
|
21
|
+
|
22
|
+
# May be renamed
|
23
|
+
def assert_request_response_schema(schema_path = nil, message = nil)
|
24
|
+
assert_request_schema(schema_path, message)
|
25
|
+
assert_response_schema(schema_path, message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def assert_schema(payload, schema_path = nil, message = nil)
|
29
|
+
matcher = AssertSchema.new(schema_path, request, response, message, payload)
|
30
|
+
assert(matcher.call, matcher.message)
|
31
|
+
end
|
19
32
|
|
20
|
-
|
21
|
-
|
33
|
+
MissingSchema = Class.new(Minitest::Assertion)
|
34
|
+
InvalidSchemaError = Class.new(Minitest::Assertion)
|
22
35
|
|
23
|
-
|
36
|
+
class AssertSchema
|
37
|
+
attr_reader :schema_path, :request, :response, :message, :payload
|
38
|
+
|
39
|
+
# Interface may change.
|
40
|
+
def initialize(schema_path, request, response, message, payload = nil)
|
24
41
|
require_json_schema!
|
42
|
+
@request = request
|
25
43
|
@response = response
|
44
|
+
@payload = payload
|
26
45
|
@schema_path = schema_path || schema_path_default
|
27
46
|
@message = message
|
28
47
|
@document_store = JsonSchema::DocumentStore.new
|
@@ -32,7 +51,7 @@ module ActiveModelSerializers
|
|
32
51
|
def call
|
33
52
|
json_schema.expand_references!(store: document_store)
|
34
53
|
status, errors = json_schema.validate(response_body)
|
35
|
-
@message
|
54
|
+
@message = [message, errors.map(&:to_s).to_sentence].compact.join(': ')
|
36
55
|
status
|
37
56
|
end
|
38
57
|
|
@@ -41,11 +60,11 @@ module ActiveModelSerializers
|
|
41
60
|
attr_reader :document_store
|
42
61
|
|
43
62
|
def controller_path
|
44
|
-
|
63
|
+
request.filtered_parameters[:controller]
|
45
64
|
end
|
46
65
|
|
47
66
|
def action
|
48
|
-
|
67
|
+
request.filtered_parameters[:action]
|
49
68
|
end
|
50
69
|
|
51
70
|
def schema_directory
|
@@ -68,6 +87,10 @@ module ActiveModelSerializers
|
|
68
87
|
load_json(response.body)
|
69
88
|
end
|
70
89
|
|
90
|
+
def request_params
|
91
|
+
request.env['action_dispatch.request.request_parameters']
|
92
|
+
end
|
93
|
+
|
71
94
|
def json_schema
|
72
95
|
@json_schema ||= JsonSchema.parse!(schema_data)
|
73
96
|
end
|
@@ -98,6 +121,18 @@ module ActiveModelSerializers
|
|
98
121
|
raise LoadError, "You don't have json_schema installed in your application. Please add it to your Gemfile and run bundle install"
|
99
122
|
end
|
100
123
|
end
|
124
|
+
class AssertResponseSchema < AssertSchema
|
125
|
+
def initialize(*)
|
126
|
+
super
|
127
|
+
@payload = response_body
|
128
|
+
end
|
129
|
+
end
|
130
|
+
class AssertRequestSchema < AssertSchema
|
131
|
+
def initialize(*)
|
132
|
+
super
|
133
|
+
@payload = request_params
|
134
|
+
end
|
135
|
+
end
|
101
136
|
end
|
102
137
|
end
|
103
138
|
end
|
@@ -60,10 +60,11 @@ module ActionController
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def render_resource_with_transform_with_global_config
|
63
|
-
setup_post
|
64
63
|
old_transform = ActiveModelSerializers.config.key_transform
|
64
|
+
setup_post
|
65
65
|
ActiveModelSerializers.config.key_transform = :camel_lower
|
66
66
|
render json: @post, serializer: PostSerializer, adapter: :json_api
|
67
|
+
ensure
|
67
68
|
ActiveModelSerializers.config.key_transform = old_transform
|
68
69
|
end
|
69
70
|
end
|
@@ -454,13 +454,15 @@ module ActionController
|
|
454
454
|
end
|
455
455
|
|
456
456
|
def test_render_event_is_emmited
|
457
|
-
::ActiveSupport::Notifications.subscribe('render.active_model_serializers') do |name|
|
457
|
+
subscriber = ::ActiveSupport::Notifications.subscribe('render.active_model_serializers') do |name|
|
458
458
|
@name = name
|
459
459
|
end
|
460
460
|
|
461
461
|
get :render_using_implicit_serializer
|
462
462
|
|
463
463
|
assert_equal 'render.active_model_serializers', @name
|
464
|
+
ensure
|
465
|
+
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
|
464
466
|
end
|
465
467
|
end
|
466
468
|
end
|
@@ -54,13 +54,15 @@ module ActiveModelSerializers
|
|
54
54
|
|
55
55
|
def test_that_raises_error_with_a_custom_message_with_a_invalid_schema
|
56
56
|
message = 'oh boy the show is broken'
|
57
|
+
exception_message = "#/name: failed schema #/properties/name: For 'properties/name', \"Name 1\" is not an integer. and #/description: failed schema #/properties/description: For 'properties/description', \"Description 1\" is not a boolean."
|
58
|
+
expected_message = "#{message}: #{exception_message}"
|
57
59
|
|
58
60
|
get :show
|
59
61
|
|
60
62
|
error = assert_raises Minitest::Assertion do
|
61
63
|
assert_response_schema(nil, message)
|
62
64
|
end
|
63
|
-
assert_equal(
|
65
|
+
assert_equal(expected_message, error.message)
|
64
66
|
end
|
65
67
|
|
66
68
|
def test_that_assert_with_a_custom_schema
|
@@ -102,14 +104,14 @@ module ActiveModelSerializers
|
|
102
104
|
end
|
103
105
|
|
104
106
|
def test_with_a_non_existent_file
|
105
|
-
message =
|
107
|
+
message = 'No Schema file at test/support/schemas/non-existent.json'
|
106
108
|
|
107
109
|
get :show
|
108
110
|
|
109
111
|
error = assert_raises ActiveModelSerializers::Test::Schema::MissingSchema do
|
110
112
|
assert_response_schema('non-existent.json')
|
111
113
|
end
|
112
|
-
|
114
|
+
assert_equal(message, error.message)
|
113
115
|
end
|
114
116
|
|
115
117
|
def test_that_raises_with_a_invalid_json_body
|
@@ -10,9 +10,8 @@ module ActiveModelSerializers
|
|
10
10
|
render json: Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
11
11
|
end
|
12
12
|
|
13
|
-
# For Rails4.0
|
14
13
|
def render_some_text
|
15
|
-
|
14
|
+
render(plain: 'ok')
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
@@ -8,9 +8,11 @@ module ActiveModelSerializers
|
|
8
8
|
context = Minitest::Mock.new
|
9
9
|
context.expect(:request_url, URI)
|
10
10
|
context.expect(:query_parameters, {})
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
options = {}
|
12
|
+
options[:key_transform] = key_transform if key_transform
|
13
|
+
options[:serialization_context] = context
|
14
|
+
serializer = CustomBlogSerializer.new(@blog)
|
15
|
+
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer, options)
|
14
16
|
end
|
15
17
|
|
16
18
|
Post = Class.new(::Model)
|
@@ -18,24 +20,22 @@ module ActiveModelSerializers
|
|
18
20
|
attributes :id, :title, :body, :publish_at
|
19
21
|
end
|
20
22
|
|
21
|
-
|
23
|
+
setup do
|
22
24
|
ActionController::Base.cache_store.clear
|
23
25
|
@blog = Blog.new(id: 1, name: 'My Blog!!', special_attribute: 'neat')
|
24
|
-
serializer = CustomBlogSerializer.new(@blog)
|
25
|
-
@adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_transform_default
|
29
29
|
mock_request
|
30
30
|
assert_equal({
|
31
31
|
blog: { id: 1, special_attribute: 'neat', articles: nil }
|
32
|
-
}, @adapter.serializable_hash
|
32
|
+
}, @adapter.serializable_hash)
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_transform_global_config
|
36
36
|
mock_request
|
37
37
|
result = with_config(key_transform: :camel_lower) do
|
38
|
-
@adapter.serializable_hash
|
38
|
+
@adapter.serializable_hash
|
39
39
|
end
|
40
40
|
assert_equal({
|
41
41
|
blog: { id: 1, specialAttribute: 'neat', articles: nil }
|
@@ -45,7 +45,7 @@ module ActiveModelSerializers
|
|
45
45
|
def test_transform_serialization_ctx_overrides_global_config
|
46
46
|
mock_request(:camel)
|
47
47
|
result = with_config(key_transform: :camel_lower) do
|
48
|
-
@adapter.serializable_hash
|
48
|
+
@adapter.serializable_hash
|
49
49
|
end
|
50
50
|
assert_equal({
|
51
51
|
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
|
@@ -56,7 +56,7 @@ module ActiveModelSerializers
|
|
56
56
|
mock_request(:blam)
|
57
57
|
result = nil
|
58
58
|
assert_raises NoMethodError do
|
59
|
-
result = @adapter.serializable_hash
|
59
|
+
result = @adapter.serializable_hash
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -64,28 +64,28 @@ module ActiveModelSerializers
|
|
64
64
|
mock_request(:dash)
|
65
65
|
assert_equal({
|
66
66
|
blog: { id: 1, :"special-attribute" => 'neat', articles: nil }
|
67
|
-
}, @adapter.serializable_hash
|
67
|
+
}, @adapter.serializable_hash)
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_transform_unaltered
|
71
71
|
mock_request(:unaltered)
|
72
72
|
assert_equal({
|
73
73
|
blog: { id: 1, special_attribute: 'neat', articles: nil }
|
74
|
-
}, @adapter.serializable_hash
|
74
|
+
}, @adapter.serializable_hash)
|
75
75
|
end
|
76
76
|
|
77
77
|
def test_transform_camel
|
78
78
|
mock_request(:camel)
|
79
79
|
assert_equal({
|
80
80
|
Blog: { Id: 1, SpecialAttribute: 'neat', Articles: nil }
|
81
|
-
}, @adapter.serializable_hash
|
81
|
+
}, @adapter.serializable_hash)
|
82
82
|
end
|
83
83
|
|
84
84
|
def test_transform_camel_lower
|
85
85
|
mock_request(:camel_lower)
|
86
86
|
assert_equal({
|
87
87
|
blog: { id: 1, specialAttribute: 'neat', articles: nil }
|
88
|
-
}, @adapter.serializable_hash
|
88
|
+
}, @adapter.serializable_hash)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
@@ -131,8 +131,9 @@ module ActiveModelSerializers
|
|
131
131
|
id: '1',
|
132
132
|
type: 'virtual-values',
|
133
133
|
relationships: {
|
134
|
-
maker: { data: { id: 1 } },
|
135
|
-
reviews: { data: [{
|
134
|
+
maker: { data: { type: 'makers', id: '1' } },
|
135
|
+
reviews: { data: [{ type: 'reviews', id: '1' },
|
136
|
+
{ type: 'reviews', id: '2' }] }
|
136
137
|
}
|
137
138
|
}
|
138
139
|
}, adapter.serializable_hash)
|
@@ -65,8 +65,9 @@ module ActiveModelSerializers
|
|
65
65
|
id: '1',
|
66
66
|
type: 'virtual-values',
|
67
67
|
relationships: {
|
68
|
-
maker: { data: { id: 1 } },
|
69
|
-
reviews: { data: [{
|
68
|
+
maker: { data: { type: 'makers', id: '1' } },
|
69
|
+
reviews: { data: [{ type: 'reviews', id: '1' },
|
70
|
+
{ type: 'reviews', id: '2' }] }
|
70
71
|
}
|
71
72
|
}
|
72
73
|
}
|
@@ -25,13 +25,13 @@ module ActiveModelSerializers
|
|
25
25
|
context = Minitest::Mock.new
|
26
26
|
context.expect(:request_url, original_url)
|
27
27
|
context.expect(:query_parameters, query_parameters)
|
28
|
-
|
29
|
-
@options[:serialization_context] = context
|
28
|
+
context.expect(:key_transform, nil)
|
30
29
|
end
|
31
30
|
|
32
|
-
def load_adapter(paginated_collection,
|
33
|
-
|
34
|
-
|
31
|
+
def load_adapter(paginated_collection, mock_request = nil)
|
32
|
+
render_options = { adapter: :json_api }
|
33
|
+
render_options[:serialization_context] = mock_request if mock_request
|
34
|
+
serializable(paginated_collection, render_options)
|
35
35
|
end
|
36
36
|
|
37
37
|
def using_kaminari(page = 2)
|
@@ -101,44 +101,62 @@ module ActiveModelSerializers
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
+
def expected_response_with_no_data_pagination_links
|
105
|
+
{}.tap do |hash|
|
106
|
+
hash[:data] = []
|
107
|
+
hash[:links] = {}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
104
111
|
def test_pagination_links_using_kaminari
|
105
|
-
adapter = load_adapter(using_kaminari)
|
112
|
+
adapter = load_adapter(using_kaminari, mock_request)
|
106
113
|
|
107
|
-
|
108
|
-
assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options)
|
114
|
+
assert_equal expected_response_with_pagination_links, adapter.serializable_hash
|
109
115
|
end
|
110
116
|
|
111
117
|
def test_pagination_links_using_will_paginate
|
112
|
-
adapter = load_adapter(using_will_paginate)
|
118
|
+
adapter = load_adapter(using_will_paginate, mock_request)
|
113
119
|
|
114
|
-
|
115
|
-
assert_equal expected_response_with_pagination_links, adapter.serializable_hash(@options)
|
120
|
+
assert_equal expected_response_with_pagination_links, adapter.serializable_hash
|
116
121
|
end
|
117
122
|
|
118
123
|
def test_pagination_links_with_additional_params
|
119
|
-
adapter = load_adapter(using_will_paginate)
|
124
|
+
adapter = load_adapter(using_will_paginate, mock_request({ test: 'test' }))
|
120
125
|
|
121
|
-
mock_request({ test: 'test' })
|
122
126
|
assert_equal expected_response_with_pagination_links_and_additional_params,
|
123
|
-
adapter.serializable_hash
|
127
|
+
adapter.serializable_hash
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_pagination_links_when_zero_results_kaminari
|
131
|
+
@array = []
|
132
|
+
|
133
|
+
adapter = load_adapter(using_kaminari(1), mock_request)
|
134
|
+
|
135
|
+
assert_equal expected_response_with_no_data_pagination_links, adapter.serializable_hash
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_pagination_links_when_zero_results_will_paginate
|
139
|
+
@array = []
|
140
|
+
|
141
|
+
adapter = load_adapter(using_will_paginate(1), mock_request)
|
142
|
+
|
143
|
+
assert_equal expected_response_with_no_data_pagination_links, adapter.serializable_hash
|
124
144
|
end
|
125
145
|
|
126
146
|
def test_last_page_pagination_links_using_kaminari
|
127
|
-
adapter = load_adapter(using_kaminari(3))
|
147
|
+
adapter = load_adapter(using_kaminari(3), mock_request)
|
128
148
|
|
129
|
-
|
130
|
-
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash(@options)
|
149
|
+
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash
|
131
150
|
end
|
132
151
|
|
133
152
|
def test_last_page_pagination_links_using_will_paginate
|
134
|
-
adapter = load_adapter(using_will_paginate(3))
|
153
|
+
adapter = load_adapter(using_will_paginate(3), mock_request)
|
135
154
|
|
136
|
-
|
137
|
-
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash(@options)
|
155
|
+
assert_equal expected_response_with_last_page_pagination_links, adapter.serializable_hash
|
138
156
|
end
|
139
157
|
|
140
158
|
def test_not_showing_pagination_links
|
141
|
-
adapter = load_adapter(@array)
|
159
|
+
adapter = load_adapter(@array, mock_request)
|
142
160
|
|
143
161
|
assert_equal expected_response_without_pagination_links, adapter.serializable_hash
|
144
162
|
end
|