macmillan-utils 1.0.11
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 +7 -0
- data/.gitignore +23 -0
- data/.hound.yml +36 -0
- data/.rspec +3 -0
- data/.rubocop.yml +36 -0
- data/.travis.yml +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.rdoc +113 -0
- data/Rakefile +12 -0
- data/lib/macmillan/utils.rb +16 -0
- data/lib/macmillan/utils/bundler/gem_helper.rb +9 -0
- data/lib/macmillan/utils/cucumber/cucumber_defaults.rb +12 -0
- data/lib/macmillan/utils/cucumber/webmock_helper.rb +9 -0
- data/lib/macmillan/utils/logger.rb +11 -0
- data/lib/macmillan/utils/logger/factory.rb +77 -0
- data/lib/macmillan/utils/logger/formatter.rb +48 -0
- data/lib/macmillan/utils/middleware.rb +7 -0
- data/lib/macmillan/utils/middleware/weak_etags.rb +33 -0
- data/lib/macmillan/utils/rails/statsd_instrumentation.rb +64 -0
- data/lib/macmillan/utils/rspec/rack_test_helper.rb +12 -0
- data/lib/macmillan/utils/rspec/rspec_defaults.rb +44 -0
- data/lib/macmillan/utils/rspec/webmock_helper.rb +13 -0
- data/lib/macmillan/utils/settings.rb +33 -0
- data/lib/macmillan/utils/settings/app_yaml_backend.rb +44 -0
- data/lib/macmillan/utils/settings/env_vars_backend.rb +13 -0
- data/lib/macmillan/utils/settings/key_not_found.rb +13 -0
- data/lib/macmillan/utils/settings/lookup.rb +29 -0
- data/lib/macmillan/utils/settings/value.rb +16 -0
- data/lib/macmillan/utils/statsd_controller_helper.rb +81 -0
- data/lib/macmillan/utils/statsd_decorator.rb +98 -0
- data/lib/macmillan/utils/statsd_middleware.rb +87 -0
- data/lib/macmillan/utils/statsd_stub.rb +43 -0
- data/lib/macmillan/utils/test_helpers/codeclimate_helper.rb +4 -0
- data/lib/macmillan/utils/test_helpers/fixture_loading_helper.rb +24 -0
- data/lib/macmillan/utils/test_helpers/simplecov_helper.rb +27 -0
- data/macmillan-utils.gemspec +33 -0
- data/spec/fixtures/config/application.yml +1 -0
- data/spec/lib/macmillan/utils/logger/factory_spec.rb +53 -0
- data/spec/lib/macmillan/utils/logger/formatter_spec.rb +57 -0
- data/spec/lib/macmillan/utils/middleware/weak_etags_spec.rb +30 -0
- data/spec/lib/macmillan/utils/settings_spec.rb +44 -0
- data/spec/lib/macmillan/utils/statsd_controller_helper_spec.rb +43 -0
- data/spec/lib/macmillan/utils/statsd_decorator_spec.rb +93 -0
- data/spec/lib/macmillan/utils/statsd_middleware_spec.rb +51 -0
- data/spec/spec_helper.rb +13 -0
- metadata +296 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Macmillan
|
2
|
+
module Utils
|
3
|
+
module Middleware
|
4
|
+
##
|
5
|
+
# Rack Middleware for use when proxying a rack app with Nginx using gzip compression.
|
6
|
+
#
|
7
|
+
# Nginx will convert ETags generated by our rack apps from STRONG etags, to WEAK etags,
|
8
|
+
# this middleware converts the ETags back to STRONG for use within the consuming app.
|
9
|
+
#
|
10
|
+
# @ref http://akshaykarle.com/blog/2014/09/17/rails-caching-with-nginx/
|
11
|
+
class WeakEtags
|
12
|
+
def initialize(app)
|
13
|
+
@app = app
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
dup.process(env)
|
18
|
+
end
|
19
|
+
|
20
|
+
def process(env)
|
21
|
+
etag = env['HTTP_IF_NONE_MATCH']
|
22
|
+
|
23
|
+
if etag && etag.match(/^W\//)
|
24
|
+
env['HTTP_IF_NONE_MATCH'] = etag.gsub(/^W\//, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
status, headers, body = @app.call(env)
|
28
|
+
[status, headers, body]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Default $statsD instrumentation for Rails applications
|
5
|
+
#
|
6
|
+
# ASSUMPTION: Your StatsD client is stored in a global variable $statsd
|
7
|
+
#
|
8
|
+
# Usage (in `config/initializers/statsd.rb`):
|
9
|
+
#
|
10
|
+
# require 'statsd-ruby'
|
11
|
+
# require 'macmillan/utils/statsd_decorator'
|
12
|
+
#
|
13
|
+
# $statsd = Statsd.new('http://statsd.example.com', 8080)
|
14
|
+
# $statsd = Macmillan::Utils::StatsdDecorator.new($statsd, Rails.env, Rails.logger)
|
15
|
+
#
|
16
|
+
# require 'macmillan/utils/rails/statsd_instrumentation'
|
17
|
+
#
|
18
|
+
# Options (Set as environment variables):
|
19
|
+
#
|
20
|
+
# STRIP_DOMAIN_FROM_HOST = domain name (string) to strip from the servers' host name
|
21
|
+
#
|
22
|
+
# Credit:
|
23
|
+
#
|
24
|
+
# The code below is knocked togther from ideas in...
|
25
|
+
# * http://railstips.org/blog/archives/2011/03/21/hi-my-name-is-john/
|
26
|
+
# * http://www.mikeperham.com/2012/08/25/using-statsd-with-rails/
|
27
|
+
# * http://37signals.com/svn/posts/3091-pssst-your-rails-application-has-a-secret-to-tell-you
|
28
|
+
#
|
29
|
+
# Notes:
|
30
|
+
#
|
31
|
+
# Uses ActiveSupport::Notifications to hook in some instrumentation...
|
32
|
+
# @see http://guides.rubyonrails.org/active_support_instrumentation.html
|
33
|
+
#
|
34
|
+
ActiveSupport::Notifications.subscribe /process_action.action_controller/ do |*args|
|
35
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
36
|
+
controller = event.payload[:controller].gsub('Controller', '_controller').gsub('::', '.')
|
37
|
+
action = event.payload[:action]
|
38
|
+
format = event.payload[:format]
|
39
|
+
format = 'other' unless %i(html json xml ris csv).include?(format)
|
40
|
+
status = event.payload[:status].to_i
|
41
|
+
status = '5xx' if status >= 500 && status <= 599
|
42
|
+
hostname = Socket.gethostname.downcase
|
43
|
+
hostname = hostname.gsub(ENV['STRIP_DOMAIN_FROM_HOST'], '') if ENV['STRIP_DOMAIN_FROM_HOST']
|
44
|
+
key = "controllers.#{controller}.#{action}.#{format}".downcase
|
45
|
+
|
46
|
+
# count reponses
|
47
|
+
$statsd.increment('http_status.overall')
|
48
|
+
$statsd.increment("http_status.#{status}")
|
49
|
+
$statsd.increment("#{key}.http_status.#{status}")
|
50
|
+
$statsd.increment("#{key}.#{hostname}.http_status.#{status}")
|
51
|
+
|
52
|
+
# only record timings on success
|
53
|
+
if status == 200
|
54
|
+
$statsd.timing('response_time', event.duration)
|
55
|
+
|
56
|
+
$statsd.timing("#{key}.response_time", event.duration)
|
57
|
+
$statsd.timing("#{key}.db_time", event.payload[:db_runtime])
|
58
|
+
$statsd.timing("#{key}.view_time", event.payload[:view_runtime])
|
59
|
+
|
60
|
+
$statsd.timing("#{key}.#{hostname}.response_time", event.duration)
|
61
|
+
$statsd.timing("#{key}.#{hostname}.db_time", event.payload[:db_runtime])
|
62
|
+
$statsd.timing("#{key}.#{hostname}.view_time", event.payload[:view_runtime])
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
def check_rubocop_and_hound
|
2
|
+
# ASSUMPTION: We are running the RSpec suite from the root of a project tree
|
3
|
+
update_rubocop = true
|
4
|
+
rubocop_file = '.rubocop.yml'
|
5
|
+
local_rubocop_file = File.join(Dir.getwd, rubocop_file)
|
6
|
+
local_hound_file = File.join(Dir.getwd, '.hound.yml')
|
7
|
+
|
8
|
+
if File.exist?(local_rubocop_file)
|
9
|
+
latest_rubocop_conf = File.read(File.expand_path("../../../../../#{rubocop_file}", __FILE__))
|
10
|
+
current_rubocop_conf = File.read(local_rubocop_file)
|
11
|
+
update_rubocop = false if current_rubocop_conf == latest_rubocop_conf
|
12
|
+
end
|
13
|
+
|
14
|
+
if !File.exist?(local_hound_file) || !File.symlink?(local_hound_file)
|
15
|
+
system "rm -f #{local_hound_file}"
|
16
|
+
system "ln -s #{rubocop_file} #{local_hound_file}"
|
17
|
+
end
|
18
|
+
|
19
|
+
if update_rubocop
|
20
|
+
puts 'WARNING: You do not have the latest set of rubocop style preferences.'
|
21
|
+
puts ' These have now been updated for you. :)'
|
22
|
+
puts ''
|
23
|
+
puts ' You can run RSpec again now.'
|
24
|
+
puts ''
|
25
|
+
puts " Don't forget to commit the '.rubocop.yml' and '.hound.yml' files to git!"
|
26
|
+
|
27
|
+
File.open(local_rubocop_file, 'w') do |file|
|
28
|
+
file.print latest_rubocop_conf
|
29
|
+
end
|
30
|
+
|
31
|
+
fail RuntimeError, '...'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
RSpec.configure do |config|
|
36
|
+
config.order = 'random'
|
37
|
+
|
38
|
+
# Exit the suite on the first failure
|
39
|
+
config.fail_fast = true if ENV['FAIL_FAST']
|
40
|
+
|
41
|
+
config.before(:suite) do
|
42
|
+
check_rubocop_and_hound
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Macmillan
|
2
|
+
module Utils
|
3
|
+
module Settings
|
4
|
+
autoload :AppYamlBackend, 'macmillan/utils/settings/app_yaml_backend'
|
5
|
+
autoload :EnvVarsBackend, 'macmillan/utils/settings/env_vars_backend'
|
6
|
+
autoload :Lookup, 'macmillan/utils/settings/lookup'
|
7
|
+
autoload :Value, 'macmillan/utils/settings/value'
|
8
|
+
autoload :KeyNotFound, 'macmillan/utils/settings/key_not_found'
|
9
|
+
|
10
|
+
class KeyNotFoundError < StandardError; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Get an instance of the settings looker-upper
|
14
|
+
def instance
|
15
|
+
@instance ||= begin
|
16
|
+
backend_instances = backends.map(&:new)
|
17
|
+
Lookup.new(backend_instances)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Backends must respond to the following interface:
|
22
|
+
# # `.new` :: Return an instance of the backend
|
23
|
+
# # `#get key`:: Return a Value for the key.
|
24
|
+
# :: If there's no setting, return
|
25
|
+
# :: a KeyNotFound
|
26
|
+
#
|
27
|
+
attr_accessor :backends
|
28
|
+
end
|
29
|
+
|
30
|
+
self.backends = [EnvVarsBackend, AppYamlBackend]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Macmillan
|
4
|
+
module Utils
|
5
|
+
module Settings
|
6
|
+
class AppYamlBackend
|
7
|
+
def get(key)
|
8
|
+
return build_value(key) if yaml.key?(key)
|
9
|
+
KeyNotFound.new(key, self, key)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def build_value(key)
|
15
|
+
Value.new(key, yaml[key], self, key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def yaml
|
19
|
+
@yaml ||= begin
|
20
|
+
YAML.load(File.open(application_yml_path))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def application_yml_path
|
25
|
+
search_pattern = File.join('config', 'application.yml')
|
26
|
+
here = File.expand_path(Dir.pwd)
|
27
|
+
path_components = here.split(/\//)
|
28
|
+
found_path = nil
|
29
|
+
|
30
|
+
path_components.size.downto(1) do |path_size|
|
31
|
+
break if found_path
|
32
|
+
search_path = path_components[0, path_size]
|
33
|
+
search_file = File.join(search_path, search_pattern)
|
34
|
+
found_path = search_file if File.exist?(search_file)
|
35
|
+
end
|
36
|
+
|
37
|
+
fail 'cannot find application.yml' if found_path.nil?
|
38
|
+
|
39
|
+
found_path
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Macmillan
|
2
|
+
module Utils
|
3
|
+
module Settings
|
4
|
+
class EnvVarsBackend
|
5
|
+
def get(key)
|
6
|
+
backend_key = key.to_s.upcase
|
7
|
+
return KeyNotFound.new(key, self, backend_key) unless ENV.key?(backend_key)
|
8
|
+
Value.new(key, ENV[backend_key].dup, self, backend_key)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Macmillan
|
2
|
+
module Utils
|
3
|
+
module Settings
|
4
|
+
class Lookup
|
5
|
+
def initialize(backends)
|
6
|
+
@backends = backends
|
7
|
+
end
|
8
|
+
|
9
|
+
def lookup(key)
|
10
|
+
value = nil
|
11
|
+
|
12
|
+
@backends.each do |backend|
|
13
|
+
break if value
|
14
|
+
result = backend.get(key)
|
15
|
+
value = result.value unless result.is_a?(KeyNotFound)
|
16
|
+
end
|
17
|
+
|
18
|
+
fail KeyNotFoundError.new("Cannot find a settings value for #{key}") unless value
|
19
|
+
|
20
|
+
value
|
21
|
+
end
|
22
|
+
|
23
|
+
# Backwards compatibility: in the past this has been used like a Hash
|
24
|
+
alias_method :[], :lookup
|
25
|
+
alias_method :fetch, :lookup
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Macmillan
|
2
|
+
module Utils
|
3
|
+
module Settings
|
4
|
+
class Value
|
5
|
+
attr_reader :value
|
6
|
+
|
7
|
+
def initialize(lookup_key, value, backend, backend_key)
|
8
|
+
@lookup_key = lookup_key
|
9
|
+
@value = value
|
10
|
+
@backend = backend
|
11
|
+
@backend_key = backend_key
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'statsd_middleware'
|
2
|
+
|
3
|
+
module Macmillan
|
4
|
+
module Utils
|
5
|
+
##
|
6
|
+
# Helper functions for working with {Macmillan::Utils::StatsdMiddleware}
|
7
|
+
# in Rack based web applications.
|
8
|
+
#
|
9
|
+
# This code is heavily inspired by {https://github.com/mleinart/sinatra-statsd-helper sinatra-statsd-helper}
|
10
|
+
#
|
11
|
+
# == Usage:
|
12
|
+
#
|
13
|
+
# Add 'macmillan-utils' to your Gemfile:
|
14
|
+
#
|
15
|
+
# gem 'macmillan-utils', require: false
|
16
|
+
#
|
17
|
+
# First, setup the {Macmillan::Utils::StatsdMiddleware} as described in its
|
18
|
+
# documentation. Then simply include this module in your controller classes.
|
19
|
+
#
|
20
|
+
# i.e. in Sinatra
|
21
|
+
#
|
22
|
+
# require 'macmillan/utils/statsd_controller_helper'
|
23
|
+
#
|
24
|
+
# class Server < Sinatra::Base
|
25
|
+
# include Macmillan::Utils::StatsdControllerHelper
|
26
|
+
#
|
27
|
+
# get '/' do
|
28
|
+
# add_statsd_timer('get.homepage') # sends a timer to the stat 'get.homepage' with the timing of the request
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# get '/inc' do
|
32
|
+
# add_statsd_increment('get.inc') # sends an increment to the stat 'get.inc'
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# get '/both' do
|
36
|
+
# add_statsd_timer_and_increment('get.both') # sends both an timer and increment stat to 'get.both'
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Rails works identically:
|
41
|
+
#
|
42
|
+
# require 'macmillan/utils/statsd_controller_helper'
|
43
|
+
#
|
44
|
+
# class SiteController < ApplicationController
|
45
|
+
# include Macmillan::Utils::StatsdControllerHelper
|
46
|
+
#
|
47
|
+
# def index
|
48
|
+
# add_statsd_timer_and_increment('get.site_controller.index')
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
module StatsdControllerHelper
|
53
|
+
module_function
|
54
|
+
|
55
|
+
##
|
56
|
+
# Send a timer stat to statsd (with the timing of the whole rack request)
|
57
|
+
#
|
58
|
+
# @param key [String] the statsd/graphite statistic name/key
|
59
|
+
def add_statsd_timer(key)
|
60
|
+
request.env[::Macmillan::Utils::StatsdMiddleware::TIMERS] << key
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Send an increment stat to statsd
|
65
|
+
#
|
66
|
+
# @param key [String] the statsd/graphite statistic name/key
|
67
|
+
def add_statsd_increment(key)
|
68
|
+
request.env[::Macmillan::Utils::StatsdMiddleware::INCREMENTS] << key
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Send both a timer and an increment stat to statsd
|
73
|
+
#
|
74
|
+
# @param key [String] the statsd/graphite statistic name/key
|
75
|
+
def add_statsd_timer_and_increment(key)
|
76
|
+
add_statsd_timer(key)
|
77
|
+
add_statsd_increment(key)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Macmillan
|
2
|
+
module Utils
|
3
|
+
##
|
4
|
+
# Utility class to wrap the Statsd class from {http://rubygems.org/gems/statsd-ruby statsd-ruby}.
|
5
|
+
#
|
6
|
+
# This will allow you to log Statsd messages to your logs, but only
|
7
|
+
# really send messages to the StatsD server when running in a
|
8
|
+
# 'production' environment.
|
9
|
+
#
|
10
|
+
# === Usage:
|
11
|
+
#
|
12
|
+
# Add 'statsd-ruby' and 'macmillan-utils' to your Gemfile:
|
13
|
+
#
|
14
|
+
# gem 'statsd-ruby'
|
15
|
+
# gem 'macmillan-utils', require: false
|
16
|
+
#
|
17
|
+
# Then in your code:
|
18
|
+
#
|
19
|
+
# require 'statsd'
|
20
|
+
# require 'macmillan/utils/statsd_decorator'
|
21
|
+
#
|
22
|
+
# statsd = Statsd.new('http://statsd.example.com', 8080)
|
23
|
+
# statsd = Macmillan::Utils::StatsdDecorator.new(statsd, ENV['RACK_ENV'])
|
24
|
+
#
|
25
|
+
# i.e. when using rails, use the rails env and logger:
|
26
|
+
#
|
27
|
+
# statsd = Statsd.new('http://statsd.example.com', 8080)
|
28
|
+
# statsd = Macmillan::Utils::StatsdDecorator.new(statsd, Rails.env, Rails.logger)
|
29
|
+
#
|
30
|
+
# @see http://rubygems.org/gems/statsd-ruby
|
31
|
+
#
|
32
|
+
class StatsdDecorator < SimpleDelegator
|
33
|
+
attr_accessor :env, :logger
|
34
|
+
|
35
|
+
##
|
36
|
+
# Builds a new instance of StatsdDecorator
|
37
|
+
#
|
38
|
+
# @param delegatee [Statsd] a Statsd object
|
39
|
+
# @param env [String] the current application environment - i.e. 'development' or 'production'
|
40
|
+
# @param logger [Logger] a Logger object
|
41
|
+
# @return [Statsd] the decorated Statsd class
|
42
|
+
#
|
43
|
+
def initialize(delegatee, env = 'development', logger = Macmillan::Utils::Logger::Factory.build_logger)
|
44
|
+
@env = env
|
45
|
+
@logger = logger
|
46
|
+
super(delegatee)
|
47
|
+
end
|
48
|
+
|
49
|
+
def increment(stat, sample_rate = 1)
|
50
|
+
log_stat %{increment - "#{stat}" (sample_rate: #{sample_rate})}
|
51
|
+
super if send_to_delegatee?
|
52
|
+
end
|
53
|
+
|
54
|
+
def decrement(stat, sample_rate = 1)
|
55
|
+
log_stat %{decrement - "#{stat}" (sample_rate: #{sample_rate})}
|
56
|
+
super if send_to_delegatee?
|
57
|
+
end
|
58
|
+
|
59
|
+
def count(stat, count, sample_rate = 1)
|
60
|
+
log_stat %{count - "#{stat}" #{count} (sample_rate: #{sample_rate})}
|
61
|
+
super if send_to_delegatee?
|
62
|
+
end
|
63
|
+
|
64
|
+
def guage(stat, value, sample_rate = 1)
|
65
|
+
log_stat %{gauge - "#{stat}" #{value} (sample_rate: #{sample_rate})}
|
66
|
+
super if send_to_delegatee?
|
67
|
+
end
|
68
|
+
|
69
|
+
def set(stat, value, sample_rate = 1)
|
70
|
+
log_stat %{set - "#{stat}" #{value} (sample_rate: #{sample_rate})}
|
71
|
+
super if send_to_delegatee?
|
72
|
+
end
|
73
|
+
|
74
|
+
def timing(stat, ms, sample_rate = 1)
|
75
|
+
log_stat %{timing - "#{stat}" #{ms}ms (sample_rate: #{sample_rate})}
|
76
|
+
super if send_to_delegatee?
|
77
|
+
end
|
78
|
+
|
79
|
+
def time(stat, sample_rate = 1, &block)
|
80
|
+
start = Time.now
|
81
|
+
result = block.call
|
82
|
+
duration = ((Time.now - start) * 1000).round
|
83
|
+
timing(stat, duration, sample_rate)
|
84
|
+
result
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def log_stat(msg)
|
90
|
+
logger.debug "[StatsD] #{msg}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def send_to_delegatee?
|
94
|
+
env == 'production'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|