baldrick 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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