timber 1.0.3
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 +15 -0
- data/.rspec +2 -0
- data/.yardopts +6 -0
- data/Appraisals +41 -0
- data/Gemfile +30 -0
- data/LICENSE.md +15 -0
- data/README.md +194 -0
- data/circle.yml +33 -0
- data/lib/timber/config.rb +18 -0
- data/lib/timber/context.rb +17 -0
- data/lib/timber/contexts/custom.rb +27 -0
- data/lib/timber/contexts/http.rb +28 -0
- data/lib/timber/contexts/organization.rb +35 -0
- data/lib/timber/contexts/user.rb +36 -0
- data/lib/timber/contexts.rb +10 -0
- data/lib/timber/current_context.rb +43 -0
- data/lib/timber/event.rb +17 -0
- data/lib/timber/events/controller_call.rb +40 -0
- data/lib/timber/events/custom.rb +42 -0
- data/lib/timber/events/exception.rb +35 -0
- data/lib/timber/events/http_request.rb +50 -0
- data/lib/timber/events/http_response.rb +36 -0
- data/lib/timber/events/sql_query.rb +26 -0
- data/lib/timber/events/template_render.rb +26 -0
- data/lib/timber/events.rb +37 -0
- data/lib/timber/frameworks/rails.rb +13 -0
- data/lib/timber/frameworks.rb +19 -0
- data/lib/timber/log_devices/http.rb +87 -0
- data/lib/timber/log_devices.rb +8 -0
- data/lib/timber/log_entry.rb +59 -0
- data/lib/timber/logger.rb +142 -0
- data/lib/timber/probe.rb +23 -0
- data/lib/timber/probes/action_controller_log_subscriber/log_subscriber.rb +64 -0
- data/lib/timber/probes/action_controller_log_subscriber.rb +20 -0
- data/lib/timber/probes/action_dispatch_debug_exceptions.rb +80 -0
- data/lib/timber/probes/action_view_log_subscriber/log_subscriber.rb +62 -0
- data/lib/timber/probes/action_view_log_subscriber.rb +20 -0
- data/lib/timber/probes/active_record_log_subscriber/log_subscriber.rb +29 -0
- data/lib/timber/probes/active_record_log_subscriber.rb +20 -0
- data/lib/timber/probes/rack_http_context.rb +51 -0
- data/lib/timber/probes/rails_rack_logger.rb +76 -0
- data/lib/timber/probes.rb +21 -0
- data/lib/timber/util/active_support_log_subscriber.rb +33 -0
- data/lib/timber/util/hash.rb +14 -0
- data/lib/timber/util.rb +8 -0
- data/lib/timber/version.rb +3 -0
- data/lib/timber.rb +22 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/action_controller.rb +4 -0
- data/spec/support/action_view.rb +4 -0
- data/spec/support/active_record.rb +28 -0
- data/spec/support/coveralls.rb +2 -0
- data/spec/support/rails/templates/_partial.html +1 -0
- data/spec/support/rails/templates/template.html +1 -0
- data/spec/support/rails.rb +37 -0
- data/spec/support/simplecov.rb +9 -0
- data/spec/support/socket_hostname.rb +12 -0
- data/spec/support/timber.rb +4 -0
- data/spec/support/timecop.rb +3 -0
- data/spec/support/webmock.rb +2 -0
- data/spec/timber/events_spec.rb +55 -0
- data/spec/timber/log_devices/http_spec.rb +62 -0
- data/spec/timber/logger_spec.rb +89 -0
- data/spec/timber/probes/action_controller_log_subscriber_spec.rb +70 -0
- data/spec/timber/probes/action_dispatch_debug_exceptions_spec.rb +51 -0
- data/spec/timber/probes/action_view_log_subscriber_spec.rb +61 -0
- data/spec/timber/probes/active_record_log_subscriber_spec.rb +52 -0
- data/spec/timber/probes/rack_http_context_spec.rb +54 -0
- data/spec/timber/probes/rails_rack_logger_spec.rb +46 -0
- data/timber.gemspec +22 -0
- metadata +149 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Timber
|
2
|
+
module Probes
|
3
|
+
# Reponsible for automatically tracking exception events in Rails applications. While
|
4
|
+
# still preserving the default log style.
|
5
|
+
class ActionDispatchDebugExceptions < Probe
|
6
|
+
# For Rails >= 3.1
|
7
|
+
# @private
|
8
|
+
module DebugExceptionsInstanceMethods
|
9
|
+
def self.included(klass)
|
10
|
+
klass.class_eval do
|
11
|
+
private
|
12
|
+
def log_error(request, wrapper)
|
13
|
+
logger = logger(request)
|
14
|
+
return unless logger
|
15
|
+
|
16
|
+
exception = wrapper.exception
|
17
|
+
|
18
|
+
trace = wrapper.application_trace
|
19
|
+
trace = wrapper.framework_trace if trace.empty?
|
20
|
+
|
21
|
+
event = Events::Exception.new(
|
22
|
+
name: exception.class.name,
|
23
|
+
exception_message: exception.message,
|
24
|
+
backtrace: trace
|
25
|
+
)
|
26
|
+
|
27
|
+
logger.fatal event
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# For Rails < 3.1
|
34
|
+
# @private
|
35
|
+
module ShowExceptionsInstanceMethods #:nodoc:
|
36
|
+
def self.included(klass)
|
37
|
+
klass.class_eval do
|
38
|
+
private
|
39
|
+
# We have to monkey patch because ruby < 2.0 does not support prepend.
|
40
|
+
alias_method :_timber_old_log_error, :log_error
|
41
|
+
|
42
|
+
def log_error(exception)
|
43
|
+
return unless logger
|
44
|
+
|
45
|
+
event = Events::Exception.new(
|
46
|
+
name: exception.class.name,
|
47
|
+
exception_message: exception.message,
|
48
|
+
backtrace: application_trace(exception)
|
49
|
+
)
|
50
|
+
|
51
|
+
logger.fatal event
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
begin
|
59
|
+
# Rails >= 3.1
|
60
|
+
require "action_dispatch/middleware/debug_exceptions"
|
61
|
+
rescue LoadError
|
62
|
+
# Rails < 3.1
|
63
|
+
require "action_dispatch/middleware/show_exceptions"
|
64
|
+
end
|
65
|
+
rescue LoadError => e
|
66
|
+
raise RequirementNotMetError.new(e.message)
|
67
|
+
end
|
68
|
+
|
69
|
+
def insert!
|
70
|
+
if defined?(::ActionDispatch::DebugExceptions)
|
71
|
+
return true if ::ActionDispatch::DebugExceptions.include?(DebugExceptionsInstanceMethods)
|
72
|
+
::ActionDispatch::DebugExceptions.send(:include, DebugExceptionsInstanceMethods)
|
73
|
+
else
|
74
|
+
return true if ::ActionDispatch::ShowExceptions.include?(ShowExceptionsInstanceMethods)
|
75
|
+
::ActionDispatch::ShowExceptions.send(:include, ShowExceptionsInstanceMethods)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Timber
|
2
|
+
module Probes
|
3
|
+
class ActionViewLogSubscriber < Probe
|
4
|
+
# The log subscriber that replaces the default `ActionView::LogSubscriber`.
|
5
|
+
# The intent of this subscriber is to, as transparently as possible, properly
|
6
|
+
# track events that are being logged here. This LogSubscriber will never change
|
7
|
+
# default behavior / log messages.
|
8
|
+
class LogSubscriber < ::ActionView::LogSubscriber
|
9
|
+
def render_template(event)
|
10
|
+
info do
|
11
|
+
full_name = from_rails_root(event.payload[:identifier])
|
12
|
+
message = " Rendered #{full_name}"
|
13
|
+
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
14
|
+
message << " (#{event.duration.round(1)}ms)"
|
15
|
+
|
16
|
+
Events::TemplateRender.new(
|
17
|
+
name: full_name,
|
18
|
+
time_ms: event.duration,
|
19
|
+
message: message
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_partial(event)
|
25
|
+
info do
|
26
|
+
full_name = from_rails_root(event.payload[:identifier])
|
27
|
+
message = " Rendered #{full_name}"
|
28
|
+
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
29
|
+
message << " (#{event.duration.round(1)}ms)"
|
30
|
+
message << " #{cache_message(event.payload)}" if event.payload.key?(:cache_hit)
|
31
|
+
|
32
|
+
Events::TemplateRender.new(
|
33
|
+
name: full_name,
|
34
|
+
time_ms: event.duration,
|
35
|
+
message: message
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_collection(event)
|
41
|
+
info do
|
42
|
+
identifier = event.payload[:identifier] || "templates"
|
43
|
+
full_name = from_rails_root(identifier)
|
44
|
+
message = " Rendered collection of #{full_name}" \
|
45
|
+
" #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
|
46
|
+
|
47
|
+
Events::TemplateRender.new(
|
48
|
+
name: full_name,
|
49
|
+
time_ms: event.duration,
|
50
|
+
message: message
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def log_rendering_start(payload)
|
57
|
+
# Rails, you silly. We don't need to template rendering messages :)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Timber
|
2
|
+
module Probes
|
3
|
+
# Reponsible for automatically tracking template rendering events in `ActionView` while
|
4
|
+
# still preserving the default log style.
|
5
|
+
class ActionViewLogSubscriber < Probe
|
6
|
+
def initialize
|
7
|
+
require "action_view/log_subscriber"
|
8
|
+
require "timber/probes/action_view_log_subscriber/log_subscriber"
|
9
|
+
rescue LoadError => e
|
10
|
+
raise RequirementNotMetError.new(e.message)
|
11
|
+
end
|
12
|
+
|
13
|
+
def insert!
|
14
|
+
return true if Util::ActiveSupportLogSubscriber.subscribed?(:action_view, LogSubscriber)
|
15
|
+
Util::ActiveSupportLogSubscriber.unsubscribe(:action_view, ::ActionView::LogSubscriber)
|
16
|
+
LogSubscriber.attach_to(:action_view)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Timber
|
2
|
+
module Probes
|
3
|
+
class ActiveRecordLogSubscriber < Probe
|
4
|
+
# The log subscriber that replaces the default `ActiveRecord::LogSubscriber`.
|
5
|
+
# The intent of this subscriber is to, as transparently as possible, properly
|
6
|
+
# track events that are being logged here. This LogSubscriber will never change
|
7
|
+
# default behavior / log messages.
|
8
|
+
class LogSubscriber < ::ActiveRecord::LogSubscriber #:nodoc:
|
9
|
+
def sql(event)
|
10
|
+
super(event)
|
11
|
+
|
12
|
+
payload = event.payload
|
13
|
+
event = Events::SQLQuery.new(
|
14
|
+
sql: payload[:sql],
|
15
|
+
time_ms: event.duration,
|
16
|
+
message: @message
|
17
|
+
)
|
18
|
+
|
19
|
+
logger.debug event
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def debug(message)
|
24
|
+
@message = message
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Timber
|
2
|
+
module Probes
|
3
|
+
# Reponsible for automatimcally tracking SQL query events in `ActiveRecord`, while still
|
4
|
+
# preserving the default log style.
|
5
|
+
class ActiveRecordLogSubscriber < Probe
|
6
|
+
def initialize
|
7
|
+
require "active_record/log_subscriber"
|
8
|
+
require "timber/probes/active_record_log_subscriber/log_subscriber"
|
9
|
+
rescue LoadError => e
|
10
|
+
raise RequirementNotMetError.new(e.message)
|
11
|
+
end
|
12
|
+
|
13
|
+
def insert!
|
14
|
+
return true if Util::ActiveSupportLogSubscriber.subscribed?(:active_record, LogSubscriber)
|
15
|
+
Util::ActiveSupportLogSubscriber.unsubscribe(:active_record, ::ActiveRecord::LogSubscriber)
|
16
|
+
LogSubscriber.attach_to(:active_record)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Timber
|
2
|
+
module Probes
|
3
|
+
# Reponsible for automatically adding the HTTP context for applications that use `Rack`.
|
4
|
+
class RackHTTPContext < Probe
|
5
|
+
class Middleware
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
request = ::Rack::Request.new(env)
|
12
|
+
context = Contexts::HTTP.new(
|
13
|
+
method: request.request_method,
|
14
|
+
path: request.path,
|
15
|
+
remote_addr: request.ip,
|
16
|
+
request_id: request_id(env)
|
17
|
+
)
|
18
|
+
CurrentContext.instance.with(context) do
|
19
|
+
@app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def request_id(env)
|
25
|
+
env["X-Request-ID"] ||
|
26
|
+
env["X-Request-Id"] ||
|
27
|
+
env["action_dispatch.request_id"]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :middleware, :insert_before
|
32
|
+
|
33
|
+
def initialize(middleware, insert_before)
|
34
|
+
if middleware.nil?
|
35
|
+
raise RequirementNotMetError.new("The middleware class attribute is not set. " +
|
36
|
+
"We need a middleware to insert the probe.")
|
37
|
+
end
|
38
|
+
@middleware = middleware
|
39
|
+
@insert_before = insert_before
|
40
|
+
end
|
41
|
+
|
42
|
+
def insert!
|
43
|
+
var_name = :"@_timber_rack_http_inserted"
|
44
|
+
return true if middleware.instance_variable_defined?(var_name) && middleware.instance_variable_get(var_name) == true
|
45
|
+
# Rails uses a proxy :/, so we need to do this instance variable hack
|
46
|
+
middleware.instance_variable_set(var_name, true)
|
47
|
+
middleware.insert_before insert_before, Middleware
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Timber
|
2
|
+
module Probes
|
3
|
+
# Responsible for automatically tracking the http request events for applications
|
4
|
+
# that use `Rack`.
|
5
|
+
class RailsRackLogger < Probe
|
6
|
+
module InstanceMethods
|
7
|
+
def self.included(klass)
|
8
|
+
klass.class_eval do
|
9
|
+
protected
|
10
|
+
if klass.method_defined?(:started_request_message)
|
11
|
+
def started_request_message(request)
|
12
|
+
http_request_event(request)
|
13
|
+
end
|
14
|
+
elsif klass.method_defined?(:before_dispatch)
|
15
|
+
def before_dispatch(env)
|
16
|
+
request = ActionDispatch::Request.new(env)
|
17
|
+
info do
|
18
|
+
http_request_event(request)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def http_request_event(request)
|
24
|
+
# No idea why rails 3.X returns a "/" :/
|
25
|
+
referrer = request.referer == "/" ? nil : request.referer
|
26
|
+
Events::HTTPRequest.new(
|
27
|
+
host: request.host,
|
28
|
+
method: request.request_method,
|
29
|
+
path: request.filtered_path,
|
30
|
+
port: request.port,
|
31
|
+
query_params: request.GET,
|
32
|
+
content_type: request.content_type,
|
33
|
+
remote_addr: request.ip,
|
34
|
+
referrer: referrer,
|
35
|
+
request_id: request_id(request.env),
|
36
|
+
user_agent: request.user_agent
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def request_id(env)
|
41
|
+
env["X-Request-ID"] ||
|
42
|
+
env["X-Request-Id"] ||
|
43
|
+
env["action_dispatch.request_id"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module BeforeDispatchInstanceMethods
|
50
|
+
def self.included(klass)
|
51
|
+
klass.class_eval do
|
52
|
+
protected
|
53
|
+
def before_dispatch(env)
|
54
|
+
request = ActionDispatch::Request.new(env)
|
55
|
+
path = request.filtered_path
|
56
|
+
|
57
|
+
info "\n\nStarted #{request.request_method} \"#{path}\" " \
|
58
|
+
"for #{request.ip} at #{Time.now.to_default_s}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
require "rails/rack/logger"
|
66
|
+
rescue LoadError => e
|
67
|
+
raise RequirementNotMetError.new(e.message)
|
68
|
+
end
|
69
|
+
|
70
|
+
def insert!
|
71
|
+
return true if ::Rails::Rack::Logger.include?(InstanceMethods)
|
72
|
+
::Rails::Rack::Logger.send(:include, InstanceMethods)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "timber/probes/action_controller_log_subscriber"
|
2
|
+
require "timber/probes/action_dispatch_debug_exceptions"
|
3
|
+
require "timber/probes/action_view_log_subscriber"
|
4
|
+
require "timber/probes/active_record_log_subscriber"
|
5
|
+
require "timber/probes/rack_http_context"
|
6
|
+
require "timber/probes/rails_rack_logger"
|
7
|
+
|
8
|
+
module Timber
|
9
|
+
# Namespace for all probes.
|
10
|
+
# @private
|
11
|
+
module Probes
|
12
|
+
def self.insert!(middleware, insert_before)
|
13
|
+
ActionControllerLogSubscriber.insert!
|
14
|
+
ActionDispatchDebugExceptions.insert!
|
15
|
+
ActionViewLogSubscriber.insert!
|
16
|
+
ActiveRecordLogSubscriber.insert!
|
17
|
+
RackHTTPContext.insert!(middleware, insert_before)
|
18
|
+
RailsRackLogger.insert!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Timber
|
2
|
+
module Util
|
3
|
+
# @private
|
4
|
+
module ActiveSupportLogSubscriber
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def find(component, type)
|
8
|
+
ActiveSupport::LogSubscriber.log_subscribers.find do |subscriber|
|
9
|
+
subscriber.class == type
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def subscribed?(component, type)
|
14
|
+
!find(component, type).nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def unsubscribe(component, type)
|
18
|
+
subscriber = find(component, type)
|
19
|
+
|
20
|
+
if subscriber
|
21
|
+
events = subscriber.public_methods(false).reject { |method| method.to_s == 'call' }
|
22
|
+
events.each do |event|
|
23
|
+
ActiveSupport::Notifications.notifier.listeners_for("#{event}.#{component}").each do |listener|
|
24
|
+
if listener.instance_variable_get('@delegate') == subscriber
|
25
|
+
ActiveSupport::Notifications.unsubscribe listener
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/timber/util.rb
ADDED
data/lib/timber.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# core classes
|
2
|
+
require "json" # brings to_json to the core classes
|
3
|
+
|
4
|
+
# Base (must come first, order matters)
|
5
|
+
require "timber/config"
|
6
|
+
require "timber/context"
|
7
|
+
require "timber/event"
|
8
|
+
require "timber/probe"
|
9
|
+
require "timber/util"
|
10
|
+
require "timber/version"
|
11
|
+
|
12
|
+
# Other (sorted alphabetically)
|
13
|
+
require "timber/contexts"
|
14
|
+
require "timber/current_context"
|
15
|
+
require "timber/events"
|
16
|
+
require "timber/log_devices"
|
17
|
+
require "timber/log_entry"
|
18
|
+
require "timber/logger"
|
19
|
+
require "timber/probes"
|
20
|
+
|
21
|
+
# Load frameworks
|
22
|
+
require "timber/frameworks"
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Base
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
# Testing
|
6
|
+
require 'pry'
|
7
|
+
require 'rspec'
|
8
|
+
require 'rspec/its'
|
9
|
+
require 'rspec/mocks'
|
10
|
+
|
11
|
+
# Support files, order is relevant
|
12
|
+
require File.join(File.dirname(__FILE__), 'support', 'coveralls')
|
13
|
+
require File.join(File.dirname(__FILE__), 'support', 'socket_hostname')
|
14
|
+
require File.join(File.dirname(__FILE__), 'support', 'simplecov')
|
15
|
+
require File.join(File.dirname(__FILE__), 'support', 'timecop')
|
16
|
+
require File.join(File.dirname(__FILE__), 'support', 'webmock')
|
17
|
+
require File.join(File.dirname(__FILE__), 'support', 'timber')
|
18
|
+
|
19
|
+
if !ENV["RAILS_23"]
|
20
|
+
require File.join(File.dirname(__FILE__), 'support', 'rails')
|
21
|
+
require File.join(File.dirname(__FILE__), 'support', 'action_controller')
|
22
|
+
require File.join(File.dirname(__FILE__), 'support', 'action_view')
|
23
|
+
require File.join(File.dirname(__FILE__), 'support', 'active_record')
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
config.color = true
|
28
|
+
config.order = :random
|
29
|
+
config.warnings = false
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.logger = Rails.logger
|
4
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
5
|
+
ActiveRecord::Schema.define do
|
6
|
+
self.verbose = false
|
7
|
+
|
8
|
+
create_table :organizations, :force => true do |t|
|
9
|
+
t.string :name
|
10
|
+
|
11
|
+
t.timestamps :null => false
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :users, :force => true do |t|
|
15
|
+
t.integer :age
|
16
|
+
t.string :email
|
17
|
+
t.string :first_name
|
18
|
+
|
19
|
+
t.timestamps :null => false
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class Organization < ::ActiveRecord::Base
|
25
|
+
end
|
26
|
+
|
27
|
+
class User < ::ActiveRecord::Base
|
28
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
I am a partial
|
@@ -0,0 +1 @@
|
|
1
|
+
I am a template
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "rails"
|
2
|
+
|
3
|
+
# Defualt the rails logger to nothing, each test shoould be
|
4
|
+
# responsible for setting up the logger
|
5
|
+
logger = ::Logger.new(nil)
|
6
|
+
Rails.logger = logger
|
7
|
+
|
8
|
+
class RailsApp < Rails::Application
|
9
|
+
if ::Rails.version =~ /^3\./
|
10
|
+
config.secret_token = '1e05af2b349457936a41427e63450937'
|
11
|
+
else
|
12
|
+
config.secret_key_base = '1e05af2b349457936a41427e63450937'
|
13
|
+
end
|
14
|
+
config.active_support.deprecation = :stderr
|
15
|
+
config.eager_load = false
|
16
|
+
end
|
17
|
+
|
18
|
+
RailsApp.initialize!
|
19
|
+
|
20
|
+
module Support
|
21
|
+
module Rails
|
22
|
+
def dispatch_rails_request(path, additional_env_options = {})
|
23
|
+
application = ::Rails.application
|
24
|
+
env = application.respond_to?(:env_config) ? application.env_config.clone : application.env_defaults.clone
|
25
|
+
env["rack.request.cookie_hash"] = {}.with_indifferent_access
|
26
|
+
env["REMOTE_ADDR"] = "123.456.789.10"
|
27
|
+
env["X-Request-Id"] = "unique-request-id-1234"
|
28
|
+
env["action_dispatch.request_id"] = env["X-Request-Id"]
|
29
|
+
env = env.merge(additional_env_options)
|
30
|
+
::Rack::MockRequest.new(application).get(path, env)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
RSpec.configure do |config|
|
36
|
+
config.include Support::Rails
|
37
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
# Stub out the hostname for tests only. This can't use a normal stub in the
|
4
|
+
# test life cycle since our test rails app is loaded once upon initialization.
|
5
|
+
# In other words, the rails app gets loaded with the server context inserted
|
6
|
+
# before any tests are run.
|
7
|
+
|
8
|
+
class ::Socket
|
9
|
+
def self.gethostname
|
10
|
+
"computer-name.domain.com"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Timber::Events, :rails_23 => true do
|
4
|
+
describe ".build" do
|
5
|
+
it "should build a Timber::Event" do
|
6
|
+
event = Timber::Events::Custom.new(
|
7
|
+
type: :payment_rejected,
|
8
|
+
message: "Payment rejected",
|
9
|
+
data: {customer_id: "abcd1234", amount: 100}
|
10
|
+
)
|
11
|
+
built_event = Timber::Events.build(event)
|
12
|
+
expect(built_event).to eq(event)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should use #to_timber_event" do
|
16
|
+
PaymentRejectedEvent = Struct.new(:customer_id, :amount) do
|
17
|
+
def to_timber_event
|
18
|
+
Timber::Events::Custom.new(
|
19
|
+
type: :payment_rejected,
|
20
|
+
message: "Payment rejected for #{customer_id}",
|
21
|
+
data: respond_to?(:hash) ? hash : to_hash
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
built_event = Timber::Events.build(PaymentRejectedEvent.new("abcd1234", 100))
|
26
|
+
expect(built_event).to be_kind_of(Timber::Events::Custom)
|
27
|
+
expect(built_event.type).to eq(:payment_rejected)
|
28
|
+
expect(built_event.message).to eq("Payment rejected for abcd1234")
|
29
|
+
Object.send(:remove_const, :PaymentRejectedEvent)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should accept a properly structured hash" do
|
33
|
+
built_event = Timber::Events.build({type: :payment_rejected, message: "Payment rejected", data: {customer_id: "abcd1234", amount: 100}})
|
34
|
+
expect(built_event).to be_kind_of(Timber::Events::Custom)
|
35
|
+
expect(built_event.type).to eq(:payment_rejected)
|
36
|
+
expect(built_event.message).to eq("Payment rejected")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should accept a struct" do
|
40
|
+
PaymentRejectedEvent = Struct.new(:customer_id, :amount) do
|
41
|
+
def message; "Payment rejected for #{customer_id}"; end
|
42
|
+
def type; :payment_rejected; end
|
43
|
+
end
|
44
|
+
built_event = Timber::Events.build(PaymentRejectedEvent.new("abcd1234", 100))
|
45
|
+
expect(built_event).to be_kind_of(Timber::Events::Custom)
|
46
|
+
expect(built_event.type).to eq(:payment_rejected)
|
47
|
+
expect(built_event.message).to eq("Payment rejected for abcd1234")
|
48
|
+
Object.send(:remove_const, :PaymentRejectedEvent)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should return nil for unsupported" do
|
52
|
+
expect(Timber::Events.build(1)).to be_nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|