baldrick 0.0.1 → 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.
@@ -1,3 +1,7 @@
1
+ == 0.0.2 2009-10-14
2
+
3
+ * Remove parenthesis warning under 1.8.6
4
+
1
5
  == 0.0.1 2009-02-24
2
6
 
3
7
  * 1 major enhancement:
@@ -2,17 +2,41 @@ History.txt
2
2
  Manifest.txt
3
3
  README.rdoc
4
4
  Rakefile
5
+ TODO.txt
6
+ features/follow_feed_orders.feature
7
+ features/follow_injour_orders.feature
8
+ features/resources/a_feed_servant
9
+ features/resources/common_configuration
10
+ features/resources/feed_server
11
+ features/resources/injour_servant
12
+ features/steps/feed_steps.rb
13
+ features/steps/helpers/command_output.rb
14
+ features/steps/helpers/scenario_process.rb
15
+ features/steps/injour_steps.rb
16
+ features/steps/order_steps.rb
17
+ features/steps/servant_steps.rb
18
+ features/support/env.rb
5
19
  lib/baldrick.rb
6
- lib/baldrick_serve.rb
7
20
  lib/baldrick/command.rb
8
- lib/baldrick/run_servant.rb
9
- lib/baldrick/servant.rb
10
- lib/baldrick/task.rb
11
21
  lib/baldrick/listeners/feed_listener.rb
12
22
  lib/baldrick/listeners/feed_orders.rb
13
23
  lib/baldrick/listeners/injour_listener.rb
14
24
  lib/baldrick/listeners/xpath_locator.rb
25
+ lib/baldrick/run_servant.rb
26
+ lib/baldrick/servant.rb
27
+ lib/baldrick/task.rb
28
+ lib/baldrick_serve.rb
15
29
  script/console
16
30
  script/destroy
17
31
  script/generate
18
- tasks/rspec.rake
32
+ spec/lib/baldrick/command_spec.rb
33
+ spec/lib/baldrick/listeners/feed_listener_spec.rb
34
+ spec/lib/baldrick/listeners/feed_orders_spec.rb
35
+ spec/lib/baldrick/listeners/injour_listener_spec.rb
36
+ spec/lib/baldrick/listeners/xpath_locator_spec.rb
37
+ spec/lib/baldrick/run_servant_spec.rb
38
+ spec/lib/baldrick/servant_spec.rb
39
+ spec/lib/baldrick/task_spec.rb
40
+ spec/spec.opts
41
+ spec/spec_helper.rb
42
+ tasks/rspec.rake
@@ -1,16 +1,18 @@
1
1
  = Baldrick
2
2
 
3
3
  * http://github.com/brentsnook/baldrick
4
+ * http://groups.google.com/group/baldrick
5
+ * http://baldrick.lighthouseapp.com
4
6
 
5
- A dogsbody. Does what you tell it.
7
+ == Description
8
+
9
+ A dogsbody. Does what you tell it. Use it to glue things together.
6
10
 
7
11
  Baldrick recognises orders and then performs appropriate tasks.
8
12
  Baldrick acts as the glue between the source of the order (for example a twitter Atom feed) and the task you would like performed (put the kettle on).
9
13
 
10
14
  == Installation
11
15
 
12
- <em>Note: the rubyforge project application is currently being considered. First gem should be up within a few days. Please build yourself in the meantime - 2009-02-23</em>
13
-
14
16
  Make Baldrick cower 'neath *your* boot:
15
17
 
16
18
  sudo gem install baldrick
@@ -25,7 +27,7 @@ Grab the code from github:
25
27
  cd baldrick
26
28
  rake install_gem
27
29
 
28
- == How does it work?
30
+ == Synopsis
29
31
 
30
32
  You tell Baldrick:
31
33
 
@@ -70,7 +72,7 @@ Don't look at me, I just made it. How about these for suggestions?
70
72
  * control a continuous integration build light
71
73
  * control a nuclear power plant
72
74
 
73
- I'd love to hear what you use it for.
75
+ We want to know what you use it for, please send us some love on the {google group}[http://groups.google.com/group/baldrick].
74
76
 
75
77
  == License
76
78
 
data/Rakefile CHANGED
@@ -1,30 +1,31 @@
1
- %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
1
+ %w[rubygems rake rake/clean hoe fileutils newgem rubigen].each { |f| require f }
2
2
  require File.dirname(__FILE__) + '/lib/baldrick'
3
3
 
4
- # Generate all the Rake tasks
5
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
- $hoe = Hoe.new('baldrick', Baldrick::VERSION) do |p|
7
- p.developer 'Brent Snook', 'brent@fuglylogic.com'
8
- p.summary = %q{Does what you tell it - glues orders to tasks.}
9
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
10
- p.rubyforge_name = p.name
11
- p.extra_deps = [
4
+ Hoe.spec 'baldrick' do
5
+ version = Baldrick::VERSION
6
+ developer 'Brent Snook', 'brent@fuglylogic.com'
7
+ self.readme_file = 'README.rdoc'
8
+ self.clean_globs |= %w[**/.DS_Store tmp *.log]
9
+ self.rsync_args = '-av --delete --ignore-errors' # is this needed?
10
+
11
+ self.extra_deps = [
12
12
  ['nokogiri','>= 1.1.1'],
13
13
  ]
14
- p.extra_dev_deps = [
14
+
15
+ self.extra_dev_deps = [
15
16
  ['newgem', ">= #{::Newgem::VERSION}"],
16
17
  ['rspec', '>= 1.1.12'],
17
- ['cucumber', '>= 0.1.16'],
18
- ['injour', '>= 0.2.3'],
18
+ ['cucumber', '>= 0.1.16']
19
19
  ]
20
-
21
- p.clean_globs |= %w[**/.DS_Store tmp *.log]
22
- path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
23
- p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
24
- p.rsync_args = '-av --delete --ignore-errors'
20
+ end
21
+
22
+ require 'cucumber/rake/task'
23
+ Cucumber::Rake::Task.new(:features) do |t|
24
+ t.cucumber_opts = "features --format pretty"
25
25
  end
26
26
 
27
27
  require 'newgem/tasks' # load /tasks/*.rake
28
28
  Dir['tasks/**/*.rake'].each { |t| load t }
29
29
 
30
- task :default => [:spec, :features]
30
+ task :default => [:spec, :features]
31
+
@@ -0,0 +1,40 @@
1
+
2
+ - add website - 2
3
+ - turnip logo
4
+ - how to include documentation from wiki? Just link?
5
+
6
+ - merge README and wiki page - 1
7
+
8
+ - create google group - 1
9
+
10
+ - test on various feeds - 1
11
+
12
+ - test on cruise.rb
13
+ - test on slashdot
14
+ - test on twitter
15
+ - test on thinkgeek
16
+
17
+
18
+ X
19
+ - set up rubyforge account
20
+ - publish gem to rubyforge
21
+ - test install from rubyforge - 2 '''
22
+
23
+ - create tag of first release
24
+
25
+
26
+
27
+
28
+ ????
29
+
30
+ - should I package specs and features in my manifest?
31
+ - how is the gemspec used? Do I need one?
32
+
33
+ LATER
34
+
35
+ - add a spec around run_servant
36
+ - add ability to pass in feed listener xpath expressions for who, what, where, when during configuration
37
+ - fix bug: displaying multiple stack traces after error
38
+ - better way to log error with listener than puts
39
+
40
+
@@ -0,0 +1,9 @@
1
+ Feature: Follow feed orders
2
+ In order to allow tasks to be triggered from RSS
3
+ As a person interested in a web feed
4
+ I want new RSS items to be followed as orders
5
+
6
+ Scenario: Feed item contains a new order
7
+ Given a servant is listening for orders from a feed
8
+ When a new item appears in the feed containing an order
9
+ Then the order is followed
@@ -0,0 +1,9 @@
1
+ Feature: Follow injour orders
2
+ In order to trigger tasks by changing my status message
3
+ As an injour user
4
+ I want orders in my status message to be followed
5
+
6
+ Scenario: Status message contains a new order
7
+ Given a servant is listening for orders from injour
8
+ When an injour status is changed to contain an order
9
+ Then the order is followed
@@ -0,0 +1,2 @@
1
+ load File.dirname(__FILE__) + '/common_configuration'
2
+ listen_to :feed, :at => 'http://localhost:1726/rss'
@@ -0,0 +1,10 @@
1
+ # as with all servant files, don't give this file a .rb extension or cucumber will try to require it resulting in the server starting when you don't want it to
2
+
3
+ require File.dirname(__FILE__) + '/../../lib/baldrick_serve'
4
+ require File.dirname(__FILE__) + '/../steps/helpers/command_output'
5
+
6
+ listen_every 0.5
7
+
8
+ on_hearing /follow an order/m do
9
+ CommandOutput.add 'Order followed'
10
+ end
@@ -0,0 +1,30 @@
1
+ require 'webrick'
2
+
3
+ server = WEBrick::HTTPServer.new :Port => 1726
4
+
5
+ server.mount_proc("/rss") do |request, response|
6
+ response.body = <<-RSS
7
+ <?xml version="1.0"?>
8
+ <rss version="2.0">
9
+ <channel>
10
+ <item>
11
+ <title>hey you, follow an order</title>
12
+ <link></link>
13
+ <description></description>
14
+ <pubDate>Tue, 20 Apr 2000 04:00:00 GMT</pubDate>
15
+ <guid></guid>
16
+ </item>
17
+ </channel>
18
+ </rss>
19
+ RSS
20
+ response['Content-Type'] = 'text/xml'
21
+ end
22
+
23
+ %w(INT TERM).each do |signal|
24
+ trap signal do
25
+ server.shutdown
26
+ exit!
27
+ end
28
+ end
29
+
30
+ server.start
@@ -0,0 +1,2 @@
1
+ load File.dirname(__FILE__) + '/common_configuration'
2
+ listen_to :injour
@@ -0,0 +1,4 @@
1
+ When 'a new item appears in the feed containing an order' do
2
+ server = File.dirname(__FILE__) + "/../resources/feed_server"
3
+ ScenarioProcess.run "ruby #{server}", 'feed_server'
4
+ end
@@ -0,0 +1,33 @@
1
+ class CommandOutput
2
+
3
+ OUTPUT_FILE = File.expand_path(File.dirname(__FILE__) + '/../../../tmp/command_output')
4
+
5
+ TIMEOUT = 10
6
+
7
+ def self.contents
8
+ wait_for {File.exists? OUTPUT_FILE and !file_contents.empty?}
9
+ file_contents
10
+ end
11
+
12
+ def self.clear
13
+ FileUtils.rm OUTPUT_FILE, :force => true
14
+ end
15
+
16
+ def self.add output
17
+ File.open(OUTPUT_FILE, 'a') {|f| f.write(output) }
18
+ end
19
+
20
+ private
21
+
22
+ def self.wait_for &condition
23
+ time_spent = 0
24
+ until yield or time_spent > TIMEOUT
25
+ sleep 0.5
26
+ time_spent += 0.5
27
+ end
28
+ end
29
+
30
+ def self.file_contents
31
+ File.read OUTPUT_FILE
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ class ScenarioProcess
2
+
3
+ @commands = []
4
+
5
+ def self.run command, name
6
+ log = File.expand_path(File.dirname(__FILE__) + "/../../../tmp/#{name}.log")
7
+ FileUtils.rm log, :force => true
8
+ process = IO.popen "(#{command}) > #{log} 2>&1"
9
+ process.sync = true
10
+ wait_for log
11
+
12
+ @commands << command
13
+ end
14
+
15
+ def self.kill_all
16
+ # this method of killing stuff sucks, feels error prone
17
+ @commands.each do |command|
18
+ `ps`.each do |process|
19
+ # the space at the front of the pattern is essential...
20
+ # otherwise this will only work intermittently; if the pid you want to kill
21
+ # is shorter than others in the process list then the group will not capture it
22
+ matches = process.match /\s*(\d*).*#{Regexp.escape(command)}/
23
+ `kill #{matches[1]}` if matches
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def self.wait_for file
31
+ until File.exists? file
32
+ sleep 0.2
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,4 @@
1
+ When 'an injour status is changed to contain an order' do
2
+ ScenarioProcess.run 'injour serve baldrickfeature 43216', 'injour_server'
3
+ ScenarioProcess.run 'injour status hey you, follow an order', 'injour_status'
4
+ end
@@ -0,0 +1,3 @@
1
+ Then 'the order is followed' do
2
+ CommandOutput.contents.should include('Order followed')
3
+ end
@@ -0,0 +1,5 @@
1
+ Given 'a servant is listening for orders from $listener' do |listener|
2
+ servant = File.dirname(__FILE__) + "/../resources/#{listener.downcase.gsub(' ', '_')}_servant"
3
+
4
+ ScenarioProcess.run "ruby #{servant}", 'servant'
5
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../../lib/baldrick'
2
+
3
+ gem 'cucumber'
4
+ require 'cucumber'
5
+ gem 'rspec'
6
+ require 'spec'
7
+
8
+ Before do
9
+ CommandOutput.clear
10
+ end
11
+
12
+ After do
13
+ ScenarioProcess.kill_all
14
+ end
15
+
@@ -2,7 +2,7 @@ $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  module Baldrick
5
- VERSION = '0.0.1'
5
+ VERSION = '0.0.2'
6
6
  end
7
7
 
8
8
  require 'baldrick/listeners/xpath_locator'
@@ -5,7 +5,7 @@ module Baldrick::Listeners
5
5
  class XPathLocator
6
6
 
7
7
  def self.from_xml xml
8
- from_node Nokogiri::XML(strip_default_namespaces_from xml)
8
+ from_node Nokogiri::XML(strip_default_namespaces_from(xml))
9
9
  end
10
10
 
11
11
  def self.from_node node
@@ -0,0 +1,115 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Baldrick
4
+
5
+ describe Command do
6
+
7
+ before :each do
8
+ @servant = mock('servant', :null_object => true)
9
+ Servant.stub!(:new).and_return @servant
10
+ @stdout = stub('stdout', :null_object => true)
11
+
12
+ # including Singleton marks new as private but I still want to instantiate it for testing
13
+ # nasty, I know
14
+ @command = Command.send(:new)
15
+ end
16
+
17
+ describe 'when executing' do
18
+
19
+ it 'registers the injour listener type by default' do
20
+ @command.listener_class_for(:injour).should == Listeners::InjourListener
21
+ end
22
+
23
+ it 'registers the feed listener type by default' do
24
+ @command.listener_class_for(:feed).should == Listeners::FeedListener
25
+ end
26
+
27
+ it 'displays a startup message' do
28
+ @command.stub!(:should_serve?).and_return false
29
+
30
+ @stdout.should_receive(:<<).with /started/
31
+
32
+ @command.execute @stdout
33
+ end
34
+
35
+ it 'tells the servant to serve repeatedly' do
36
+ @command.stub!(:should_serve?).and_return true, true, false
37
+ @command.stub!(:sleep)
38
+
39
+ @servant.should_receive(:serve).twice
40
+
41
+ @command.execute @stdout
42
+ end
43
+
44
+ it 'waits for 2 seconds between service by default' do
45
+ @command.stub!(:should_serve?).and_return true, false
46
+
47
+ @command.should_receive(:sleep).with(2)
48
+
49
+ @command.execute @stdout
50
+ end
51
+
52
+ it 'allows wait period between service to be specified' do
53
+ @command.stub!(:should_serve?).and_return true, false
54
+ @command.listen_every 10
55
+
56
+ @command.should_receive(:sleep).with(10)
57
+
58
+ @command.execute @stdout
59
+ end
60
+ end
61
+
62
+ describe 'configuration' do
63
+
64
+ it 'can be told of how to listen for orders of a particular type' do
65
+ @command.register_listener_type :type, (listener_class = mock('listener class'))
66
+
67
+ @command.listener_class_for(:type).should == listener_class
68
+ end
69
+
70
+ describe 'when told where to listen for orders' do
71
+
72
+ it 'adds a new listener of a recognised type to the servant' do
73
+ listener_class = stub('listener class', :new => (listener = mock('listener')))
74
+ @command.stub!(:listener_class_for).with(:recognised_type).and_return listener_class
75
+
76
+ @servant.should_receive(:add_listener).with listener
77
+
78
+ @command.listen_to :recognised_type
79
+ end
80
+
81
+ it 'configures a new listener with given options' do
82
+ listener_class = stub 'listener class'
83
+ @command.stub!(:listener_class_for).with(:listener_type).and_return listener_class
84
+ @servant.stub! :add_listener
85
+
86
+ listener_class.should_receive(:new).with({:option => true})
87
+
88
+ @command.listen_to :listener_type, :option => true
89
+ end
90
+
91
+ it 'fails if the listener type is not recognised' do
92
+ @command.stub!(:listener_class_for).with(:unrecognised_type).and_return nil
93
+
94
+ lambda{@command.listen_to :unrecognised_type}.should raise_error(RuntimeError, 'No order listener implementation found for unrecognised_type')
95
+ end
96
+
97
+ end
98
+
99
+ describe 'when told how to perform a task' do
100
+
101
+ %w{to on_hearing}.each do |to_alias|
102
+
103
+ it "adds a new task to the servant using the instructions given via '#{to_alias}'" do
104
+ procedure = lambda {}
105
+ Task.stub!(:new).with(/put the kettle on/, procedure).and_return(task = stub('job'))
106
+
107
+ @servant.should_receive(:add_task).with task
108
+
109
+ @command.send to_alias, /put the kettle on/, &procedure
110
+ end
111
+
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Baldrick::Listeners
4
+
5
+ describe FeedListener do
6
+
7
+ before(:each) do
8
+ @listener = FeedListener.new :at => ''
9
+ end
10
+
11
+ it 'allows RSS URL to be specified' do
12
+ @listener = FeedListener.new :at => 'http://billywitchdoctor.com/rss'
13
+ FeedOrders.stub!(:within).and_return []
14
+
15
+ @listener.should_receive(:open).with 'http://billywitchdoctor.com/rss'
16
+
17
+ @listener.orders
18
+ end
19
+
20
+ it 'only return orders newer than that last stored' do
21
+
22
+ now = Time.now
23
+ old_news = {:what => 'shake has gone...', :when => now}
24
+ new_news = {:what => 'newsflash! the drizzle is coming', :when => now + 1}
25
+ stale_news = {:what => 'carl is incredibly lonely', :when => now - 1}
26
+ latest_news = {:what => 'the drizzle is the shizzle!', :when => now + 2}
27
+
28
+ @listener.stub! :open
29
+ FeedOrders.stub!(:within).and_return [old_news], [old_news, new_news, stale_news, latest_news]
30
+
31
+ @listener.orders
32
+
33
+ @listener.orders.should == [new_news, latest_news]
34
+ end
35
+
36
+ it "handles orders with no 'when' specified" do
37
+ @listener.stub! :open
38
+ FeedOrders.stub!(:within).and_return [{:what => 'shake has gone...', :when => nil}]
39
+
40
+ @listener.orders
41
+ end
42
+
43
+ end
@@ -0,0 +1,63 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Baldrick::Listeners
4
+
5
+ describe FeedOrders do
6
+
7
+ before :each do
8
+ @item_locator = mock('item locator')
9
+ XPathLocator.stub!(:from_xml).and_return @item_locator
10
+ end
11
+
12
+ it 'first searches items then elements for orders' do
13
+ @item_locator.should_receive(:find_nodes_matching).with('//item', '//entry').and_return []
14
+
15
+ FeedOrders.within ''
16
+ end
17
+
18
+ it 'creates an order for every item found' do
19
+ node = stub 'node', :null_object => true
20
+ @item_locator.stub!(:find_nodes_matching).and_return [node, node, node]
21
+
22
+ FeedOrders.within('').size.should == 3
23
+ end
24
+
25
+ describe 'when creating an order' do
26
+
27
+ before :each do
28
+ @node = stub 'node', :null_object => true
29
+ @item_locator.stub!(:find_nodes_matching).and_return [@node]
30
+ @text_locator = stub('item locator', :null_object => true)
31
+ XPathLocator.stub!(:from_node).and_return @text_locator
32
+
33
+ @time = Time.now
34
+ Time.stub!(:parse).and_return @time
35
+ end
36
+
37
+ it "parses the publish time and use it as 'when'" do
38
+ Time.stub!(:parse).with(@time.to_s).and_return @time
39
+ @text_locator.stub!(:find_text_matching).with('published/text()', 'pubDate/text()', 'date/text()', 'updated/text()').and_return @time.to_s
40
+
41
+ FeedOrders.within('').first[:when].should == @time
42
+ end
43
+
44
+ it "uses the contents of the item as 'what'" do
45
+ @text_locator.stub!(:find_text_matching).and_return stub('text', :null_object => true)
46
+ @node.stub!(:to_s).and_return 'item node contents'
47
+
48
+ FeedOrders.within('').first[:what].should == 'item node contents'
49
+ end
50
+
51
+ it "uses the author as the 'who'" do
52
+ @text_locator.stub!(:find_text_matching).with('author/name/text()', 'author/text()').and_return 'Alan Moore'
53
+
54
+ FeedOrders.within('').first[:who].should == 'Alan Moore'
55
+ end
56
+
57
+ it "uses the link as the 'where'" do
58
+ @text_locator.stub!(:find_text_matching).with('link/text()', "link[@rel='alternate']/@href").and_return 'http://internet.com'
59
+
60
+ FeedOrders.within('').first[:where].should == 'http://internet.com'
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,82 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Baldrick::Listeners
4
+
5
+ describe InjourListener do
6
+
7
+ before(:each) do
8
+ @listener = InjourListener.new
9
+ Time.stub!(:parse)
10
+ end
11
+
12
+ it 'only return new orders' do
13
+
14
+ first_status = <<-FIRST_STATUS
15
+ === nathan on dk3.mordhaus.net:43215 ===
16
+ * [05-Feb-2009 11:30 PM] i like chips
17
+ FIRST_STATUS
18
+ second_status = <<-SECOND_STATUS
19
+ === nathan on dk3.mordhaus.net:43215 ===
20
+ * [05-Feb-2009 11:30 PM] i like chips
21
+ === murderface on dk4.mordhaus.net:43215 ===
22
+ * [05-Feb-2009 11:31 PM] no kidding
23
+ SECOND_STATUS
24
+
25
+ @listener.stub!(:`).with('injour ls').and_return first_status, second_status
26
+
27
+ @listener.orders
28
+
29
+ @listener.orders.should == [
30
+ {
31
+ :who => 'murderface',
32
+ :what => 'no kidding',
33
+ :where => 'dk4.mordhaus.net:43215',
34
+ :when => nil
35
+ }]
36
+ end
37
+
38
+ it 'handles multiple orders from a single user' do
39
+
40
+ status = <<-STATUS
41
+ === skwisgaar on dk5.mordhaus.net:43215 ===
42
+ * [05-Feb-2009 11:30 PM] stops copies me!
43
+ === skwisgaar on dk5.mordhaus.net:43215 ===
44
+ * [05-Feb-2009 11:31 PM] I means its, stops copies me!
45
+ === skwisgaar on dk5.mordhaus.net:43215 ===
46
+ * [05-Feb-2009 11:32 PM] STOPS COPIES ME!
47
+ STATUS
48
+
49
+ @listener.stub!(:`).with('injour ls').and_return status
50
+
51
+ @listener.orders.collect{|order| order[:what]}.should == ['stops copies me!', 'I means its, stops copies me!', 'STOPS COPIES ME!']
52
+ end
53
+
54
+ describe 'reading individual parts of an order' do
55
+
56
+ before :each do
57
+ status = <<-STATUS
58
+ === twinkletits on dk6.mordhaus.net:43215 ===
59
+ * [05-Feb-2009 11:30 PM] who wants a banana sticker?
60
+ STATUS
61
+ @listener.stub!(:`).with('injour ls').and_return status
62
+
63
+ @order = @listener.orders.first
64
+ end
65
+
66
+ it "interprets injour user as 'who'" do
67
+ @order[:who].should == 'twinkletits'
68
+ end
69
+
70
+ it "interprets status as 'what'" do
71
+ @order[:what].should == 'who wants a banana sticker?'
72
+ end
73
+
74
+ it "interprets injour address as 'where'" do
75
+ @order[:where].should == 'dk6.mordhaus.net:43215'
76
+ end
77
+
78
+ it "interprets parsed time as 'when'" do
79
+ @order[:when].should == Time.parse('05-Feb-2009 11:30 PM')
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+
3
+ include Baldrick::Listeners
4
+
5
+ describe XPathLocator do
6
+
7
+ describe 'when created' do
8
+
9
+ it 'allows a new locator to be created from an XML string' do
10
+ locator = XPathLocator.from_xml '<root><child/></root>'
11
+
12
+ locator.find_nodes_matching('//child').should_not be_empty
13
+ end
14
+
15
+ it 'allows a new locator to be created from a previously returned node' do
16
+ node = XPathLocator.from_xml('<root><child><grandchild/></child></root>').find_nodes_matching('//child').first
17
+ locator = XPathLocator.from_node node
18
+
19
+ locator.find_nodes_matching('//grandchild').should_not be_empty
20
+ end
21
+ end
22
+
23
+ describe 'when finding nodes' do
24
+
25
+ it 'finds all nodes matching any of the given xpath expressions' do
26
+ locator = XPathLocator.from_xml '<root><match/></root>'
27
+
28
+ locator.find_nodes_matching('//nomatches', '//match').size.should == 1
29
+ end
30
+
31
+ it 'handles tags with a default namespace' do
32
+ locator = XPathLocator.from_xml '<root xmlns="http://namespace.com"><match/></root>'
33
+
34
+ locator.find_nodes_matching('//match').size.should == 1
35
+ end
36
+
37
+ it 'finds only the first text matching any of the given xpath expressions' do
38
+ locator = XPathLocator.from_xml '<root><match>first</match><match>second</match></root>'
39
+
40
+ locator.find_text_matching('//match').should == 'first'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Baldrick
4
+
5
+ describe RunServant do
6
+
7
+ describe 'when exiting' do
8
+ it 'rethrows any exceptions thrown in the script body'
9
+ it 'causes the command to execute'
10
+ end
11
+
12
+ it 'delegates all calls for missing methods to the command'
13
+ it 'causes an interrupt to stop execution of the command'
14
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Baldrick
4
+
5
+ describe Servant do
6
+
7
+ before(:each) do
8
+ @servant = Servant.new
9
+ end
10
+
11
+ it 'executes all orders from all listeners' do
12
+ 2.times do |listener_number|
13
+ orders = [stub("order #{listener_number} a"), stub("order #{listener_number} b")]
14
+ listener = mock("listener #{listener_number}", :orders => orders)
15
+ @servant.add_listener listener
16
+
17
+ orders.each {|order| @servant.should_receive(:follow).with order}
18
+ end
19
+
20
+ @servant.serve
21
+ end
22
+
23
+ it 'performs all tasks for an executed order' do
24
+ order = stub('order')
25
+ @servant.add_listener mock('listener', :orders => [order])
26
+ 2.times do |task_number|
27
+
28
+ @servant.add_task(task = mock("task #{task_number}"))
29
+
30
+ task.should_receive(:run).with order
31
+ end
32
+
33
+ @servant.serve
34
+ end
35
+
36
+ it 'recovers from a problem with a listener and continues' do
37
+ @servant.add_listener(first_listener = mock('first listener'))
38
+ @servant.add_listener(second_listener = mock('second listener'))
39
+ first_listener.stub!(:orders).and_raise Exception
40
+
41
+ second_listener.should_receive(:orders).and_return []
42
+
43
+ @servant.serve
44
+ end
45
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ include Baldrick
4
+
5
+ describe Task do
6
+
7
+ it 'checks the what value of the command when determining whether or not to run' do
8
+ @matcher = mock('matcher')
9
+
10
+ @matcher.should_receive(:match).with 'what value'
11
+
12
+ run :what => 'what value'
13
+ end
14
+
15
+ it 'runs if order matches' do
16
+ @matcher = stub('matcher', :match => [''])
17
+ @procedure = stub('procedure', :arity => 1)
18
+
19
+ @procedure.should_receive :call
20
+
21
+ run ''
22
+ end
23
+
24
+ it "doesn't run if order does not match" do
25
+ @matcher = stub('matcher', :match => nil)
26
+ @procedure = stub('procedure', :arity => 1)
27
+
28
+ @procedure.should_not_receive :call
29
+
30
+ run ''
31
+ end
32
+
33
+ it 'only passes as many arguments as the procedure can handle' do
34
+ @matcher = stub('matcher', :match => ['', '1', '2', '3', '4'])
35
+ @procedure = stub('procedure', :arity => 3)
36
+
37
+ @procedure.should_receive(:call).with('1', '2', '3')
38
+
39
+ run ''
40
+ end
41
+
42
+ it "doesn't pass the matched string to the procedure" do
43
+ @matcher = stub('matcher', :match => ['matching string', '1', '2'])
44
+ @procedure = stub('procedure', :arity => 1)
45
+
46
+ @procedure.should_receive(:call).with('1')
47
+
48
+ run ''
49
+ end
50
+
51
+ it 'passes all matching portions of the command and the command to the procedure' do
52
+ @matcher = stub('matcher', :match => ['', 'com', 'mand'])
53
+ @procedure = stub('procedure', :arity => 3)
54
+
55
+ @procedure.should_receive(:call).with('com', 'mand', {:what => 'command'})
56
+
57
+ run :what => 'command'
58
+ end
59
+
60
+ def run order
61
+ Task.new(@matcher, @procedure).run order
62
+ end
63
+
64
+ end
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format
3
+ profile
4
+ --timeout
5
+ 20
6
+ --diff
@@ -0,0 +1,11 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ $:.unshift File.dirname(__FILE__)
11
+ require 'baldrick'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: baldrick
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brent Snook
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-24 00:00:00 +11:00
12
+ date: 2009-10-14 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 1.2.3
33
+ version: 1.5.2
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: rspec
@@ -52,16 +52,6 @@ dependencies:
52
52
  - !ruby/object:Gem::Version
53
53
  version: 0.1.16
54
54
  version:
55
- - !ruby/object:Gem::Dependency
56
- name: injour
57
- type: :development
58
- version_requirement:
59
- version_requirements: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: 0.2.3
64
- version:
65
55
  - !ruby/object:Gem::Dependency
66
56
  name: hoe
67
57
  type: :development
@@ -70,9 +60,13 @@ dependencies:
70
60
  requirements:
71
61
  - - ">="
72
62
  - !ruby/object:Gem::Version
73
- version: 1.8.0
63
+ version: 2.3.3
74
64
  version:
75
- description: ""
65
+ description: |-
66
+ A dogsbody. Does what you tell it. Use it to glue things together.
67
+
68
+ Baldrick recognises orders and then performs appropriate tasks.
69
+ Baldrick acts as the glue between the source of the order (for example a twitter Atom feed) and the task you would like performed (put the kettle on).
76
70
  email:
77
71
  - brent@fuglylogic.com
78
72
  executables: []
@@ -82,28 +76,54 @@ extensions: []
82
76
  extra_rdoc_files:
83
77
  - History.txt
84
78
  - Manifest.txt
85
- - README.rdoc
79
+ - TODO.txt
86
80
  files:
87
81
  - History.txt
88
82
  - Manifest.txt
89
83
  - README.rdoc
90
84
  - Rakefile
85
+ - TODO.txt
86
+ - features/follow_feed_orders.feature
87
+ - features/follow_injour_orders.feature
88
+ - features/resources/a_feed_servant
89
+ - features/resources/common_configuration
90
+ - features/resources/feed_server
91
+ - features/resources/injour_servant
92
+ - features/steps/feed_steps.rb
93
+ - features/steps/helpers/command_output.rb
94
+ - features/steps/helpers/scenario_process.rb
95
+ - features/steps/injour_steps.rb
96
+ - features/steps/order_steps.rb
97
+ - features/steps/servant_steps.rb
98
+ - features/support/env.rb
91
99
  - lib/baldrick.rb
92
- - lib/baldrick_serve.rb
93
100
  - lib/baldrick/command.rb
94
- - lib/baldrick/run_servant.rb
95
- - lib/baldrick/servant.rb
96
- - lib/baldrick/task.rb
97
101
  - lib/baldrick/listeners/feed_listener.rb
98
102
  - lib/baldrick/listeners/feed_orders.rb
99
103
  - lib/baldrick/listeners/injour_listener.rb
100
104
  - lib/baldrick/listeners/xpath_locator.rb
105
+ - lib/baldrick/run_servant.rb
106
+ - lib/baldrick/servant.rb
107
+ - lib/baldrick/task.rb
108
+ - lib/baldrick_serve.rb
101
109
  - script/console
102
110
  - script/destroy
103
111
  - script/generate
112
+ - spec/lib/baldrick/command_spec.rb
113
+ - spec/lib/baldrick/listeners/feed_listener_spec.rb
114
+ - spec/lib/baldrick/listeners/feed_orders_spec.rb
115
+ - spec/lib/baldrick/listeners/injour_listener_spec.rb
116
+ - spec/lib/baldrick/listeners/xpath_locator_spec.rb
117
+ - spec/lib/baldrick/run_servant_spec.rb
118
+ - spec/lib/baldrick/servant_spec.rb
119
+ - spec/lib/baldrick/task_spec.rb
120
+ - spec/spec.opts
121
+ - spec/spec_helper.rb
104
122
  - tasks/rspec.rake
105
123
  has_rdoc: true
106
124
  homepage: http://github.com/brentsnook/baldrick
125
+ licenses: []
126
+
107
127
  post_install_message:
108
128
  rdoc_options:
109
129
  - --main
@@ -125,9 +145,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
145
  requirements: []
126
146
 
127
147
  rubyforge_project: baldrick
128
- rubygems_version: 1.3.1
148
+ rubygems_version: 1.3.5
129
149
  signing_key:
130
- specification_version: 2
131
- summary: Does what you tell it - glues orders to tasks.
150
+ specification_version: 3
151
+ summary: A dogsbody
132
152
  test_files: []
133
153