baldrick 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/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 0.0.1 2009-02-24
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
5
+ * mmmm...has that new gem smell
data/Manifest.txt ADDED
@@ -0,0 +1,18 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/baldrick.rb
6
+ lib/baldrick_serve.rb
7
+ lib/baldrick/command.rb
8
+ lib/baldrick/run_servant.rb
9
+ lib/baldrick/servant.rb
10
+ lib/baldrick/task.rb
11
+ lib/baldrick/listeners/feed_listener.rb
12
+ lib/baldrick/listeners/feed_orders.rb
13
+ lib/baldrick/listeners/injour_listener.rb
14
+ lib/baldrick/listeners/xpath_locator.rb
15
+ script/console
16
+ script/destroy
17
+ script/generate
18
+ tasks/rspec.rake
data/README.rdoc ADDED
@@ -0,0 +1,98 @@
1
+ = Baldrick
2
+
3
+ * http://github.com/brentsnook/baldrick
4
+
5
+ A dogsbody. Does what you tell it.
6
+
7
+ Baldrick recognises orders and then performs appropriate tasks.
8
+ 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
+
10
+ == Installation
11
+
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
+ Make Baldrick cower 'neath *your* boot:
15
+
16
+ sudo gem install baldrick
17
+
18
+ If you want to use the injour listener you will also need to install injour[http://github.com/arunthampi/injour]
19
+
20
+ === Building the Gem yourself
21
+
22
+ Grab the code from github:
23
+
24
+ git clone git://github.com/brentsnook/baldrick.git
25
+ cd baldrick
26
+ rake install_gem
27
+
28
+ == How does it work?
29
+
30
+ You tell Baldrick:
31
+
32
+ * how to perform tasks
33
+ * which tasks will be performed when a particular order is received
34
+ * where to listen for new orders
35
+
36
+ Baldrick keeps an ear out at the places you tell it to listen and then does its thing.
37
+
38
+ To tell Baldrick to print out a list of people in the twitterverse who desire a tasty beverage:
39
+
40
+ #cuppa.rb
41
+ require 'rubygems'
42
+ require 'baldrick_serve'
43
+
44
+ listen_to :feed, :at => 'http://search.twitter.com/search.atom?q=cup+of'
45
+
46
+ on_hearing /cup of (.*?)[\.,]/ do |beverage, order|
47
+ puts "#{order[:who]} would like a cup of #{beverage}"
48
+ end
49
+
50
+ Just run the script and the magic ruby fairies will take care of the rest:
51
+
52
+ ruby cuppa.rb
53
+
54
+ Dismiss Baldrick with <tt>CTRL C</tt>.
55
+
56
+ == How does it all work?
57
+
58
+ The regex describes the phrase that triggers the task, the contents of any capturing groups are passed to the body of your task along with the order.
59
+ The order contains *who*, *what*, *where* and *when*.
60
+
61
+ Check out http://wiki.github.com/brentsnook/baldrick for details.
62
+
63
+ == What is it good for?
64
+
65
+ Don't look at me, I just made it. How about these for suggestions?
66
+
67
+ * do cool stuff based on injour[http://github.com/arunthampi/injour] statuses
68
+ * do cool stuff based on web (RSS/Atom) feeds
69
+ * do cool stuff with an {Arduino board}[http://www.arduino.cc/]
70
+ * control a continuous integration build light
71
+ * control a nuclear power plant
72
+
73
+ I'd love to hear what you use it for.
74
+
75
+ == License
76
+
77
+ (The MIT License)
78
+
79
+ Copyright (c) 2009 Brent Snook http://fuglylogic.com
80
+
81
+ Permission is hereby granted, free of charge, to any person obtaining
82
+ a copy of this software and associated documentation files (the
83
+ 'Software'), to deal in the Software without restriction, including
84
+ without limitation the rights to use, copy, modify, merge, publish,
85
+ distribute, sublicense, and/or sell copies of the Software, and to
86
+ permit persons to whom the Software is furnished to do so, subject to
87
+ the following conditions:
88
+
89
+ The above copyright notice and this permission notice shall be
90
+ included in all copies or substantial portions of the Software.
91
+
92
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
93
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
94
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
95
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
96
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
97
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
98
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/baldrick'
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 = [
12
+ ['nokogiri','>= 1.1.1'],
13
+ ]
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"],
16
+ ['rspec', '>= 1.1.12'],
17
+ ['cucumber', '>= 0.1.16'],
18
+ ['injour', '>= 0.2.3'],
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'
25
+ end
26
+
27
+ require 'newgem/tasks' # load /tasks/*.rake
28
+ Dir['tasks/**/*.rake'].each { |t| load t }
29
+
30
+ task :default => [:spec, :features]
data/lib/baldrick.rb ADDED
@@ -0,0 +1,17 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Baldrick
5
+ VERSION = '0.0.1'
6
+ end
7
+
8
+ require 'baldrick/listeners/xpath_locator'
9
+ require 'baldrick/listeners/injour_listener'
10
+ require 'baldrick/listeners/feed_listener'
11
+ require 'baldrick/listeners/feed_orders'
12
+
13
+ require 'baldrick/servant'
14
+ require 'baldrick/task'
15
+
16
+ require 'baldrick/command'
17
+ require 'baldrick/run_servant'
@@ -0,0 +1,53 @@
1
+ require 'singleton'
2
+
3
+ module Baldrick
4
+ class Command
5
+ include Singleton
6
+
7
+ def initialize
8
+ @listener_classes = {:injour => Listeners::InjourListener, :feed => Listeners::FeedListener}
9
+ @servant = Servant.new
10
+ @wait_period = 2
11
+ @should_serve = true
12
+ end
13
+
14
+ def execute(stdout)
15
+ stdout << "Servant started...\n"; stdout.flush
16
+ while should_serve? do
17
+ @servant.serve
18
+ sleep @wait_period
19
+ end
20
+ end
21
+
22
+ def should_serve?
23
+ @should_serve
24
+ end
25
+
26
+ def stop!
27
+ @should_serve = false
28
+ end
29
+
30
+ def listen_every period
31
+ @wait_period = period
32
+ end
33
+
34
+ def listen_to listener_type, options={}
35
+ listener_class = listener_class_for(listener_type) || raise("No order listener implementation found for #{listener_type}")
36
+ @servant.add_listener(listener_class.new(options))
37
+ end
38
+
39
+ def on_hearing(matcher, &procedure)
40
+ @servant.add_task Task.new(matcher, procedure)
41
+ end
42
+ alias :to :on_hearing
43
+
44
+ def register_listener_type type, listener_class
45
+ @listener_classes[type] = listener_class
46
+ end
47
+
48
+ def listener_class_for type
49
+ @listener_classes[type]
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,25 @@
1
+ require 'open-uri'
2
+
3
+ module Baldrick::Listeners
4
+ class FeedListener
5
+
6
+ def initialize options
7
+ @url = options[:at]
8
+ # just don't go trying to consume feeds before 1970
9
+ @time_of_last_order = Time.at 0
10
+ end
11
+
12
+ def orders
13
+ content = open(@url){|f| f.read}
14
+ select_new_orders_from FeedOrders.within(content)
15
+ end
16
+
17
+ private
18
+
19
+ def select_new_orders_from orders
20
+ new_orders = orders.select {|order| order[:when] and order[:when] > @time_of_last_order}
21
+ @time_of_last_order = new_orders.collect {|order| order[:when]}.sort.last || @time_of_last_order
22
+ new_orders
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module Baldrick::Listeners
2
+
3
+ class FeedOrders
4
+
5
+ def self.within xml
6
+ XPathLocator.from_xml(xml).find_nodes_matching('//item', '//entry').collect {|item| order_from item}
7
+ end
8
+
9
+ private
10
+
11
+ def self.order_from item
12
+ locator = XPathLocator.from_node item
13
+ publish_time = locator.find_text_matching 'published/text()', 'pubDate/text()', 'date/text()', 'updated/text()'
14
+ {
15
+ :what => item.to_s,
16
+ :who => locator.find_text_matching('author/name/text()', 'author/text()'),
17
+ :when => publish_time ? Time.parse(publish_time) : nil,
18
+ :where => locator.find_text_matching('link/text()', "link[@rel='alternate']/@href")
19
+ }
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module Baldrick::Listeners
2
+ class InjourListener
3
+
4
+ def initialize options = {}
5
+ @last_orders = {}
6
+ end
7
+
8
+ def orders
9
+ order_pattern = /=== (.*?) on (.*?) ===\n\* \[(.*?)\] (.*?)\n/m
10
+ all_orders = `injour ls`.scan(order_pattern).collect do |match|
11
+ {
12
+ :who => match[0],
13
+ :where => match[1],
14
+ :when => Time.parse(match[2]),
15
+ :what => match[3]
16
+ }
17
+ end
18
+
19
+ select_new_orders_from all_orders
20
+ end
21
+
22
+ private
23
+
24
+ def select_new_orders_from all_orders
25
+ new_orders = all_orders.reject do |order|
26
+ who = order[:who]
27
+ previous_order = @last_orders[who]
28
+ @last_orders[who] = order
29
+ !previous_order.nil? and previous_order == order
30
+ end
31
+ new_orders
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ require 'nokogiri'
2
+
3
+ module Baldrick::Listeners
4
+
5
+ class XPathLocator
6
+
7
+ def self.from_xml xml
8
+ from_node Nokogiri::XML(strip_default_namespaces_from xml)
9
+ end
10
+
11
+ def self.from_node node
12
+ self.new node
13
+ end
14
+
15
+ def initialize node
16
+ @doc = node
17
+ end
18
+
19
+ def find_text_matching *paths
20
+ matches = find_nodes_matching *paths
21
+ matches.first.text unless matches.empty?
22
+ end
23
+
24
+ def find_nodes_matching *paths
25
+ @doc.search *paths || []
26
+ end
27
+
28
+ private
29
+
30
+ # need to do this so that default namespaced elements can be located
31
+ # http://nokogiri.lighthouseapp.com/projects/19607/tickets/8-nokogirixml-str-not-allow-an-xpath-result
32
+ # has to be a better way though
33
+ def self.strip_default_namespaces_from xml
34
+ xml.gsub /xmlns="[^"]*"/, ''
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ module Baldrick
2
+ module RunServant
3
+
4
+ def self.included clazz
5
+ # register shutdown hook ONLY when the module is included
6
+ at_exit do
7
+ raise $! if $!
8
+ Baldrick::Command.instance.execute STDOUT
9
+ end
10
+
11
+ trap(:INT) do
12
+ Baldrick::Command.instance.stop!
13
+ end
14
+ end
15
+
16
+ # send configuration messages to the command
17
+ def method_missing name, *args, &block
18
+ Baldrick::Command.instance.respond_to?(name) ? Baldrick::Command.instance.send(name, *args, &block) : super(name, args)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ module Baldrick
2
+
3
+ class Servant
4
+
5
+ def initialize
6
+ @tasks, @listeners = [], []
7
+ end
8
+
9
+ def add_task task
10
+ @tasks << task
11
+ end
12
+
13
+ def add_listener listener
14
+ @listeners << listener
15
+ end
16
+
17
+ def serve
18
+ orders.each {|order| follow order}
19
+ end
20
+
21
+ def follow order
22
+ @tasks.each{|task| task.run order}
23
+ end
24
+
25
+ private
26
+
27
+ def orders
28
+ @listeners.inject([]) do |all_orders, listener|
29
+ begin
30
+ all_orders + listener.orders
31
+ rescue Exception => exception
32
+ puts exception
33
+ all_orders
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,16 @@
1
+ module Baldrick
2
+ class Task
3
+ def initialize(matcher, procedure)
4
+ @matcher, @procedure = matcher, procedure
5
+ end
6
+
7
+ def run order
8
+ if matches = @matcher.match(order[:what])
9
+ all_arguments = matches[1..-1] << order
10
+ accepted_arguments = all_arguments[0..(@procedure.arity - 1)]
11
+ @procedure.call(*accepted_arguments)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ require File.dirname(__FILE__) + '/baldrick'
2
+
3
+ include Baldrick::RunServant
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/baldrick.rb'}"
9
+ puts "Loading baldrick gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/tasks/rspec.rake ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: baldrick
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brent Snook
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-24 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nokogiri
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: newgem
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.3
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.1.12
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: cucumber
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.1.16
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
+ - !ruby/object:Gem::Dependency
66
+ name: hoe
67
+ type: :development
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 1.8.0
74
+ version:
75
+ description: ""
76
+ email:
77
+ - brent@fuglylogic.com
78
+ executables: []
79
+
80
+ extensions: []
81
+
82
+ extra_rdoc_files:
83
+ - History.txt
84
+ - Manifest.txt
85
+ - README.rdoc
86
+ files:
87
+ - History.txt
88
+ - Manifest.txt
89
+ - README.rdoc
90
+ - Rakefile
91
+ - lib/baldrick.rb
92
+ - lib/baldrick_serve.rb
93
+ - lib/baldrick/command.rb
94
+ - lib/baldrick/run_servant.rb
95
+ - lib/baldrick/servant.rb
96
+ - lib/baldrick/task.rb
97
+ - lib/baldrick/listeners/feed_listener.rb
98
+ - lib/baldrick/listeners/feed_orders.rb
99
+ - lib/baldrick/listeners/injour_listener.rb
100
+ - lib/baldrick/listeners/xpath_locator.rb
101
+ - script/console
102
+ - script/destroy
103
+ - script/generate
104
+ - tasks/rspec.rake
105
+ has_rdoc: true
106
+ homepage: http://github.com/brentsnook/baldrick
107
+ post_install_message:
108
+ rdoc_options:
109
+ - --main
110
+ - README.rdoc
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: "0"
118
+ version:
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: "0"
124
+ version:
125
+ requirements: []
126
+
127
+ rubyforge_project: baldrick
128
+ rubygems_version: 1.3.1
129
+ signing_key:
130
+ specification_version: 2
131
+ summary: Does what you tell it - glues orders to tasks.
132
+ test_files: []
133
+