fluoride-collector 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/fluoride-collector.rb +2 -0
- data/lib/fluoride-collector/middleware.rb +98 -0
- data/lib/fluoride-collector/rails.rb +1 -0
- data/lib/fluoride-collector/rails/railtie.rb +14 -0
- data/spec/middleware.rb +118 -0
- data/spec/railtie.rb +10 -0
- data/spec_help/file-sandbox.rb +164 -0
- data/spec_help/gem_test_suite.rb +17 -0
- data/spec_help/railtie-help.rb +24 -0
- data/spec_help/spec_helper.rb +10 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 79df9f5613527c47e3bfc8d757c779c992e0e34b
|
4
|
+
data.tar.gz: d00870129d5e02049f0d502dffcf5e2ba06f11e2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: edcf5210f705bcf17fd40c5734a95537fe09877365761e6bfe68ccb159ceae23bbda5c949969fad19871418702695e648a3a38c22f2bd4e1ebcd36bb417923d0
|
7
|
+
data.tar.gz: 355afa02a28de428224911db463581798c9dec9d257e6ba91030056c3e8b7b07b55ec3ebfaebbb133acd3ba2d02a804f5c2e354b3ecb819ecd4c0533335b915a
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Fluoride
|
2
|
+
module Collector
|
3
|
+
class Middleware
|
4
|
+
def initialize(app, directory, tagging = nil)
|
5
|
+
@app = app
|
6
|
+
@directory = directory
|
7
|
+
@tagging = tagging
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@app.call(env).tap do |response|
|
12
|
+
record_exchange(env, response)
|
13
|
+
end
|
14
|
+
rescue Object => ex
|
15
|
+
record_exception(env, ex)
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def record_exchange(env, response)
|
22
|
+
store(
|
23
|
+
"type" => "normal_exchange",
|
24
|
+
"tags" => @tagging,
|
25
|
+
"request" => request_hash(env),
|
26
|
+
"response" => response_hash(response)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def record_exception(env, ex)
|
31
|
+
store(
|
32
|
+
"type" => "exception_raised",
|
33
|
+
"tags" => @tagging,
|
34
|
+
"request" => request_hash(env),
|
35
|
+
"response" => exception_hash(ex)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def request_hash(env)
|
40
|
+
body = nil
|
41
|
+
if env['rack.input'].respond_to? :read
|
42
|
+
body = env['rack.input'].read
|
43
|
+
env['rack.input'].rewind rescue nil
|
44
|
+
end
|
45
|
+
{
|
46
|
+
"content_type" => env['CONTENT_TYPE'],
|
47
|
+
"accept" => env["HTTP_ACCEPT_ENCODING"],
|
48
|
+
"referer" => env["HTTP_REFERER"],
|
49
|
+
"cookies" => env["HTTP_COOKIE"],
|
50
|
+
"authorization" => env["HTTP_AUTHORIZATION"],
|
51
|
+
"method" => env["REQUEST_METHOD"],
|
52
|
+
"host" => env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}",
|
53
|
+
"path" => env["SCRIPT_NAME"].to_s + env["PATH_INFO"].to_s,
|
54
|
+
"query_string" => env["QUERY_STRING"].to_s,
|
55
|
+
"body" => body,
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def response_hash(response)
|
60
|
+
status, headers, body = *response
|
61
|
+
|
62
|
+
{
|
63
|
+
"status" => status,
|
64
|
+
"headers" => headers,
|
65
|
+
"body" => body.to_a.join("") #every body? all of it?
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def exception_hash(ex)
|
70
|
+
{
|
71
|
+
"type" => ex.class.name,
|
72
|
+
"message" => ex.message,
|
73
|
+
"backtrace" => ex.backtrace[0..10]
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def thread_locals
|
78
|
+
Thread.current[:fluoride_collector] ||= {}
|
79
|
+
end
|
80
|
+
|
81
|
+
def storage_path
|
82
|
+
thread_locals[:storage_path] ||= File::join(@directory, "collection-#{Process.pid}-#{Thread.current.object_id}.yml")
|
83
|
+
end
|
84
|
+
|
85
|
+
def storage_file
|
86
|
+
File::open(storage_path, "a") do |file|
|
87
|
+
yield file
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def store(record)
|
92
|
+
storage_file do |file|
|
93
|
+
file.write(YAML::dump(record))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'fluoride-collector/rails/railtie'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fluoride
|
2
|
+
module Collector
|
3
|
+
class Railtie < ::Rails::Railtie
|
4
|
+
config.fluoride = ActiveSupport::OrderedOptions.new
|
5
|
+
config.fluoride.tags = nil
|
6
|
+
config.fluoride.directory = "fluoride-collector"
|
7
|
+
|
8
|
+
initializer "fluoride-collector.add_middleware" do |app|
|
9
|
+
app.middleware.insert_after("ActionDispatch::ShowExceptions",
|
10
|
+
Fluoride::Collector::Middleware, config.fluoride.directory, config.fluoride.tags)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/spec/middleware.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'fluoride-collector'
|
3
|
+
|
4
|
+
describe Fluoride::Collector::Middleware do
|
5
|
+
include FileSandbox
|
6
|
+
|
7
|
+
let! :collection_directory do
|
8
|
+
dir = sandbox.new(:directory => "collections")
|
9
|
+
Dir.new(dir.path)
|
10
|
+
end
|
11
|
+
|
12
|
+
let :app do
|
13
|
+
run_app = test_app
|
14
|
+
Rack::Builder.app do
|
15
|
+
use Fluoride::Collector::Middleware, "collections", "TEST"
|
16
|
+
run run_app
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let :env do
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "handling exception" do
|
25
|
+
let :test_ex_class do
|
26
|
+
Class.new(Exception)
|
27
|
+
end
|
28
|
+
|
29
|
+
let :test_app do
|
30
|
+
lambda{|env| raise test_ex_class, "test exception"}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not change the response" do
|
34
|
+
expect{
|
35
|
+
app.call(env)
|
36
|
+
}.to raise_error
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should create a collection file" do
|
40
|
+
expect do
|
41
|
+
begin
|
42
|
+
app.call(env)
|
43
|
+
rescue test_ex_class
|
44
|
+
end
|
45
|
+
end.to change{collection_directory.each.to_a.grep(/collection.*/).size}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should keep using the same collection file" do
|
49
|
+
begin
|
50
|
+
app.call(env)
|
51
|
+
rescue test_ex_class
|
52
|
+
end
|
53
|
+
|
54
|
+
expect do
|
55
|
+
begin
|
56
|
+
app.call(env)
|
57
|
+
rescue test_ex_class
|
58
|
+
end
|
59
|
+
path = File::join(collection_directory.path, collection_directory.each.to_a.grep(/collection.*/).first)
|
60
|
+
end.not_to change{collection_directory.each.to_a.grep(/collection.*/).size}
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "creates a file" do
|
64
|
+
let :yaml do
|
65
|
+
begin
|
66
|
+
app.call(env)
|
67
|
+
rescue test_ex_class
|
68
|
+
end
|
69
|
+
path = File::join(collection_directory.path, collection_directory.each.to_a.grep(/collection.*/).first)
|
70
|
+
YAML.load_stream(File.read(path)).first
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should have tags" do
|
74
|
+
yaml["tags"].should == "TEST"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "handling successful responses" do
|
80
|
+
let :test_app do
|
81
|
+
lambda{|env| response }
|
82
|
+
end
|
83
|
+
|
84
|
+
let :response do
|
85
|
+
[200, {'Content-Type' => 'text/plain'}, ['Just a test']]
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should not change the response" do
|
89
|
+
app.call(env).should == response
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should create a collection file" do
|
93
|
+
expect do
|
94
|
+
app.call(env)
|
95
|
+
end.to change{collection_directory.each.to_a.grep(/collection.*/).size}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should keep using the same collection file" do
|
99
|
+
app.call(env)
|
100
|
+
expect do
|
101
|
+
app.call(env)
|
102
|
+
path = File::join(collection_directory.path, collection_directory.each.to_a.grep(/collection.*/).first)
|
103
|
+
end.not_to change{collection_directory.each.to_a.grep(/collection.*/).size}
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "creates a file" do
|
107
|
+
let :yaml do
|
108
|
+
app.call(env)
|
109
|
+
path = File::join(collection_directory.path, collection_directory.each.to_a.grep(/collection.*/).first)
|
110
|
+
YAML.load_stream(File.read(path)).first
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should have tags" do
|
114
|
+
yaml["tags"].should == "TEST"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/spec/railtie.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
#require 'ftools'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module FileSandbox
|
5
|
+
def self.included(spec)
|
6
|
+
return unless spec.respond_to? :before
|
7
|
+
|
8
|
+
spec.before do
|
9
|
+
setup_sandbox
|
10
|
+
end
|
11
|
+
|
12
|
+
spec.after do
|
13
|
+
teardown_sandbox
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class HaveContents
|
18
|
+
def initialize(contents)
|
19
|
+
@contents = contents
|
20
|
+
end
|
21
|
+
|
22
|
+
def matches?(target)
|
23
|
+
case @contents
|
24
|
+
when Regexp
|
25
|
+
@contents =~ target.contents
|
26
|
+
when String
|
27
|
+
@contents == target.contents
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def have_contents(expected)
|
33
|
+
HaveContents.new(expected)
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :sandbox
|
37
|
+
|
38
|
+
def in_sandbox(&block)
|
39
|
+
raise "I expected to create a sandbox as you passed in a block to me" if !block_given?
|
40
|
+
|
41
|
+
setup_sandbox
|
42
|
+
original_error = nil
|
43
|
+
|
44
|
+
begin
|
45
|
+
yield @sandbox
|
46
|
+
rescue => e
|
47
|
+
original_error = e
|
48
|
+
raise
|
49
|
+
ensure
|
50
|
+
begin
|
51
|
+
teardown_sandbox
|
52
|
+
rescue
|
53
|
+
if original_error
|
54
|
+
STDERR.puts "ALERT: a test raised an error and failed to release some lock(s) in the sandbox directory"
|
55
|
+
raise(original_error)
|
56
|
+
else
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup_sandbox(path = '__sandbox')
|
64
|
+
unless @sandbox
|
65
|
+
@sandbox = Sandbox.new(path)
|
66
|
+
@__old_path_for_sandbox = Dir.pwd
|
67
|
+
Dir.chdir(@sandbox.root)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def teardown_sandbox
|
72
|
+
if @sandbox
|
73
|
+
Dir.chdir(@__old_path_for_sandbox)
|
74
|
+
@sandbox.clean_up
|
75
|
+
@sandbox = nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Sandbox
|
80
|
+
attr_reader :root
|
81
|
+
|
82
|
+
def initialize(path = '__sandbox')
|
83
|
+
@root = File.expand_path(path)
|
84
|
+
clean_up
|
85
|
+
FileUtils.mkdir_p @root
|
86
|
+
end
|
87
|
+
|
88
|
+
def [](name)
|
89
|
+
SandboxFile.new(File.join(@root, name), name)
|
90
|
+
end
|
91
|
+
|
92
|
+
# usage new :file=>'my file.rb', :with_contents=>'some stuff'
|
93
|
+
def new(options)
|
94
|
+
if options.has_key? :directory
|
95
|
+
dir = self[options.delete(:directory)]
|
96
|
+
FileUtils.mkdir_p dir.path
|
97
|
+
else
|
98
|
+
file = self[options.delete(:file)]
|
99
|
+
if (binary_content = options.delete(:with_binary_content) || options.delete(:with_binary_contents))
|
100
|
+
file.binary_content = binary_content
|
101
|
+
else
|
102
|
+
file.content = (options.delete(:with_content) || options.delete(:with_contents) || '')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
raise "unexpected keys '#{options.keys.join(', ')}'" unless options.empty?
|
107
|
+
|
108
|
+
dir || file
|
109
|
+
end
|
110
|
+
|
111
|
+
def remove(options)
|
112
|
+
name = File.join(@root, options[:file])
|
113
|
+
FileUtils.remove_file name
|
114
|
+
end
|
115
|
+
|
116
|
+
def clean_up
|
117
|
+
FileUtils.rm_rf @root
|
118
|
+
if File.exists? @root
|
119
|
+
raise "Could not remove directory #{@root.inspect}, something is probably still holding a lock on it"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
class SandboxFile
|
126
|
+
attr_reader :path
|
127
|
+
|
128
|
+
def initialize(path, sandbox_path)
|
129
|
+
@path = path
|
130
|
+
@sandbox_path = sandbox_path
|
131
|
+
end
|
132
|
+
|
133
|
+
def inspect
|
134
|
+
"SandboxFile: #@sandbox_path"
|
135
|
+
end
|
136
|
+
|
137
|
+
def exist?
|
138
|
+
File.exist? path
|
139
|
+
end
|
140
|
+
|
141
|
+
def content
|
142
|
+
File.read path
|
143
|
+
end
|
144
|
+
|
145
|
+
def content=(content)
|
146
|
+
FileUtils.mkdir_p File.dirname(@path)
|
147
|
+
File.open(@path, "w") {|f| f << content}
|
148
|
+
end
|
149
|
+
|
150
|
+
def binary_content=(content)
|
151
|
+
FileUtils.mkdir_p File.dirname(@path)
|
152
|
+
File.open(@path, "wb") {|f| f << content}
|
153
|
+
end
|
154
|
+
|
155
|
+
def create
|
156
|
+
self.content = ''
|
157
|
+
end
|
158
|
+
|
159
|
+
alias exists? exist?
|
160
|
+
alias contents content
|
161
|
+
alias contents= content=
|
162
|
+
alias binary_contents= binary_content=
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
puts Dir::pwd
|
2
|
+
require 'test/unit'
|
3
|
+
begin
|
4
|
+
require 'spec'
|
5
|
+
rescue LoadError
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
class RSpecTest < Test::Unit::TestCase
|
10
|
+
def test_that_rspec_is_available
|
11
|
+
assert_nothing_raised("\n\n * RSpec isn't available - please run: gem install rspec *\n\n"){ ::Spec }
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_that_specs_pass
|
15
|
+
assert(system(*%w{spec -f e -p **/*.rb spec}),"\n\n * Specs failed *\n\n")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
ENV["RAILS_ENV"] ||= 'test'
|
2
|
+
|
3
|
+
# Only the parts of rails we want to use
|
4
|
+
# if you want everything, use "rails/all"
|
5
|
+
require "action_controller/railtie"
|
6
|
+
require "rails/test_unit/railtie"
|
7
|
+
require 'fluoride-collector/rails'
|
8
|
+
|
9
|
+
#root = File.expand_path(File.dirname(__FILE__))
|
10
|
+
|
11
|
+
# Define the application and configuration
|
12
|
+
module Fixture
|
13
|
+
class Application < ::Rails::Application
|
14
|
+
# configuration here if needed
|
15
|
+
config.active_support.deprecation = :stderr
|
16
|
+
config.eager_load = false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Initialize the application
|
21
|
+
Fixture::Application.initialize!
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rspec/core/formatters/base_formatter'
|
3
|
+
require 'cadre/rspec'
|
4
|
+
require 'file-sandbox'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
config.add_formatter(Cadre::RSpec::NotifyOnCompleteFormatter)
|
9
|
+
config.add_formatter(Cadre::RSpec::QuickfixFormatter)
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluoride-collector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Judson Lester
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |2
|
14
|
+
Part of the Fluoride suite - tools for making your black box a bit whiter
|
15
|
+
email:
|
16
|
+
- nyarly@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/fluoride-collector/rails/railtie.rb
|
22
|
+
- lib/fluoride-collector/rails.rb
|
23
|
+
- lib/fluoride-collector/middleware.rb
|
24
|
+
- lib/fluoride-collector.rb
|
25
|
+
- spec/railtie.rb
|
26
|
+
- spec/middleware.rb
|
27
|
+
- spec_help/spec_helper.rb
|
28
|
+
- spec_help/gem_test_suite.rb
|
29
|
+
- spec_help/railtie-help.rb
|
30
|
+
- spec_help/file-sandbox.rb
|
31
|
+
homepage: http://nyarly.github.com/fluoride-collector
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options:
|
37
|
+
- --inline-source
|
38
|
+
- --main
|
39
|
+
- doc/README
|
40
|
+
- --title
|
41
|
+
- fluoride-collector-0.0.1 Documentation
|
42
|
+
require_paths:
|
43
|
+
- lib/
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project: fluoride-collector
|
56
|
+
rubygems_version: 2.0.14
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: Middleware to collect request and reponse pairs
|
60
|
+
test_files:
|
61
|
+
- spec_help/gem_test_suite.rb
|