Skip to content

Commit aa43848

Browse files
committed
Merge pull request #1322 from bf4/maurogeorge-patch-10
Instrumenting rendering of resources
2 parents 0365303 + 733f5bc commit aa43848

File tree

15 files changed

+340
-9
lines changed

15 files changed

+340
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Breaking changes:
1414

1515
Features:
1616

17+
- [#1291](https://github.com/rails-api/active_model_serializers/pull/1291) Add logging (@maurogeorge)
1718
- [#1225](https://github.com/rails-api/active_model_serializers/pull/1125) Better serializer lookup, use nested serializer when it exists (@beauby)
1819
- [#1172](https://github.com/rails-api/active_model_serializers/pull/1172) Better serializer registration, get more than just the first module (@bf4)
1920
- [#1158](https://github.com/rails-api/active_model_serializers/pull/1158) Add support for wildcards in `include` option (@beauby)

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,10 @@ All serializable resources must pass the ActiveModel::Serializer::Lint::Tests.
381381
See the ActiveModelSerializers::Model for a base class that implements the full
382382
API for a plain-old Ruby object (PORO).
383383

384+
## Hooks
385+
386+
To run a hook when ActiveModelSerializers is loaded, use `ActiveSupport.on_load(:active_model_serializers) do end`
387+
384388
## Getting Help
385389

386390
If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new).

docs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ This is the documentation of AMS, it's focused on the **0.10.x version.**
99
- [Getting Started](general/getting_started.md)
1010
- [Adapters](general/adapters.md)
1111
- [Configuration Options](general/configuration_options.md)
12+
- [Logging](general/logging.md)
13+
- [Instrumentation](general/instrumentation.md)
1214

1315
## How to
1416

docs/general/instrumentation.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Instrumentation
2+
3+
ActiveModelSerializers uses the
4+
[ActiveSupport::Notification API](http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event),
5+
which allows for subscribing to events, such as for logging.
6+
7+
## Events
8+
9+
Name:
10+
11+
`render.active_model_serializers`
12+
13+
Payload (example):
14+
15+
```ruby
16+
{
17+
serializer: PostSerializer,
18+
adapter: ActiveModel::Serializer::Adapter::Attributes
19+
}
20+
```
21+
22+
Subscribing:
23+
24+
```ruby
25+
ActiveSupport::Notifications.subscribe 'render.active_model_serializers' do |name, started, finished, unique_id, data|
26+
# whatever
27+
end
28+
ActiveSupport::Notifications.subscribe 'render.active_model_serializers' do |*args|
29+
event = ActiveSupport::Notifications::Event.new(*args)
30+
# event.payload
31+
# whatever
32+
end
33+
34+
## [LogSubscriber](http://api.rubyonrails.org/classes/ActiveSupport/LogSubscriber.html)
35+
36+
ActiveModelSerializers includes an `ActiveModelSerializers::LogSubscriber` that attaches to
37+
`render.active_model_serializers`.

docs/general/logging.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Logging
2+
3+
If we are using ActiveModel::Serializers on Rails app by default the `Rails.logger` will be used.
4+
5+
On a non Rails enviroment by default the `ActiveSupport::TaggedLogging` will be
6+
used.
7+
8+
If we need to customize the logger we can define this in an initializer:
9+
10+
```ruby
11+
ActiveModelSerializers.logger = Logger.new(STDOUT)
12+
```

lib/action_controller/serialization.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ def get_serializer(resource, options = {})
3131
serializable_resource.serialization_scope ||= serialization_scope
3232
serializable_resource.serialization_scope_name = _serialization_scope
3333
begin
34-
serializable_resource.adapter
34+
# Necessary to ensure we have an adapter for the serializable resource
35+
# after it has been figured.
36+
# TODO: This logic should be less opaque and probably moved into the SerializableResource.
37+
serializable_resource.tap(&:adapter)
3538
rescue ActiveModel::Serializer::CollectionSerializer::NoSerializerError
3639
resource
3740
end

lib/active_model/serializable_resource.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
module ActiveModel
33
class SerializableResource
44
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links])
5+
include ActiveModelSerializers::Logging
6+
7+
delegate :serializable_hash, :as_json, :to_json, to: :adapter
8+
notify :serializable_hash, :render
9+
notify :as_json, :render
10+
notify :to_json, :render
511

612
# Primary interface to composing a resource with a serializer and adapter.
713
# @return the serializable_resource, ready for #as_json/#to_json/#serializable_hash.
@@ -11,8 +17,6 @@ def initialize(resource, options = {})
1117
options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] }
1218
end
1319

14-
delegate :serializable_hash, :as_json, :to_json, to: :adapter
15-
1620
def serialization_scope=(scope)
1721
serializer_opts[:scope] = scope
1822
end

lib/active_model/serializer/railtie.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
require 'rails/railtie'
2+
23
module ActiveModel
34
class Railtie < Rails::Railtie
45
initializer 'active_model_serializers.logger' do
5-
ActiveSupport.on_load(:action_controller) do
6-
ActiveModelSerializers.logger = ActionController::Base.logger
6+
ActiveSupport.on_load(:active_model_serializers) do
7+
self.logger = ActionController::Base.logger
78
end
89
end
910

lib/active_model_serializers.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
require 'logger'
21
require 'active_model'
32
require 'active_support'
43
require 'action_controller'
54
require 'action_controller/railtie'
65
module ActiveModelSerializers
7-
mattr_accessor :logger
8-
self.logger = Rails.logger || Logger.new(IO::NULL)
6+
mattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) }
97

108
extend ActiveSupport::Autoload
119
autoload :Model
10+
autoload :Callbacks
11+
autoload :Logging
1212

1313
module_function
1414

@@ -50,6 +50,7 @@ def silence_warnings
5050

5151
require 'action_controller/serialization'
5252
ActiveSupport.on_load(:action_controller) do
53+
ActiveSupport.run_load_hooks(:active_model_serializers, ActiveModelSerializers)
5354
include ::ActionController::Serialization
5455
ActionDispatch::Reloader.to_prepare do
5556
ActiveModel::Serializer.serializers_cache.clear
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Adapted from
2+
# https://github.com/rails/rails/blob/7f18ea14c8/activejob/lib/active_job/callbacks.rb
3+
require 'active_support/callbacks'
4+
5+
module ActiveModelSerializers
6+
# = ActiveModelSerializers Callbacks
7+
#
8+
# ActiveModelSerializers provides hooks during the life cycle of serialization and
9+
# allow you to trigger logic. Available callbacks are:
10+
#
11+
# * <tt>around_render</tt>
12+
#
13+
module Callbacks
14+
extend ActiveSupport::Concern
15+
include ActiveSupport::Callbacks
16+
17+
included do
18+
define_callbacks :render
19+
end
20+
21+
# These methods will be included into any ActiveModelSerializers object, adding
22+
# callbacks for +render+.
23+
module ClassMethods
24+
# Defines a callback that will get called around the render method,
25+
# whether it is as_json, to_json, or serializable_hash
26+
#
27+
# class ActiveModel::SerializableResource
28+
# include ActiveModelSerializers::Callbacks
29+
#
30+
# around_render do |args, block|
31+
# tag_logger do
32+
# notify_render do
33+
# block.call(args)
34+
# end
35+
# end
36+
# end
37+
#
38+
# def as_json
39+
# run_callbacks :render do
40+
# adapter.as_json
41+
# end
42+
# end
43+
# # Note: So that we can re-use the instrumenter for as_json, to_json, and
44+
# # serializable_hash, we aren't using the usual format, which would be:
45+
# # def render(args)
46+
# # adapter.as_json
47+
# # end
48+
# end
49+
#
50+
def around_render(*filters, &blk)
51+
set_callback(:render, :around, *filters, &blk)
52+
end
53+
end
54+
end
55+
end

0 commit comments

Comments
 (0)