ccdk 0.2.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.
@@ -0,0 +1,7 @@
1
+ README.markdown
2
+ Manifest.txt
3
+ bin/cc_track_user
4
+ lib/ccdk.rb
5
+ lib/ccdk/log_file.rb
6
+ lib/ccdk/tdump.rb
7
+ lib/ccdk/version.rb
@@ -0,0 +1,54 @@
1
+ # CCDK
2
+
3
+ The Crystal Commerce Debugging Kit
4
+
5
+ ## Description
6
+
7
+ CCDK is a set of tools made to ease development of Rails applications
8
+ developed at [Crystal Commerce](http://www.crystalcommerce.com). It
9
+ includes both executable scripts and modules that can be mixed into
10
+ classes to help debug. The code herein is not designed to be run as
11
+ part of a production application, just for use while building one.
12
+
13
+ ## Included Executables
14
+
15
+ * `cc_track_user`
16
+
17
+ ## `cc_track_user`
18
+
19
+ `cc_track_user` is a script that will search a Rails log file for a
20
+ specific IP address and display the actions that the user has
21
+ performed. This can be helpful in tracking the steps a user took to
22
+ get their session into a particular state.
23
+
24
+ ### Usage
25
+
26
+ `cc_track_user IP_ADDR FILE [FILE ...]`
27
+
28
+ * IP_ADDR is the ip address to track
29
+ * FILE is a a file (or list of files) to search across
30
+
31
+ If any the file names passed to `cc_track_user` end in `.gz`,
32
+ `cc_track_user` will assume that they have been gzipped when it opens
33
+ them. This makes it easy to follow a user's actions across several
34
+ days of logs without having to unzip the file first.
35
+
36
+ It is suggested to pass the files to `cc_track_user` from oldest to
37
+ newest so the output is chronological.
38
+
39
+ ## Included Modules
40
+
41
+ * `Ccdk::TDump`
42
+
43
+ ## `Ccdk::TDump`
44
+
45
+ `TDump` is a module that allows you to display a templated
46
+ representation of a record using the `#tdump` method. It can be passed
47
+ a filename, a string template, an erb object, or an IO (really
48
+ anything that responds to `#read`).
49
+
50
+ ### Examples
51
+
52
+ car.tdump('I have a <%= year %> <%= make %> <%= model %>')
53
+
54
+ product.tdump('path/to/product_template.erb')
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ccdk/log_file'
3
+
4
+ class ActionCounter
5
+ attr_reader :ignore_domain, :counts
6
+
7
+ def initialize(ignore_domain = false)
8
+ @counts = {}
9
+ @ignore_domain = ignore_domain
10
+ end
11
+
12
+ def count(file_name)
13
+ log = Ccdk::LogFile.open(file_name)
14
+
15
+ log.each_block do |blk|
16
+ a = get_action(blk)
17
+
18
+ if a
19
+ counts[a] ||= 0
20
+ counts[a] += 1
21
+ end
22
+ end
23
+ end
24
+
25
+ def print_results
26
+ counts.sort{ |a, b| b[1] <=> a[1] }.each do |(action, count)|
27
+ printf("%6d: %s\n", count, action)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def get_action(block)
34
+ url = block.match(/\[http(s?):\/\/([^\/?]+(\/[^?]+))(\?.*)?\]/)
35
+ return nil unless url
36
+
37
+ if ignore_domain
38
+ url[3]
39
+ else
40
+ url[2]
41
+ end
42
+ end
43
+ end
44
+
45
+ if ARGV.size < 1
46
+ puts "Usage: cc_count_actions [--ignore-domain] FILE [FILE ...]"
47
+ exit 1
48
+ elsif ARGV[0] == "--ignore-domain"
49
+ counter = ActionCounter.new(true)
50
+ ARGV.shift
51
+ else
52
+ counter = ActionCounter.new
53
+ end
54
+
55
+ ARGV.each do |file_name|
56
+ counter.count(file_name)
57
+ end
58
+
59
+ begin
60
+ counter.print_results
61
+ rescue Errno::EPIPE, Interrupt
62
+ # Allow the output to be passed to head etc
63
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ccdk/log_file'
3
+
4
+ class Tracker
5
+ def initialize(ip_address)
6
+ @ip_address = ip_address
7
+ end
8
+
9
+ def track(file_name)
10
+ log = Ccdk::LogFile.open(file_name)
11
+
12
+ log.each_block do |blk|
13
+ display(blk) if blk =~ /for #{@ip_address}/
14
+ end
15
+
16
+ log.close
17
+ end
18
+
19
+ def display(block)
20
+ timestamp_method = block.match(/at (.+)\) \[(\w+)\]/)
21
+ timestamp, method = timestamp_method[1], timestamp_method[2]
22
+ url = block.match(/\[(http(s?):\/\/.+\/.*)\]/)
23
+ params = block.match(/Parameters: (\{.*\})/)
24
+
25
+ if url && timestamp && method && params
26
+ puts timestamp
27
+ puts " #{method} #{url[1]}"
28
+ puts " #{params[1]}"
29
+ puts
30
+ end
31
+ end
32
+ end
33
+
34
+ if ARGV.size < 2
35
+ puts "Usage: cc_track_user IP_ADDR FILE [FILE ...]"
36
+ exit 1
37
+ end
38
+
39
+ tracker = Tracker.new(ARGV.shift)
40
+ ARGV.each do |file_name|
41
+ tracker.track(file_name)
42
+ end
@@ -0,0 +1,19 @@
1
+ module Ccdk
2
+ MODULES = {
3
+ :tdump => 'ccdk/tdump'
4
+ }
5
+
6
+ class << self
7
+ def load_all!
8
+ MODULES.values.each do |lib|
9
+ Kernel.require lib
10
+ end
11
+ end
12
+
13
+ def load(*sym)
14
+ sym.each do |sym|
15
+ Kernel.require MODULES[sym]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,105 @@
1
+ require 'zlib'
2
+
3
+ module Ccdk #:nodoc:
4
+
5
+ # A class for reading Rails log files. Provides a way to step through the log
6
+ # by assuming the format of the output conforms to the Rails defaul.
7
+ class LogFile
8
+
9
+ class << self
10
+
11
+ # Open a LogFile with the provided name. If the file name ends in '.gz'
12
+ # assume that the file is gzipped
13
+ #
14
+ # ==== Parameters
15
+ #
16
+ # * +file_name+ - The name of the log file to open
17
+ #
18
+ # ==== Examples
19
+ # # Open the log file 'log/production.log'
20
+ # LogFile.open('log/production.log')
21
+ #
22
+ # # Open the gzipped log file 'log/old/production.log.1.gz'
23
+ # LogFile.open('log/old/production.log.1.gz')
24
+ def open(file_name)
25
+ if file_name =~ /\.gz$/
26
+ GzipLogFile.new(file_name)
27
+ else
28
+ LogFile.new(file_name)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Create a new LogFile
34
+ #
35
+ # ==== Parameters
36
+ #
37
+ # * +name+ - The name of the file to open
38
+ def initialize(name)
39
+ @fh = File.open(name, 'r')
40
+ end
41
+
42
+ # execute code on each block of the log file. A block is defined as starting
43
+ # with Processing and ending with a blank line
44
+ #
45
+ # ==== Examples
46
+ # log.each_block{ |b| puts b if b =~ ApplicationController }
47
+ def each_block
48
+ while !@fh.eof?
49
+ yield next_block
50
+ end
51
+ end
52
+
53
+ # Read the next block from the log file
54
+ #
55
+ # TODO: please refactor me
56
+ def next_block
57
+ lines = []
58
+
59
+ line = @fh.gets
60
+ while !start_of_block?(line)
61
+ line = @fh.gets
62
+ return "" if @fh.eof?
63
+ end
64
+
65
+ while(!@fh.eof? && !end_of_block?(line))
66
+ lines << line.chomp
67
+ line = @fh.gets
68
+ end
69
+
70
+ lines.join("\n")
71
+ end
72
+
73
+ # Close the log file
74
+ def close
75
+ @fh.close
76
+ end
77
+
78
+ private
79
+
80
+ def start_of_block?(line)
81
+ line && line =~ /^Processing/
82
+ end
83
+
84
+ def end_of_block?(line)
85
+ line.nil? || line =~ /^$/ ||
86
+ line =~ /^Starting the New Relic/ ||
87
+ line =~ /^\*\* \[Hoptoad\]/
88
+ end
89
+ end
90
+
91
+ # A Gzipped version of the log file
92
+ # All behavior is the same as a standard log file except it assumes the
93
+ # content has been gzipped
94
+ class GzipLogFile < LogFile
95
+
96
+ # Create a new GzipLogFile. The file name passed must be gzipped
97
+ #
98
+ # ==== Parameters
99
+ #
100
+ # * +file_name+ - The name of the gzipped log file to open
101
+ def initialize(file_name)
102
+ @fh = Zlib::GzipReader.new(File.open(file_name, 'r'))
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,62 @@
1
+ require 'erb'
2
+
3
+ module Ccdk #:nodoc:
4
+
5
+ # Create a templated representation of the object. This module can be useful
6
+ # For debugging objects by dumping them out to a template of your choosing.
7
+ # Similar in use to <tt>pp</tt>, but more flexible so that you can display
8
+ # only the information that you care about.
9
+ module TDump
10
+
11
+ # Convert the object to a <tt>String</tt> based on the provided template.
12
+ # Templates can be specified in several ways
13
+ #
14
+ # * A <tt>String</tt> representation of the template.
15
+ # * A <tt>String</tt> path to a template file
16
+ # * An <tt>Erb</tt> object of the template
17
+ # * An <tt>IO<tt> object that can be read to retrieve a template
18
+ # (Really anything that responds to <tt>#read</tt>)
19
+ #
20
+ # ==== Parameters
21
+ #
22
+ # * +template+ - The template to use for formatting the object
23
+ #
24
+ # ==== Examples
25
+ # # Output information to a templating string
26
+ # car.tdump('I have a <%= year %> <%= make %> <%= model %>')
27
+ #
28
+ # # Use the path to an erb template file
29
+ # product.tdump('path/to/product_template.erb')
30
+ def tdump(template)
31
+ if template.is_a? ERB
32
+ tdump_erb(template)
33
+ elsif template.respond_to?(:read)
34
+ tdump_io(template)
35
+ elsif template.is_a? String
36
+ tdump_string(template)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def tdump_erb(erb)
43
+ erb.result(binding)
44
+ end
45
+
46
+ def tdump_string(string)
47
+ if File.exist?(string)
48
+ tdump_erb(ERB.new(File.read(string)))
49
+ else
50
+ tdump_erb(ERB.new(string))
51
+ end
52
+ end
53
+
54
+ def tdump_io(io)
55
+ tdump_erb(ERB.new(io.read))
56
+ end
57
+ end
58
+ end
59
+
60
+ class Object
61
+ include Ccdk::TDump
62
+ end
@@ -0,0 +1,3 @@
1
+ module Ccdk
2
+ VERSION = "0.2.0"
3
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ccdk
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Ryan Burrows
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-19 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: " Ccdk is a set of extensions and scripts helpful to debugging and development\n"
36
+ email: rhburrows@crystalcommerce.com
37
+ executables:
38
+ - cc_track_user
39
+ - cc_count_actions
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - README.markdown
46
+ - Manifest.txt
47
+ - bin/cc_track_user
48
+ - lib/ccdk.rb
49
+ - lib/ccdk/log_file.rb
50
+ - lib/ccdk/tdump.rb
51
+ - lib/ccdk/version.rb
52
+ - bin/cc_count_actions
53
+ has_rdoc: true
54
+ homepage: http://www.crystalcommerce.com
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 23
77
+ segments:
78
+ - 1
79
+ - 3
80
+ - 6
81
+ version: 1.3.6
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.3.7
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Crystal Commerce's Debugging Kit
89
+ test_files: []
90
+