guillaumegentil-rspactor 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest ADDED
@@ -0,0 +1,10 @@
1
+ asset/rails_fail.png
2
+ asset/rails_ok.png
3
+ bin/rspactor
4
+ lib/inspector.rb
5
+ lib/interactor.rb
6
+ lib/listener.rb
7
+ lib/resulting.rb
8
+ lib/runner.rb
9
+ Rakefile
10
+ Manifest
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('rspactor', '0.2.1') do |p|
6
+ p.description = "RSpactor is a little command line tool to automatically run your changed specs (much like autotest)."
7
+ p.url = "http://github.com/guillaumegentil/rspactor/"
8
+ p.author = "Andreas Wolff"
9
+ p.email = "treas@dynamicdudes.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.executable_pattern = "bin/rspactor"
12
+ p.development_dependencies = []
13
+ end
14
+
15
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each
Binary file
Binary file
data/bin/rspactor ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'interactor')
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'listener')
5
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'inspector')
6
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'runner')
7
+
8
+ Runner.load
data/lib/inspector.rb ADDED
@@ -0,0 +1,92 @@
1
+ # The inspector make some assumptions about how your project is structured and where your spec files are located.
2
+ # That said: The 'spec' directory, containing all your test files, must rest in the root directory of your project.
3
+ # Futhermore it tries to locate controller, model, helper and view specs for a rails app (or projects with an identical structure)
4
+ # in root/spec/controllers, root/spec/models, root/spec/helpers and root/spec/views.
5
+
6
+ class Inspector
7
+
8
+ attr_accessor :base_spec_root
9
+
10
+ def self.file_is_invalid?(file)
11
+ return true unless File.basename(file) =~ /.rb\z|.rhtml\z|.erb\z|.haml\z/
12
+ false
13
+ end
14
+
15
+ def find_spec_file(file)
16
+ begin
17
+ return file if file_is_a_spec?(file)
18
+ spec_root = find_base_spec_root_by_file(file)
19
+ if spec_root
20
+ guessed_spec_location = guess_spec_location(file, spec_root)
21
+ if File.exist?(guessed_spec_location)
22
+ @base_spec_root = spec_root
23
+ return guessed_spec_location
24
+ end
25
+ end
26
+ nil
27
+ rescue => e
28
+ puts "Error while parsing a file: '#{file}'"
29
+ puts e
30
+ end
31
+ end
32
+
33
+ def inner_spec_directory(path)
34
+ spec_base_root = find_base_spec_root_by_file(Dir.pwd + "/.")
35
+ inner_location = extract_inner_project_location(Dir.pwd, spec_base_root)
36
+ File.join(spec_base_root, inner_location)
37
+ end
38
+
39
+ def find_base_spec_root_by_file(file)
40
+ if @base_spec_root
41
+ return @base_spec_root
42
+ else
43
+ dir_parts = File.dirname(file).split("/")
44
+ dir_parts.size.times do |i|
45
+ search_dir = dir_parts[0..dir_parts.length - i - 1].join("/") + "/"
46
+ if Dir.entries(search_dir).include?('spec')
47
+ @assumed_spec_root = search_dir + "spec"
48
+ break
49
+ end
50
+ end
51
+ return @assumed_spec_root
52
+ end
53
+ end
54
+
55
+ def guess_spec_location(file, spec_root)
56
+ inner_location = extract_inner_project_location(file, spec_root)
57
+ append_spec_file_extension(File.join(spec_root, inner_location))
58
+ end
59
+
60
+ def project_root(spec_root)
61
+ spec_root.split("/")[0...-1].join("/")
62
+ end
63
+
64
+ def extract_inner_project_location(file, spec_root)
65
+ location = file.sub(project_root(spec_root), "")
66
+ adapt_rails_specific_app_structure(location)
67
+ end
68
+
69
+ def adapt_rails_specific_app_structure(location)
70
+ # Removing 'app' if its a rails controller, model, helper or view
71
+ fu = location.split("/")
72
+ if fu[1] == "app" && (fu[2] == 'controllers' || fu[2] == 'helpers' || fu[2] == 'models' || fu[2] == 'views')
73
+ return "/" + fu[2..fu.length].join("/")
74
+ end
75
+ location
76
+ end
77
+
78
+ def append_spec_file_extension(spec_file)
79
+ if File.extname(spec_file) == ".rb"
80
+ return File.join(File.dirname(spec_file), File.basename(spec_file, ".rb")) + "_spec.rb"
81
+ else
82
+ return spec_file + "_spec.rb"
83
+ end
84
+ end
85
+
86
+ def file_is_a_spec?(file)
87
+ if file.split("/").include?('spec') && File.basename(file).match(/_spec.rb\z/)
88
+ return true
89
+ end
90
+ false
91
+ end
92
+ end
data/lib/interactor.rb ADDED
@@ -0,0 +1,63 @@
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 ADDED
@@ -0,0 +1,55 @@
1
+ # Some code borrowed from http://rails.aizatto.com/2007/11/28/taming-the-autotest-beast-with-fsevents/
2
+
3
+ class Listener
4
+
5
+ def initialize(&block)
6
+ require 'osx/foundation'
7
+ begin
8
+ @spec_run_time = Time.now
9
+ callback = lambda do |stream, ctx, num_events, paths, marks, event_ids|
10
+ changed_files = extract_changed_files_from_paths(split_paths(paths, num_events))
11
+ @spec_run_time = Time.now
12
+ yield changed_files
13
+ end
14
+
15
+ OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
16
+ stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, callback, nil, [Dir.pwd], OSX::KFSEventStreamEventIdSinceNow, 0.5, 0)
17
+ unless stream
18
+ puts "Failed to create stream"
19
+ exit
20
+ end
21
+
22
+ OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
23
+ unless OSX::FSEventStreamStart(stream)
24
+ puts "Failed to start stream"
25
+ exit
26
+ end
27
+
28
+ OSX::CFRunLoopRun()
29
+ rescue Interrupt
30
+ OSX::FSEventStreamStop(stream)
31
+ OSX::FSEventStreamInvalidate(stream)
32
+ OSX::FSEventStreamRelease(stream)
33
+ end
34
+ end
35
+
36
+ def split_paths(paths, num_events)
37
+ paths.regard_as('*')
38
+ rpaths = []
39
+ num_events.times { |i| rpaths << paths[i] }
40
+ rpaths
41
+ end
42
+
43
+ def extract_changed_files_from_paths(paths)
44
+ changed_files = []
45
+ paths.each do |path|
46
+ Dir.glob(path + "*").each do |file|
47
+ next if Inspector.file_is_invalid?(file)
48
+ file_time = File.stat(file).mtime
49
+ changed_files << file if file_time > @spec_run_time
50
+ end
51
+ end
52
+ changed_files
53
+ end
54
+
55
+ end
data/lib/resulting.rb ADDED
@@ -0,0 +1,47 @@
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(example_count)
14
+ end
15
+
16
+ def add_example_group(example_group)
17
+ end
18
+
19
+ def example_started(example)
20
+ end
21
+
22
+ def example_passed(example)
23
+ end
24
+
25
+ def example_failed(example, counter, failure)
26
+ end
27
+
28
+ def example_pending(example_group_description, example, message)
29
+ end
30
+
31
+ def start_dump
32
+ end
33
+
34
+ def dump_failure(counter, failure)
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 ADDED
@@ -0,0 +1,82 @@
1
+ class Runner
2
+
3
+ def self.load
4
+ @inspector = Inspector.new
5
+ @interactor = Interactor.new
6
+
7
+ puts "** RSpactor is now watching at '#{Dir.pwd}'"
8
+
9
+ if initial_spec_run_abort
10
+ @interactor.start_termination_handler
11
+ else
12
+ @interactor.start_termination_handler
13
+ run_all_specs
14
+ end
15
+
16
+ Listener.new do |files|
17
+ files_to_spec = []
18
+ files.each do |file|
19
+ spec_file = @inspector.find_spec_file(file)
20
+ if spec_file
21
+ puts spec_file
22
+ files_to_spec << spec_file
23
+ end
24
+ end
25
+ run_spec_command(files_to_spec) unless files_to_spec.empty?
26
+ end
27
+ end
28
+
29
+ def self.run_all_specs
30
+ run_spec_command([@inspector.inner_spec_directory(Dir.pwd)])
31
+ end
32
+
33
+ def self.run_specs_for_files(files, verbose = false)
34
+ files_to_spec = []
35
+ files.each do |file|
36
+ spec_file = @inspector.find_spec_file(file)
37
+ if spec_file
38
+ puts spec_file if verbose
39
+ files_to_spec << spec_file
40
+ end
41
+ end
42
+ run_spec_command(files_to_spec) unless files_to_spec.empty?
43
+ end
44
+
45
+ def self.run_spec_command(locations)
46
+ base_spec_root = extract_spec_root(locations.first)
47
+ spec_runner_bin = script_runner(locations.first)
48
+ locations = locations.join(" ")
49
+ cmd = "RAILS_ENV=test; "
50
+ cmd << "#{spec_runner_bin}"
51
+ cmd << " -X "
52
+ cmd << "#{locations} #{spec_opts(base_spec_root)} "
53
+ cmd << "-r #{File.dirname(__FILE__)}/../lib/resulting.rb -f RSpactorFormatter:STDOUT"
54
+ # puts cmd
55
+ system(cmd)
56
+ end
57
+
58
+ def self.extract_spec_root(file)
59
+ file[0..file.index("spec") + 4]
60
+ end
61
+
62
+ def self.spec_opts(base_spec_root)
63
+ if File.exist?("#{base_spec_root}spec.opts")
64
+ return "-O #{base_spec_root}spec.opts"
65
+ else
66
+ return "-c -f progress"
67
+ end
68
+ end
69
+
70
+ def self.initial_spec_run_abort
71
+ @interactor.wait_for_enter_key("** Hit <enter> to skip initial spec run", 3)
72
+ end
73
+
74
+ def self.script_runner(file)
75
+ root = file[0..file.index("spec") - 1]
76
+ if File.exist?(root + "script/spec")
77
+ return root + "script/spec"
78
+ else
79
+ "spec"
80
+ end
81
+ end
82
+ end
data/rspactor.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{rspactor}
5
+ s.version = "0.2.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Andreas Wolff"]
9
+ s.date = %q{2008-11-26}
10
+ s.default_executable = %q{rspactor}
11
+ s.description = %q{RSpactor is a little command line tool to automatically run your changed specs (much like autotest).}
12
+ s.email = %q{treas@dynamicdudes.com}
13
+ s.executables = ["rspactor"]
14
+ s.extra_rdoc_files = ["bin/rspactor", "lib/inspector.rb", "lib/interactor.rb", "lib/listener.rb", "lib/resulting.rb", "lib/runner.rb"]
15
+ s.files = ["asset/rails_fail.png", "asset/rails_ok.png", "bin/rspactor", "lib/inspector.rb", "lib/interactor.rb", "lib/listener.rb", "lib/resulting.rb", "lib/runner.rb", "Rakefile", "Manifest", "rspactor.gemspec"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://github.com/guillaumegentil/rspactor/}
18
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rspactor"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{rspactor}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{RSpactor is a little command line tool to automatically run your changed specs (much like autotest).}
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ else
30
+ end
31
+ else
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: guillaumegentil-rspactor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Andreas Wolff
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-26 00:00:00 -08:00
13
+ default_executable: rspactor
14
+ dependencies: []
15
+
16
+ description: RSpactor is a little command line tool to automatically run your changed specs (much like autotest).
17
+ email: treas@dynamicdudes.com
18
+ executables:
19
+ - rspactor
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - bin/rspactor
24
+ - lib/inspector.rb
25
+ - lib/interactor.rb
26
+ - lib/listener.rb
27
+ - lib/resulting.rb
28
+ - lib/runner.rb
29
+ files:
30
+ - asset/rails_fail.png
31
+ - asset/rails_ok.png
32
+ - bin/rspactor
33
+ - lib/inspector.rb
34
+ - lib/interactor.rb
35
+ - lib/listener.rb
36
+ - lib/resulting.rb
37
+ - lib/runner.rb
38
+ - Rakefile
39
+ - Manifest
40
+ - rspactor.gemspec
41
+ has_rdoc: true
42
+ homepage: http://github.com/guillaumegentil/rspactor/
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --line-numbers
46
+ - --inline-source
47
+ - --title
48
+ - Rspactor
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "1.2"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: rspactor
66
+ rubygems_version: 1.2.0
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: RSpactor is a little command line tool to automatically run your changed specs (much like autotest).
70
+ test_files: []
71
+