mislav-rspactor 0.3.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ begin
2
+ require 'jeweler'
3
+
4
+ Jeweler.class_eval do
5
+ protected
6
+
7
+ def fill_in_gemspec_defaults(gemspec)
8
+ if gemspec.files.nil? || gemspec.files.empty?
9
+ gemspec.files = FileList['Rakefile', '{bin,lib,images,spec}/**/*', 'README*', 'LICENSE*']
10
+ end
11
+
12
+ if gemspec.executables.nil? || gemspec.executables.empty?
13
+ gemspec.executables = Dir["#{@base_dir}/bin/*"].map do |f|
14
+ File.basename(f)
15
+ end
16
+ end
17
+
18
+ gemspec
19
+ end
20
+ end
21
+
22
+ Jeweler::Tasks.new do |gem|
23
+ gem.name = "rspactor"
24
+ gem.summary = "RSpactor is a command line tool to automatically run your changed specs (much like autotest)."
25
+ gem.email = "mislav.marohnic@gmail.com"
26
+ gem.homepage = "http://github.com/mislav/rspactor"
27
+ gem.authors = ["Mislav Marohnić", "Andreas Wolff", "Pelle Braendgaard"]
28
+ gem.has_rdoc = false
29
+ end
30
+ rescue LoadError
31
+ puts "Jeweler gem (technicalpickles-jeweler) not found"
32
+ end
33
+
34
+ task :spec do
35
+ system 'ruby -Ilib bin/rspactor'
36
+ end
37
+
38
+ task :default => :spec
data/bin/rspactor CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rspactor'
2
+ require 'rspactor/runner'
3
3
 
4
- Runner.load
4
+ RSpactor::Runner.load
data/images/failed.png ADDED
Binary file
Binary file
Binary file
data/lib/rspactor.rb CHANGED
@@ -1,4 +1,6 @@
1
- require 'interactor'
2
- require 'listener'
3
- require 'inspector'
4
- require 'runner'
1
+ module RSpactor
2
+ autoload :Interactor, 'rspactor/interactor'
3
+ autoload :Listener, 'rspactor/listener'
4
+ autoload :Inspector, 'rspactor/inspector'
5
+ autoload :Runner, 'rspactor/runner'
6
+ end
@@ -0,0 +1,84 @@
1
+ module RSpactor
2
+ # Maps the changed filenames to list of specs to run in the next go.
3
+ # Assumes Rails-like directory structure
4
+ class Inspector
5
+ EXTENSIONS = %w(rb erb builder haml rhtml rxml yml conf opts)
6
+
7
+ def initialize(dir)
8
+ @root = dir
9
+ end
10
+
11
+ def determine_spec_files(file)
12
+ candidates = translate(file)
13
+ spec_files = candidates.select { |candidate| File.exists? candidate }
14
+
15
+ if spec_files.empty?
16
+ $stderr.puts "doesn't exist: #{candidates.inspect}"
17
+ end
18
+ spec_files
19
+ end
20
+
21
+ # mappings for Rails are inspired by autotest mappings in rspec-rails
22
+ def translate(file)
23
+ file = file.sub(%r:^#{Regexp.escape(@root)}/:, '')
24
+ candidates = []
25
+
26
+ if spec_file?(file)
27
+ candidates << file
28
+ else
29
+ spec_file = append_spec_file_extension(file)
30
+
31
+ case file
32
+ when %r:^app/:
33
+ if file =~ %r:^app/controllers/application(_controller)?.rb$:
34
+ candidates << 'controllers'
35
+ elsif file == 'app/helpers/application_helper.rb'
36
+ candidates << 'helpers' << 'views'
37
+ else
38
+ candidates << spec_file.sub('app/', '')
39
+
40
+ if file =~ %r:^app/(views/.+\.[a-z]+)\.[a-z]+$:
41
+ candidates << append_spec_file_extension($1)
42
+ elsif file =~ %r:app/helpers/(\w+)_helper.rb:
43
+ candidates << "views/#{$1}"
44
+ end
45
+ end
46
+ when %r:^lib/:
47
+ candidates << spec_file
48
+ # lib/foo/bar_spec.rb -> lib/bar_spec.rb
49
+ candidates << candidates.last.sub($&, '')
50
+ # lib/bar_spec.rb -> bar_spec.rb
51
+ candidates << candidates.last.sub(%r:\w+/:, '') if candidates.last.index('/')
52
+ when 'config/routes.rb'
53
+ candidates << 'controllers' << 'helpers' << 'views'
54
+ when 'config/database.yml', 'db/schema.rb'
55
+ candidates << 'models'
56
+ when %r:^(spec/(spec_helper|shared/.*)|config/(boot|environment(s/test)?))\.rb$:, 'spec/spec.opts'
57
+ candidates << 'spec'
58
+ else
59
+ candidates << spec_file
60
+ end
61
+ end
62
+
63
+ candidates.map do |candidate|
64
+ if candidate.index('spec') == 0
65
+ candidate
66
+ else
67
+ 'spec/' + candidate
68
+ end
69
+ end
70
+ end
71
+
72
+ def append_spec_file_extension(file)
73
+ if File.extname(file) == ".rb"
74
+ file.sub(/.rb$/, "_spec.rb")
75
+ else
76
+ file + "_spec.rb"
77
+ end
78
+ end
79
+
80
+ def spec_file?(file)
81
+ file =~ /^spec\/.+_spec.rb$/
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,63 @@
1
+ require 'timeout'
2
+
3
+ module RSpactor
4
+ class Interactor
5
+ def initialize
6
+ ticker
7
+ end
8
+
9
+ def wait_for_enter_key(msg, seconds_to_wait)
10
+ begin
11
+ Timeout::timeout(seconds_to_wait) do
12
+ ticker(:start => true, :msg => msg)
13
+ $stdin.gets
14
+ return true
15
+ end
16
+ rescue Timeout::Error
17
+ false
18
+ ensure
19
+ ticker(:stop => true)
20
+ end
21
+ end
22
+
23
+ def start_termination_handler
24
+ @main_thread = Thread.current
25
+ Thread.new do
26
+ loop do
27
+ sleep 0.5
28
+ if $stdin.gets
29
+ if wait_for_enter_key("** Running all specs.. Hit <enter> again to exit RSpactor", 3)
30
+ @main_thread.exit
31
+ exit
32
+ end
33
+ Runner.run_all_specs
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def ticker(opts = {})
42
+ if opts[:stop]
43
+ $stdout.puts "\n"
44
+ @pointer_running = false
45
+ elsif opts[:start]
46
+ @pointer_running = true
47
+ write(opts[:msg]) if opts[:msg]
48
+ else
49
+ Thread.new do
50
+ loop do
51
+ write('.') if @pointer_running == true
52
+ sleep 1.0
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def write(msg)
59
+ $stdout.print(msg)
60
+ $stdout.flush
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,86 @@
1
+ require 'osx/foundation'
2
+ OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
3
+
4
+ module RSpactor
5
+ # based on http://rails.aizatto.com/2007/11/28/taming-the-autotest-beast-with-fsevents/
6
+ class Listener
7
+ attr_reader :last_check, :callback, :valid_extensions
8
+
9
+ def initialize(valid_extensions = nil)
10
+ @valid_extensions = valid_extensions
11
+ timestamp_checked
12
+
13
+ @callback = lambda do |stream, ctx, num_events, paths, marks, event_ids|
14
+ changed_files = extract_changed_files_from_paths(split_paths(paths, num_events))
15
+ timestamp_checked
16
+ yield changed_files unless changed_files.empty?
17
+ end
18
+ end
19
+
20
+ def run(directories)
21
+ dirs = Array(directories)
22
+ stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, callback, nil, dirs, OSX::KFSEventStreamEventIdSinceNow, 0.5, 0)
23
+ unless stream
24
+ $stderr.puts "Failed to create stream"
25
+ exit(1)
26
+ end
27
+
28
+ OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
29
+ unless OSX::FSEventStreamStart(stream)
30
+ $stderr.puts "Failed to start stream"
31
+ exit(1)
32
+ end
33
+
34
+ begin
35
+ OSX::CFRunLoopRun()
36
+ rescue Interrupt
37
+ OSX::FSEventStreamStop(stream)
38
+ OSX::FSEventStreamInvalidate(stream)
39
+ OSX::FSEventStreamRelease(stream)
40
+ end
41
+ end
42
+
43
+ def timestamp_checked
44
+ @last_check = Time.now
45
+ end
46
+
47
+ def split_paths(paths, num_events)
48
+ paths.regard_as('*')
49
+ rpaths = []
50
+ num_events.times { |i| rpaths << paths[i] }
51
+ rpaths
52
+ end
53
+
54
+ def extract_changed_files_from_paths(paths)
55
+ changed_files = []
56
+ paths.each do |path|
57
+ next if ignore_path?(path)
58
+ Dir.glob(path + "*").each do |file|
59
+ next if ignore_file?(file)
60
+ changed_files << file if file_changed?(file)
61
+ end
62
+ end
63
+ changed_files
64
+ end
65
+
66
+ def file_changed?(file)
67
+ File.stat(file).mtime > last_check
68
+ end
69
+
70
+ def ignore_path?(path)
71
+ path =~ /(?:^|\/)\.(git|svn)/
72
+ end
73
+
74
+ def ignore_file?(file)
75
+ File.basename(file).index('.') == 0 or not valid_extension?(file)
76
+ end
77
+
78
+ def file_extension(file)
79
+ file =~ /\.(\w+)$/ and $1
80
+ end
81
+
82
+ def valid_extension?(file)
83
+ valid_extensions.nil? or valid_extensions.include?(file_extension(file))
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,84 @@
1
+ require 'rspactor'
2
+
3
+ module RSpactor
4
+ class Runner
5
+ def self.load
6
+ dotfile = File.join(ENV['HOME'], '.rspactor')
7
+ Kernel.load dotfile if File.exists?(dotfile)
8
+
9
+ dir = Dir.pwd
10
+ @inspector = Inspector.new(dir)
11
+ @interactor = Interactor.new
12
+
13
+ puts "** RSpactor is now watching at '#{dir}'"
14
+
15
+ aborted = initial_spec_run_abort
16
+ @interactor.start_termination_handler
17
+ run_all_specs unless aborted
18
+
19
+ Listener.new(Inspector::EXTENSIONS) do |files|
20
+ files_to_spec = []
21
+ files.each do |file|
22
+ spec_files = @inspector.determine_spec_files(file)
23
+ unless spec_files.empty?
24
+ puts spec_files.join("\n")
25
+ files_to_spec.concat spec_files
26
+ end
27
+ end
28
+ run_spec_command(files_to_spec)
29
+ end.run(dir)
30
+ end
31
+
32
+ def self.initial_spec_run_abort
33
+ @interactor.wait_for_enter_key("** Hit <enter> to skip initial spec run", 3)
34
+ end
35
+
36
+ def self.run_all_specs
37
+ run_spec_command('spec')
38
+ end
39
+
40
+ def self.run_spec_command(paths)
41
+ paths = Array(paths)
42
+ return if paths.empty?
43
+ run_command [ruby_opts, spec_runner, paths, spec_opts].flatten.join(' ')
44
+ end
45
+
46
+ def self.run_command(cmd)
47
+ system(cmd)
48
+ end
49
+
50
+ def self.spec_opts
51
+ if File.exist?('spec/spec.opts')
52
+ opts = File.read('spec/spec.opts').gsub("\n", ' ')
53
+ else
54
+ opts = "--color"
55
+ end
56
+
57
+ opts << ' ' << formatter_opts
58
+ # only add the "progress" formatter unless no other (besides growl) is specified
59
+ opts << ' -f progress' unless opts.scan(/\s(?:-f|--format)\b/).length > 1
60
+
61
+ opts
62
+ end
63
+
64
+ def self.formatter_opts
65
+ "-r #{File.dirname(__FILE__)}/../rspec_growler.rb -f RSpecGrowler:STDOUT"
66
+ end
67
+
68
+ def self.spec_runner
69
+ if File.exist?("script/spec")
70
+ "script/spec"
71
+ else
72
+ "spec"
73
+ end
74
+ end
75
+
76
+ def self.ruby_opts
77
+ other = ENV['RUBYOPT'] ? " #{ENV['RUBYOPT']}" : ''
78
+ %(RUBYOPT='-Ilib:spec#{other}')
79
+ end
80
+ end
81
+ end
82
+
83
+ # backward compatibility
84
+ Runner = RSpactor::Runner
@@ -0,0 +1,27 @@
1
+ require 'spec/runner/formatter/base_formatter'
2
+
3
+ class RSpecGrowler < Spec::Runner::Formatter::BaseFormatter
4
+ def dump_summary(duration, total, failures, pending)
5
+ icon = if failures > 0
6
+ 'failed'
7
+ elsif pending > 0
8
+ 'pending'
9
+ else
10
+ 'success'
11
+ end
12
+
13
+ image_path = File.dirname(__FILE__) + "/../images/#{icon}.png"
14
+ message = "#{total} examples, #{failures} failures"
15
+
16
+ if pending > 0
17
+ message << " (#{pending} pending)"
18
+ end
19
+
20
+ growl "Test Results", message, image_path, 0
21
+ end
22
+
23
+ def growl(title, msg, img, pri = 0)
24
+ system("growlnotify -w -n rspactor --image #{img} -p #{pri} -m #{msg.inspect} #{title} &")
25
+ end
26
+ end
27
+
@@ -1,8 +1,8 @@
1
- require 'inspector'
1
+ require 'rspactor/inspector'
2
2
 
3
- describe Inspector do
3
+ describe RSpactor::Inspector do
4
4
  before(:all) do
5
- @inspector = Inspector.new('/project')
5
+ @inspector = described_class.new('/project')
6
6
  end
7
7
 
8
8
  def translate(file)
@@ -1,8 +1,8 @@
1
- require 'listener'
1
+ require 'rspactor/listener'
2
2
 
3
- describe Listener do
3
+ describe RSpactor::Listener do
4
4
  before(:all) do
5
- @listener = Listener.new(%w(rb erb haml))
5
+ @listener = described_class.new(%w(rb erb haml))
6
6
  end
7
7
 
8
8
  it "should be timestamped" do
@@ -21,8 +21,12 @@ describe Listener do
21
21
  @listener.ignore_file?('/project/.foo').should be
22
22
  end
23
23
 
24
+ it "should not ignore files in directories which start with a dot" do
25
+ @listener.ignore_file?('/project/.foo/bar.rb').should be_false
26
+ end
27
+
24
28
  it "should not ignore files without extension" do
25
- @listener.ignore_file?('/project/foo.rb').should_not be
29
+ @listener.ignore_file?('/project/foo.rb').should be_false
26
30
  end
27
31
 
28
32
  it "should ignore files without extension" do
data/spec/runner_spec.rb CHANGED
@@ -1,16 +1,16 @@
1
- require 'runner'
1
+ require 'rspactor/runner'
2
2
 
3
- class ::Runner
4
- def self.run_command(cmd)
5
- # never shell out in tests
6
- cmd
3
+ describe RSpactor::Runner do
4
+
5
+ described_class.class_eval do
6
+ def self.run_command(cmd)
7
+ # never shell out in tests
8
+ cmd
9
+ end
7
10
  end
8
- end
9
-
10
- describe ::Runner do
11
11
 
12
- before(:all) do
13
- @runner = ::Runner
12
+ def runner
13
+ described_class
14
14
  end
15
15
 
16
16
  def with_env(name, value)
@@ -27,18 +27,18 @@ describe ::Runner do
27
27
  before(:each) do
28
28
  Dir.stub!(:pwd).and_return('/my/path')
29
29
  File.stub!(:exists?).and_return(false)
30
- @runner.stub!(:puts)
31
- Inspector.stub!(:new)
32
- Interactor.stub!(:new).and_return(mock('Interactor').as_null_object)
33
- Listener.stub!(:new).and_return(mock('Listener').as_null_object)
30
+ runner.stub!(:puts)
31
+ RSpactor::Inspector.stub!(:new)
32
+ RSpactor::Interactor.stub!(:new).and_return(mock('Interactor').as_null_object)
33
+ RSpactor::Listener.stub!(:new).and_return(mock('Listener').as_null_object)
34
34
  end
35
35
 
36
36
  def setup
37
- @runner.load
37
+ runner.load
38
38
  end
39
39
 
40
40
  it "should initialize Inspector" do
41
- Inspector.should_receive(:new).with('/my/path')
41
+ RSpactor::Inspector.should_receive(:new).with('/my/path')
42
42
  setup
43
43
  end
44
44
 
@@ -46,35 +46,35 @@ describe ::Runner do
46
46
  interactor = mock('Interactor')
47
47
  interactor.should_receive(:wait_for_enter_key).with(instance_of(String), 3)
48
48
  interactor.should_receive(:start_termination_handler)
49
- Interactor.should_receive(:new).and_return(interactor)
49
+ RSpactor::Interactor.should_receive(:new).and_return(interactor)
50
50
  setup
51
51
  end
52
52
 
53
53
  it "should run all specs if Interactor isn't interrupted" do
54
54
  interactor = mock('Interactor', :start_termination_handler => nil)
55
55
  interactor.should_receive(:wait_for_enter_key).and_return(nil)
56
- Interactor.should_receive(:new).and_return(interactor)
57
- @runner.should_receive(:run_spec_command).with('spec')
56
+ RSpactor::Interactor.should_receive(:new).and_return(interactor)
57
+ runner.should_receive(:run_spec_command).with('spec')
58
58
  setup
59
59
  end
60
60
 
61
61
  it "should skip running all specs if Interactor is interrupted" do
62
62
  interactor = mock('Interactor', :start_termination_handler => nil)
63
63
  interactor.should_receive(:wait_for_enter_key).and_return(true)
64
- Interactor.should_receive(:new).and_return(interactor)
65
- @runner.should_not_receive(:run_spec_command)
64
+ RSpactor::Interactor.should_receive(:new).and_return(interactor)
65
+ runner.should_not_receive(:run_spec_command)
66
66
  setup
67
67
  end
68
68
 
69
69
  it "should run Listener" do
70
70
  listener = mock('Listener')
71
71
  listener.should_receive(:run).with('/my/path')
72
- Listener.should_receive(:new).with(instance_of(Array)).and_return(listener)
72
+ RSpactor::Listener.should_receive(:new).with(instance_of(Array)).and_return(listener)
73
73
  setup
74
74
  end
75
75
 
76
76
  it "should output 'watching' message on start" do
77
- @runner.should_receive(:puts).with("** RSpactor is now watching at '/my/path'")
77
+ runner.should_receive(:puts).with("** RSpactor is now watching at '/my/path'")
78
78
  setup
79
79
  end
80
80
 
@@ -93,11 +93,11 @@ describe ::Runner do
93
93
  end
94
94
 
95
95
  def run(paths)
96
- @runner.run_spec_command(paths)
96
+ runner.run_spec_command(paths)
97
97
  end
98
98
 
99
99
  it "should exit if the paths argument is empty" do
100
- @runner.should_not_receive(:run_command)
100
+ runner.should_not_receive(:run_command)
101
101
  run([])
102
102
  end
103
103
 
@@ -122,7 +122,7 @@ describe ::Runner do
122
122
  end
123
123
 
124
124
  it "should include growl formatter" do
125
- run('foo').should include(' -f RSpactorFormatter:STDOUT')
125
+ run('foo').should include(' -f RSpecGrowler:STDOUT')
126
126
  end
127
127
 
128
128
  it "should include 'progress' formatter" do
@@ -130,9 +130,14 @@ describe ::Runner do
130
130
  end
131
131
 
132
132
  it "should not include 'progress' formatter if there already are 2 or more formatters" do
133
- @runner.should_receive(:formatter_opts).and_return('-f foo --format bar')
133
+ runner.should_receive(:formatter_opts).and_return('-f foo --format bar')
134
134
  run('foo').should_not include('-f progress')
135
135
  end
136
136
  end
137
137
 
138
+ it "should have Runner in global namespace for backwards compatibility" do
139
+ defined?(::Runner).should be_true
140
+ ::Runner.should == runner
141
+ end
142
+
138
143
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mislav-rspactor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Mislav Marohni\xC4\x87"
@@ -21,27 +21,30 @@ executables:
21
21
  - rspactor
22
22
  extensions: []
23
23
 
24
- extra_rdoc_files:
25
- - LICENSE
24
+ extra_rdoc_files: []
25
+
26
26
  files:
27
- - VERSION.yml
27
+ - Rakefile
28
28
  - bin/rspactor
29
- - lib/inspector.rb
30
- - lib/interactor.rb
31
- - lib/listener.rb
32
- - lib/resulting.rb
29
+ - lib/rspactor
30
+ - lib/rspactor/inspector.rb
31
+ - lib/rspactor/interactor.rb
32
+ - lib/rspactor/listener.rb
33
+ - lib/rspactor/runner.rb
33
34
  - lib/rspactor.rb
34
- - lib/runner.rb
35
+ - lib/rspec_growler.rb
36
+ - images/failed.png
37
+ - images/pending.png
38
+ - images/success.png
35
39
  - spec/inspector_spec.rb
36
40
  - spec/listener_spec.rb
37
41
  - spec/runner_spec.rb
38
42
  - LICENSE
39
- has_rdoc: true
43
+ has_rdoc: false
40
44
  homepage: http://github.com/mislav/rspactor
41
45
  post_install_message:
42
- rdoc_options:
43
- - --inline-source
44
- - --charset=UTF-8
46
+ rdoc_options: []
47
+
45
48
  require_paths:
46
49
  - lib
47
50
  required_ruby_version: !ruby/object:Gem::Requirement
data/VERSION.yml DELETED
@@ -1,4 +0,0 @@
1
- ---
2
- :major: 0
3
- :minor: 3
4
- :patch: 0
data/lib/inspector.rb DELETED
@@ -1,84 +0,0 @@
1
- # Maps the changed filenames to list of specs to run in the next go.
2
- # Assumes Rails-like directory structure
3
- class Inspector
4
-
5
- EXTENSIONS = %w(rb erb builder haml rhtml rxml yml conf opts)
6
-
7
- def initialize(dir)
8
- @root = dir
9
- end
10
-
11
- def determine_spec_files(file)
12
- candidates = translate(file)
13
- spec_files = candidates.select { |candidate| File.exists? candidate }
14
-
15
- if spec_files.empty?
16
- $stderr.puts "doesn't exist: #{candidates.inspect}"
17
- end
18
- spec_files
19
- end
20
-
21
- # mappings for Rails are inspired by autotest mappings in rspec-rails
22
- def translate(file)
23
- file = file.sub(%r:^#{Regexp.escape(@root)}/:, '')
24
- candidates = []
25
-
26
- if spec_file?(file)
27
- candidates << file
28
- else
29
- spec_file = append_spec_file_extension(file)
30
-
31
- case file
32
- when %r:^app/:
33
- if file =~ %r:^app/controllers/application(_controller)?.rb$:
34
- candidates << 'controllers'
35
- elsif file == 'app/helpers/application_helper.rb'
36
- candidates << 'helpers' << 'views'
37
- else
38
- candidates << spec_file.sub('app/', '')
39
-
40
- if file =~ %r:^app/(views/.+\.[a-z]+)\.[a-z]+$:
41
- candidates << append_spec_file_extension($1)
42
- elsif file =~ %r:app/helpers/(\w+)_helper.rb:
43
- candidates << "views/#{$1}"
44
- end
45
- end
46
- when %r:^lib/:
47
- candidates << spec_file
48
- # lib/foo/bar_spec.rb -> lib/bar_spec.rb
49
- candidates << candidates.last.sub($&, '')
50
- # lib/bar_spec.rb -> bar_spec.rb
51
- candidates << candidates.last.sub(%r:\w+/:, '') if candidates.last.index('/')
52
- when 'config/routes.rb'
53
- candidates << 'controllers' << 'helpers' << 'views'
54
- when 'config/database.yml', 'db/schema.rb'
55
- candidates << 'models'
56
- when %r:^(spec/(spec_helper|shared/.*)|config/(boot|environment(s/test)?))\.rb$:, 'spec/spec.opts'
57
- candidates << 'spec'
58
- else
59
- candidates << spec_file
60
- end
61
- end
62
-
63
- candidates.map do |candidate|
64
- if candidate.index('spec') == 0
65
- candidate
66
- else
67
- 'spec/' + candidate
68
- end
69
- end
70
- end
71
-
72
- def append_spec_file_extension(file)
73
- if File.extname(file) == ".rb"
74
- file.sub(/.rb$/, "_spec.rb")
75
- else
76
- file + "_spec.rb"
77
- end
78
- end
79
-
80
- def spec_file?(file)
81
- file =~ /^spec\/.+_spec.rb$/
82
- end
83
-
84
- end
data/lib/interactor.rb DELETED
@@ -1,63 +0,0 @@
1
- require 'timeout'
2
-
3
- class Interactor
4
-
5
- def initialize
6
- ticker
7
- end
8
-
9
- def wait_for_enter_key(msg, seconds_to_wait)
10
- begin
11
- Timeout::timeout(seconds_to_wait) do
12
- ticker(:start => true, :msg => msg)
13
- $stdin.gets
14
- return true
15
- end
16
- rescue Timeout::Error
17
- false
18
- ensure
19
- ticker(:stop => true)
20
- end
21
- end
22
-
23
- def start_termination_handler
24
- @main_thread = Thread.current
25
- Thread.new do
26
- loop do
27
- sleep 0.5
28
- if $stdin.gets
29
- if wait_for_enter_key("** Running all specs.. Hit <enter> again to exit RSpactor", 3)
30
- @main_thread.exit
31
- exit
32
- end
33
- Runner.run_all_specs
34
- end
35
- end
36
- end
37
- end
38
-
39
-
40
- private
41
-
42
- def ticker(opts = {})
43
- if opts[:stop]
44
- $stdout.puts "\n"
45
- @pointer_running = false
46
- elsif opts[:start]
47
- @pointer_running = true
48
- write(opts[:msg]) if opts[:msg]
49
- else
50
- Thread.new do
51
- loop do
52
- write('.') if @pointer_running == true
53
- sleep 1.0
54
- end
55
- end
56
- end
57
- end
58
-
59
- def write(msg)
60
- $stdout.print(msg)
61
- $stdout.flush
62
- end
63
- end
data/lib/listener.rb DELETED
@@ -1,86 +0,0 @@
1
- require 'osx/foundation'
2
- OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
3
-
4
- # Some code borrowed from http://rails.aizatto.com/2007/11/28/taming-the-autotest-beast-with-fsevents/
5
- class Listener
6
-
7
- attr_reader :last_check, :callback, :valid_extensions
8
-
9
- def initialize(valid_extensions = nil)
10
- @valid_extensions = valid_extensions
11
- timestamp_checked
12
-
13
- @callback = lambda do |stream, ctx, num_events, paths, marks, event_ids|
14
- changed_files = extract_changed_files_from_paths(split_paths(paths, num_events))
15
- timestamp_checked
16
- yield changed_files unless changed_files.empty?
17
- end
18
- end
19
-
20
- def run(directories)
21
- dirs = Array(directories)
22
- stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, callback, nil, dirs, OSX::KFSEventStreamEventIdSinceNow, 0.5, 0)
23
- unless stream
24
- $stderr.puts "Failed to create stream"
25
- exit(1)
26
- end
27
-
28
- OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
29
- unless OSX::FSEventStreamStart(stream)
30
- $stderr.puts "Failed to start stream"
31
- exit(1)
32
- end
33
-
34
- begin
35
- OSX::CFRunLoopRun()
36
- rescue Interrupt
37
- OSX::FSEventStreamStop(stream)
38
- OSX::FSEventStreamInvalidate(stream)
39
- OSX::FSEventStreamRelease(stream)
40
- end
41
- end
42
-
43
- def timestamp_checked
44
- @last_check = Time.now
45
- end
46
-
47
- def split_paths(paths, num_events)
48
- paths.regard_as('*')
49
- rpaths = []
50
- num_events.times { |i| rpaths << paths[i] }
51
- rpaths
52
- end
53
-
54
- def extract_changed_files_from_paths(paths)
55
- changed_files = []
56
- paths.each do |path|
57
- next if ignore_path?(path)
58
- Dir.glob(path + "*").each do |file|
59
- next if ignore_file?(file)
60
- changed_files << file if file_changed?(file)
61
- end
62
- end
63
- changed_files
64
- end
65
-
66
- def file_changed?(file)
67
- File.stat(file).mtime > last_check
68
- end
69
-
70
- def ignore_path?(path)
71
- path =~ /(?:^|\/)\.(git|svn)/
72
- end
73
-
74
- def ignore_file?(file)
75
- File.basename(file).index('.') == 0 or not valid_extension?(file)
76
- end
77
-
78
- def file_extension(file)
79
- file =~ /\.(\w+)$/ and $1
80
- end
81
-
82
- def valid_extension?(file)
83
- valid_extensions.nil? or valid_extensions.include?(file_extension(file))
84
- end
85
-
86
- end
data/lib/resulting.rb DELETED
@@ -1,47 +0,0 @@
1
- class RSpactorFormatter
2
- attr_accessor :example_group, :options, :where
3
- def initialize(options, where)
4
- @options = options
5
- @where = where
6
- end
7
-
8
- def dump_summary(duration, example_count, failure_count, pending_count)
9
- img = (failure_count == 0) ? "rails_ok.png" : "rails_fail.png"
10
- growl "Test Results", "#{example_count} examples, #{failure_count} failures", File.dirname(__FILE__) + "/../asset/#{img}", 0
11
- end
12
-
13
- def start(*ignore_these)
14
- end
15
-
16
- def add_example_group(*ignore_these)
17
- end
18
-
19
- def example_started(*ignore_these)
20
- end
21
-
22
- def example_passed(*ignore_these)
23
- end
24
-
25
- def example_failed(*ignore_these)
26
- end
27
-
28
- def example_pending( *ignore_these)
29
- end
30
-
31
- def start_dump
32
- end
33
-
34
- def dump_failure(*ignore_these)
35
- end
36
-
37
- def dump_pending
38
- end
39
-
40
- def close
41
- end
42
-
43
- def growl(title, msg, img, pri = 0)
44
- system("growlnotify -w -n rspactor --image #{img} -p #{pri} -m #{msg.inspect} #{title} &")
45
- end
46
- end
47
-
data/lib/runner.rb DELETED
@@ -1,82 +0,0 @@
1
- require 'inspector'
2
- require 'interactor'
3
- require 'listener'
4
-
5
- class Runner
6
-
7
- def self.load
8
- dotfile = File.join(ENV['HOME'], '.rspactor')
9
- Kernel.load dotfile if File.exists?(dotfile)
10
-
11
- dir = Dir.pwd
12
- @inspector = Inspector.new(dir)
13
- @interactor = Interactor.new
14
-
15
- puts "** RSpactor is now watching at '#{dir}'"
16
-
17
- aborted = initial_spec_run_abort
18
- @interactor.start_termination_handler
19
- run_all_specs unless aborted
20
-
21
- Listener.new(Inspector::EXTENSIONS) do |files|
22
- files_to_spec = []
23
- files.each do |file|
24
- spec_files = @inspector.determine_spec_files(file)
25
- unless spec_files.empty?
26
- puts spec_files.join("\n")
27
- files_to_spec.concat spec_files
28
- end
29
- end
30
- run_spec_command(files_to_spec)
31
- end.run(dir)
32
- end
33
-
34
- def self.initial_spec_run_abort
35
- @interactor.wait_for_enter_key("** Hit <enter> to skip initial spec run", 3)
36
- end
37
-
38
- def self.run_all_specs
39
- run_spec_command('spec')
40
- end
41
-
42
- def self.run_spec_command(paths)
43
- paths = Array(paths)
44
- return if paths.empty?
45
- run_command [ruby_opts, spec_runner, paths, spec_opts].flatten.join(' ')
46
- end
47
-
48
- def self.run_command(cmd)
49
- system(cmd)
50
- end
51
-
52
- def self.spec_opts
53
- if File.exist?('spec/spec.opts')
54
- opts = File.read('spec/spec.opts').gsub("\n", ' ')
55
- else
56
- opts = "--color"
57
- end
58
-
59
- opts << ' ' << formatter_opts
60
- # only add the "progress" formatter unless no other (besides growl) is specified
61
- opts << ' -f progress' unless opts.scan(/\s(?:-f|--format)\b/).length > 1
62
-
63
- opts
64
- end
65
-
66
- def self.formatter_opts
67
- "-r #{File.dirname(__FILE__)}/resulting.rb -f RSpactorFormatter:STDOUT"
68
- end
69
-
70
- def self.spec_runner
71
- if File.exist?("script/spec")
72
- "script/spec"
73
- else
74
- "spec"
75
- end
76
- end
77
-
78
- def self.ruby_opts
79
- other = ENV['RUBYOPT'] ? " #{ENV['RUBYOPT']}" : ''
80
- %(RUBYOPT='-Ilib:spec#{other}')
81
- end
82
- end