request_recorder 0.0.1

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.
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - ree
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ gem "rake"
5
+ gem "rspec", "~>2"
6
+ gem "sqlite3"
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ request_recorder (0.0.1)
5
+ activerecord (= 2.3.14)
6
+ rack
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ activerecord (2.3.14)
12
+ activesupport (= 2.3.14)
13
+ activesupport (2.3.14)
14
+ diff-lcs (1.1.3)
15
+ rack (1.4.1)
16
+ rake (0.9.2)
17
+ rspec (2.6.0)
18
+ rspec-core (~> 2.6.0)
19
+ rspec-expectations (~> 2.6.0)
20
+ rspec-mocks (~> 2.6.0)
21
+ rspec-core (2.6.4)
22
+ rspec-expectations (2.6.0)
23
+ diff-lcs (~> 1.1.2)
24
+ rspec-mocks (2.6.0)
25
+ sqlite3 (1.3.6)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ rake
32
+ request_recorder!
33
+ rspec (~> 2)
34
+ sqlite3
data/MIGRATION ADDED
@@ -0,0 +1,12 @@
1
+ class AddRecordedRequests < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :recorded_request_log do |t|
4
+ t.text "log"
5
+ t.timestamp :created_at
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :recorded_request_log
11
+ end
12
+ end
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default do
4
+ sh "rspec spec/"
5
+ end
6
+
7
+ # extracted from https://github.com/grosser/project_template
8
+ rule /^version:bump:.*/ do |t|
9
+ file = "lib/request_recorder/version.rb"
10
+
11
+ sh "git status | grep 'nothing to commit'" # ensure we are not dirty
12
+ index = ["major", "minor", "patch"].index(t.name.split(':').last)
13
+ version_file = File.read(file)
14
+ old_version, *version_parts = version_file.match(/(\d+)\.(\d+)\.(\d+)/).to_a
15
+ version_parts[index] = version_parts[index].to_i + 1
16
+ version_parts[2] = 0 if index < 2 # remove patch for minor
17
+ version_parts[1] = 0 if index < 1 # remove minor for major
18
+ new_version = version_parts * '.'
19
+
20
+ File.open(file,"w"){|f| f.write(version_file.sub(old_version, new_version)) }
21
+ sh "bundle && git add #{file} Gemfile.lock && git commit -m 'bump version to #{new_version}'"
22
+ end
data/Readme.md ADDED
@@ -0,0 +1,22 @@
1
+ Record your rack/rails requests and store them for future inspection
2
+
3
+ Install
4
+ =======
5
+
6
+ gem install request_recorder
7
+ # generate a add_recorded_requests migration and paste content of MIGRATION
8
+
9
+ Usage
10
+ =====
11
+
12
+ - Add it to the bottom of you middleware stack
13
+ - request a page with __request_recording=10 -> record next 10 requests from my browser
14
+ - RequestRecorded::Requests gets a new entry with all the logging info from rails + activerecord
15
+ - go to the database or build a nice frontend for RequestRecorded::Requests
16
+
17
+ Author
18
+ ======
19
+ [Michael Grosser](http://grosser.it)<br/>
20
+ michael@grosser.it<br/>
21
+ License: MIT<br/>
22
+ [![Build Status](https://travis-ci.org/grosser/request_recorder.png)](https://travis-ci.org/grosser/request_recorder)
@@ -0,0 +1,7 @@
1
+ require "active_record"
2
+
3
+ module RequestRecorder
4
+ class Log < ActiveRecord::Base
5
+ set_table_name "recorded_request_log" # TODO use self.table_name = in rails 3
6
+ end
7
+ end
@@ -0,0 +1,77 @@
1
+ require "rack/request"
2
+ require "rack/response"
3
+
4
+ module RequestRecorder
5
+ class Middleware
6
+ MARKER = "__request_recording"
7
+ MAX_STEPS = 100
8
+
9
+ def initialize(app, options={})
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ # keep this part as fast as possible, since 99.99999% of requests will not need it
15
+ return @app.call(env) unless (
16
+ (env["QUERY_STRING"] && env["QUERY_STRING"].include?(MARKER)) or
17
+ (env["HTTP_COOKIE"] && env["HTTP_COOKIE"].include?(MARKER))
18
+ )
19
+
20
+ result = logging_to_recorded do
21
+ @app.call(env)
22
+ end
23
+
24
+ steps_left, id = read_state_from_env(env)
25
+ return [500, {}, "__request_recording exceeded maximum value #{MAX_STEPS}"] if steps_left > MAX_STEPS
26
+ id = persist_log(id, @recorder.log)
27
+ response_with_data_in_cookie(result, steps_left, id)
28
+ end
29
+
30
+ private
31
+
32
+ def persist_log(id, log)
33
+ record = (id ? Log.find(id) : Log.new(:log => ""))
34
+ record.log += log.join("\n") + "\n\n"
35
+ record.save!
36
+ record.id
37
+ end
38
+
39
+ def read_state_from_env(env)
40
+ request = Rack::Request.new(env)
41
+ if request.cookies[MARKER]
42
+ request.cookies[MARKER].split(":").map(&:to_i)
43
+ else
44
+ [env["QUERY_STRING"][/#{MARKER}=(\d+)/, 1].to_i, nil]
45
+ end
46
+ end
47
+
48
+ def response_with_data_in_cookie(result, to_go, id)
49
+ status, headers, body = result
50
+ response = Rack::Response.new(body, status, headers)
51
+ if to_go <= 1
52
+ response.delete_cookie(MARKER)
53
+ else
54
+ response.set_cookie(MARKER, {:value => "#{to_go.to_i - 1}:#{id}", :path => "/", :expires => Time.now+24*60*60})
55
+ end
56
+
57
+ response.finish # finish writes out the response in the expected format.
58
+ end
59
+
60
+ def logging_to_recorded
61
+ @recorder = Recorder.new
62
+ old = [
63
+ ActiveRecord::Base.logger.instance_variable_get("@log"),
64
+ ActiveRecord::Base.logger.auto_flushing,
65
+ ActiveRecord::Base.logger.level
66
+ ]
67
+ ActiveRecord::Base.logger.instance_variable_set("@log", @recorder)
68
+ ActiveRecord::Base.logger.auto_flushing = true
69
+ ActiveRecord::Base.logger.level = Logger::DEBUG
70
+ yield
71
+ ensure
72
+ ActiveRecord::Base.logger.instance_variable_set("@log", old[0])
73
+ ActiveRecord::Base.logger.auto_flushing = old[1]
74
+ ActiveRecord::Base.logger.level = old[2]
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,13 @@
1
+ module RequestRecorder
2
+ class Recorder
3
+ attr_reader :log
4
+
5
+ def initialize
6
+ @log = []
7
+ end
8
+
9
+ def write(s)
10
+ @log << s
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module RequestRecorder
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "request_recorder/version"
2
+ require "request_recorder/log"
3
+ require "request_recorder/middleware"
4
+ require "request_recorder/recorder"
5
+
6
+ module RequestRecorder
7
+ end
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ name = "request_recorder"
3
+ require "#{name}/version"
4
+
5
+ Gem::Specification.new name, RequestRecorder::VERSION do |s|
6
+ s.summary = "Record your rack/rails requests and store them for future inspection"
7
+ s.authors = ["Michael Grosser"]
8
+ s.email = "michael@grosser.it"
9
+ s.homepage = "http://github.com/grosser/#{name}"
10
+ s.files = `git ls-files`.split("\n")
11
+ s.license = "MIT"
12
+ s.add_dependency "activerecord", "2.3.14"
13
+ s.add_dependency "rack"
14
+ end
@@ -0,0 +1,84 @@
1
+ require "spec_helper"
2
+
3
+ describe RequestRecorder do
4
+ let(:original_logger){ ActiveSupport::BufferedLogger.new("/dev/null") }
5
+ let(:activate_logger){ {"QUERY_STRING" => "__request_recording=10"} }
6
+ let(:inner_app){ lambda{|env|
7
+ Car.first
8
+ [200, {}, "assadasd"]
9
+ } }
10
+ let(:existing_request){ RequestRecorder::Log.create(:log => "BEFORE") }
11
+
12
+ before do
13
+ ActiveRecord::Base.logger.instance_variable_set("@log", original_logger)
14
+ ActiveRecord::Base.logger.auto_flushing = 1000
15
+ ActiveRecord::Base.logger.level = Logger::ERROR
16
+ end
17
+
18
+ after do
19
+ RequestRecorder::Log.delete_all
20
+ end
21
+
22
+ it "has a VERSION" do
23
+ RequestRecorder::VERSION.should =~ /^[\.\da-z]+$/
24
+ end
25
+
26
+ it "records activerecord queries" do
27
+ middleware = RequestRecorder::Middleware.new(inner_app)
28
+ middleware.call(activate_logger)
29
+ RequestRecorder::Log.last.log.should include "SELECT"
30
+ end
31
+
32
+ it "blows up if you go over the maximum" do
33
+ middleware = RequestRecorder::Middleware.new(inner_app)
34
+ status, headers, body = middleware.call("QUERY_STRING" => "__request_recording=99999")
35
+ status.should == 500
36
+ body.should include "maximum"
37
+ end
38
+
39
+ context "subsequent requests" do
40
+ it "sets cookie in first step" do
41
+ middleware = RequestRecorder::Middleware.new(inner_app)
42
+ status, headers, body = middleware.call(activate_logger)
43
+ generated = RequestRecorder::Log.last
44
+ headers["Set-Cookie"].should include "__request_recording=9%3A#{generated.id}; path=/; expires="
45
+ end
46
+
47
+ it "appends to existing log" do
48
+ middleware = RequestRecorder::Middleware.new(inner_app)
49
+ middleware.call("HTTP_COOKIE" => "__request_recording=8:#{existing_request.id}")
50
+ existing_request.reload
51
+ existing_request.log.should include "SELECT"
52
+ existing_request.log.should include "BEFORE"
53
+ end
54
+
55
+ it "decrements cookie on each step" do
56
+ middleware = RequestRecorder::Middleware.new(inner_app)
57
+ status, headers, body = middleware.call("HTTP_COOKIE" => "__request_recording=2:#{existing_request.id};foo=bar")
58
+ headers["Set-Cookie"].should include "__request_recording=1%3A#{existing_request.id}; path=/; expires="
59
+ end
60
+
61
+ it "removes cookie if final step is reached" do
62
+ middleware = RequestRecorder::Middleware.new(inner_app)
63
+ status, headers, body = middleware.call("HTTP_COOKIE" => "__request_recording=1:#{existing_request.id};foo=bar")
64
+ headers["Set-Cookie"].should include "__request_recording=; expires="
65
+ end
66
+ end
67
+
68
+ it "should not record if __request_recording is not given" do
69
+ middleware = RequestRecorder::Middleware.new(inner_app)
70
+ middleware.call(
71
+ "QUERY_STRING" => "stuff=hello", "HTTP_COOKIE" => "bar=foo"
72
+ )
73
+ RequestRecorder::Log.count.should == 0
74
+ end
75
+
76
+ it "restores the AR logger after executing" do
77
+ middleware = RequestRecorder::Middleware.new(inner_app)
78
+ middleware.call(activate_logger)
79
+
80
+ ActiveRecord::Base.logger.instance_variable_get("@log").object_id.should == original_logger.object_id
81
+ ActiveRecord::Base.logger.auto_flushing.should == 1000
82
+ ActiveRecord::Base.logger.level.should == Logger::ERROR
83
+ end
84
+ end
@@ -0,0 +1,23 @@
1
+ require "request_recorder"
2
+
3
+ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new("/dev/null")
4
+
5
+ # connect
6
+ ActiveRecord::Base.establish_connection(
7
+ :adapter => "sqlite3",
8
+ :database => ":memory:"
9
+ )
10
+
11
+ # create tables
12
+ ActiveRecord::Schema.verbose = false
13
+ ActiveRecord::Schema.define(:version => 1) do
14
+ eval(File.read(File.expand_path("../../MIGRATION", __FILE__)))
15
+ AddRecordedRequests.up
16
+
17
+ create_table :cars do |t|
18
+ t.timestamps
19
+ end
20
+ end
21
+
22
+ class Car < ActiveRecord::Base
23
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: request_recorder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Grosser
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.3.14
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.3.14
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description:
47
+ email: michael@grosser.it
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - .travis.yml
53
+ - Gemfile
54
+ - Gemfile.lock
55
+ - MIGRATION
56
+ - Rakefile
57
+ - Readme.md
58
+ - lib/request_recorder.rb
59
+ - lib/request_recorder/log.rb
60
+ - lib/request_recorder/middleware.rb
61
+ - lib/request_recorder/recorder.rb
62
+ - lib/request_recorder/version.rb
63
+ - request_recorder.gemspec
64
+ - spec/request_recorder_spec.rb
65
+ - spec/spec_helper.rb
66
+ homepage: http://github.com/grosser/request_recorder
67
+ licenses:
68
+ - MIT
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ segments:
80
+ - 0
81
+ hash: -1997442405915248740
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ segments:
89
+ - 0
90
+ hash: -1997442405915248740
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 1.8.24
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Record your rack/rails requests and store them for future inspection
97
+ test_files: []