ktlacaelel-plog 0.0.2
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/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/plog +22 -0
- data/lib/cli.rb +112 -0
- data/lib/completed_line.rb +110 -0
- data/lib/log_file.rb +46 -0
- data/lib/object_file.rb +153 -0
- data/lib/plog.rb +13 -0
- data/lib/url.rb +36 -0
- data/plog.gemspec +70 -0
- data/test/completed_line_test.rb +156 -0
- data/test/data/example.log +10 -0
- data/test/log_file_test.rb +36 -0
- data/test/object_file_test.rb +115 -0
- data/test/plog_test.rb +8 -0
- data/test/test_helper.rb +10 -0
- data/test/url_test.rb +28 -0
- metadata +88 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Kazuyoshi Tlacaelel
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
= plog
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but
|
13
|
+
bump version in a commit by itself I can ignore when I pull)
|
14
|
+
* Send me a pull request. Bonus points for topic branches.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2009 Kazuyoshi Tlacaelel. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "plog"
|
8
|
+
gem.summary = %Q{TODO: one-line summary of your gem}
|
9
|
+
gem.description = %Q{TODO: longer description of your gem}
|
10
|
+
gem.email = "kazu.dev@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/ktlacaelel/plog"
|
12
|
+
gem.authors = ["Kazuyoshi Tlacaelel"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/*_test.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/*_test.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
if File.exist?('VERSION')
|
47
|
+
version = File.read('VERSION')
|
48
|
+
else
|
49
|
+
version = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "plog #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
data/bin/plog
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/home/jognote/ruby/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'plog'
|
5
|
+
|
6
|
+
banner = '
|
7
|
+
ERROR!
|
8
|
+
No logs given, I have nothing to do!
|
9
|
+
|
10
|
+
HELP?
|
11
|
+
The "plog" ( production log ) executable receives one option.
|
12
|
+
This must be a directory containing one or more production logs.
|
13
|
+
Only logs in the first level will be parsed.
|
14
|
+
Recursive reading is not allowed
|
15
|
+
|
16
|
+
USAGE:
|
17
|
+
$ plog directory_with_logs/
|
18
|
+
|
19
|
+
'
|
20
|
+
|
21
|
+
abort banner if ARGV.size != 1
|
22
|
+
Plog::Cli.new(ARGV[0]).run!
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class Cli
|
4
|
+
|
5
|
+
attr_reader :directory, :log_files
|
6
|
+
|
7
|
+
LOG_FILE = 'log_statistics.txt'
|
8
|
+
|
9
|
+
# ==========================================================================
|
10
|
+
# CLIENT INTERFACE
|
11
|
+
# ==========================================================================
|
12
|
+
|
13
|
+
def initialize(directory)
|
14
|
+
@directory = directory
|
15
|
+
@log_files = []
|
16
|
+
validate!
|
17
|
+
load_log_files!
|
18
|
+
end
|
19
|
+
|
20
|
+
def run!
|
21
|
+
stdout count_of_logs_banner(@log_files.size)
|
22
|
+
stdout notice_banner
|
23
|
+
create_object_files
|
24
|
+
destroy_old_log_file
|
25
|
+
append_headers_to_log_file
|
26
|
+
parse_object_files
|
27
|
+
end
|
28
|
+
|
29
|
+
# ==========================================================================
|
30
|
+
# INTERNAL INTERFACE
|
31
|
+
# ==========================================================================
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def trim(string)
|
36
|
+
return '' unless string.is_a? String
|
37
|
+
string.gsub(/^\s+/, '').gsub(/\s+$/, '')
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_object_files
|
41
|
+
@log_files.each do |log_file|
|
42
|
+
stdout parsing_log_file_banner(log_file.name)
|
43
|
+
log_file.parse_completed_lines!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_object_files
|
48
|
+
Dir.glob('objects/*').each do |object_file|
|
49
|
+
file = File.new(LOG_FILE, 'a+')
|
50
|
+
file.puts trim(ObjectFile.new(object_file).export)
|
51
|
+
file.close
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def destroy_old_log_file
|
56
|
+
FileUtils.touch LOG_FILE
|
57
|
+
FileUtils.rm LOG_FILE
|
58
|
+
end
|
59
|
+
|
60
|
+
def append_headers_to_log_file
|
61
|
+
file = File.new(LOG_FILE, 'a+')
|
62
|
+
file.puts trim(ObjectFile.formated_headers)
|
63
|
+
file.close
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate!
|
67
|
+
abort directory_not_found_banner unless File.exist? @directory
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_log_files!
|
71
|
+
Dir.glob(File.join(@directory, '*.log')).each do |log_file|
|
72
|
+
@log_files << LogFile.new(log_file)
|
73
|
+
stdout loading_log_file_banner(log_file)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def stdout(string)
|
78
|
+
puts ' ** ' + string
|
79
|
+
end
|
80
|
+
|
81
|
+
# ==========================================================================
|
82
|
+
# BANNERS
|
83
|
+
# ==========================================================================
|
84
|
+
|
85
|
+
def notice_banner
|
86
|
+
'
|
87
|
+
NOTICE:
|
88
|
+
|
89
|
+
Parsing logs, this may take a logn-while go get yourself a coffe!
|
90
|
+
While I hanlde this stuff for you.
|
91
|
+
'
|
92
|
+
end
|
93
|
+
|
94
|
+
def count_of_logs_banner(size)
|
95
|
+
'Count of loaded logs : %s ' % size
|
96
|
+
end
|
97
|
+
|
98
|
+
def parsing_log_file_banner(file)
|
99
|
+
'Parsing log located at : %s ' % file
|
100
|
+
end
|
101
|
+
|
102
|
+
def loading_log_file_banner(file)
|
103
|
+
'Initializing log file: %s' % file
|
104
|
+
end
|
105
|
+
|
106
|
+
def directory_not_found_banner
|
107
|
+
'No such dir: %s' % @directory
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class CompletedLine
|
4
|
+
|
5
|
+
TOTAL_TIME_REGEX = /^(Completed in )(\d+)ms/ # \2
|
6
|
+
VIEW_TIME_REGEX = /([^\(]+)([^V]+)(View: )(\d+)(.*)/ # \4
|
7
|
+
DB_TIME_REGEX = /([^\(]+)([^D]+)(DB: )(\d+)(.*)/ # \4
|
8
|
+
URL_REGEX = /([^\[]+)(\[)([^\]]+)(.*)/
|
9
|
+
STATUS_REGEX = /(\s)(\d+)(\s)(\w+)(\s)(\[)(.*)/ # \2, \4
|
10
|
+
COMPLETED_TIME_REGEX = /^Completed/
|
11
|
+
|
12
|
+
def self.url
|
13
|
+
URL.new(@url)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.valid?
|
17
|
+
completed_line?
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.completed_line?
|
21
|
+
return false unless @line.is_a? String
|
22
|
+
return false unless COMPLETED_TIME_REGEX =~ @line
|
23
|
+
return false unless DB_TIME_REGEX =~ @line
|
24
|
+
return false if @line.size < 10
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.read!(string_line)
|
29
|
+
@line = string_line
|
30
|
+
validate
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.db_time
|
34
|
+
@db_time
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.view_time
|
38
|
+
@view_time
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.total_time
|
42
|
+
@total_time
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.status_string
|
46
|
+
@status_string
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.status_number
|
50
|
+
@status_number
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.to_csv
|
54
|
+
[total_time, view_time, db_time, url.simplify].join(',')
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.merge(view, db)
|
58
|
+
@view_time += view.to_i
|
59
|
+
@db_time += db.to_i
|
60
|
+
@total_time = (@view_time + @db_time)
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def self.validate
|
66
|
+
return unless completed_line?
|
67
|
+
fragmentize!
|
68
|
+
extract_time!
|
69
|
+
extract_url!
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.fragmentize!
|
73
|
+
@first_fragment, @second_fragment = @line.split('|')
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.extract_time!
|
77
|
+
@total_time = extract_total_time
|
78
|
+
@view_time = extract_view_time
|
79
|
+
@db_time = extract_db_time
|
80
|
+
@status_number, @status_string = extract_status_number_and_string
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.extract_total_time
|
85
|
+
return 0 unless TOTAL_TIME_REGEX =~ @first_fragment
|
86
|
+
@first_fragment.gsub(TOTAL_TIME_REGEX, '\2').to_i
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.extract_db_time
|
90
|
+
return 0 unless DB_TIME_REGEX =~ @first_fragment
|
91
|
+
@first_fragment.gsub(DB_TIME_REGEX, '\4').to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.extract_view_time
|
95
|
+
return 0 unless VIEW_TIME_REGEX =~ @first_fragment
|
96
|
+
@first_fragment.gsub(VIEW_TIME_REGEX, '\4').to_i
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.extract_status_number_and_string
|
100
|
+
return 0 unless STATUS_REGEX =~ @second_fragment
|
101
|
+
@second_fragment.gsub(STATUS_REGEX, '\2,\4').split(',')
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.extract_url!
|
105
|
+
@url = @second_fragment.gsub(URL_REGEX, '\3')
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
data/lib/log_file.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class LogFile < File
|
4
|
+
|
5
|
+
DUMP_DIR = 'objects'
|
6
|
+
|
7
|
+
attr_accessor :name
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
unless File.exist? DUMP_DIR
|
11
|
+
Dir.mkdir DUMP_DIR
|
12
|
+
end
|
13
|
+
super(file, 'r')
|
14
|
+
@name = file
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate(file)
|
18
|
+
abort 'File not found: ' + file.inspect unless File.exist? file
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_completed_lines!
|
22
|
+
each_line do |line|
|
23
|
+
CompletedLine.read! line
|
24
|
+
parse_completed_line
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_completed_line
|
29
|
+
return unless CompletedLine.valid?
|
30
|
+
of = ObjectFile.new(object_path, 'w+')
|
31
|
+
of.simplified_url = CompletedLine.url.simplify
|
32
|
+
of.append_total_time CompletedLine.total_time
|
33
|
+
of.append_db_time CompletedLine.db_time
|
34
|
+
of.append_view_time CompletedLine.view_time
|
35
|
+
of.append_hits 1
|
36
|
+
of.save_changes!
|
37
|
+
of.close
|
38
|
+
end
|
39
|
+
|
40
|
+
def object_path
|
41
|
+
File.join(DUMP_DIR, CompletedLine.url.hashify)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/lib/object_file.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class ObjectFile < File
|
4
|
+
|
5
|
+
DEFAULT_VALUES = {
|
6
|
+
:@total_hits => 0,
|
7
|
+
:@total_time => 0,
|
8
|
+
:@view_time => 0,
|
9
|
+
:@db_time => 0,
|
10
|
+
:@simplified_url => 'default'
|
11
|
+
}
|
12
|
+
|
13
|
+
KEY_ORDER = [
|
14
|
+
:@total_hits,
|
15
|
+
:@total_time,
|
16
|
+
:@view_time,
|
17
|
+
:@db_time,
|
18
|
+
:@simplified_url
|
19
|
+
]
|
20
|
+
|
21
|
+
attr_accessor :simplified_url
|
22
|
+
attr_reader :view_time, :total_time, :db_time, :total_hits
|
23
|
+
|
24
|
+
def initialize(*args)
|
25
|
+
raw_data(args.first)
|
26
|
+
super(*args)
|
27
|
+
setup!
|
28
|
+
end
|
29
|
+
|
30
|
+
def setup!
|
31
|
+
unpack_default_values if ObjectFile.zero? path
|
32
|
+
load_changes!
|
33
|
+
end
|
34
|
+
|
35
|
+
def unpack_default_values
|
36
|
+
DEFAULT_VALUES.each do |k, v|
|
37
|
+
instance_variable_set k, v
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def append_view_time(value)
|
42
|
+
@view_time += value.to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
def append_db_time(value)
|
46
|
+
@db_time += value.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def append_total_time(value)
|
50
|
+
@total_time += value
|
51
|
+
end
|
52
|
+
|
53
|
+
def append_hits(value)
|
54
|
+
@total_hits += value
|
55
|
+
end
|
56
|
+
|
57
|
+
def save_changes!
|
58
|
+
puts serialize_changes
|
59
|
+
end
|
60
|
+
|
61
|
+
def serialize_changes
|
62
|
+
KEY_ORDER.collect { |key| instance_variable_get key }.join(',')
|
63
|
+
end
|
64
|
+
|
65
|
+
def load_changes!
|
66
|
+
return if @raw_data == ''
|
67
|
+
@total_hits, @total_time, @view_time, @db_time, @simplified_url = @raw_data.split(',')
|
68
|
+
intify!
|
69
|
+
end
|
70
|
+
|
71
|
+
def intify!
|
72
|
+
@total_hits = @total_hits.to_i
|
73
|
+
@total_time = @total_time.to_i
|
74
|
+
@db_time = @db_time.to_i
|
75
|
+
@view_time = @view_time.to_i
|
76
|
+
end
|
77
|
+
|
78
|
+
def raw_data(some_string)
|
79
|
+
if File.exist? some_string
|
80
|
+
@raw_data = File.new(some_string).read
|
81
|
+
else
|
82
|
+
@raw_data = ''
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def export_settings
|
87
|
+
[
|
88
|
+
[@total_hits, [:left, 10]],
|
89
|
+
[@total_time, [:left, 10]],
|
90
|
+
[avg_total_time, [:left, 10]],
|
91
|
+
[@db_time, [:left, 10]],
|
92
|
+
[avg_db_time, [:left, 10]],
|
93
|
+
[@view_time, [:left, 10]],
|
94
|
+
[avg_view_time, [:left, 10]],
|
95
|
+
[simplified_url, [:left, 40]],
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.export_header_settings
|
100
|
+
[
|
101
|
+
['Hits', [:left, 10]],
|
102
|
+
['Time', [:left, 10]],
|
103
|
+
['Avg-Time', [:left, 10]],
|
104
|
+
['DbTime', [:left, 10]],
|
105
|
+
['Avg-DB', [:left, 10]],
|
106
|
+
['View', [:left, 10]],
|
107
|
+
['Avg-View', [:left, 10]],
|
108
|
+
['Url', [:left, 40]],
|
109
|
+
]
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.formated_headers
|
113
|
+
export_header_settings.collect do |value, setting|
|
114
|
+
if setting.first == :left
|
115
|
+
value.to_s.ljust(setting[1])
|
116
|
+
else
|
117
|
+
value.to_s.rjust(setting[1])
|
118
|
+
end
|
119
|
+
end.join('') + "\n"
|
120
|
+
end
|
121
|
+
|
122
|
+
def export_headers
|
123
|
+
export_header_settings.collect { |header| header.ljust(20) }.join('')
|
124
|
+
end
|
125
|
+
|
126
|
+
def export
|
127
|
+
export_settings.collect do |value, setting|
|
128
|
+
if setting.first == :left
|
129
|
+
value.to_s.ljust(setting[1])
|
130
|
+
else
|
131
|
+
value.to_s.rjust(setting[1])
|
132
|
+
end
|
133
|
+
end.join('')
|
134
|
+
end
|
135
|
+
|
136
|
+
def avg_total_time
|
137
|
+
return 0 if @total_time == 0
|
138
|
+
(@total_time / @total_hits / 1000.0)
|
139
|
+
end
|
140
|
+
|
141
|
+
def avg_view_time
|
142
|
+
return 0 if @view_time == 0
|
143
|
+
(@view_time / @total_hits / 1000.0)
|
144
|
+
end
|
145
|
+
|
146
|
+
def avg_db_time
|
147
|
+
return 0 if @db_time == 0
|
148
|
+
(@db_time / @total_hits / 1000.0)
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
data/lib/plog.rb
ADDED
data/lib/url.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class URL
|
4
|
+
|
5
|
+
def initialize(url)
|
6
|
+
@url = url
|
7
|
+
@obj = URI.parse URI.escape(url)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@url
|
12
|
+
end
|
13
|
+
|
14
|
+
def simplify
|
15
|
+
simplify_url = @url.dup
|
16
|
+
simplifiers.each { |k, v| simplify_url.gsub!(k, v) if k =~ simplify_url }
|
17
|
+
simplify_url
|
18
|
+
end
|
19
|
+
|
20
|
+
def hashify
|
21
|
+
::MD5.hexdigest simplify
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def simplifiers
|
27
|
+
{
|
28
|
+
/\?.*/ => '?',
|
29
|
+
/\d+/ => '[0-9]',
|
30
|
+
/http:..[^\/]+/ => ''
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/plog.gemspec
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{plog}
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Kazuyoshi Tlacaelel"]
|
12
|
+
s.date = %q{2009-09-07}
|
13
|
+
s.default_executable = %q{plog}
|
14
|
+
s.description = %q{TODO: longer description of your gem}
|
15
|
+
s.email = %q{kazu.dev@gmail.com}
|
16
|
+
s.executables = ["plog"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
".gitignore",
|
24
|
+
"LICENSE",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"bin/plog",
|
29
|
+
"lib/cli.rb",
|
30
|
+
"lib/completed_line.rb",
|
31
|
+
"lib/log_file.rb",
|
32
|
+
"lib/object_file.rb",
|
33
|
+
"lib/plog.rb",
|
34
|
+
"lib/url.rb",
|
35
|
+
"plog.gemspec",
|
36
|
+
"test/completed_line_test.rb",
|
37
|
+
"test/data/example.log",
|
38
|
+
"test/log_file_test.rb",
|
39
|
+
"test/object_file_test.rb",
|
40
|
+
"test/plog_test.rb",
|
41
|
+
"test/test_helper.rb",
|
42
|
+
"test/url_test.rb"
|
43
|
+
]
|
44
|
+
s.homepage = %q{http://github.com/ktlacaelel/plog}
|
45
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubygems_version = %q{1.3.3}
|
48
|
+
s.summary = %q{TODO: one-line summary of your gem}
|
49
|
+
s.test_files = [
|
50
|
+
"test/completed_line_test.rb",
|
51
|
+
"test/log_file_test.rb",
|
52
|
+
"test/object_file_test.rb",
|
53
|
+
"test/plog_test.rb",
|
54
|
+
"test/test_helper.rb",
|
55
|
+
"test/url_test.rb"
|
56
|
+
]
|
57
|
+
|
58
|
+
if s.respond_to? :specification_version then
|
59
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
60
|
+
s.specification_version = 3
|
61
|
+
|
62
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
63
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
64
|
+
else
|
65
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CompletedLineTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@valid_first_fragment = 'Completed in 5ms (View: 4, DB: 1) '
|
7
|
+
@valid_line = 'Completed in 5ms (View: 4, DB: 1) | 200 OK [http://www.example.com/users/12972]'
|
8
|
+
@valid_line_no_view = 'Completed in 5ms (DB: 5) | 200 OK [http://www.example.com/users/12972]'
|
9
|
+
@valid_second_fragment = ' 200 OK [http://www.example.com/users/12972]'
|
10
|
+
|
11
|
+
@valid_db_time = 1
|
12
|
+
@valid_view_time = 4
|
13
|
+
@valid_total_time = 5
|
14
|
+
|
15
|
+
@valid_db_time_no_view = 5
|
16
|
+
@valid_view_time_no_view = 0
|
17
|
+
@valid_total_time_no_view = 5
|
18
|
+
|
19
|
+
@valid_csv_line = '5,4,1,/users/[0-9]'
|
20
|
+
@valid_status_string = 'OK'
|
21
|
+
@valid_status_number = '200'
|
22
|
+
end
|
23
|
+
|
24
|
+
# ============================================================================
|
25
|
+
# READ
|
26
|
+
# ============================================================================
|
27
|
+
|
28
|
+
should 'read a valid completed line (standard)' do
|
29
|
+
Plog::CompletedLine.read! @valid_line
|
30
|
+
assert_equal @valid_view_time, Plog::CompletedLine.view_time
|
31
|
+
assert_equal @valid_db_time, Plog::CompletedLine.db_time
|
32
|
+
assert_equal @valid_total_time, Plog::CompletedLine.total_time
|
33
|
+
end
|
34
|
+
|
35
|
+
should 'read valid completed line (without view time)' do
|
36
|
+
Plog::CompletedLine.read! @valid_line_no_view
|
37
|
+
assert_equal 5, Plog::CompletedLine.total_time
|
38
|
+
assert_equal 0, Plog::CompletedLine.view_time
|
39
|
+
assert_equal 5, Plog::CompletedLine.db_time
|
40
|
+
end
|
41
|
+
|
42
|
+
# ============================================================================
|
43
|
+
# RECALCULATION
|
44
|
+
# ============================================================================
|
45
|
+
|
46
|
+
should 'recalculate acurately!' do
|
47
|
+
Plog::CompletedLine.read! @valid_line
|
48
|
+
Plog::CompletedLine.merge(1, 1)
|
49
|
+
assert_equal (@valid_view_time + 1), Plog::CompletedLine.view_time
|
50
|
+
assert_equal (@valid_db_time + 1), Plog::CompletedLine.db_time
|
51
|
+
assert_equal (@valid_total_time + 2), Plog::CompletedLine.total_time
|
52
|
+
end
|
53
|
+
|
54
|
+
# ============================================================================
|
55
|
+
# FRAGMENTATION
|
56
|
+
# ============================================================================
|
57
|
+
|
58
|
+
should 'fragmentize correctly' do
|
59
|
+
Plog::CompletedLine.read! @valid_line
|
60
|
+
first, second = Plog::CompletedLine.fragmentize!
|
61
|
+
assert_equal @valid_first_fragment, first
|
62
|
+
assert_equal @valid_second_fragment, second
|
63
|
+
end
|
64
|
+
|
65
|
+
# ============================================================================
|
66
|
+
# TIME RETRIVAL ( when view & db are *both* present )
|
67
|
+
# ============================================================================
|
68
|
+
|
69
|
+
should 'retrive correct total time (view + db)' do
|
70
|
+
Plog::CompletedLine.read! @valid_line
|
71
|
+
assert_equal @valid_total_time, Plog::CompletedLine.total_time
|
72
|
+
end
|
73
|
+
|
74
|
+
should 'retrive correct view time (view + db)' do
|
75
|
+
Plog::CompletedLine.read! @valid_line
|
76
|
+
assert_equal @valid_view_time, Plog::CompletedLine.view_time
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'retrive correct db time (view + db)' do
|
80
|
+
Plog::CompletedLine.read! @valid_line
|
81
|
+
assert_equal @valid_db_time, Plog::CompletedLine.db_time
|
82
|
+
end
|
83
|
+
|
84
|
+
# ============================================================================
|
85
|
+
# TIME RETRIVAL ( when only db time is present )
|
86
|
+
# ============================================================================
|
87
|
+
|
88
|
+
should 'retrive correct total time (only db)' do
|
89
|
+
Plog::CompletedLine.read! @valid_line_no_view
|
90
|
+
assert_equal @valid_total_time_no_view, Plog::CompletedLine.total_time
|
91
|
+
end
|
92
|
+
|
93
|
+
should 'retrive correct view time (only db)' do
|
94
|
+
Plog::CompletedLine.read! @valid_line_no_view
|
95
|
+
assert_equal @valid_view_time_no_view, Plog::CompletedLine.view_time
|
96
|
+
end
|
97
|
+
|
98
|
+
should 'retrive correct db time (only db)' do
|
99
|
+
Plog::CompletedLine.read! @valid_line_no_view
|
100
|
+
assert_equal @valid_db_time_no_view, Plog::CompletedLine.db_time
|
101
|
+
end
|
102
|
+
|
103
|
+
# ============================================================================
|
104
|
+
# CSV COMPILATION
|
105
|
+
# ============================================================================
|
106
|
+
|
107
|
+
should 'export to csv' do
|
108
|
+
Plog::CompletedLine.read! @valid_line
|
109
|
+
assert_equal @valid_csv_line, Plog::CompletedLine.to_csv
|
110
|
+
end
|
111
|
+
|
112
|
+
# ============================================================================
|
113
|
+
# COMPLETED LINE DETECTION
|
114
|
+
# ============================================================================
|
115
|
+
|
116
|
+
should 'ignore *NON* completed lines' do
|
117
|
+
Plog::CompletedLine.read! 'completed'
|
118
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
119
|
+
|
120
|
+
Plog::CompletedLine.read! 'Completed'
|
121
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
122
|
+
|
123
|
+
Plog::CompletedLine.read! 'Completed Completed Completed Completed'
|
124
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
125
|
+
|
126
|
+
Plog::CompletedLine.read! 'aalsdjflasdjflsa dflksjdflkasjdf lfCompleted'
|
127
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
128
|
+
|
129
|
+
Plog::CompletedLine.read! nil
|
130
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
131
|
+
|
132
|
+
Plog::CompletedLine.read! false
|
133
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
134
|
+
|
135
|
+
Plog::CompletedLine.read! 0
|
136
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
137
|
+
|
138
|
+
Plog::CompletedLine.read! ''
|
139
|
+
assert_equal false, Plog::CompletedLine.completed_line?
|
140
|
+
end
|
141
|
+
|
142
|
+
# ============================================================================
|
143
|
+
# STATUS EXTRACTION
|
144
|
+
# ============================================================================
|
145
|
+
|
146
|
+
should 'extract status string' do
|
147
|
+
Plog::CompletedLine.read! @valid_line
|
148
|
+
assert_equal @valid_status_string, Plog::CompletedLine.status_string
|
149
|
+
end
|
150
|
+
|
151
|
+
should 'extract status number' do
|
152
|
+
Plog::CompletedLine.read! @valid_line
|
153
|
+
assert_equal @valid_status_number, Plog::CompletedLine.status_number
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
2
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
3
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
4
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
5
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
6
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
7
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
8
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
9
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
10
|
+
Completed in 7ms (View: 4, DB: 3) | 200 OK [http://www.example.com/users/12972]
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class LogFileTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@log_file = 'test/data/example.log'
|
7
|
+
@file = Plog::LogFile.new(@log_file)
|
8
|
+
@object_hash = 'a1e0e00d04e82bdf0f1ac151de03591c'
|
9
|
+
@object_path = 'objects/' + @object_hash
|
10
|
+
@final_csv_result = '10,70,40,30,/users/[0-9]'
|
11
|
+
@default_object_path = 'objects/2a5565416b0c92c6c5081342322bf945'
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
@file.close
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'generate an appropriate object_path' do
|
19
|
+
assert_equal @default_object_path, @file.object_path
|
20
|
+
end
|
21
|
+
|
22
|
+
def whipe_out_objects_dir
|
23
|
+
Dir.glob('./objects/*').each do |file|
|
24
|
+
FileUtils.rm_r(file, :force => true)
|
25
|
+
end
|
26
|
+
FileUtils.rmdir './objects'
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'parse data appropriately' do
|
30
|
+
whipe_out_objects_dir
|
31
|
+
@file = Plog::LogFile.new(@log_file)
|
32
|
+
@file.parse_completed_lines!
|
33
|
+
assert_equal @final_csv_result, File.new(@object_path).read.chomp
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ObjectFileTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@testing_filename = 'kazu.test'
|
7
|
+
@file = Plog::ObjectFile.new(@testing_filename, 'w+')
|
8
|
+
@default_serialized_data = '0,0,0,0,default'
|
9
|
+
|
10
|
+
@changed_hits = 1
|
11
|
+
@changed_total_time = 2
|
12
|
+
@changed_view_time = 3
|
13
|
+
@changed_db_time = 4
|
14
|
+
@changed_simplified_url = 'http://example.com'
|
15
|
+
@changed_serialized_data = '1,2,3,4,http://example.com'
|
16
|
+
@changed_serialized_data_twice = '2,4,6,8,http://example.com'
|
17
|
+
|
18
|
+
@export_data = '1 2 0.002 4 0.004 3 0.003 http://example.com '
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown
|
22
|
+
@file.close unless @skip_close
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'load default values on initialization' do
|
26
|
+
assert_equal 'default', @file.simplified_url
|
27
|
+
assert_equal 0, @file.view_time
|
28
|
+
assert_equal 0, @file.db_time
|
29
|
+
assert_equal 0, @file.total_time
|
30
|
+
assert_equal 0, @file.total_hits
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'serialize changes correctly' do
|
34
|
+
assert_equal @default_serialized_data, @file.serialize_changes
|
35
|
+
end
|
36
|
+
|
37
|
+
# ============================================================================
|
38
|
+
# DATA APPENDING
|
39
|
+
# ============================================================================
|
40
|
+
|
41
|
+
should 'increment view time correctly' do
|
42
|
+
@file.append_view_time 500
|
43
|
+
assert_equal 500, @file.view_time
|
44
|
+
@file.append_view_time 500
|
45
|
+
assert_equal 1000, @file.view_time
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'increment db time correctly' do
|
49
|
+
@file.append_db_time 500
|
50
|
+
assert_equal 500, @file.db_time
|
51
|
+
@file.append_db_time 500
|
52
|
+
assert_equal 1000, @file.db_time
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'increment total time correctly' do
|
56
|
+
@file.append_total_time 500
|
57
|
+
assert_equal 500, @file.total_time
|
58
|
+
@file.append_total_time 500
|
59
|
+
assert_equal 1000, @file.total_time
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'increment hits count correctly' do
|
63
|
+
@file.append_hits 500
|
64
|
+
assert_equal 500, @file.total_hits
|
65
|
+
@file.append_hits 500
|
66
|
+
assert_equal 1000, @file.total_hits
|
67
|
+
end
|
68
|
+
|
69
|
+
# ============================================================================
|
70
|
+
# DATA STORAGE
|
71
|
+
# ============================================================================
|
72
|
+
|
73
|
+
should 'store default data correctly if nothing is given' do
|
74
|
+
@file.save_changes!
|
75
|
+
@file.close
|
76
|
+
@skip_close = true
|
77
|
+
stored_data = File.new(@testing_filename).read.chomp
|
78
|
+
assert_equal @default_serialized_data, stored_data
|
79
|
+
end
|
80
|
+
|
81
|
+
def alter_data
|
82
|
+
@file.append_hits @changed_hits
|
83
|
+
@file.append_db_time @changed_db_time
|
84
|
+
@file.append_view_time @changed_view_time
|
85
|
+
@file.append_total_time @changed_total_time
|
86
|
+
@file.simplified_url = @changed_simplified_url
|
87
|
+
end
|
88
|
+
|
89
|
+
# ============================================================================
|
90
|
+
# DATA LOADING
|
91
|
+
# ============================================================================
|
92
|
+
|
93
|
+
should 'store altered data & retrive correctly' do
|
94
|
+
alter_data
|
95
|
+
@file.save_changes!
|
96
|
+
@file.close
|
97
|
+
@skip_close = true
|
98
|
+
|
99
|
+
@file = Plog::ObjectFile.new(@testing_filename, 'w+')
|
100
|
+
assert_equal @changed_total_time, @file.total_time, 'total'
|
101
|
+
assert_equal @changed_view_time, @file.view_time, 'view'
|
102
|
+
assert_equal @changed_db_time, @file.db_time, 'db'
|
103
|
+
assert_equal @changed_hits, @file.total_hits, 'hits'
|
104
|
+
end
|
105
|
+
|
106
|
+
# ============================================================================
|
107
|
+
# DATA EXPORTS
|
108
|
+
# ============================================================================
|
109
|
+
|
110
|
+
should 'export data correctly' do
|
111
|
+
alter_data
|
112
|
+
assert_equal @export_data, @file.export
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
data/test/plog_test.rb
ADDED
data/test/test_helper.rb
ADDED
data/test/url_test.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class URLTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@valid_line = 'Completed in 5ms (View: 4, DB: 1) | 200 OK [http://www.example.com/users/12972]'
|
7
|
+
@valid_url = 'http://www.example.com/users/12972'
|
8
|
+
@url = Plog::URL.new(@valid_url)
|
9
|
+
end
|
10
|
+
|
11
|
+
should 'return a valid url object when instantiated' do
|
12
|
+
assert_equal @valid_url, @url.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'simplify correctly' do
|
16
|
+
assert_equal '/users/[0-9]', @url.simplify
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'simplify removing arguments' do
|
20
|
+
@url = Plog::URL.new('http://www.example.com/users/1234?key1=val1&key2=val2')
|
21
|
+
assert_equal '/users/[0-9]?', @url.simplify
|
22
|
+
end
|
23
|
+
|
24
|
+
should 'hashify correctly' do
|
25
|
+
assert_equal '2a5565416b0c92c6c5081342322bf945', @url.hashify
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ktlacaelel-plog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kazuyoshi Tlacaelel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-07 00:00:00 -07:00
|
13
|
+
default_executable: plog
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thoughtbot-shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: "TODO: longer description of your gem"
|
26
|
+
email: kazu.dev@gmail.com
|
27
|
+
executables:
|
28
|
+
- plog
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- bin/plog
|
42
|
+
- lib/cli.rb
|
43
|
+
- lib/completed_line.rb
|
44
|
+
- lib/log_file.rb
|
45
|
+
- lib/object_file.rb
|
46
|
+
- lib/plog.rb
|
47
|
+
- lib/url.rb
|
48
|
+
- plog.gemspec
|
49
|
+
- test/completed_line_test.rb
|
50
|
+
- test/data/example.log
|
51
|
+
- test/log_file_test.rb
|
52
|
+
- test/object_file_test.rb
|
53
|
+
- test/plog_test.rb
|
54
|
+
- test/test_helper.rb
|
55
|
+
- test/url_test.rb
|
56
|
+
has_rdoc: false
|
57
|
+
homepage: http://github.com/ktlacaelel/plog
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options:
|
60
|
+
- --charset=UTF-8
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.2.0
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: "TODO: one-line summary of your gem"
|
82
|
+
test_files:
|
83
|
+
- test/completed_line_test.rb
|
84
|
+
- test/log_file_test.rb
|
85
|
+
- test/object_file_test.rb
|
86
|
+
- test/plog_test.rb
|
87
|
+
- test/test_helper.rb
|
88
|
+
- test/url_test.rb
|