caprese 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -2
- data/CHANGELOG.md +8 -0
- data/caprese.gemspec +2 -2
- data/lib/caprese/adapter/json_api/resource_identifier.rb +11 -15
- data/lib/caprese/controller/concerns/callbacks.rb +15 -3
- data/lib/caprese/controller/concerns/persistence.rb +18 -25
- data/lib/caprese/controller/concerns/relationships.rb +22 -5
- data/lib/caprese/controller/concerns/rendering.rb +17 -1
- data/lib/caprese/record/associated_validator.rb +33 -0
- data/lib/caprese/record.rb +1 -0
- data/lib/caprese/serializer/concerns/links.rb +26 -8
- data/lib/caprese/serializer/concerns/lookup.rb +17 -1
- data/lib/caprese/serializer.rb +4 -0
- data/lib/caprese/version.rb +1 -1
- data/lib/caprese.rb +1 -1
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c285f46b32620d9d16fe5b6698098a385f5e163f
|
4
|
+
data.tar.gz: 559a81f42af6e01fac7cecec6b8a9b9c8134139b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 346d727d18ae79c56606c0dc60e5badc09995e0430dff1fff2147a2f85a3cdeeef63a6e1ab0d4f0aea2bcb120ad1470de2d0b04fa60de347e93f11f819b9f53e
|
7
|
+
data.tar.gz: f7fe6949fb0241f3a4fec64ad91b3470a55bfffb3bc4131e0ac4690ec53829be8da23d69f11e0699aeb2f96f5e4943c9e2f18f4e5524b044700b6c876f2f3ddf
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
## 0.3.0
|
2
|
+
|
3
|
+
* Have children controllers inherit callbacks from their parent
|
4
|
+
* Have child records without their own serializers inherit their type names from their parent
|
5
|
+
* Enforce `Caprese::Record` on all records serialized
|
6
|
+
* Allow editing of meta tag document
|
7
|
+
* Modify `validates_associated` to propagate nested association errors to the record itself
|
8
|
+
* Fail with `422 Unprocessable Entity` if any callbacks add an error to a record being persisted or updated
|
data/caprese.gemspec
CHANGED
@@ -21,8 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.required_ruby_version = '>= 2.1'
|
23
23
|
|
24
|
-
spec.add_dependency 'active_model_serializers', '
|
25
|
-
spec.add_dependency 'kaminari'
|
24
|
+
spec.add_dependency 'active_model_serializers', '0.10.2'
|
25
|
+
spec.add_dependency 'kaminari', '0.17.0'
|
26
26
|
spec.add_dependency 'rails', '>= 4.2.0'
|
27
27
|
|
28
28
|
spec.add_development_dependency 'bundler'
|
@@ -3,20 +3,16 @@ module Caprese
|
|
3
3
|
class JsonApi
|
4
4
|
class ResourceIdentifier
|
5
5
|
def self.type_for(class_name, serializer_type = nil, transform_options = {})
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
raw_type = class_name.underscore
|
17
|
-
raw_type = ActiveSupport::Inflector.public_send(inflection, raw_type)
|
18
|
-
raw_type
|
19
|
-
end
|
6
|
+
inflection =
|
7
|
+
if ActiveModelSerializers.config.jsonapi_resource_type == :singular
|
8
|
+
:singularize
|
9
|
+
else
|
10
|
+
:pluralize
|
11
|
+
end
|
12
|
+
|
13
|
+
raw_type = serializer_type || class_name.underscore
|
14
|
+
raw_type = ActiveSupport::Inflector.public_send(inflection, raw_type)
|
15
|
+
|
20
16
|
JsonApi.send(:transform_key_casing!, raw_type, transform_options)
|
21
17
|
end
|
22
18
|
|
@@ -37,7 +33,7 @@ module Caprese
|
|
37
33
|
private
|
38
34
|
|
39
35
|
def type_for(serializer, transform_options)
|
40
|
-
self.class.type_for(serializer.object.class.name, serializer.
|
36
|
+
self.class.type_for(serializer.object.class.name, serializer.json_key, transform_options)
|
41
37
|
end
|
42
38
|
|
43
39
|
def id_for(serializer)
|
@@ -49,10 +49,22 @@ module Caprese
|
|
49
49
|
#
|
50
50
|
# @param [Symbol,Array<Symbol>] callbacks the name(s) of callbacks to add to list of callbacks
|
51
51
|
define_singleton_method method_name do |*callbacks|
|
52
|
-
|
53
|
-
all_callbacks = self.instance_variable_set(instance_variable_name, [])
|
54
|
-
end
|
52
|
+
all_callbacks = self.instance_variable_get(instance_variable_name) || []
|
55
53
|
all_callbacks.push *callbacks
|
54
|
+
self.instance_variable_set(instance_variable_name, all_callbacks)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module ClassMethods
|
60
|
+
# Is called when any controller class inherits from a parent controller, copying to the child controller
|
61
|
+
# all of the callbacks that have been stored in instance variables on the parent
|
62
|
+
#
|
63
|
+
# @param [Class] subclass the child class that is to inherit the callbacks
|
64
|
+
def inherited(subclass)
|
65
|
+
CALLBACKS.each do |method_name|
|
66
|
+
instance_variable_name = "@#{method_name}_callbacks"
|
67
|
+
subclass.instance_variable_set(instance_variable_name, instance_variable_get(instance_variable_name))
|
56
68
|
end
|
57
69
|
end
|
58
70
|
end
|
@@ -35,28 +35,19 @@ module Caprese
|
|
35
35
|
#
|
36
36
|
# @note For this action to succeed, the given controller must define `create_params`
|
37
37
|
# @see #create_params
|
38
|
-
#
|
39
|
-
# 1. Check that type of record to be created matches type that the given controller manages
|
40
|
-
# 2. Build the appropriate attributes/associations for the create action
|
41
|
-
# 3. Build a record with the attributes
|
42
|
-
# 4. Execute after_initialize callbacks
|
43
|
-
# 5. Execute before_create callbacks
|
44
|
-
# 6. Execute before_save callbacks
|
45
|
-
# 7. Create the record by saving it (or fail with RecordInvalid and render errors)
|
46
|
-
# 8. Execute after_create callbacks
|
47
|
-
# 9. Execute after_save callbacks
|
48
|
-
# 10. Return the created resource with 204 Created
|
49
|
-
# @see #rescue_from ActiveRecord::RecordInvalid
|
50
38
|
def create
|
51
39
|
fail_on_type_mismatch(data_params[:type])
|
52
40
|
|
53
41
|
record = queried_record_scope.build
|
54
42
|
assign_record_attributes(record, permitted_params_for(:create), data_params)
|
43
|
+
|
55
44
|
execute_after_initialize_callbacks(record)
|
56
45
|
|
57
46
|
execute_before_create_callbacks(record)
|
58
47
|
execute_before_save_callbacks(record)
|
59
48
|
|
49
|
+
fail RecordInvalidError.new(record) if record.errors.any?
|
50
|
+
|
60
51
|
record.save!
|
61
52
|
|
62
53
|
execute_after_create_callbacks(record)
|
@@ -74,22 +65,16 @@ module Caprese
|
|
74
65
|
#
|
75
66
|
# @note For this action to succeed, the given controller must define `update_params`
|
76
67
|
# @see #update_params
|
77
|
-
#
|
78
|
-
# 1. Check that type of record to be updated matches type that the given controller manages
|
79
|
-
# 2. Execute before_update callbacks
|
80
|
-
# 3. Execute before_save callbacks
|
81
|
-
# 4. Update the record (or fail with RecordInvalid and render errors)
|
82
|
-
# 5. Execute after_update callbacks
|
83
|
-
# 6. Execute after_save callbacks
|
84
|
-
# 7. Return the updated resource
|
85
|
-
# @see #rescue_from ActiveRecord::RecordInvalid
|
86
68
|
def update
|
87
69
|
fail_on_type_mismatch(data_params[:type])
|
88
70
|
|
71
|
+
assign_record_attributes(queried_record, permitted_params_for(:update), data_params)
|
72
|
+
|
89
73
|
execute_before_update_callbacks(queried_record)
|
90
74
|
execute_before_save_callbacks(queried_record)
|
91
75
|
|
92
|
-
|
76
|
+
fail RecordInvalidError.new(queried_record) if queried_record.errors.any?
|
77
|
+
|
93
78
|
queried_record.save!
|
94
79
|
|
95
80
|
execute_after_update_callbacks(queried_record)
|
@@ -285,21 +270,29 @@ module Caprese
|
|
285
270
|
# @param [Hash] resource_identifier the resource identifier for the resource
|
286
271
|
# @return [ActiveRecord::Base] the found or built resource for the relationship
|
287
272
|
def record_for_relationship(owner, relationship_name, resource_identifier)
|
288
|
-
|
273
|
+
if resource_identifier[:type]
|
274
|
+
# { type: '...', id: '...' }
|
289
275
|
if (id = resource_identifier[Caprese.config.resource_primary_key])
|
290
276
|
get_record!(
|
291
277
|
resource_identifier[:type],
|
292
278
|
Caprese.config.resource_primary_key,
|
293
279
|
id
|
294
280
|
)
|
281
|
+
|
282
|
+
# { type: '...', attributes: { ... } }
|
295
283
|
elsif contains_constructable_data?(resource_identifier)
|
296
284
|
record_scope(resource_identifier[:type]).build
|
285
|
+
|
286
|
+
# { type: '...' }
|
297
287
|
else
|
298
288
|
owner.errors.add(relationship_name)
|
299
289
|
nil
|
300
290
|
end
|
301
|
-
|
302
|
-
|
291
|
+
else
|
292
|
+
# { id: '...' } && { attributes: { ... } }
|
293
|
+
owner.errors.add(relationship_name)
|
294
|
+
nil
|
295
|
+
end
|
303
296
|
end
|
304
297
|
|
305
298
|
# Assigns permitted attributes for a record in a relationship, for a given action
|
@@ -58,12 +58,14 @@ module Caprese
|
|
58
58
|
links = { self: request.original_url }
|
59
59
|
|
60
60
|
if !target.respond_to?(:to_ary) &&
|
61
|
-
|
61
|
+
Rails.application.routes.url_helpers
|
62
|
+
.respond_to?(related_url = version_name("#{params[:relationship].singularize}_url"))
|
62
63
|
|
63
64
|
links[:related] =
|
64
|
-
send(
|
65
|
+
Rails.application.routes.url_helpers.send(
|
65
66
|
related_url,
|
66
|
-
target.read_attribute(self.config.resource_primary_key)
|
67
|
+
target.read_attribute(self.config.resource_primary_key),
|
68
|
+
host: caprese_default_url_options_host
|
67
69
|
)
|
68
70
|
end
|
69
71
|
|
@@ -110,8 +112,15 @@ module Caprese
|
|
110
112
|
links = { self: request.original_url }
|
111
113
|
|
112
114
|
# Add related link for this relationship if it exists
|
113
|
-
if
|
114
|
-
|
115
|
+
if Rails.application.routes.url_helpers
|
116
|
+
.respond_to?(related_path = "relationship_data_#{version_name(unversion(params[:controller]).singularize)}_url")
|
117
|
+
|
118
|
+
links[:related] = Rails.application.routes.url_helpers.send(
|
119
|
+
related_path,
|
120
|
+
params[:id],
|
121
|
+
params[:relationship],
|
122
|
+
host: caprese_default_url_options_host
|
123
|
+
)
|
115
124
|
end
|
116
125
|
|
117
126
|
target = queried_association.reader
|
@@ -246,5 +255,13 @@ module Caprese
|
|
246
255
|
|
247
256
|
@queried_association
|
248
257
|
end
|
258
|
+
|
259
|
+
# Fetches the host from Caprese.config.default_url_options or fails if it is not set
|
260
|
+
# @note default_url_options[:host] is used to render the host in links that are serialized in the response
|
261
|
+
def caprese_default_url_options_host
|
262
|
+
Caprese.config.default_url_options.fetch(:host) do
|
263
|
+
fail 'Caprese requires that config.default_url_options[:host] be set when rendering links.'
|
264
|
+
end
|
265
|
+
end
|
249
266
|
end
|
250
267
|
end
|
@@ -9,6 +9,7 @@ module Caprese
|
|
9
9
|
# instead of requiring that they be explicity stated
|
10
10
|
def render(options = {})
|
11
11
|
options[:adapter] = Caprese::Adapter::JsonApi
|
12
|
+
options[:meta] = meta unless meta.empty?
|
12
13
|
|
13
14
|
if options[:json].respond_to?(:to_ary)
|
14
15
|
if options[:json].first.is_a?(Error)
|
@@ -27,6 +28,16 @@ module Caprese
|
|
27
28
|
super
|
28
29
|
end
|
29
30
|
|
31
|
+
# Allows for meta tags to be added in response document
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# meta[:redirect_url] = ...
|
35
|
+
#
|
36
|
+
# @return [Hash] the meta tag object
|
37
|
+
def meta
|
38
|
+
@caprese_meta ||= {}
|
39
|
+
end
|
40
|
+
|
30
41
|
private
|
31
42
|
|
32
43
|
# Finds a versioned serializer for a given resource
|
@@ -34,10 +45,15 @@ module Caprese
|
|
34
45
|
# @example
|
35
46
|
# serializer_for(post) => API::V1::PostSerializer
|
36
47
|
#
|
48
|
+
# @note The reason this method is a duplicate of Caprese::Serializer.serializer_for is
|
49
|
+
# because the latter is only ever called from children of Caprese::Serializer, like those
|
50
|
+
# in the API::V1:: scope. If we tried to use that method instead of this one, we end up
|
51
|
+
# with Caprese::[record.class.name]Serializer instead of the proper versioned serializer
|
52
|
+
#
|
37
53
|
# @param [ActiveRecord::Base] record the record to find a serializer for
|
38
54
|
# @return [Serializer,Nil] the serializer for the given record
|
39
55
|
def serializer_for(record)
|
40
|
-
version_module("#{record.class.name}Serializer").constantize
|
56
|
+
version_module("#{record.class.name}Serializer").constantize if Serializer.valid_for_serialization(record)
|
41
57
|
end
|
42
58
|
end
|
43
59
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Caprese
|
2
|
+
module Record
|
3
|
+
# Formats nested errors on associations in a more useful way than Rails alone
|
4
|
+
#
|
5
|
+
# @note BEFORE
|
6
|
+
# POST /posts (with invalid resources) =>
|
7
|
+
# [
|
8
|
+
# { "key"=>"invalid", "field"=>"attachment", "message"=>"Attachment is invalid." }
|
9
|
+
# ]
|
10
|
+
#
|
11
|
+
# @note AFTER
|
12
|
+
# POST /posts (with invalid resources) =>
|
13
|
+
# [
|
14
|
+
# { "key"=>"not_found", "field"=>"attachment.file", "message"=>"Could not find a file at ..."}
|
15
|
+
# ]
|
16
|
+
class AssociatedValidator < ActiveModel::EachValidator
|
17
|
+
def validate_each(record, attribute, value)
|
18
|
+
Array(value).reject { |r| r.marked_for_destruction? || r.valid? }.each do |invalid_record|
|
19
|
+
invalid_record.errors.to_a.each do |error|
|
20
|
+
field_name = error.field ? "#{attribute}.#{error.field}" : attribute
|
21
|
+
record.errors.add(field_name, error.code, options.merge(value: invalid_record))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def validates_associated(*attr_names)
|
29
|
+
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/caprese/record.rb
CHANGED
@@ -12,8 +12,14 @@ module Caprese
|
|
12
12
|
# object = Order<@token='asd27h'>
|
13
13
|
# links = { self: '/api/v1/orders/asd27hß' }
|
14
14
|
link :self do
|
15
|
-
if
|
16
|
-
|
15
|
+
if Rails.application.routes.url_helpers
|
16
|
+
.respond_to?(url = serializer.version_name("#{object.class.name.underscore}_url"))
|
17
|
+
|
18
|
+
Rails.application.routes.url_helpers.send(
|
19
|
+
url,
|
20
|
+
object.read_attribute(Caprese.config.resource_primary_key),
|
21
|
+
host: serializer.send(:caprese_default_url_options_host)
|
22
|
+
)
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
@@ -67,22 +73,24 @@ module Caprese
|
|
67
73
|
Proc.new do |serializer|
|
68
74
|
link :self do
|
69
75
|
url = "relationship_definition_#{serializer.version_name("#{object.class.name.underscore}_url")}"
|
70
|
-
if respond_to? url
|
71
|
-
send(
|
76
|
+
if Rails.application.routes.url_helpers.respond_to? url
|
77
|
+
Rails.application.routes.url_helpers.send(
|
72
78
|
url,
|
73
79
|
primary_key => object.read_attribute(primary_key),
|
74
|
-
relationship: reflection_name
|
80
|
+
relationship: reflection_name,
|
81
|
+
host: serializer.send(:caprese_default_url_options_host)
|
75
82
|
)
|
76
83
|
end
|
77
84
|
end
|
78
85
|
|
79
86
|
link :related do
|
80
87
|
url = "relationship_data_#{serializer.version_name("#{object.class.name.underscore}_url")}"
|
81
|
-
if respond_to? url
|
82
|
-
send(
|
88
|
+
if Rails.application.routes.url_helpers.respond_to? url
|
89
|
+
Rails.application.routes.url_helpers.send(
|
83
90
|
url,
|
84
91
|
primary_key => object.read_attribute(primary_key),
|
85
|
-
relationship: reflection_name
|
92
|
+
relationship: reflection_name,
|
93
|
+
host: serializer.send(:caprese_default_url_options_host)
|
86
94
|
)
|
87
95
|
end
|
88
96
|
end
|
@@ -91,6 +99,16 @@ module Caprese
|
|
91
99
|
end
|
92
100
|
end
|
93
101
|
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# Fetches the host from Caprese.config.default_url_options or fails if it is not set
|
106
|
+
# @note default_url_options[:host] is used to render the host in links that are serialized in the response
|
107
|
+
def caprese_default_url_options_host
|
108
|
+
Caprese.config.default_url_options.fetch(:host) do
|
109
|
+
fail 'Caprese requires that config.default_url_options[:host] be set when rendering links.'
|
110
|
+
end
|
111
|
+
end
|
94
112
|
end
|
95
113
|
end
|
96
114
|
end
|
@@ -14,7 +14,23 @@ module Caprese
|
|
14
14
|
# @param [Hash] options options for `super` to use when getting the serializer
|
15
15
|
# @return [Serializer,Nil] the serializer for the given record
|
16
16
|
def serializer_for(record, options = {})
|
17
|
-
|
17
|
+
return ActiveModel::Serializer::CollectionSerializer if record.respond_to?(:to_ary)
|
18
|
+
|
19
|
+
get_serializer_for(record.class) if valid_for_serialization(record)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Indicates whether or not the record specified can be serialized by Caprese
|
23
|
+
#
|
24
|
+
# @note The only requirement right now is that the record model has Caprese::Record included
|
25
|
+
#
|
26
|
+
# @param [Object] record the record to check if is valid for serialization
|
27
|
+
# @return [True] this method either returns true, or fails - breaking control flow
|
28
|
+
def valid_for_serialization(record)
|
29
|
+
if record && !record.class.included_modules.include?(Caprese::Record)
|
30
|
+
fail 'All models managed by Caprese must include Caprese::Record'
|
31
|
+
end
|
32
|
+
|
33
|
+
true
|
18
34
|
end
|
19
35
|
|
20
36
|
private
|
data/lib/caprese/serializer.rb
CHANGED
data/lib/caprese/version.rb
CHANGED
data/lib/caprese.rb
CHANGED
@@ -23,7 +23,7 @@ module Caprese
|
|
23
23
|
config.only_path_links ||= true
|
24
24
|
|
25
25
|
# If true, relationship data will not be serialized unless it is in `include`
|
26
|
-
config.optimize_relationships ||=
|
26
|
+
config.optimize_relationships ||= false
|
27
27
|
|
28
28
|
# Defines the translation scope for model and controller errors
|
29
29
|
config.i18n_scope ||= '' # 'api.v1.errors'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caprese
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Landgrebe
|
@@ -10,36 +10,36 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2017-02-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: active_model_serializers
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - '='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.10.
|
21
|
+
version: 0.10.2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - '='
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.10.
|
28
|
+
version: 0.10.2
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: kaminari
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - '='
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
35
|
+
version: 0.17.0
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - '='
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
42
|
+
version: 0.17.0
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: rails
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- ".coveralls.yml"
|
205
205
|
- ".gitignore"
|
206
206
|
- ".travis.yml"
|
207
|
+
- CHANGELOG.md
|
207
208
|
- Gemfile
|
208
209
|
- LICENSE.txt
|
209
210
|
- README.md
|
@@ -233,6 +234,7 @@ files:
|
|
233
234
|
- lib/caprese/error.rb
|
234
235
|
- lib/caprese/errors.rb
|
235
236
|
- lib/caprese/record.rb
|
237
|
+
- lib/caprese/record/associated_validator.rb
|
236
238
|
- lib/caprese/record/errors.rb
|
237
239
|
- lib/caprese/routing/caprese_resources.rb
|
238
240
|
- lib/caprese/serializer.rb
|