activetracker 0.4.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.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CHANGELOG.md +11 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +160 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +399 -0
  10. data/Rakefile +21 -0
  11. data/THOUGHTS.md +50 -0
  12. data/activetracker.gemspec +49 -0
  13. data/app/assets/config/active_tracker_manifest.js +4 -0
  14. data/app/assets/images/active_tracker/.keep +0 -0
  15. data/app/assets/images/active_tracker/logo.svg +6 -0
  16. data/app/assets/images/active_tracker/reload.svg +10 -0
  17. data/app/assets/javascripts/active_tracker/active_tracker.js +15 -0
  18. data/app/assets/javascripts/active_tracker/tabs.js +12 -0
  19. data/app/assets/javascripts/active_tracker/tags.js +18 -0
  20. data/app/assets/javascripts/active_tracker/zepto.js +2 -0
  21. data/app/assets/stylesheets/active_tracker/active_tracker.css.scss +21 -0
  22. data/app/assets/stylesheets/active_tracker/tailwind.min.scss +1 -0
  23. data/app/controllers/active_tracker/base_controller.rb +22 -0
  24. data/app/controllers/active_tracker/dashboard_controller.rb +10 -0
  25. data/app/controllers/active_tracker/exceptions_controller.rb +41 -0
  26. data/app/controllers/active_tracker/queries_controller.rb +39 -0
  27. data/app/controllers/active_tracker/requests_controller.rb +41 -0
  28. data/app/helpers/active_tracker/application_helper.rb +4 -0
  29. data/app/helpers/active_tracker/images_helper.rb +8 -0
  30. data/app/helpers/active_tracker/output_helper.rb +35 -0
  31. data/app/helpers/active_tracker/pagination_helper.rb +16 -0
  32. data/app/jobs/active_tracker/application_job.rb +4 -0
  33. data/app/mailers/active_tracker/application_mailer.rb +6 -0
  34. data/app/models/active_tracker/application_record.rb +5 -0
  35. data/app/views/active_tracker/common/_empty.html.erb +9 -0
  36. data/app/views/active_tracker/common/_pagination.html.erb +22 -0
  37. data/app/views/active_tracker/common/_plugin_nav.html.erb +10 -0
  38. data/app/views/active_tracker/dashboard/index.html.erb +18 -0
  39. data/app/views/active_tracker/exceptions/index.html.erb +44 -0
  40. data/app/views/active_tracker/exceptions/show.html.erb +56 -0
  41. data/app/views/active_tracker/queries/index.html.erb +38 -0
  42. data/app/views/active_tracker/queries/show.html.erb +39 -0
  43. data/app/views/active_tracker/requests/_request.html.erb +30 -0
  44. data/app/views/active_tracker/requests/index.html.erb +26 -0
  45. data/app/views/active_tracker/requests/show.html.erb +81 -0
  46. data/app/views/layouts/active_tracker/active_tracker.html.erb +46 -0
  47. data/bin/console +14 -0
  48. data/bin/rails +25 -0
  49. data/bin/setup +8 -0
  50. data/doc/logo.md +11 -0
  51. data/integration/generators/installer.rb +7 -0
  52. data/integration/templates/initializer.rb +32 -0
  53. data/lib/active_tracker.rb +41 -0
  54. data/lib/active_tracker/configuration.rb +67 -0
  55. data/lib/active_tracker/engine.rb +24 -0
  56. data/lib/active_tracker/exception_capturer.rb +38 -0
  57. data/lib/active_tracker/model.rb +153 -0
  58. data/lib/active_tracker/output_capturer.rb +37 -0
  59. data/lib/active_tracker/plugin.rb +4 -0
  60. data/lib/active_tracker/plugin/base.rb +9 -0
  61. data/lib/active_tracker/plugin/exception.rb +113 -0
  62. data/lib/active_tracker/plugin/query.rb +128 -0
  63. data/lib/active_tracker/plugin/request.rb +163 -0
  64. data/lib/active_tracker/rails_logger.rb +120 -0
  65. data/lib/active_tracker/router.rb +16 -0
  66. data/lib/active_tracker/version.rb +3 -0
  67. data/lib/activetracker.rb +3 -0
  68. data/lib/tasks/active_tracker_tasks.rake +12 -0
  69. data/spec/activetracker_spec.rb +36 -0
  70. data/spec/dummy/Rakefile +6 -0
  71. data/spec/dummy/app/assets/config/manifest.js +3 -0
  72. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  73. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  74. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  75. data/spec/dummy/app/javascript/packs/application.js +15 -0
  76. data/spec/dummy/app/jobs/application_job.rb +7 -0
  77. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  78. data/spec/dummy/app/models/application_record.rb +3 -0
  79. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  80. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  81. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  82. data/spec/dummy/bin/rails +4 -0
  83. data/spec/dummy/bin/rake +4 -0
  84. data/spec/dummy/bin/setup +33 -0
  85. data/spec/dummy/config.ru +5 -0
  86. data/spec/dummy/config/application.rb +37 -0
  87. data/spec/dummy/config/boot.rb +5 -0
  88. data/spec/dummy/config/database.yml +54 -0
  89. data/spec/dummy/config/environment.rb +5 -0
  90. data/spec/dummy/config/environments/development.rb +62 -0
  91. data/spec/dummy/config/environments/production.rb +107 -0
  92. data/spec/dummy/config/environments/test.rb +48 -0
  93. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  94. data/spec/dummy/config/initializers/assets.rb +12 -0
  95. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  96. data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
  97. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  98. data/spec/dummy/config/initializers/disable_remote_forms.rb +1 -0
  99. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  100. data/spec/dummy/config/initializers/inflections.rb +16 -0
  101. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  102. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  103. data/spec/dummy/config/locales/en.yml +33 -0
  104. data/spec/dummy/config/puma.rb +38 -0
  105. data/spec/dummy/config/routes.rb +3 -0
  106. data/spec/dummy/config/spring.rb +6 -0
  107. data/spec/dummy/config/storage.yml +34 -0
  108. data/spec/dummy/public/404.html +67 -0
  109. data/spec/dummy/public/422.html +67 -0
  110. data/spec/dummy/public/500.html +66 -0
  111. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  112. data/spec/dummy/public/apple-touch-icon.png +0 -0
  113. data/spec/dummy/public/favicon.ico +0 -0
  114. data/spec/lib/active_tracker/model_spec.rb +38 -0
  115. data/spec/spec_helper.rb +18 -0
  116. metadata +348 -0
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "activetracker"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/active_tracker/engine', __dir__)
7
+ APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
12
+
13
+ require "rails"
14
+ # Pick the frameworks you want:
15
+ require "active_model/railtie"
16
+ require "active_job/railtie"
17
+ require "active_record/railtie"
18
+ require "active_storage/engine"
19
+ require "action_controller/railtie"
20
+ require "action_mailer/railtie"
21
+ require "action_view/railtie"
22
+ # require "action_cable/engine"
23
+ require "sprockets/railtie"
24
+ require "rails/test_unit/railtie"
25
+ require 'rails/engine/commands'
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ # Logo
2
+
3
+ The icon used as part of ActiveTracker's logo was a free/promotional order from Logofound.com (order #24181, September 27, 2019).
4
+
5
+ When used as part of referring to ActiveTracker, it should not be used in any way implying any form of endorsement.
6
+
7
+ ## Original SVG
8
+
9
+ The SVG below is the one originally supplied to us, but run through [SVGOMG](https://jakearchibald.github.io/svgomg/) to optimise it
10
+
11
+ <svg xmlns="http://www.w3.org/2000/svg" width="11in" height="816" viewBox="0 0 11000 8500" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"><g fill="#333"><path d="M7361 4662c-55 403-243 764-517 1039-330 330-786 534-1289 534-146 0-289-17-425-50l-912 1502c-142 234-447 309-681 167s-309-447-167-681l895-1474c-329-330-532-785-532-1287 0-85 6-169 17-251 209 90 429 167 647 230v21c0 320 130 610 339 819 210 210 499 339 819 339s610-130 819-339c142-142 247-320 300-519 222-3 441-17 652-45l34-5z" fill-rule="nonzero"/><path d="M4658 1194c315 0 478 330 975 330s696-330 1059-330c239 0 381 202 434 434 73 316 135 744 306 1225 111 45 216 93 312 141 479 243 769 528 726 794-46 282-454 481-1081 564-637 85-1469 47-2310-128-810-169-1443-428-1811-701-347-257-456-524-273-745 172-207 575-343 1090-400 67-255 90-511 138-750 48-234 195-434 434-434zm2866 2305c-438 81-864 96-1305 71-398-22-820-77-1245-165-417-87-692-185-1039-327-154-63-116-123-82-177 46-75 96-165 137-245 873 293 2373 625 3494 397 46 102 100 209 138 313 19 54 39 107-98 132z"/></g></svg>
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ initializer "activetracker.rb" do
4
+ File.read(ActiveTracker::Engine.root.join("integration", "templates", "initializer.rb"))
5
+ end
6
+
7
+ route "ActiveTracker::Router.load"
@@ -0,0 +1,32 @@
1
+ # Which plugins are active. The full list installed by default is:
2
+ #
3
+ # ActiveTracker::Plugin::Request # Entire request log, with response
4
+ # ActiveTracker::Plugin::Query # Queries performed with ms taken
5
+ # ActiveTracker::Plugin::Exception # When an exception is raised
6
+ # ActiveTracker::Plugin::Schedule # When a scheduled job is queued
7
+ # ActiveTracker::Plugin::ActiveJob # When a job is processed, with output
8
+ # ActiveTracker::Plugin::ActionMail # When an email is sent, with the body
9
+ # ActiveTracker::Plugin::Event # Random events triggered in the system
10
+
11
+ ActiveTracker::Configuration.plugins = [
12
+ ActiveTracker::Plugin::Request,
13
+ ActiveTracker::Plugin::Query,
14
+ ActiveTracker::Plugin::Exception,
15
+ ]
16
+
17
+ # You should change this to be your correct Redis URL
18
+ ActiveTracker::Configuration.redis_url = "redis://localhost:6379/15"
19
+
20
+ # By default ActiveTracker is mounted at /activetracker
21
+ # if you'd like to change it, you can do so like this:
22
+ # ActiveTracker::Configuration.mountpoint = "debugger"
23
+
24
+ # If you want to authenticate requests to ActiveTracker with username and password
25
+ # ActiveTracker::Configuration.authentication = "username:password"
26
+ # or using a proc:
27
+ # ActiveTracker::Configuration.authentication do
28
+ # false unless params[:password] == "password"
29
+ # end
30
+
31
+ # If you would like a particular number of items per page
32
+ # ActiveTracker::Configuration.per_page = 20
@@ -0,0 +1,41 @@
1
+ require "active_tracker/version"
2
+ require "redis"
3
+
4
+ module ActiveTracker
5
+ class Error < StandardError; end
6
+
7
+ def self.reset_connection
8
+ @redis = nil
9
+ end
10
+
11
+ def self.connection
12
+ if @redis
13
+ begin
14
+ @redis.ping
15
+ rescue
16
+ @redis = nil
17
+ end
18
+ end
19
+
20
+ @redis ||= Redis.new(url: ActiveTracker::Configuration.redis_url)
21
+
22
+ begin
23
+ @redis.ping
24
+ rescue
25
+ @redis = nil
26
+ end
27
+
28
+ @redis
29
+ rescue Errno::ECONNREFUSED, Redis::CannotConnectError
30
+ @redis = nil
31
+ end
32
+
33
+ def self.connection_offline?
34
+ offline = connection.nil?
35
+ Rails.logger.error("ActiveTracker: Redis is offline/unreachable") if offline
36
+ offline
37
+ end
38
+ end
39
+
40
+ require "active_tracker/configuration"
41
+ require "active_tracker/engine"
@@ -0,0 +1,67 @@
1
+ module ActiveTracker
2
+ class Configuration
3
+ def self.plugins
4
+ @@plugins ||= [
5
+ ActiveTracker::Plugin::Request,
6
+ ]
7
+ @@plugins
8
+ end
9
+
10
+ def self.plugins=(items)
11
+ items.each do |i|
12
+ if i.respond_to?(:register)
13
+ i.register
14
+ else
15
+ raise PluginInvalidError.new("#{i.name} doesn't correctly implement the ActiveTracker API")
16
+ end
17
+ end
18
+
19
+ @@plugins = items.dup
20
+ end
21
+
22
+ def self.redis_url
23
+ @@redis_url ||= "redis://localhost:6379/15"
24
+ end
25
+
26
+ def self.redis_url=(url)
27
+ unless url.start_with?("redis://")
28
+ raise PluginInvalidError.new("redis_url isn't a valid Redis URL - should begin with redis://")
29
+ end
30
+ @@redis_url = url
31
+ end
32
+
33
+ def self.mountpoint
34
+ @@mountpoint ||= "activetracker"
35
+ end
36
+
37
+ def self.root_path
38
+ "/#{mountpoint}"
39
+ end
40
+
41
+ def self.mountpoint=(path)
42
+ @@mountpoint = path
43
+ end
44
+
45
+ def self.per_page=(value)
46
+ @@per_page = value
47
+ end
48
+
49
+ def self.per_page
50
+ @@per_page ||= 20
51
+ @@per_page.to_i
52
+ end
53
+
54
+ def self.authentication(&block)
55
+ if block
56
+ @@authentication = block
57
+ end
58
+ @@authentication ||= nil
59
+ end
60
+
61
+ def self.authentication=(value)
62
+ @@authentication = value
63
+ end
64
+
65
+ class PluginInvalidError < ActiveTracker::Error ; end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveTracker
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ActiveTracker
4
+ config.autoload_paths += Dir["#{config.root}/lib/**/"]
5
+
6
+ initializer 'active_tracker_helper.action_controller' do
7
+ ActiveSupport.on_load :action_controller do
8
+ helper ActiveTracker::ImagesHelper
9
+ helper ActiveTracker::ApplicationHelper
10
+ helper ActiveTracker::PaginationHelper
11
+ helper ActiveTracker::OutputHelper
12
+ end
13
+ end
14
+
15
+ config.generators do |g|
16
+ g.test_framework :rspec, fixture: false
17
+ g.fixture_replacement :factory_girl, dir: 'spec/factories'
18
+ g.assets false
19
+ g.helper false
20
+ end
21
+
22
+ config.assets.precompile += %w(active_tracker_manifest active_tracker/active_tracker.js active_tracker/active_tracker.css)
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveTracker
2
+ class ExceptionCapturer
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+
9
+ response = @app.call(env)
10
+
11
+ framework_exception = env['action_dispatch.exception']
12
+ if framework_exception
13
+ record_exception(env, framework_exception)
14
+ end
15
+
16
+ response
17
+ rescue Exception => exception
18
+
19
+ record_exception(env, exception)
20
+ raise exception
21
+ end
22
+
23
+ def record_exception(env, exception)
24
+ if env['action_dispatch.backtrace_cleaner']
25
+ backtrace = env['action_dispatch.backtrace_cleaner'].filter(exception.backtrace)
26
+ backtrace = exception.backtrace if backtrace.blank?
27
+ else
28
+ backtrace = exception.backtrace
29
+ end
30
+ class_name = exception.class.name
31
+ message = exception.message
32
+ backtrace = backtrace || []
33
+
34
+ ActiveTracker::Plugin::Exception.exception_capture(class_name, message, backtrace)
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,153 @@
1
+ require 'json'
2
+
3
+ module ActiveTracker
4
+ class Model
5
+ PREFIX = "/ActiveTracker".freeze
6
+
7
+ def self.find(key)
8
+ return nil if ActiveTracker.connection_offline?
9
+
10
+ connection = ActiveTracker.connection
11
+ value = connection.get(key)
12
+ if value.nil?
13
+ raise NotFound.new("Couldn't find entry - #{key}")
14
+ else
15
+ if value.start_with?(PREFIX)
16
+ find(value)
17
+ else
18
+ self.new(key, value)
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.delete(key)
24
+ ActiveTracker.connection.del(key)
25
+ end
26
+
27
+ def self.all(type, tags: {}, data_type: nil)
28
+ return [] if ActiveTracker.connection_offline?
29
+
30
+ keys = "#{PREFIX}/#{type}/"
31
+
32
+ keys += "*"
33
+
34
+ keys += tags.sort_by { |k,v| k.to_s }.map do |k,v|
35
+ "#{k}:#{CGI.escape(v.to_s)}"
36
+ end.join("*")
37
+
38
+ keys += "*" unless keys.end_with?("*")
39
+
40
+ if data_type
41
+ keys += "/#{data_type}"
42
+ end
43
+
44
+ connection = ActiveTracker.connection
45
+ result = connection.keys(keys).sort { |a,b| b <=> a }.map { |key| self.new(key, {}.to_json) }
46
+ end
47
+
48
+ def self.generate_key(type, log_time, tags, data_type)
49
+ key = PREFIX + "/#{type}/#{log_time}"
50
+ converted_tags = tags.sort_by { |k,v| k.to_s }.map {|k,v| "#{k}:#{CGI.escape(v.to_s)}"}
51
+ if converted_tags.any?
52
+ key = key + "/" + converted_tags.join("/")
53
+ end
54
+ key += "/#{data_type}"
55
+ key.gsub!(%r{/{2,}}, '/')
56
+ key
57
+ end
58
+
59
+ def self.save(type, data, tags: {}, data_type: nil, expiry: 7.days, log_at: Time.current)
60
+ return nil if ActiveTracker.connection_offline?
61
+
62
+ if log_at.respond_to?(:strftime)
63
+ log_time = log_at.strftime("%Y%m%d%H%M%S")
64
+ else
65
+ log_time = log_at
66
+ end
67
+ key = generate_key(type, log_time, tags, data_type)
68
+ value = data.to_json
69
+ connection = ActiveTracker.connection
70
+ connection.set(key, value)
71
+ connection.expire(key, expiry)
72
+ if tags[:id].present?
73
+ connection.set(tags[:id], key)
74
+ connection.expire(tags[:id], expiry)
75
+ end
76
+ key
77
+ end
78
+
79
+ def self.find_or_create(type, tags: {}, data_type: nil)
80
+ keys = all(type, tags: tags, data_type: data_type)
81
+ if keys.length > 0
82
+ obj = find(keys.first.key) rescue nil
83
+ end
84
+
85
+ if !obj
86
+ obj = new(generate_key(type, "-", tags, data_type), {}.to_json, false)
87
+ end
88
+ yield obj
89
+ obj.save
90
+ end
91
+
92
+ def self.paginate(items, page, per_page)
93
+ page = (page || 1).to_i
94
+
95
+ total = items.length
96
+ start_point = (page - 1) * per_page
97
+
98
+ items = items[start_point, per_page]
99
+
100
+ total_pages = total / per_page
101
+ total_pages += 1 if (total % per_page != 0)
102
+
103
+ window = []
104
+ window << page - 2 if page > 2
105
+ window << page - 1 if page > 1
106
+ window << page
107
+ window << page + 1 if (total_pages - page) > 0
108
+ window << page + 2 if (total_pages - page) > 1
109
+
110
+ [items, {total: total, total_pages: total_pages, page: page, window: window}]
111
+ end
112
+
113
+ def initialize(key, value, persisted = true)
114
+ @attrs = {key: key, persisted: persisted}
115
+ key.gsub!(/^A#{PREFIX}/, "")
116
+ parts = key.split("/")
117
+ _ = parts.shift # Starts with a /
118
+ _ = parts.shift # then comes PREFIX
119
+ @attrs[:type] = parts.shift
120
+ @attrs[:data_type] = parts.pop
121
+ t = parts.shift
122
+ @attrs[:log_at] = Time.parse(t) rescue t
123
+ @attrs[:tags] = {}
124
+ parts.sort.each do |part|
125
+ tag_name, tag_value = part.split(":")
126
+ if tag_name == "id"
127
+ @attrs[:id] = tag_value
128
+ else
129
+ @attrs[:tags][tag_name.to_sym] = CGI.unescape("#{tag_value}")
130
+ end
131
+ end
132
+ value = JSON.parse(value)
133
+ self.send("data=", value)
134
+ value.each do |key, value|
135
+ self.send("#{key}=", value)
136
+ end
137
+ end
138
+
139
+ def save
140
+ self.class.save(@attrs[:type], data, tags: {id: id}.merge(@attrs[:tags]), data_type: @attrs[:data_type], expiry: expiry, log_at: log_at)
141
+ end
142
+
143
+ def method_missing(name, value = nil)
144
+ if name.to_s.end_with?("=")
145
+ @attrs[name.to_s.gsub("=", "").to_sym] = value
146
+ else
147
+ @attrs[name]
148
+ end
149
+ end
150
+
151
+ class NotFound < ActiveTracker::Error ; end
152
+ end
153
+ end
@@ -0,0 +1,37 @@
1
+ module ActiveTracker
2
+ class OutputCapturer
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ start_time = Time.current
9
+ status, headers, response = @app.call(env)
10
+ [status, headers, response]
11
+ ensure
12
+ capture(response)
13
+ duration = (Time.current.to_f - start_time.to_f) * 1000
14
+ ActiveTracker::Plugin::Request.record_duration(duration)
15
+ end
16
+
17
+ def capture(response)
18
+ body = response.body rescue nil
19
+ unless body.is_a?(String)
20
+ body = body.to_a rescue [body.body] rescue "No body given"
21
+ end
22
+
23
+ if body.respond_to?(:each)
24
+ output = []
25
+ body.each do |line|
26
+ output << line
27
+ end
28
+ output = output.join("\n")
29
+ else
30
+ output = body.to_s
31
+ end
32
+
33
+ ActiveTracker::Plugin::Request.output_capture(output)
34
+ end
35
+
36
+ end
37
+ end