mislav-rspactor 0.3.0 → 0.3.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/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