spec_run_queue 0.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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in spec_run_queue.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,44 @@
1
+ WHAT
2
+ ====
3
+
4
+ spec_run_queue lets you build a lightweight queue watcher to watch for commands to run specs for your project.
5
+
6
+ On projects with a slow test suite, I don't like waiting for an autotest-like setup to complete a full spec run of a file. Also, I don't like watching my editor lock up waiting for a spec run to finish.
7
+
8
+ My development flow on such files tends to be 1) write a new spec example 2) run a focused example 3) repeat a few times 4) run the full spec
9
+
10
+ The runner works by doing a blocking queue read, until it receives a YAML dump of a hash like
11
+
12
+ { :target => "spec/models/foo_spec.rb" }
13
+
14
+ or
15
+
16
+ { :target => "spec/models/bar_spec.rb", :line => 42 }
17
+
18
+ and then runs the command, via backticks, in the shell. Summary results from the run are sent to the Notifier#notify method for notifiers you have setup.
19
+
20
+ NOTIFIERS
21
+ =========
22
+
23
+ Notifiers must provide at least one instane method, <tt>notify</tt>, which processes the output from the spec run. Currently, there are two runners, Stdout, and Growl. Stdout prints the results from the run to the terminal window, while growl sends a brief pass/fail summary to growl.
24
+
25
+
26
+ USAGE
27
+ =====
28
+
29
+ In your project root, run
30
+
31
+ redis_runner [1|2]
32
+
33
+ where 1 or 2 indicates the major version of rspec you are using for the project.
34
+
35
+
36
+ In order to trigger a queue run, you need your editor to insert a YAML dump of the instructions described above to the queue.
37
+
38
+
39
+ TODO
40
+ ====
41
+
42
+ * Genericify and configure the runner
43
+ * Include code or a plugin for the vim script I'm using to inject instructions into the queue
44
+ * Investigate a custom rspec runner in place of the current shell execution method
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/redis_runner ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #TODO make this more generic and configurable
4
+
5
+ require 'spec_run_queue'
6
+ require 'spec_run_queue/queue/redis'
7
+ require 'spec_run_queue/notifier/growl'
8
+ require 'spec_run_queue/notifier/stdout'
9
+
10
+ rspec_major_version = ARGV.shift
11
+ unless rspec_major_version
12
+ $stderr.puts "please specify 1 or 2 for rspec version"
13
+ exit -1
14
+ end
15
+
16
+ runner = SpecRunQueue::SystemRunner.new(rspec_major_version)
17
+ runner.add_notifier SpecRunQueue::Notifier::Growl.new(:password => "gr0wl")
18
+ runner.add_notifier SpecRunQueue::Notifier::Stdout.new()
19
+
20
+ queue = SpecRunQueue::Queue::Redis.new(runner)
21
+ queue.run
22
+
@@ -0,0 +1,21 @@
1
+ module SpecRunQueue
2
+ module Notifier
3
+ class Base
4
+ attr_reader :config, :message
5
+
6
+ def initialize(config = {})
7
+ @config = config
8
+ end
9
+
10
+ def notify(message, options = {})
11
+ @message = message
12
+ end
13
+
14
+ private
15
+
16
+ def failed?
17
+ message !~ /\s+0\s+failure/
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ module SpecRunQueue
2
+ module Notifier
3
+ class Growl < Base
4
+ def notify(message, options = {})
5
+ super
6
+ message = options[:use_full_message] ? message : short_message
7
+ growl.notify "rspec-growl Notification", "rspec-growl", message, priority
8
+ end
9
+
10
+ private
11
+
12
+ def priority
13
+ failed? ? fail_priority : pass_priority
14
+ end
15
+
16
+ def fail_priority
17
+ config[:fail_priority] || 1
18
+ end
19
+
20
+ def pass_priority
21
+ config[:pass_priority] || -2
22
+ end
23
+
24
+ def growl
25
+ @growl ||= Growl.new "127.0.0.1", "rspec-growl", ["rspec-growl Notification"], nil, config[:password]
26
+ end
27
+
28
+ def short_message
29
+ if message =~ /0 failure/
30
+ "specs passed"
31
+ elsif message =~ /(\d+\s+failures?)/
32
+ $1
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ module SpecRunQueue
2
+ module Notifier
3
+ class Stdout < Base
4
+ def notify(message, options = {})
5
+ super
6
+ puts message
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module SpecRunQueue
2
+ module Queue
3
+ class Base
4
+ attr_reader :runner
5
+
6
+ def initialize(runner)
7
+ @runner = runner
8
+ connect
9
+ reset_queue
10
+ end
11
+
12
+ def run
13
+ while (raw_instruction = queue_fetch)
14
+ instruction = YAML.load(raw_instruction[1])
15
+ runner.run_spec(instruction)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def connect
22
+ raise "Abstract method"
23
+ end
24
+
25
+ def reset_queue
26
+ raise "Abstract method"
27
+ end
28
+
29
+ def queue_fetch
30
+ raise "Abstract method"
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,27 @@
1
+ require 'redis'
2
+
3
+ module SpecRunQueue
4
+ module Queue
5
+ class Redis < Base
6
+ attr_reader :redis
7
+
8
+ def self.queue_key
9
+ "rspec"
10
+ end
11
+
12
+ private
13
+
14
+ def connect
15
+ @redis = ::Redis.new
16
+ end
17
+
18
+ def reset_queue
19
+ redis.del self.class.queue_key
20
+ end
21
+
22
+ def queue_fetch
23
+ redis.blpop self.class.queue_key, 0
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ module SpecRunQueue
2
+ class SystemRunner
3
+ attr_reader :version, :options, :notifiers
4
+
5
+ def initialize(version, options = {})
6
+ @version = version.to_i
7
+ @options = options
8
+ end
9
+
10
+ def rspec_bin
11
+ if version == 1
12
+ options[:spec_bin] || "spec"
13
+ elsif version == 2
14
+ options[:rspec_bin] || "rspec"
15
+ else
16
+ raise "Couldn't determine rspec bin'"
17
+ end
18
+ end
19
+
20
+ def add_notifier(notifier)
21
+ @notifiers ||= []
22
+ @notifiers << notifier
23
+ end
24
+
25
+ def run_spec(instruction)
26
+ unless sane_instruction?(instruction)
27
+ $stderr.puts "Insane instruction #{instruction.inspect}"
28
+ return
29
+ end
30
+
31
+ begin
32
+ cmd = "#{rspec_bin} -f progress --drb"
33
+ cmd << " -l #{instruction[:line]}" if instruction[:line]
34
+ cmd << " #{instruction[:target]}"
35
+ output = run_cmd(cmd)
36
+
37
+ output_to_notifiers(output)
38
+ rescue => e
39
+ shutdown_message = "Exception #{e.class} occurred, shutting down"
40
+ output_to_notifiers(shutdown_message, :use_full_message => true)
41
+ sleep(1)
42
+ raise e
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def run_cmd(cmd)
49
+ `#{cmd}`
50
+ end
51
+
52
+ def output_to_notifiers(output, options = {})
53
+ notifiers.each do |notifier|
54
+ notifier.notify(output, options)
55
+ end
56
+ end
57
+
58
+ def sane_instruction?(instruction)
59
+ # Line must be numeric or nil, and the target must be a path to a spec file
60
+ (instruction[:line].nil? || instruction[:line].to_s =~ /^\d+$/) &&
61
+ (instruction[:target] =~ /^[a-z0-9_\-\/]+_spec\.rb$/)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ module SpecRunQueue
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'ruby-growl'
4
+
5
+ module SpecRunQueue
6
+ end
7
+
8
+ require 'spec_run_queue/queue/base'
9
+ require 'spec_run_queue/notifier/base'
10
+ require 'spec_run_queue/system_runner'
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ class FooNotifier < SpecRunQueue::Notifier::Base
4
+ def notify(message, options = {})
5
+ super
6
+ some_callout unless failed?
7
+ end
8
+
9
+ def some_callout
10
+ end
11
+ end
12
+
13
+ describe "Base" do
14
+ let(:notifier) { @notifier ||= FooNotifier.new() }
15
+
16
+ before(:each) do
17
+ @foo_notifier = FooNotifier.new
18
+ end
19
+
20
+ it "should store the config" do
21
+ config = { :foo => :bar }
22
+ notifier = FooNotifier.new(config)
23
+ notifier.config.should == config
24
+ end
25
+
26
+ it "should be considered failed if the message doesn't match '0 failure'" do
27
+ notifier.should_not_receive(:some_callout)
28
+ notifier.notify('13 examples, 2 failures')
29
+ end
30
+
31
+ it "should not be consider failed if the message doesn't match '0 failure'" do
32
+ notifier.should_receive(:some_callout)
33
+ notifier.notify('13 examples, 0 failures')
34
+ end
35
+
36
+ describe "notify" do
37
+ it "should assign the message" do
38
+ message = '13 examples, 0 failures'
39
+ notifier.notify(message)
40
+ notifier.message.should == message
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'spec_run_queue/notifier/growl'
3
+
4
+ describe SpecRunQueue::Notifier::Growl do
5
+ before(:each) do
6
+ @message = "13 examples, 0 failures"
7
+ @fail_message = "12 examples, 1 failure"
8
+ @growl_mock = mock("Growl", :notify => true)
9
+ @notifier = SpecRunQueue::Notifier::Growl.new(:password => "secret")
10
+ @notifier.stub!(:growl).and_return(@growl_mock)
11
+ end
12
+
13
+ describe "notify" do
14
+ it "should assign the message" do
15
+ @notifier.notify(@message)
16
+ @notifier.message.should == @message
17
+ end
18
+
19
+ context "with a failure message" do
20
+ it "should send growl the expected message" do
21
+ @growl_mock.should_receive(:notify).with('rspec-growl Notification', 'rspec-growl', '1 failure', 1)
22
+ @notifier.notify(@fail_message)
23
+ end
24
+ end
25
+
26
+ context "with a passing message" do
27
+ it "should send growl the expected message" do
28
+ @growl_mock.should_receive(:notify).with('rspec-growl Notification', 'rspec-growl', 'specs passed', -2)
29
+ @notifier.notify(@message)
30
+ end
31
+ end
32
+
33
+ context "when instruction to use the full message" do
34
+ it "should send growl the full message" do
35
+ @growl_mock.should_receive(:notify).with('rspec-growl Notification', 'rspec-growl', 'the full message', 1)
36
+ @notifier.notify("the full message", :use_full_message => true)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'spec_run_queue/notifier/stdout'
3
+
4
+ describe SpecRunQueue::Notifier::Stdout do
5
+ before(:each) do
6
+ @message = "spec message"
7
+ @notifier = SpecRunQueue::Notifier::Stdout.new
8
+ end
9
+
10
+ describe "notify" do
11
+
12
+ it "should assign the message" do
13
+ @notifier.notify(@message)
14
+ @notifier.message.should == @message
15
+ end
16
+
17
+ it "should puts the message" do
18
+ @notifier.should_receive(:puts).with(@message)
19
+ @notifier.notify(@message)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ class SpecBase < SpecRunQueue::Queue::Base
4
+ attr_accessor :connect_called, :reset_queue_called
5
+
6
+ def connect
7
+ self.connect_called = true
8
+ end
9
+
10
+ def reset_queue
11
+ self.reset_queue_called = true
12
+ end
13
+ end
14
+
15
+ describe "Base" do
16
+ let(:runner) { @runner ||= mock("Runner", :run_spec => true) }
17
+ let(:spec_base) { @spec_base ||= SpecBase.new(runner) }
18
+
19
+ describe "initilization" do
20
+ it "should set the runner" do
21
+ spec_base.runner.should == runner
22
+ end
23
+
24
+ it "should call to connect" do
25
+ spec_base.connect_called.should be_true
26
+ end
27
+
28
+ it "should reset the queue" do
29
+ spec_base.reset_queue_called.should be_true
30
+ end
31
+ end
32
+
33
+ describe "running of the queue" do
34
+ before(:each) do
35
+ @first_raw_instruction = YAML.dump({ :target => "foo_spec.rb" })
36
+ @second_raw_instruction = YAML.dump({ :target => "bar_spec.rb", :line => 42 })
37
+ end
38
+
39
+ it "should fetch raw instructions from the queue" do
40
+ spec_base.should_receive(:queue_fetch).and_return(nil)
41
+ spec_base.run
42
+ end
43
+
44
+ it "should parse each raw instruction as yaml" do
45
+ spec_base.stub!(:queue_fetch).and_return(["rspec", @first_raw_instruction], ["rspec", @second_raw_instruction], nil)
46
+ YAML.should_receive(:load).with(@first_raw_instruction).ordered
47
+ YAML.should_receive(:load).with(@second_raw_instruction).ordered
48
+ spec_base.run
49
+ end
50
+
51
+ it "should pass the parsed instruction to runner.run_spec" do
52
+ spec_base.stub!(:queue_fetch).and_return(["rspec", @first_raw_instruction], ["rspec", @second_raw_instruction], nil)
53
+ runner.should_receive(:run_spec).with({ :target => "foo_spec.rb" }).ordered
54
+ runner.should_receive(:run_spec).with({ :target => "bar_spec.rb", :line => 42 }).ordered
55
+ spec_base.run
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'spec_run_queue/queue/redis'
3
+
4
+ describe SpecRunQueue::Queue::Redis do
5
+ before(:each) do
6
+ @redis_mock = mock("Redis", :del => true, :blpop => true)
7
+ ::Redis.stub!(:new).and_return(@redis_mock)
8
+ @runner_mock = mock("Runner")
9
+ @redis_queue = SpecRunQueue::Queue::Redis.new(@runner_mock)
10
+ end
11
+
12
+ it "should have 'rspec' as the key" do
13
+ SpecRunQueue::Queue::Redis.queue_key.should == "rspec"
14
+ end
15
+
16
+ it "should reset the queue by deleting the queue key" do
17
+ @redis_mock.should_receive(:del).with("rspec")
18
+ SpecRunQueue::Queue::Redis.new(@runner_mock)
19
+ end
20
+
21
+ it "should blpop instructions off the redis queue" do
22
+ @redis_mock.should_receive(:blpop).with("rspec", 0).and_return(nil)
23
+ @redis_queue.run
24
+ end
25
+ end
@@ -0,0 +1 @@
1
+ require "spec_run_queue"
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpecRunQueue::SystemRunner do
4
+ let(:foo_notifier) { @foo_notifier ||= mock("FooNotifier", :notify => true) }
5
+ let(:bar_notifier) { @bar_notifier ||= mock("BarNotifier", :notify => true) }
6
+ let(:runner) { @runner ||=
7
+ begin
8
+ runner = SpecRunQueue::SystemRunner.new(1)
9
+ runner.add_notifier(foo_notifier)
10
+ runner.add_notifier(bar_notifier)
11
+ runner
12
+ end
13
+ }
14
+
15
+ describe "initializing" do
16
+ it "should set the version" do
17
+ runner = SpecRunQueue::SystemRunner.new(1, { :password => "foo" })
18
+ runner.version.should == 1
19
+ end
20
+
21
+ it "should set the options" do
22
+ runner = SpecRunQueue::SystemRunner.new(1, { :password => "foo" })
23
+ runner.options.should == { :password => "foo" }
24
+ end
25
+ end
26
+
27
+ describe "getting the rspec binary" do
28
+ it "for version 1 is spec by default" do
29
+ runner = SpecRunQueue::SystemRunner.new(1)
30
+ runner.rspec_bin.should == "spec"
31
+ end
32
+
33
+ it "for varsion 2 is rspec by default" do
34
+ runner = SpecRunQueue::SystemRunner.new(2)
35
+ runner.rspec_bin.should == "rspec"
36
+ end
37
+ end
38
+
39
+ describe "adding a notifier" do
40
+ it "should append the notifier onto the notifiers list" do
41
+ runner = SpecRunQueue::SystemRunner.new(1)
42
+ foo_notifier = mock("FooNotifier")
43
+ runner.add_notifier(foo_notifier)
44
+ bar_notifier = mock("BarNotifier")
45
+ runner.add_notifier(bar_notifier)
46
+ runner.notifiers.should == [foo_notifier, bar_notifier]
47
+ end
48
+ end
49
+
50
+ describe "running a spec" do
51
+ it "should not run a spec if it's not a safe target" do
52
+ runner.should_not_receive(:run_cmd)
53
+ runner.run_spec(:target => "ls /tmp")
54
+ runner.run_spec(:target => "ls /tmp;foo_spec.rb")
55
+ end
56
+
57
+ it "should not run a spec if the target doesn't end in _spec.rb" do
58
+ runner.should_not_receive(:run_cmd)
59
+ runner.run_spec(:target => "spec/foo.rb")
60
+ end
61
+
62
+ it "should not run a spec if the line number doesn't look like a number" do
63
+ runner.should_not_receive(:run_cmd)
64
+ runner.run_spec(:target => "foo_spec.rb", :line => "foo")
65
+ end
66
+
67
+ it "should call to run the spec" do
68
+ runner.should_receive(:run_cmd).with("spec -f progress --drb foo_spec.rb")
69
+ runner.run_spec(:target => "foo_spec.rb")
70
+ end
71
+
72
+ it "should send the output from the run to all the notifiers" do
73
+ runner.should_receive(:run_cmd).with("spec -f progress --drb foo_spec.rb").and_return("spec output")
74
+ foo_notifier.should_receive(:notify).with("spec output", {})
75
+ bar_notifier.should_receive(:notify).with("spec output", {})
76
+ runner.run_spec(:target => "foo_spec.rb")
77
+ end
78
+
79
+ context "with a line number" do
80
+ it "should call to run the spec using the -l flag" do
81
+ runner.should_receive(:run_cmd).with("spec -f progress --drb -l 42 foo_spec.rb")
82
+ runner.run_spec(:target => "foo_spec.rb", :line => 42)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "spec_run_queue/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "spec_run_queue"
7
+ s.version = SpecRunQueue::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Brendon Murphy"]
10
+ s.email = ["xternal1+github@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Use a queue to run specs outside your editor}
13
+ s.description = %q{Use a queue to run specs outside your editor}
14
+
15
+ s.rubyforge_project = "spec_run_queue"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = ["redis_runner"]
20
+ s.require_paths = ["lib"]
21
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spec_run_queue
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Brendon Murphy
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-24 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Use a queue to run specs outside your editor
23
+ email:
24
+ - xternal1+github@gmail.com
25
+ executables:
26
+ - redis_runner
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.markdown
35
+ - Rakefile
36
+ - bin/redis_runner
37
+ - lib/spec_run_queue.rb
38
+ - lib/spec_run_queue/notifier/base.rb
39
+ - lib/spec_run_queue/notifier/growl.rb
40
+ - lib/spec_run_queue/notifier/stdout.rb
41
+ - lib/spec_run_queue/queue/base.rb
42
+ - lib/spec_run_queue/queue/redis.rb
43
+ - lib/spec_run_queue/system_runner.rb
44
+ - lib/spec_run_queue/version.rb
45
+ - spec/notifier/base_spec.rb
46
+ - spec/notifier/growl_spec.rb
47
+ - spec/notifier/stdout_spec.rb
48
+ - spec/queue/base_spec.rb
49
+ - spec/queue/redis_spec.rb
50
+ - spec/spec_helper.rb
51
+ - spec/system_runner_spec.rb
52
+ - spec_run_queue.gemspec
53
+ has_rdoc: true
54
+ homepage: ""
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project: spec_run_queue
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Use a queue to run specs outside your editor
87
+ test_files:
88
+ - spec/notifier/base_spec.rb
89
+ - spec/notifier/growl_spec.rb
90
+ - spec/notifier/stdout_spec.rb
91
+ - spec/queue/base_spec.rb
92
+ - spec/queue/redis_spec.rb
93
+ - spec/spec_helper.rb
94
+ - spec/system_runner_spec.rb