rail_chaser 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/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.db
2
+ *.gem
3
+ .rvmrc
4
+ .bundle
5
+ Gemfile.lock
6
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ ## Rail Chaser
2
+ Find the minimum set of specs based on dependency analysis.
3
+
4
+ ## Install
5
+
6
+ rake build
7
+ gem install rail_chaser-0.0.1.gem
8
+
9
+ ## Setup
10
+
11
+ Edit Rakefile:
12
+
13
+ require 'rail_chaser/task'
14
+ RailChaser::Task.new
15
+
16
+ Edit spec_helper.rb:
17
+
18
+ require 'rail_chaser'
19
+ RailChaser.on
20
+
21
+ ## Usage
22
+
23
+ rake spec # generate spec.db
24
+ rake spec:min
25
+
26
+ ## Configuration
27
+
28
+ RailChaser.on do |config|
29
+ config.skip_gem = true # default: true
30
+ config.skip_ruby_core = true # default: true
31
+ config.skip_spec = true # default: true
32
+ # config.db_path defaults to 'spec.db', not available yet
33
+ end
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ require 'rspec/core/rake_task'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ task :default => ['spec']
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ spec.pattern = 'spec/**/*_spec.rb'
9
+ spec.rspec_opts = ['--backtrace']
10
+ end
@@ -0,0 +1,26 @@
1
+ module RailChaser
2
+ class Configuration
3
+ attr_accessor :skip_gem, :skip_ruby_core, :skip_spec, :db_path
4
+
5
+ def initialize
6
+ @skip_gem = true
7
+ @skip_ruby_core = true
8
+ @skip_spec = true
9
+ @db_path = 'spec.db'
10
+ end
11
+
12
+ def example_collection_options
13
+ {
14
+ :skip_gem => @skip_gem,
15
+ :skip_ruby_core => @skip_ruby_core,
16
+ :skip_spec => @skip_spec
17
+ }
18
+ end
19
+
20
+ def storage_options
21
+ {
22
+ :db_path => @db_path
23
+ }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ module RailChaser
2
+ class ExampleCollection
3
+ attr_accessor :examples, :options
4
+
5
+ def initialize(options={})
6
+ @examples = Hash.new { |h, spec| h[spec] = [] }
7
+ @options = options
8
+ end
9
+
10
+ def gem_path_pattern
11
+ @gem_path_pattern ||= Regexp.compile ENV['GEM_HOME'].gsub(':', '|')
12
+ end
13
+
14
+ def gem?(file)
15
+ file =~ gem_path_pattern
16
+ end
17
+
18
+ def ruby_core?(file)
19
+ file =~ /#{ENV['MY_RUBY_HOME']}/
20
+ end
21
+
22
+ def spec?(file)
23
+ file =~ /\/spec\//
24
+ end
25
+
26
+ def add_class?(file, classes)
27
+ return false if @options[:skip_spec] && spec?(file)
28
+ return false if @options[:skip_gem] && gem?(file)
29
+ return false if @options[:skip_ruby_core] && ruby_core?(file)
30
+ return false if classes.include?(file)
31
+ return true
32
+ end
33
+
34
+ def add_example(file)
35
+ @spec = file if spec?(file)
36
+
37
+ if @spec
38
+ classes = @examples[@spec]
39
+ classes << file if add_class?(file, classes)
40
+ end
41
+ end
42
+
43
+ def each_spec_and_classes
44
+ if block_given?
45
+ @examples.each_pair do |spec, classes|
46
+ yield spec, classes
47
+ end
48
+ end
49
+ end
50
+
51
+ def classes
52
+ @examples.values.flatten.uniq
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,108 @@
1
+ require 'sqlite3'
2
+
3
+ module RailChaser
4
+ class Storage
5
+ attr_accessor :options
6
+
7
+ def initialize(options={:db_path => 'spec.db'})
8
+ @options = options
9
+ end
10
+
11
+ def self.create(options={:db_path => 'spec.db'})
12
+ storage = RailChaser::Storage.new(options)
13
+ storage.destroy!
14
+ storage.create!
15
+ storage
16
+ end
17
+
18
+ def list_specs
19
+ @connection.execute("SELECT file FROM specs").flatten
20
+ end
21
+
22
+ def list_classes
23
+ @connection.execute("SELECT file FROM classes").flatten
24
+ end
25
+
26
+ def add_class(file)
27
+ @connection.execute("INSERT INTO classes (file) values ('#{file}');")
28
+ end
29
+
30
+ def add_spec(file)
31
+ @connection.execute("INSERT INTO specs (file) values ('#{file}');")
32
+ end
33
+
34
+ def associate_spec_with_class(spec_id, class_id)
35
+ @connection.execute("INSERT INTO specs_classes (spec_id, class_id) values (#{spec_id}, #{class_id});")
36
+ end
37
+
38
+ def find_spec_by_file(file)
39
+ @connection.execute("SELECT id FROM specs WHERE file = '#{file}';").first.first
40
+ end
41
+
42
+ def find_class_by_file(file)
43
+ @connection.execute("SELECT id FROM classes WHERE file = '#{file}';").first.first
44
+ end
45
+
46
+ def find_specs_by_class(file)
47
+ sql = <<-SQL
48
+ SELECT s.file
49
+ FROM specs_classes sc JOIN classes c JOIN specs s ON sc.class_id = c.id AND sc.spec_id = s.id
50
+ WHERE c.file LIKE '%#{file}';
51
+ SQL
52
+ @connection.execute(sql).flatten
53
+ end
54
+
55
+ def db_path
56
+ options[:db_path]
57
+ end
58
+
59
+ def load!
60
+ @connection = SQLite3::Database.open(db_path)
61
+ end
62
+
63
+ def destroy!
64
+ File.delete(db_path) if File.exists?(db_path)
65
+ end
66
+
67
+ def create!
68
+ @connection = SQLite3::Database.new(db_path)
69
+
70
+ # PRAGMA foreign_keys = ON;
71
+ sql = <<-SQL
72
+ CREATE TABLE IF NOT EXISTS specs(
73
+ id INTEGER PRIMARY KEY NOT NULL,
74
+ file TEXT UNIQUE NOT NULL
75
+ );
76
+ CREATE TABLE IF NOT EXISTS classes(
77
+ id INTEGER PRIMARY KEY NOT NULL,
78
+ file TEXT UNIQUE NOT NULL
79
+ );
80
+ CREATE TABLE IF NOT EXISTS specs_classes(
81
+ spec_id INTEGER, class_id INTEGER,
82
+ FOREIGN KEY (spec_id) REFERENCES spec(id) ON DELETE CASCADE,
83
+ FOREIGN KEY (class_id) REFERENCES class(id) ON DELETE CASCADE
84
+ );
85
+ CREATE INDEX IF NOT EXISTS spec_index ON specs_classes(spec_id);
86
+ CREATE INDEX IF NOT EXISTS class_index ON specs_classes(class_id);
87
+ SQL
88
+
89
+ @connection.execute_batch(sql)
90
+ end
91
+
92
+ def save!(example_collection)
93
+ example_collection.classes.each do |klass|
94
+ add_class(klass)
95
+ end
96
+
97
+ example_collection.each_spec_and_classes do |spec, classes|
98
+ add_spec(spec)
99
+ spec_id = find_spec_by_file(spec)
100
+
101
+ classes.each do |klass|
102
+ class_id = find_class_by_file(klass)
103
+ associate_spec_with_class(spec_id, class_id)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'rail_chaser/storage'
4
+
5
+ module RailChaser
6
+ class Task < Rake::TaskLib
7
+ include Rake::DSL
8
+
9
+ def initialize(name = :min)
10
+ define(name)
11
+ end
12
+
13
+ def define(name)
14
+ desc 'Runs minimum set of specs'
15
+ namespace :spec do
16
+ task name do
17
+ diff_files = `git diff HEAD --name-only`.split("\n")
18
+
19
+ # ToDo: possibly add new spec files to specs
20
+ # new_files = `git ls-files --exclude-standard --others`.split("\n")
21
+ # new_files.select { |file| file =~ /(\/spec\/(.*)?)?\_spec\.rb/ }
22
+
23
+ storage = RailChaser::Storage.new
24
+ storage.load!
25
+
26
+ puts "# Changed files:\n"
27
+ specs = []
28
+ diff_files.each do |file|
29
+ puts "# - #{file}"
30
+ specs << storage.find_specs_by_class(file)
31
+ end
32
+
33
+ puts "#\n# Specs to run:\n"
34
+ specs = specs.flatten.compact.uniq
35
+ specs.each do |spec|
36
+ puts "# - #{spec}"
37
+ end
38
+
39
+ rspec_command = "rspec #{specs.join(' ')}"
40
+ result = `#{rspec_command}`
41
+ puts "\n#{result}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module RailChaser
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,33 @@
1
+ require 'rail_chaser/configuration'
2
+ require 'rail_chaser/example_collection'
3
+ require 'rail_chaser/storage'
4
+
5
+ module RailChaser
6
+ class << self
7
+ attr_accessor :config, :collection, :storage
8
+
9
+ def on
10
+ @config = Configuration.new
11
+ yield @config if block_given?
12
+
13
+ @collection = ExampleCollection.new(@config.example_collection_options)
14
+ @storage = Storage.create(@config.storage_options)
15
+
16
+ RSpec.configure do |config|
17
+ config.before(:suite) { start }
18
+ config.after(:suite) { finish }
19
+ end
20
+ end
21
+
22
+ def start
23
+ set_trace_func proc { |event, file, line, method, binding, klass|
24
+ @collection.add_example(file)
25
+ }
26
+ end
27
+
28
+ def finish
29
+ set_trace_func(nil)
30
+ @storage.save!(@collection)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rail_chaser/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rail_chaser"
7
+ s.version = RailChaser::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Li-Hsuan Lung"]
10
+ s.email = ["lihsuan@8thlight.com"]
11
+ s.homepage = "http://github.com/naush/rail_chaser"
12
+ s.summary = %q{A tool to determine the the minimum set of specs to run based on dependency analysis}
13
+ s.description = %q{}
14
+
15
+ s.add_development_dependency "rake", "0.9.2"
16
+ s.add_development_dependency "rspec", "2.9.0"
17
+ s.add_development_dependency "sqlite3", "1.3.5"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- spec/*`.split("\n")
21
+ s.require_paths = ["lib"]
22
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'rail_chaser/configuration'
3
+
4
+ describe RailChaser::Configuration do
5
+ describe "options" do
6
+ it "default to true" do
7
+ RailChaser::Configuration.new.skip_gem.should be_true
8
+ end
9
+
10
+ it "defaults skip_ruby_code to true" do
11
+ RailChaser::Configuration.new.skip_ruby_core.should be_true
12
+ end
13
+
14
+ it "defaults skip_spec to true" do
15
+ RailChaser::Configuration.new.skip_spec.should be_true
16
+ end
17
+
18
+ it "defaults db_path to 'spec.db'" do
19
+ RailChaser::Configuration.new.db_path.should == 'spec.db'
20
+ end
21
+ end
22
+
23
+ describe "example_collection_options" do
24
+ it "has options for example_collection" do
25
+ config = RailChaser::Configuration.new
26
+ options = config.example_collection_options
27
+ options.has_key?(:skip_gem).should be_true
28
+ options.has_key?(:skip_ruby_core).should be_true
29
+ options.has_key?(:skip_spec).should be_true
30
+ end
31
+ end
32
+
33
+ describe "storage_options" do
34
+ it "has options for storage" do
35
+ config = RailChaser::Configuration.new
36
+ options = config.storage_options
37
+ options.has_key?(:db_path).should be_true
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'rail_chaser/example_collection'
3
+
4
+ describe RailChaser::ExampleCollection do
5
+ describe "add_example"
6
+
7
+ describe "ruby_core?" do
8
+ it "matches any file in the ruby core directory" do
9
+ collection = RailChaser::ExampleCollection.new
10
+ collection.ruby_core?("#{ENV['MY_RUBY_HOME']}/file.rb").should be_true
11
+ end
12
+
13
+ it "does not match lib file" do
14
+ collection = RailChaser::ExampleCollection.new
15
+ collection.ruby_core?("/lib/file.rb").should be_false
16
+ end
17
+ end
18
+
19
+ describe "spec?" do
20
+ it "matches any file ending with _spec.rb" do
21
+ collection = RailChaser::ExampleCollection.new
22
+ collection.spec?("/spec/rail_chaser/example_collection_spec.rb").should be_true
23
+ end
24
+
25
+ it "matches files inside /spec/ directory" do
26
+ collection = RailChaser::ExampleCollection.new
27
+ collection.spec?("/spec/rail_chaser/example_collection_spec.rb").should be_true
28
+ end
29
+
30
+ it "does not match lib file" do
31
+ collection = RailChaser::ExampleCollection.new
32
+ collection.spec?("/lib/rail_chaser.rb").should be_false
33
+ end
34
+ end
35
+
36
+ describe "initialize" do
37
+ it "initializes a hash defaults to arrays" do
38
+ collection = RailChaser::ExampleCollection.new
39
+ collection.examples["spec"].should == []
40
+ end
41
+
42
+ it "inherits options" do
43
+ collection = RailChaser::ExampleCollection.new({
44
+ :skip_gem => true
45
+ })
46
+ collection.options[:skip_gem].should be_true
47
+ end
48
+ end
49
+
50
+ describe "add_class" do
51
+ it "returns false if file is spec and skip_spec is true" do
52
+ collection = RailChaser::ExampleCollection.new(:skip_spec => true)
53
+ collection.add_class?('/spec/spec.rb', []).should be_false
54
+ end
55
+
56
+ it "returns false if file is spec and skip_spec is false" do
57
+ collection = RailChaser::ExampleCollection.new(:skip_spec => false)
58
+ collection.add_class?('/spec/spec.rb', []).should be_true
59
+ end
60
+
61
+ it "does not add duplicated class" do
62
+ collection = RailChaser::ExampleCollection.new
63
+ collection.add_class?('class_a', ['class_a']).should be_false
64
+ end
65
+ end
66
+
67
+ describe "classes" do
68
+ it "has all classes" do
69
+ collection = RailChaser::ExampleCollection.new
70
+ examples = collection.examples
71
+ examples["spec1"] = ["/path/a.rb"]
72
+ examples["spec2"] = ["/path/b.rb"]
73
+ collection.classes.should include("/path/a.rb")
74
+ collection.classes.should include("/path/b.rb")
75
+ end
76
+
77
+ it "has unique classes" do
78
+ collection = RailChaser::ExampleCollection.new
79
+ examples = collection.examples
80
+ examples["spec1"] = ["/path/a.rb"]
81
+ examples["spec2"] = ["/path/a.rb"]
82
+ class_names = collection.classes
83
+ class_names.size.should == 1
84
+ class_names.should == ["/path/a.rb"]
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+ require 'rail_chaser/storage'
3
+ require 'rail_chaser/example_collection'
4
+
5
+ describe RailChaser::Storage do
6
+ describe "save!" do
7
+ before(:all) do
8
+ @storage = RailChaser::Storage.new(:db_path => 'test.db')
9
+ @storage.destroy!
10
+ @storage.create!
11
+ end
12
+
13
+ after(:all) do
14
+ @storage.destroy!
15
+ end
16
+
17
+ def test_example_collection
18
+ collection = RailChaser::ExampleCollection.new
19
+ examples = collection.examples
20
+ examples["/a_spec.rb"] = ["/a.rb"]
21
+ examples["/b_spec.rb"] = ["/b.rb"]
22
+ collection
23
+ end
24
+
25
+ it "saves a new example collection" do
26
+ @storage.save!(test_example_collection)
27
+
28
+ classes = @storage.list_classes
29
+ classes.should include('/a.rb')
30
+ classes.should include('/b.rb')
31
+
32
+ specs = @storage.list_specs
33
+ specs.should include('/a_spec.rb')
34
+ specs.should include('/b_spec.rb')
35
+ end
36
+ end
37
+
38
+ describe "initialize" do
39
+ it "has default options" do
40
+ storage = RailChaser::Storage.new
41
+ storage.db_path.should == 'spec.db'
42
+ end
43
+
44
+ it "inherits options" do
45
+ storage = RailChaser::Storage.new(:db_path => 'custom.db')
46
+ storage.db_path.should == 'custom.db'
47
+ end
48
+ end
49
+
50
+ describe "self.create" do
51
+ it "destroys previous database file"
52
+ it "creates new database file"
53
+ end
54
+
55
+ describe "destroy!" do
56
+ it "deletes the generated db file"
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+ require 'rail_chaser/task'
3
+
4
+ describe RailChaser::Task do
5
+ it "detects file changes"
6
+ end
7
+
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'rail_chaser'
3
+
4
+ describe RailChaser do
5
+ describe "self.on" do
6
+ it "initializes a new example collection" do
7
+ RailChaser.on
8
+ RailChaser.collection.should_not be_nil
9
+ end
10
+
11
+ it "configures RSpec before and after suite" do
12
+ RailChaser.on
13
+ RSpec.configure do |config|
14
+ config.hooks[:before][:suite].should_not be_empty
15
+ config.hooks[:after][:suite].should_not be_empty
16
+ end
17
+ end
18
+
19
+ it "configures options for example collection" do
20
+ RailChaser.on do |config|
21
+ config.skip_gem = true
22
+ end
23
+
24
+ RailChaser.collection.options[:skip_gem].should be_true
25
+ end
26
+
27
+ it "configures options for storage" do
28
+ RailChaser.on do |config|
29
+ config.db_path = 'spec.rb'
30
+ end
31
+
32
+ RailChaser.storage.options[:db_path].should == 'spec.rb'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ $:<< File.expand_path('../../lib', __FILE__)
data/spec.rb ADDED
Binary file
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rail_chaser
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.2
6
+ platform: ruby
7
+ authors:
8
+ - Li-Hsuan Lung
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-03-26 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rake
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - "="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.9.2
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - "="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.9.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3
39
+ requirement: &id003 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - "="
43
+ - !ruby/object:Gem::Version
44
+ version: 1.3.5
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *id003
48
+ description: ""
49
+ email:
50
+ - lihsuan@8thlight.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - README.md
61
+ - Rakefile
62
+ - lib/rail_chaser.rb
63
+ - lib/rail_chaser/configuration.rb
64
+ - lib/rail_chaser/example_collection.rb
65
+ - lib/rail_chaser/storage.rb
66
+ - lib/rail_chaser/task.rb
67
+ - lib/rail_chaser/version.rb
68
+ - rail_chaser.gemspec
69
+ - spec.rb
70
+ - spec/rail_chaser/configuration_spec.rb
71
+ - spec/rail_chaser/example_collection_spec.rb
72
+ - spec/rail_chaser/storage_spec.rb
73
+ - spec/rail_chaser/task_spec.rb
74
+ - spec/rail_chaser_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: http://github.com/naush/rail_chaser
77
+ licenses: []
78
+
79
+ post_install_message:
80
+ rdoc_options: []
81
+
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 4586795509589251585
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 4586795509589251585
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.21
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: A tool to determine the the minimum set of specs to run based on dependency analysis
109
+ test_files:
110
+ - spec/rail_chaser/configuration_spec.rb
111
+ - spec/rail_chaser/example_collection_spec.rb
112
+ - spec/rail_chaser/storage_spec.rb
113
+ - spec/rail_chaser/task_spec.rb
114
+ - spec/rail_chaser_spec.rb
115
+ - spec/spec_helper.rb