rkid 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.
@@ -0,0 +1,17 @@
1
+ Manifest.txt
2
+ README.txt
3
+ Rakefile
4
+ TODO
5
+ bin/rkid
6
+ db/schema.rb
7
+ lib/rkid.rb
8
+ lib/rkid/analyzer.rb
9
+ lib/rkid/models.rb
10
+ lib/rkid/rake/task.rb
11
+ rkid.db
12
+ spec/fixtures/test_class.rb
13
+ spec/rkid/analyzer_spec.rb
14
+ spec/rkid/sample.db
15
+ spec/spec_helper.rb
16
+ test.db
17
+ test.html
@@ -0,0 +1,5 @@
1
+ Rkid is Rcov In Database
2
+
3
+ Until there is better packaging (see the todo), customize the following line for your needs:
4
+
5
+ ruby -S bin/rkid "/opt/local/lib/ruby/gems/1.8/gems/rspec-1.1.2/bin/spec" -- "/Users/nkallen/Sites/arel/spec/active_relation/unit/predicates/binary_spec.rb"
@@ -0,0 +1,24 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/lib")
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'hoe'
6
+ require 'activerecord'
7
+ require 'spec/rake/spectask'
8
+ require 'rkid'
9
+ require 'rkid/rake/task'
10
+
11
+ Spec::Rake::SpecTask.new do |t|
12
+ t.spec_files = FileList['spec/**/*_spec.rb']
13
+ end
14
+
15
+ Hoe.new('Rkid', Rkid::VERSION) do |p|
16
+ p.name = "rkid"
17
+ p.author = "Nick Kallen"
18
+ p.description = "Rcov in Database"
19
+ p.email = 'nick@pivotallabs.com'
20
+ p.summary = "Rcov in Database"
21
+ p.url = ""
22
+ p.extra_deps << ['rspec']
23
+ p.extra_deps << ['rcov']
24
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ 1. Create rake task factory a la Rspec/HOE and/or patch Rspec's factory
2
+ 2. Build primitive browser
3
+ 3. Improve performance by moving code to C
4
+ 4. Fancify browser
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'rkid'
3
+ Rkid.env = 'production'
4
+
5
+ $ORIGINAL_ARGV = ARGV.clone
6
+ if idx = ARGV.index("--")
7
+ extra_args = ARGV[idx+1..-1]
8
+ ARGV.replace(ARGV[0,idx])
9
+ else
10
+ extra_args = []
11
+ end
12
+
13
+ at_exit {
14
+ Rkid::report
15
+ }
16
+
17
+ pending_scripts = ARGV.clone
18
+ ARGV.replace extra_args
19
+ until pending_scripts.empty?
20
+ prog = pending_scripts.shift
21
+ Rkid::prepare
22
+ load prog
23
+ end
@@ -0,0 +1,34 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :klasses, :force => true do |t|
3
+ t.string :name
4
+ end
5
+ add_index :klasses, :name, :unique => true
6
+
7
+ create_table :methods, :force => true do |t|
8
+ t.string :name
9
+ t.integer :klass_id, :defsite_id
10
+ end
11
+ add_index :methods, [:klass_id, :name, :defsite_id], :unique => true
12
+
13
+ create_table :files, :force => true do |t|
14
+ t.string :name
15
+ end
16
+ add_index :files, [:name], :unique => true
17
+
18
+ create_table :callsites, :force => true do |t|
19
+ t.integer :method_id, :count
20
+ end
21
+
22
+ create_table :frames, :force => true do |t|
23
+ t.integer :line_id, :callsite_id, :level
24
+ end
25
+ add_index :frames, [:line_id, :callsite_id, :level], :unique => true
26
+
27
+ create_table :lines, :force => true do |t|
28
+ t.integer :times_called, :method_id, :file_id, :number
29
+ t.boolean :covered
30
+ t.string :body
31
+ end
32
+ add_index :lines, [:file_id, :number], :unique => true
33
+
34
+ end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ module Rkid
4
+ VERSION = '0.1'
5
+ end
6
+
7
+ require 'rubygems'
8
+ require 'activesupport'
9
+ require 'activerecord'
10
+ require 'rcov'
11
+ require 'sqlite3'
12
+ require 'rbconfig'
13
+
14
+ require 'rkid/analyzer'
15
+ require 'rkid/models'
16
+
17
+ Rkid.root = File.join(File.dirname(__FILE__), '..')
@@ -0,0 +1,80 @@
1
+ module Rkid
2
+ mattr_accessor :root, :env, :callsite_analyzer, :coverage_analyzer
3
+
4
+ IGNORE_FILES = [
5
+ /\A#{Regexp.escape(Pathname.new(Config::CONFIG["libdir"]).cleanpath.to_s)}/,
6
+ /\btc_[^.]*.rb/,
7
+ /\bgems\//,
8
+ /\bvendor\//,
9
+ /\A#{Regexp.escape(__FILE__)}\z/
10
+ ]
11
+
12
+ def self.analyze(&block)
13
+ prepare
14
+ yield
15
+ report
16
+ end
17
+
18
+ def self.prepare
19
+ self.coverage_analyzer, self.callsite_analyzer = Rcov::CodeCoverageAnalyzer.new, Rcov::CallSiteAnalyzer.new
20
+ callsite_analyzer.install_hook; coverage_analyzer.install_hook
21
+ end
22
+
23
+ def self.report
24
+ callsite_analyzer.remove_hook; coverage_analyzer.remove_hook
25
+ prepare_connection_to_database
26
+ analyze_callsite(callsite_analyzer); analyze_coverage(coverage_analyzer)
27
+ end
28
+
29
+ private
30
+
31
+ def self.prepare_connection_to_database
32
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :dbfile => 'rkid.db')
33
+ load ::File.join(root, 'db/schema.rb')
34
+ end
35
+
36
+ def self.analyze_callsite(callsite_analyzer)
37
+ total = callsite_analyzer.analyzed_classes.size
38
+ callsite_analyzer.analyzed_classes.each_with_index do |klass_name, i|
39
+ puts "Processing class '#{klass_name}', #{i+1} of #{total}"
40
+ klass = nil
41
+
42
+ callsite_analyzer.methods_for_class(klass_name).each do |method_name|
43
+ defsite = callsite_analyzer.defsite(klass_method = klass_name + "#" + method_name)
44
+ next if IGNORE_FILES.any? { |pattern| defsite.file =~ pattern }
45
+
46
+ klass ||= Klass.create('name' => klass_name)
47
+ defsite_file = File.create('name' => defsite.file)
48
+ defsite_line = Line.create('file_id' => defsite_file.id, 'number' => defsite.line)
49
+ method = Method.create(
50
+ 'klass_id' => klass.id,
51
+ 'name' => method_name,
52
+ 'defsite_id' => defsite_line.id
53
+ )
54
+ callsite_analyzer.callsites(klass_method).each do |site, count|
55
+ callsite = Callsite.create('method_id' => method.id, 'count' => count)
56
+ site.backtrace.each_with_index do |frame, i|
57
+ file_name = file_name = frame[2]
58
+ next if IGNORE_FILES.any? { |pattern| file_name =~ pattern }
59
+
60
+ file = File.create('name' => file_name)
61
+ line = Line.create('file_id' => file.id, 'number' => frame[3])
62
+ Frame.create('callsite_id' => callsite.id, 'line_id' => line.id, 'level' => i)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def self.analyze_coverage(coverage_analyzer)
70
+ coverage_analyzer.analyzed_files.each do |file_name|
71
+ next if IGNORE_FILES.any? { |pattern| file_name =~ pattern }
72
+
73
+ lines, marked, count = coverage_analyzer.data(file_name)
74
+ lines.each_with_index do |body, i|
75
+ file = File.create('name' => file_name)
76
+ line = Line.create('file_id' => file.id, 'number' => i, 'body' => body, 'covered' => marked[i], 'times_called' => count[i])
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,94 @@
1
+ module Rkid
2
+ module FastCreate
3
+ def db
4
+ ActiveRecord::Base.connection.raw_connection
5
+ end
6
+
7
+ def insert(attributes)
8
+ insert = db.prepare <<-SQL
9
+ INSERT INTO #{table_name}
10
+ (#{attributes.keys.join(', ')}) VALUES (#{attributes.keys.collect { |c| ":#{c}" }.join(', ')})
11
+ SQL
12
+ insert.execute attributes
13
+ instantiate(attributes.merge('id' => db.last_insert_row_id))
14
+ end
15
+
16
+ def create(attributes)
17
+ begin
18
+ insert(attributes)
19
+ rescue SQLite3::SQLException
20
+ update(attributes)
21
+ end
22
+ end
23
+ end
24
+
25
+ class Klass < ::ActiveRecord::Base
26
+ extend FastCreate
27
+
28
+ has_many :methods, :class_name => 'Method', :dependent => :destroy
29
+
30
+ def self.update(attributes)
31
+ find_by_name(attributes['name'])
32
+ end
33
+ end
34
+
35
+ class Method < ::ActiveRecord::Base
36
+ extend FastCreate
37
+
38
+ belongs_to :klass
39
+ has_many :callsites, :dependent => :destroy
40
+ has_many :lines, :dependent => :destroy
41
+ belongs_to :defsite, :class_name => 'Line'
42
+
43
+ def self.update(attributes)
44
+ find_by_name_and_klass_id(attributes['name'], attributes['klass_id'])
45
+ end
46
+ end
47
+
48
+ class File < ::ActiveRecord::Base
49
+ extend FastCreate
50
+
51
+ has_many :lines, :order => :number, :dependent => :destroy
52
+
53
+ def self.update(attributes)
54
+ find_by_name(attributes['name'])
55
+ end
56
+ end
57
+
58
+ class Callsite < ::ActiveRecord::Base
59
+ extend FastCreate
60
+
61
+ belongs_to :method
62
+ has_many :frames, :order => :level, :dependent => :destroy
63
+ has_many :lines, :through => :frames, :dependent => :destroy
64
+ end
65
+
66
+ class Frame < ::ActiveRecord::Base
67
+ extend FastCreate
68
+
69
+ belongs_to :callsite
70
+ belongs_to :line
71
+ belongs_to :callsite
72
+ end
73
+
74
+ class Line < ::ActiveRecord::Base
75
+ extend FastCreate
76
+
77
+ belongs_to :method
78
+ belongs_to :file
79
+
80
+ def self.update(attributes)
81
+ line = find_by_number_and_file_id(attributes['number'], attributes['file_id'])
82
+ assignments = attributes.keys.collect do |key|
83
+ "#{key} = :#{key}"
84
+ end.join ', '
85
+ update = db.prepare <<-SQL
86
+ UPDATE #{table_name} SET
87
+ #{assignments}
88
+ WHERE id = :id
89
+ SQL
90
+ update.execute attributes.except('covered').merge('id' => line.id)
91
+ line
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,16 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+
4
+ module Rkid
5
+ class Task < Rake::TaskLib
6
+ def initialize
7
+ desc "Run specs with rkid"
8
+ task :rkid do
9
+ rkid_script = ::File.expand_path(::File.dirname(__FILE__) + '/../../../bin/rkid')
10
+ specs = FileList['spec/**/*_spec.rb'].collect { |fn| %["#{fn}"] }.join(' ')
11
+
12
+ system %(ruby #{rkid_script} `which spec` -- #{specs})
13
+ end
14
+ end
15
+ end
16
+ end
data/rkid.db ADDED
Binary file
@@ -0,0 +1,9 @@
1
+ class TestedClass
2
+ def tested_method
3
+ 'tested_method'
4
+ end
5
+
6
+ def untested_method
7
+ 'untested_method'
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Rkid do
4
+
5
+ before(:all) do
6
+ require 'spec/fixtures/test_class.rb'
7
+ Rkid.env = 'test'
8
+ Rkid.analyze { TestedClass.new.tested_method }
9
+ end
10
+
11
+ describe '::analyze' do
12
+ before do
13
+ @file = Rkid::File.find_by_name("./spec/fixtures/test_class.rb")
14
+ end
15
+
16
+ it 'records classes and methods' do
17
+ methods = Rkid::Klass.find_by_name('TestedClass').methods
18
+ methods.find_by_name('tested_method').should_not be_nil
19
+ methods.find_by_name('untested_method').should be_nil
20
+ end
21
+
22
+ it 'records callsites of methods' do
23
+ method = Rkid::Klass.find_by_name('TestedClass').methods.find_by_name('tested_method')
24
+ callsite = method.callsites.first
25
+ callsite.count.should == 1
26
+ callsite.frames.first.line.number.should == 8
27
+ callsite.frames.first.line.file.name.should == "./spec/rkid/analyzer_spec.rb"
28
+ end
29
+
30
+ it 'records the source of parsed files' do
31
+ @file.lines.first.body.should == File.open("./spec/fixtures/test_class.rb").readline
32
+ end
33
+
34
+ it 'indicates whether lines are covered' do
35
+ @file.lines.first.should_not be_covered
36
+ @file.lines[1].should be_covered
37
+ @file.lines[-1].should_not be_covered
38
+ end
39
+ end
40
+ end
File without changes
@@ -0,0 +1,12 @@
1
+ dir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift "#{dir}/../lib"
3
+
4
+ require 'rubygems'
5
+ require 'spec'
6
+ require 'pp'
7
+ require 'rkid'
8
+ Dir["#{dir}/matchers/*"].each { |m| require "#{dir}/matchers/#{File.basename(m)}" }
9
+
10
+ Spec::Runner.configure do |config|
11
+ config.mock_with :rr
12
+ end
data/test.db ADDED
File without changes
File without changes
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: rkid
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2008-03-31 00:00:00 -07:00
8
+ summary: Rcov in Database
9
+ require_paths:
10
+ - lib
11
+ email: nick@pivotallabs.com
12
+ homepage:
13
+ rubyforge_project: rkid
14
+ description: Rcov in Database
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Nick Kallen
31
+ files:
32
+ - Manifest.txt
33
+ - README.txt
34
+ - Rakefile
35
+ - TODO
36
+ - bin/rkid
37
+ - db/schema.rb
38
+ - lib/rkid.rb
39
+ - lib/rkid/analyzer.rb
40
+ - lib/rkid/models.rb
41
+ - lib/rkid/rake/task.rb
42
+ - rkid.db
43
+ - spec/fixtures/test_class.rb
44
+ - spec/rkid/analyzer_spec.rb
45
+ - spec/rkid/sample.db
46
+ - spec/spec_helper.rb
47
+ - test.db
48
+ - test.html
49
+ test_files: []
50
+
51
+ rdoc_options:
52
+ - --main
53
+ - README.txt
54
+ extra_rdoc_files:
55
+ - Manifest.txt
56
+ - README.txt
57
+ executables:
58
+ - rkid
59
+ extensions: []
60
+
61
+ requirements: []
62
+
63
+ dependencies:
64
+ - !ruby/object:Gem::Dependency
65
+ name: rspec
66
+ version_requirement:
67
+ version_requirements: !ruby/object:Gem::Version::Requirement
68
+ requirements:
69
+ - - ">"
70
+ - !ruby/object:Gem::Version
71
+ version: 0.0.0
72
+ version:
73
+ - !ruby/object:Gem::Dependency
74
+ name: rcov
75
+ version_requirement:
76
+ version_requirements: !ruby/object:Gem::Version::Requirement
77
+ requirements:
78
+ - - ">"
79
+ - !ruby/object:Gem::Version
80
+ version: 0.0.0
81
+ version:
82
+ - !ruby/object:Gem::Dependency
83
+ name: hoe
84
+ version_requirement:
85
+ version_requirements: !ruby/object:Gem::Version::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 1.5.1
90
+ version: