summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog2
-rw-r--r--MIT-LICENSE18
-rw-r--r--README.rdoc26
-rw-r--r--Rakefile56
-rw-r--r--TODO7
-rw-r--r--lib/evendoors.rb37
-rw-r--r--lib/evendoors/door.rb54
-rw-r--r--lib/evendoors/link.rb31
-rw-r--r--lib/evendoors/particle.rb138
-rw-r--r--lib/evendoors/room.rb123
-rw-r--r--lib/evendoors/space.rb24
-rw-r--r--lib/evendoors/spot.rb28
-rw-r--r--lib/evendoors/twirl.rb59
-rw-r--r--spec/evendoors_spec.rb12
-rw-r--r--tasks/ann.rake83
-rw-r--r--tasks/constants.rb118
-rw-r--r--tasks/gem.rake196
-rw-r--r--tasks/git.rake38
-rw-r--r--tasks/helpers.rb130
-rw-r--r--tasks/notes.rake27
-rw-r--r--tasks/post_load.rake35
-rw-r--r--tasks/rdoc.rake46
-rw-r--r--tasks/rubyforge.rake54
-rw-r--r--tasks/setup.rb129
-rw-r--r--tasks/spec.rake44
-rw-r--r--tasks/svn.rake48
-rw-r--r--tasks/test.rake41
-rw-r--r--test/test_evendoors.rb78
28 files changed, 1682 insertions, 0 deletions
diff --git a/Changelog b/Changelog
new file mode 100644
index 0000000..35c53aa
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,2 @@
+2012-05-01 Jérémy Zurcher <jeremy@asynk.ch>
+ * Project creation
diff --git a/MIT-LICENSE b/MIT-LICENSE
new file mode 100644
index 0000000..11b4943
--- /dev/null
+++ b/MIT-LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2012-2013 Jéérmy Zurcher
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.rdoc b/README.rdoc
new file mode 100644
index 0000000..ba5bab3
--- /dev/null
+++ b/README.rdoc
@@ -0,0 +1,26 @@
+evendoors
+ by Jérémy Zurcher
+ http://asynk.ch
+
+== DESCRIPTION:
+
+* a ruby port of evenja C++ application framework
+* see http://www.revena.com/evenja/telecharger-evenja
+
+== FEATURES/PROBLEMS:
+
+* experimental material, we'll see where it leads
+
+== SYNOPSIS:
+
+# hum hum, well ...
+
+== CREDITS:
+
+Special thanks to:
+
+* Fabian Padilla
+
+== LICENSE:
+
+See LICENSE file.
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..3a04dbc
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,56 @@
+# -*- coding: UTF-8 -*-
+#
+load './tasks/setup.rb'
+#
+# Project general information
+PROJ.name = 'evendoors-ruby'
+PROJ.authors = 'Jérémy Zurcher'
+PROJ.email = 'jeremy@asynk.ch'
+PROJ.url = 'https://github.com/jeremyz/evendoors-ruby'
+PROJ.version = '0.0.1'
+PROJ.rubyforge.name = 'FIXME'
+PROJ.readme_file = 'README.rdoc'
+#
+# Annoucement
+PROJ.ann.paragraphs << 'FEATURES' << 'SYNOPSIS' << 'REQUIREMENTS' << 'DOWNLOAD/INSTALL' << 'CREDITS' << 'LICENSE'
+PROJ.ann.email[:from] = PROJ.email
+PROJ.ann.email[:to] = ['FIXME']
+PROJ.ann.email[:server] = 'FIXME'
+PROJ.ann.email[:tls] = false
+# Gem specifications
+PROJ.gem.need_tar = false
+PROJ.gem.files = %w(Changelog MIT-LICENSE README.rdoc Rakefile) + Dir.glob("{ext,lib,spec,tasks}/**/*[^~]").reject { |fn| test ?d, fn }
+PROJ.gem.platform = Gem::Platform::RUBY
+PROJ.gem.required_ruby_version = ">= 1.9.2"
+#
+# Override Mr. Bones autogenerated extensions and force ours in
+#PROJ.gem.extras['extensions'] = %w(ext/extconf.rb)
+#PROJ.gem.extras['required_ruby_version'] = ">= 1.9.2"
+#
+# RDoc
+PROJ.rdoc.exclude << '^ext\/'
+PROJ.rdoc.opts << '-x' << 'ext'
+#
+# Ruby
+PROJ.ruby_opts = []
+PROJ.ruby_opts << '-I' << 'lib'
+#
+# RSpec
+PROJ.spec.files.exclude /rbx/
+PROJ.spec.opts << '--color'
+#
+# Rcov
+PROJ.rcov.opts << '-I lib'
+#
+# Dependencies
+depend_on 'rake', '>=0.8.7'
+#
+task :default => [:spec]
+#
+desc "Build all packages"
+task :package => 'gem:package'
+#
+desc "Install the gem locally"
+task :install => 'gem:install'
+#
+# EOF
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..448c18b
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+
+ - Board
+ - cache routing
+ - JSON data from and to file
+ - Twirl.suspend
+ - Twirl.resume
+
diff --git a/lib/evendoors.rb b/lib/evendoors.rb
new file mode 100644
index 0000000..16504b6
--- /dev/null
+++ b/lib/evendoors.rb
@@ -0,0 +1,37 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+#
+module EvenDoors
+ #
+ PATH_SEP = '/'.freeze
+ LINK_SEP = ','.freeze
+ ACT_SEP = '?'.freeze
+ ACT_GET = 'get'.freeze
+ ACT_ADD_LINK = 'add_link'.freeze
+ ACT_ERROR = 'error'.freeze
+ #
+ LNK_SRC = 'edoors_lnk_src'.freeze
+ LNK_DSTS = 'edoors_lnk_dsts'.freeze
+ LNK_FIELDS = 'edoors_lnk_fields'.freeze
+ LNK_CONDF = 'edoors_lnk_condf'.freeze
+ LNK_CONDV = 'edoors_lnk_condv'.freeze
+ #
+ ERROR_FIELD = 'edoors_error'.freeze
+ ERROR_ROUTE_RRWD = 'routing error: right room, wrong door'.freeze
+ ERROR_ROUTE_TRWR = 'routing error: top room, wrong room'.freeze
+ ERROR_ROUTE_NDNL = 'routing error: no destination, no link'.freeze
+ ERROR_ROUTE_SND = 'routing error: system no destination'.freeze
+ #
+ class Exception < ::Exception; end
+ #
+end
+#
+require 'evendoors/particle'
+require 'evendoors/spot'
+require 'evendoors/twirl'
+require 'evendoors/room'
+require 'evendoors/space'
+require 'evendoors/door'
+require 'evendoors/link'
+#
+# EOF
diff --git a/lib/evendoors/door.rb b/lib/evendoors/door.rb
new file mode 100644
index 0000000..9807d46
--- /dev/null
+++ b/lib/evendoors/door.rb
@@ -0,0 +1,54 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+#
+module EvenDoors
+ #
+ class Door < Spot
+ #
+ def initialize n, p=nil
+ super n, p
+ @saved = nil
+ @parent.add_spot self if @parent
+ end
+ #
+ def require_p p_kls
+ p = EvenDoors::Twirl.require_p p_kls
+ p.src = self
+ p
+ end
+ #
+ def release_p p
+ @saved=nil if @saved==p # particle is released, all is good
+ EvenDoors::Twirl.release_p p
+ end
+ #
+ def process p
+ @viewer.receive p if @viewer
+ @saved = p
+ receive p
+ if not @saved.nil?
+ puts "application didn't give that particle back #{p}" if EvenDoors::Twirl.debug
+ puts "\t#{p.data EvenDoors::ERROR_FIELD}" if p.action==EvenDoors::ACT_ERROR
+ release_p @saved
+ @saved = nil
+ end
+ end
+ #
+ def send_p p
+ p.src = self
+ @saved=nil if @saved==p # particle is sent back the data, all is good
+ @parent.send_p p # daddy will know what to do
+ end
+ #
+ def send_sys_p p
+ p.src = self
+ @saved=nil if @saved==p # particle is sent back the data, all is good
+ @parent.send_sys_p p # daddy will know what to do
+ end
+ #
+ end
+ #
+end
+#
+# EOF
diff --git a/lib/evendoors/link.rb b/lib/evendoors/link.rb
new file mode 100644
index 0000000..b2a9bba
--- /dev/null
+++ b/lib/evendoors/link.rb
@@ -0,0 +1,31 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+#
+module EvenDoors
+ #
+ class Link
+ #
+ def initialize src, dsts, fields, cond_fields, cond_value
+ @src = src # link source name
+ @dsts = dsts # , separated destinations to apply to the particle on linking success
+ @fields = fields # , separated fields to apply to the particle on linking success
+ @cond_fields = cond_fields # , separated fields used to generate the link value with particle payload
+ @cond_value = cond_value # value which will be compared to the particle link value to link or not
+ @door = nil # pointer to the source
+ end
+ #
+ def self.from_particle_data p
+ EvenDoors::Link.new(p.get_data(EvenDoors::LNK_SRC), p.get_data(EvenDoors::LNK_DSTS),
+ p.get_data(EvenDoors::LNK_FIELDS), p.get_data(EvenDoors::LNK_CONDF),
+ p.get_data(EvenDoors::LNK_CONDV))
+ end
+ #
+ attr_accessor :door
+ attr_reader :src, :dsts, :fields, :cond_fields, :cond_value
+ #
+ end
+ #
+end
+#
+# EOF
diff --git a/lib/evendoors/particle.rb b/lib/evendoors/particle.rb
new file mode 100644
index 0000000..7b951f9
--- /dev/null
+++ b/lib/evendoors/particle.rb
@@ -0,0 +1,138 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+#
+module EvenDoors
+ #
+ class Particle
+ #
+ def initialize
+ reset!
+ end
+ #
+ def reset!
+ @ts = Time.now # creation time
+ @src = nil # Spot.path where it's originated from
+ @room = nil
+ @door = nil # Door where it's currently heading to
+ @action = nil # action to perform on the Door
+ @dsts = [] # fifo of Spot.path where to travel to
+ @link_fields = [] # the fields used to generate the link value
+ @link_value = nil # the value computed with the link_fields values extracted from the payload
+ # used for pearing in Door and linking in routing process
+ @payload = {} # the actual data carried by this particle
+ @merged = [] # list of merged particles
+ end
+ #
+ attr_accessor :src
+ attr_reader :ts, :room, :door, :action, :link_value
+ #
+ # routing
+ #
+ def dst
+ @dsts[0]
+ end
+ #
+ def split_dst!
+ p, @action = @dsts[0].split EvenDoors::ACT_SEP
+ i = p.rindex EvenDoors::PATH_SEP
+ if i.nil?
+ @room = nil
+ door_name = p
+ else
+ @room = p[0..i-1]
+ door_name = p[i+1..-1]
+ end
+ door_name
+ end
+ #
+ def dst_done! door
+ @dsts.shift
+ @door = door
+ end
+ #
+ def error! e
+ @action = EvenDoors::ACT_ERROR
+ @door = @src
+ @payload[EvenDoors::ERROR_FIELD]=e
+ end
+ #
+ def clear_dsts!
+ @dsts.clear
+ end
+ #
+ def add_dsts paths
+ paths.split(EvenDoors::LINK_SEP).each do |path|
+ @dsts << path
+ end
+ end
+ #
+ def set_dst a, l=nil
+ @room = nil
+ @door = nil
+ @action = nil
+ clear_dsts!
+ @dsts << ( l ? l.to_str : '' )+EvenDoors::ACT_SEP+a.to_str
+ end
+ #
+ # data manipulation
+ #
+ def set_data k, v
+ @payload[k] = v
+ compute_link_value! if @link_fields.include? k
+ end
+ #
+ def get_data k
+ @payload[k]
+ end
+ alias :data :get_data
+ #
+ def data k
+ @payload[k]
+ end
+ #
+ def clone_data p
+ @payload = p.payload.clone
+ end
+ #
+ # link value and fields
+ #
+ def clear_link_fields!
+ @link_fields.clear
+ compute_link_value!
+ end
+ #
+ def set_link_fields *args
+ @link_fields.clear if not @link_fields.empty?
+ args.compact!
+ args.each do |lfs|
+ lfs.split(',').each do |lf|
+ @link_fields << lf
+ end
+ end
+ compute_link_value!
+ end
+ #
+ def compute_link_value!
+ @link_value = @link_fields.inject('') { |s,lf| s+=@payload[lf].to_s if @payload[lf]; s }
+ end
+ #
+ # merge particles management
+ #
+ def merged_count
+ @merged.length
+ end
+ #
+ def merge p
+ @merged << p
+ end
+ #
+ def merged i
+ @merged[i]
+ end
+ #
+ end
+ #
+end
+#
+# EOF
diff --git a/lib/evendoors/room.rb b/lib/evendoors/room.rb
new file mode 100644
index 0000000..671b529
--- /dev/null
+++ b/lib/evendoors/room.rb
@@ -0,0 +1,123 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+#
+module EvenDoors
+ #
+ class Room < Spot
+ #
+ def initialize n, p=nil
+ super n, p
+ @spots = {}
+ @links = {}
+ @cache = {}
+ @parent.add_spot self if @parent
+ end
+ #
+ def add_spot s
+ raise EvenDoors::Exception.new "Spot #{s.name} already has #{s.parent.name} as parent" if not s.parent.nil? and s.parent!=self
+ raise EvenDoors::Exception.new "Spot #{s.name} already exists in #{path}" if @spots.has_key? s.name
+ s.parent = self if s.parent.nil?
+ @spots[s.name]=s
+ end
+ #
+ def add_link l
+ l.door = @spots[l.src]
+ raise EvenDoors::Exception.new "Link source #{l.src} does not exist in #{path}" if l.door.nil?
+ (@links[l.src] ||= [])<< l
+ end
+ #
+ def start!
+ puts " * start #{path}" if EvenDoors::Twirl.debug
+ @spots.values.each do |spot| spot.start! if spot.respond_to? :start! end
+ end
+ #
+ def stop!
+ puts " * stop #{path}" if EvenDoors::Twirl.debug
+ @spots.values.each do |spot| spot.stop! if spot.respond_to? :stop! end
+ end
+ #
+ def try_links p
+ pending_link = nil
+ apply_link = false
+ links = @links[p.src.name]
+ return false if links.nil?
+ links.each do |link|
+ apply_link = link.cond_fields.nil? # unconditional link
+ p.set_link_fields link.cond_fields if not apply_link
+ if apply_link or (p.link_value==link.cond_value)
+ # link matches !
+ if not pending_link.nil?
+ p2 = require_p p.class
+ p2.clone_data p
+ p2.src = link.door
+ p2.clear_dsts!
+ p2.add_dsts link.dsts
+ p2.set_link_fields link.fields
+ send_p p2
+ end
+ pending_link = link
+ end
+ end
+ if pending_link
+ p.src = pending_link.door
+ p.clear_dsts!
+ p.add_dsts pending_link.dsts
+ p.set_link_fields pending_link.fields
+ send_p p
+ end
+ (not pending_link.nil?)
+ end
+ #
+ def route_p p, door_name
+ if p.room.nil? or p.room==path
+ if door = @spots[door_name]
+ p.dst_done! door
+ else
+ p.error! EvenDoors::ERROR_ROUTE_RRWD
+ end
+ elsif @parent
+ @parent.route_p p, door_name
+ else
+ p.error! EvenDoors::ERROR_ROUTE_TRWR
+ end
+ end
+ #
+ def send_p p
+ if d = p.dst
+ puts " * send #{d.to_str} ..." if EvenDoors::Twirl.debug
+ route_p p, p.split_dst!
+ puts " -> #{p.door.path}:#{p.action}" if EvenDoors::Twirl.debug
+ EvenDoors::Twirl.send_p p
+ elsif not try_links p
+ p.error! EvenDoors::ERROR_ROUTE_NDNL
+ puts " -> #{p.door.path}:#{p.action}" if EvenDoors::Twirl.debug
+ EvenDoors::Twirl.send_p p
+ end
+ end
+ #
+ def send_sys_p p
+ if d = p.dst
+ puts " * send_sys #{d.to_str} ..." if EvenDoors::Twirl.debug
+ route_p p, p.split_dst!
+ puts " -> #{p.door.path}:#{p.action}" if EvenDoors::Twirl.debug
+ EvenDoors::Twirl.send_sys_p p
+ else
+ p.error! EvenDoors::ERROR_ROUTE_SND
+ puts " -> #{p.door.path}:#{p.action}" if EvenDoors::Twirl.debug
+ EvenDoors::Twirl.send_sys_p p
+ end
+ end
+ #
+ def process_sys p
+ if p.action==ACT_ADD_LINK
+ add_link EvenDoors::Link.from_particle_data p
+ end
+ EvenDoors::Twirl.release_p p
+ end
+ #
+ end
+ #
+end
+#
+# EOF
diff --git a/lib/evendoors/space.rb b/lib/evendoors/space.rb
new file mode 100644
index 0000000..6579003
--- /dev/null
+++ b/lib/evendoors/space.rb
@@ -0,0 +1,24 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+#
+module EvenDoors
+ #
+ class Space < Room
+ #
+ def initialize n, args={}
+ super n, nil
+ EvenDoors::Twirl.debug = args[:debug] || false
+ end
+ #
+ def twirl!
+ @spots.values.each do |spot| spot.start! end
+ EvenDoors::Twirl.twirl!
+ @spots.values.each do |spot| spot.stop! end
+ end
+ #
+ end
+ #
+end
+#
+# EOF
diff --git a/lib/evendoors/spot.rb b/lib/evendoors/spot.rb
new file mode 100644
index 0000000..e88bac7
--- /dev/null
+++ b/lib/evendoors/spot.rb
@@ -0,0 +1,28 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+#
+module EvenDoors
+ #
+ class Spot
+ #
+ def initialize n, p
+ @name = n # unique in it's room
+ @parent = p # single direct parent
+ @viewer = nil # particle going through that position will be sent there readonly
+ end
+ #
+ attr_reader :name
+ attr_accessor :viewer, :parent
+ #
+ def path
+ return @path if @path
+ p = ( @parent ? @parent.path+'/' : '') + name
+ @path = p.sub(/^\/+/,'').gsub(/\/{2,}/,'/').sub(/\/+$/,'')
+ end
+ #
+ end
+ #
+end
+#
+# EOF
diff --git a/lib/evendoors/twirl.rb b/lib/evendoors/twirl.rb
new file mode 100644
index 0000000..30ef169
--- /dev/null
+++ b/lib/evendoors/twirl.rb
@@ -0,0 +1,59 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+#
+module EvenDoors
+ #
+ class Twirl
+ #
+ @debug = false
+ @pool = {} # per particle class free list
+ @sys_fifo = [] # system particles fifo list
+ @app_fifo = [] # application particles fifo list
+ #
+ #
+ class << self
+ #
+ attr_accessor :debug
+ #
+ def release_p p
+ ( @pool[p.class] ||= [] ) << p
+ end
+ #
+ def require_p p_kls
+ l = @pool[p_kls]
+ return p_kls.new if l.nil?
+ p = l.pop
+ return p_kls.new if p.nil?
+ p.reset!
+ p
+ end
+ #
+ def send_p p
+ @app_fifo << p
+ end
+ #
+ def send_sys_p p
+ @sys_fifo << p
+ end
+ #
+ def twirl!
+ while @sys_fifo.length>0 or @app_fifo.length>0
+ while @sys_fifo.length>0
+ p = @sys_fifo.shift
+ p.door.process_sys p
+ end
+ while @app_fifo.length>0
+ p = @app_fifo.shift
+ p.door.process p
+ end
+ end
+ end
+ #
+ end
+ #
+ end
+ #
+end
+#
+# EOF
diff --git a/spec/evendoors_spec.rb b/spec/evendoors_spec.rb
new file mode 100644
index 0000000..e25e425
--- /dev/null
+++ b/spec/evendoors_spec.rb
@@ -0,0 +1,12 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+#
+require 'evendoors'
+#
+describe EvenDoors do
+ #
+ it "EvenDoors module should exxists" do
+ expect{ EvenDoors }.not_to raise_error(NameError)
+ end
+ #
+end
diff --git a/tasks/ann.rake b/tasks/ann.rake
new file mode 100644
index 0000000..4515ccb
--- /dev/null
+++ b/tasks/ann.rake
@@ -0,0 +1,83 @@
+# -*- coding: UTF-8 -*-
+#
+begin
+ require 'bones/smtp_tls'
+rescue LoadError
+ require 'net/smtp'
+end
+require 'time'
+
+namespace :ann do
+
+ # A prerequisites task that all other tasks depend upon
+ task :prereqs
+ file PROJ.ann.file do
+ ann = PROJ.ann
+ puts "Generating #{ann.file}"
+ File.open(ann.file,'w') do |fd|
+ fd.puts("#{PROJ.name} version #{PROJ.version}")
+ fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
+ fd.puts(" #{PROJ.url}") if PROJ.url.valid?
+ fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
+ fd.puts
+ fd.puts("== DESCRIPTION")
+ fd.puts
+ fd.puts(PROJ.description)
+ fd.puts
+ fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
+ fd.puts
+ ann.paragraphs.each do |p|
+ fd.puts "== #{p.upcase}"
+ fd.puts
+ fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
+ fd.puts
+ end
+ fd.puts ann.text if ann.text
+ end
+ end
+
+ desc "Create an announcement file"
+ task :announcement => ['ann:prereqs', PROJ.ann.file]
+
+ desc "Send an email announcement"
+ task :email => ['ann:prereqs', PROJ.ann.file] do
+ ann = PROJ.ann
+ from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
+ to = Array(ann.email[:to])
+
+ ### build a mail header for RFC 822
+ rfc822msg = "From: #{from}\n"
+ rfc822msg << "To: #{to.join(',')}\n"
+ rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
+ rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
+ rfc822msg << "\n"
+ rfc822msg << "Date: #{Time.new.rfc822}\n"
+ rfc822msg << "Message-Id: "
+ rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
+ rfc822msg << File.read(ann.file)
+
+ params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
+ ann.email[key]
+ end
+
+ params[3] = (PROJ.ann.email[:from] || PROJ.email) if params[3].nil?
+
+# if ann.email[:tls] and params[4].nil?
+ if params[4].nil?
+ STDOUT.write "Please enter your e-mail password (#{params[3]}): "
+ params[4] = STDIN.gets.chomp
+ end
+# params = params.shift 2 if not ann.email[:tls]
+
+ ### send email
+ # TODO find a way to bypass /var/lib/gems/1.9/gems/bones-3.6.5/lib/bones/smtp_tls.rb which forces starttls usage
+ Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
+ end
+end # namespace :ann
+
+desc 'Alias to ann:announcement'
+task :ann => 'ann:announcement'
+
+CLOBBER << PROJ.ann.file
+
+# EOF
diff --git a/tasks/constants.rb b/tasks/constants.rb
new file mode 100644
index 0000000..8a7fdfa
--- /dev/null
+++ b/tasks/constants.rb
@@ -0,0 +1,118 @@
+# -*- coding: UTF-8 -*-
+#
+require 'rbconfig'
+
+# Setup some constants
+WIN32 = %r/djgpp|(cyg|ms|bcc)win|mingw/ =~ RUBY_PLATFORM unless defined? WIN32
+
+DEV_NULL = WIN32 ? 'NUL:' : '/dev/null'
+
+def quiet( &block )
+ io = [STDOUT.dup, STDERR.dup]
+ STDOUT.reopen DEV_NULL
+ STDERR.reopen DEV_NULL
+ block.call
+ensure
+ STDOUT.reopen io.first
+ STDERR.reopen io.last
+ $stdout, $stderr = STDOUT, STDERR
+end
+
+BUILD_DIR = "build"
+
+USE_RAKE_COMPILER = ( ( (RUBY_PLATFORM =~ /java/) ? false : true ) and test ?d, 'ext' )
+if USE_RAKE_COMPILER
+ gem 'rake-compiler', '>=0.6.0'
+ require 'rake/extensiontask'
+ ENV['RUBY_CC_VERSION'] = '1.8.7:1.9.2'
+end
+
+LIBEXT = case RbConfig::CONFIG['host_os'].downcase
+ when /darwin/
+ "dylib"
+ when /mswin|mingw/
+ "dll"
+ else
+ RbConfig::CONFIG['DLEXT']
+ end
+
+CPU = case RbConfig::CONFIG['host_cpu'].downcase
+ when /i[3456]86/
+ # Darwin always reports i686, even when running in 64bit mode
+ if RbConfig::CONFIG['host_os'] =~ /darwin/ && 0xfee1deadbeef.is_a?(Fixnum)
+ "x86_64"
+ else
+ "i386"
+ end
+ when /amd64|x86_64/
+ "x86_64"
+ when /ppc64|powerpc64/
+ "powerpc64"
+ when /ppc|powerpc/
+ "powerpc"
+ else
+ RbConfig::CONFIG['host_cpu']
+ end
+
+OS = case RbConfig::CONFIG['host_os'].downcase
+ when /linux/
+ "linux"
+ when /darwin/
+ "darwin"
+ when /freebsd/
+ "freebsd"
+ when /openbsd/
+ "openbsd"
+ when /sunos|solaris/
+ "solaris"
+ when /mswin|mingw/
+ "win32"
+ else
+ RbConfig::CONFIG['host_os'].downcase
+ end
+
+CC=ENV['CC'] || RbConfig::CONFIG['CC'] || "gcc"
+
+GMAKE = RbConfig::CONFIG['host_os'].downcase =~ /bsd|solaris/ ? "gmake" : "make"
+
+
+DIFF = if WIN32 then 'diff.exe'
+ else
+ if quiet {system "gdiff", __FILE__, __FILE__} then 'gdiff'
+ else 'diff' end
+ end unless defined? DIFF
+
+SUDO = if WIN32 then ''
+ else
+ if quiet {system 'which sudo'} then 'sudo'
+ else '' end
+ end
+
+RCOV = WIN32 ? 'rcov.bat' : 'rcov'
+RDOC = WIN32 ? 'rdoc.bat' : 'rdoc'
+GEM = WIN32 ? 'gem.bat' : 'gem'
+
+%w(rcov spec/rake/spectask rubyforge bones facets/ansicode).each do |lib|
+ begin
+ require lib
+ Object.instance_eval {const_set "HAVE_#{lib.tr('/','_').upcase}", true}
+ rescue LoadError
+ Object.instance_eval {const_set "HAVE_#{lib.tr('/','_').upcase}", false}
+ end
+end
+
+HAVE_SVN = (Dir.entries(Dir.pwd).include?('.svn') and system("svn --version 2>&1 > #{DEV_NULL}"))
+HAVE_GIT = (Dir.entries(Dir.pwd).include?('.git') and system("git --version 2>&1 > #{DEV_NULL}"))
+
+# Add rake as a development dependency
+#
+PROJ.gem.development_dependencies << ['rake', '>=0.8.7']
+
+# Add bones as a development dependency
+#
+if HAVE_BONES
+ bones_version = defined?(Bones::VERSION) ? Bones::VERSION : Bones.version
+ PROJ.gem.development_dependencies << ['bones', ">= #{bones_version}"]
+end
+
+# EOF
diff --git a/tasks/gem.rake b/tasks/gem.rake
new file mode 100644
index 0000000..e5e5d13
--- /dev/null
+++ b/tasks/gem.rake
@@ -0,0 +1,196 @@
+# -*- coding: UTF-8 -*-
+#
+require 'find'
+require 'rake/packagetask'
+require 'rubygems/user_interaction'
+require 'rubygems/builder'
+
+module Bones
+class GemPackageTask < Rake::PackageTask
+ # Ruby GEM spec containing the metadata for this package. The
+ # name, version and package_files are automatically determined
+ # from the GEM spec and don't need to be explicitly provided.
+ #
+ attr_accessor :gem_spec
+
+ # Tasks from the Bones gem directory
+ attr_reader :bones_files
+
+ # Create a GEM Package task library. Automatically define the gem
+ # if a block is given. If no block is supplied, then +define+
+ # needs to be called to define the task.
+ #
+ def initialize(gem_spec)
+ init(gem_spec)
+ yield self if block_given?
+ define if block_given?
+ end
+
+ # Initialization tasks without the "yield self" or define
+ # operations.
+ #
+ def init(gem)
+ super(gem.name, gem.version)
+ @gem_spec = gem
+ @package_files += gem_spec.files if gem_spec.files
+ @bones_files = []
+
+ local_setup = File.join(Dir.pwd, %w[tasks setup.rb])
+ if !test(?e, local_setup)
+ Dir.glob(::Bones.path(%w[lib bones tasks *])).each {|fn| bones_files << fn}
+ end
+ end
+
+ # Create the Rake tasks and actions specified by this
+ # GemPackageTask. (+define+ is automatically called if a block is
+ # given to +new+).
+ #
+ def define
+ super
+ task :prereqs
+ task :package => ['gem:prereqs', "#{package_dir_path}/#{gem_file}"]
+ file "#{package_dir_path}/#{gem_file}" => [package_dir_path] + package_files + bones_files do
+ when_writing("Creating GEM") {
+ chdir(package_dir_path) do
+ Gem::Builder.new(gem_spec).build
+ verbose(true) { mv gem_file, "../#{gem_file}" }
+ end
+ }
+ end
+
+ file package_dir_path => bones_files do
+ mkdir_p package_dir rescue nil
+
+ gem_spec.files = (gem_spec.files + bones_files.map {|fn| File.join('tasks', File.basename(fn))}).sort
+
+ bones_files.each do |fn|
+ base_fn = File.join('tasks', File.basename(fn))
+ f = File.join(package_dir_path, base_fn)
+ fdir = File.dirname(f)
+ mkdir_p(fdir) if !File.exist?(fdir)
+ if File.directory?(fn)
+ mkdir_p(f)
+ else
+ raise "file name conflict for '#{base_fn}' (conflicts with '#{fn}')" if test(?e, f)
+ safe_ln(fn, f)
+ end
+ end
+ end
+ end
+
+ def gem_file
+ if @gem_spec.platform == Gem::Platform::RUBY
+ "#{package_name}.gem"
+ else
+ "#{package_name}-#{@gem_spec.platform}.gem"
+ end
+ end
+end # class GemPackageTask
+end # module Bones
+
+namespace :gem do
+
+ PROJ.gem._spec = Gem::Specification.new do |s|
+ s.name = PROJ.name
+ s.version = PROJ.version
+ s.summary = PROJ.summary
+ s.authors = Array(PROJ.authors)
+ s.email = PROJ.email
+ s.homepage = Array(PROJ.url).first
+ s.rubyforge_project = PROJ.rubyforge.name
+
+ s.description = PROJ.description
+
+ PROJ.gem.dependencies.each do |dep|
+ s.add_dependency(*dep)
+ end
+
+ PROJ.gem.development_dependencies.each do |dep|
+ s.add_development_dependency(*dep)
+ end
+
+ s.files = PROJ.gem.files
+ s.executables = PROJ.gem.executables.map {|fn| File.basename(fn)}
+ s.extensions = PROJ.gem.files.grep %r/extconf\.rb$/
+
+ s.bindir = 'bin'
+ dirs = Dir["{#{PROJ.libs.join(',')}}"]
+ s.require_paths = dirs unless dirs.empty?
+
+ incl = Regexp.new(PROJ.rdoc.include.join('|'))
+ excl = PROJ.rdoc.exclude.dup.concat %w[\.rb$ ^(\.\/|\/)?ext]
+ excl = Regexp.new(excl.join('|'))
+ rdoc_files = PROJ.gem.files.find_all do |fn|
+ case fn
+ when excl; false
+ when incl; true
+ else false end
+ end
+ s.rdoc_options = PROJ.rdoc.opts + ['--main', PROJ.rdoc.main]
+ s.extra_rdoc_files = rdoc_files
+
+ if test ?f, PROJ.test.file
+ s.test_file = PROJ.test.file
+ else
+ s.test_files = PROJ.test.files.to_a
+ end
+
+ # Do any extra stuff the user wants
+ PROJ.gem.extras.each do |msg, val|
+ case val
+ when Proc
+ val.call(s.send(msg))
+ else
+ s.send "#{msg}=", val
+ end
+ end
+ end # Gem::Specification.new
+
+ Bones::GemPackageTask.new(PROJ.gem._spec) do |pkg|
+ pkg.need_tar = PROJ.gem.need_tar
+ pkg.need_zip = PROJ.gem.need_zip
+ end
+
+ desc 'Show information about the gem'
+ task :debug => 'gem:prereqs' do
+ puts PROJ.gem._spec.to_ruby
+ end
+
+ desc 'Write the gemspec '
+ task :spec => 'gem:prereqs' do
+ File.open("#{PROJ.name}.gemspec", 'w') do |f|
+ f.write PROJ.gem._spec.to_ruby
+ end
+ end
+
+ desc 'Install the gem'
+ task :install => [:clobber, 'gem:package'] do
+ sh "#{SUDO} #{GEM} install --local pkg/#{PROJ.gem._spec.full_name}"
+ # use this version of the command for rubygems > 1.0.0
+ #sh "#{SUDO} #{GEM} install --no-update-sources pkg/#{PROJ.gem._spec.full_name}"
+ end
+
+ desc 'Uninstall the gem'
+ task :uninstall do
+ installed_list = Gem.source_index.find_name(PROJ.name)
+ if installed_list and installed_list.collect { |s| s.version.to_s}.include?(PROJ.version) then
+ sh "#{SUDO} #{GEM} uninstall --version '#{PROJ.version}' --ignore-dependencies --executables #{PROJ.name}"
+ end
+ end
+
+ desc 'Reinstall the gem'
+ task :reinstall => [:uninstall, :install]
+
+ desc 'Cleanup the gem'
+ task :cleanup do
+ sh "#{SUDO} #{GEM} cleanup #{PROJ.gem._spec.name}"
+ end
+end # namespace :gem
+
+desc 'Alias to gem:package'
+task :gem => 'gem:package'
+
+task :clobber => 'gem:clobber_package'
+remove_desc_for_task 'gem:clobber_package'
+
+# EOF
diff --git a/tasks/git.rake b/tasks/git.rake
new file mode 100644
index 0000000..b219923
--- /dev/null
+++ b/tasks/git.rake
@@ -0,0 +1,38 @@
+# -*- coding: UTF-8 -*-
+#
+if HAVE_GIT
+
+namespace :git do
+
+ # A prerequisites task that all other tasks depend upon
+ task :prereqs
+
+ desc 'Show tags from the Git repository'
+ task :show_tags => 'git:prereqs' do |t|
+ puts %x/git tag/
+ end
+
+ desc 'Create a new tag in the Git repository'
+ task :create_tag => 'git:prereqs' do |t|
+ v = ENV['VERSION'] or abort 'Must supply VERSION=x.y.z'
+ abort "Versions don't match #{v} vs #{PROJ.version}" if v != PROJ.version
+ tag = "%s" % [ PROJ.version ]
+ msg = "Creating tag for #{PROJ.name} version #{PROJ.version}"
+ puts "Creating Git tag '#{tag}'"
+ unless system "git tag -a -m '#{msg}' #{tag}"
+ abort "Tag creation failed"
+ end
+# if %x/git remote/ =~ %r/^origin\s*$/
+# unless system "git push origin #{tag}"
+# abort "Could not push tag to remote Git repository"
+# end
+# end
+ end
+
+end # namespace :git
+
+#task 'gem:release' => 'git:create_tag'
+
+end # if HAVE_GIT
+
+# EOF
diff --git a/tasks/helpers.rb b/tasks/helpers.rb
new file mode 100644
index 0000000..df5bd50
--- /dev/null
+++ b/tasks/helpers.rb
@@ -0,0 +1,130 @@
+# -*- coding: UTF-8 -*-
+#
+require 'fileutils'
+require 'find'
+require 'date'
+#
+# Reads a file at +path+ and spits out an array of the +paragraphs+
+# specified.
+#
+# changes = paragraphs_of('History.txt', 0..1).join("\n\n")
+# summary, *description = paragraphs_of('README.txt', 3, 3..8)
+#
+def paragraphs_of( path, *paragraphs )
+ title = String === paragraphs.first ? paragraphs.shift : nil
+ ary = File.read(path).delete("\r").split(/\n\n+/)
+
+ result = if title
+ tmp, matching = [], false
+ rgxp = %r/^=+\s*#{Regexp.escape(title)}/i
+ paragraphs << (0..-1) if paragraphs.empty?
+
+ ary.each do |val|
+ if val =~ rgxp
+ break if matching
+ matching = true
+ rgxp = %r/^=+/i
+ elsif matching
+ tmp << val
+ end
+ end
+ tmp
+ else ary end
+
+ result.values_at(*paragraphs)
+end
+
+# Adds the given gem _name_ to the current project's dependency list. An
+# optional gem _version_ can be given. If omitted, the newest gem version
+# will be used.
+#
+def depend_on( name, version = nil )
+ spec = Gem::Specification.find_by_name(name)
+ version = spec.version.to_s if version.nil? and !spec.nil?
+
+ PROJ.gem.dependencies << case version
+ when nil; [name]
+ when %r/^\d/; [name, ">= #{version}"]
+ else [name, version] end
+end
+
+# Adds the given arguments to the include path if they are not already there
+#
+#def ensure_in_path( *args )
+# args.each do |path|
+# path = File.expand_path(path)
+# $:.unshift(path) if test(?d, path) and not $:.include?(path)
+# end
+#end
+
+# Find a rake task using the task name and remove any description text. This
+# will prevent the task from being displayed in the list of available tasks.
+#
+def remove_desc_for_task( names )
+ Array(names).each do |task_name|
+ task = Rake.application.tasks.find {|t| t.name == task_name}
+ next if task.nil?
+ task.instance_variable_set :@comment, nil
+ end
+end
+
+# Change working directories to _dir_, call the _block_ of code, and then
+# change back to the original working directory (the current directory when
+# this method was called).
+#
+#def in_directory( dir, &block )
+# curdir = pwd
+# begin
+# cd dir
+# return block.call
+# ensure
+# cd curdir
+# end
+#end
+
+# Scans the current working directory and creates a list of files that are
+# candidates to be in the manifest.
+#
+#def manifest
+# files = []
+# exclude = PROJ.exclude.dup
+# comment = %r/^\s*#/
+#
+# # process the ignore file and add the items there to the exclude list
+# if test(?f, PROJ.ignore_file)
+# ary = []
+# File.readlines(PROJ.ignore_file).each do |line|
+# next if line =~ comment
+# line.chomp!
+# line.strip!
+# next if line.nil? or line.empty?
+#
+# glob = line =~ %r/\*\./ ? File.join('**', line) : line
+# Dir.glob(glob).each {|fn| ary << "^#{Regexp.escape(fn)}"}
+# end
+# exclude.concat ary
+# end
+#
+# # generate a regular expression from the exclude list
+# exclude = Regexp.new(exclude.join('|'))
+#
+# Find.find '.' do |path|
+# path.sub! %r/^(\.\/|\/)/o, ''
+# next unless test ?f, path
+# next if path =~ exclude
+# files << path
+# end
+# files.sort!
+#end
+
+# We need a "valid" method that determines if a string is suitable for use
+# in the gem specification.
+#
+class Object
+ def valid?
+ return !(self.empty? or self == "\000") if self.respond_to?(:to_str)
+ return false
+ end
+end
+
+# EOF
diff --git a/tasks/notes.rake b/tasks/notes.rake
new file mode 100644
index 0000000..7f7ee79
--- /dev/null
+++ b/tasks/notes.rake
@@ -0,0 +1,27 @@
+# -*- coding: UTF-8 -*-
+#
+if HAVE_BONES
+
+desc "Enumerate all annotations"
+task :notes do |t|
+ id = if t.application.top_level_tasks.length > 1
+ t.application.top_level_tasks.slice!(1..-1).join(' ')
+ end
+ Bones::AnnotationExtractor.enumerate(PROJ, PROJ.notes.tags.join('|'), id, :tag => true)
+end
+
+namespace :notes do
+ PROJ.notes.tags.each do |tag|
+ desc "Enumerate all #{tag} annotations"
+ task tag.downcase.to_sym do |t|
+ id = if t.application.top_level_tasks.length > 1
+ t.application.top_level_tasks.slice!(1..-1).join(' ')
+ end
+ Bones::AnnotationExtractor.enumerate(PROJ, tag, id)
+ end
+ end
+end
+
+end # if HAVE_BONES
+
+# EOF
diff --git a/tasks/post_load.rake b/tasks/post_load.rake
new file mode 100644
index 0000000..8ccdfc9
--- /dev/null
+++ b/tasks/post_load.rake
@@ -0,0 +1,35 @@
+# -*- coding: UTF-8 -*-
+#
+# This file does not define any rake tasks. It is used to load some project
+# settings if they are not defined by the user.
+
+PROJ.exclude << ["^#{Regexp.escape(PROJ.ann.file)}$",
+ "^#{Regexp.escape(PROJ.ignore_file)}$",
+ "^#{Regexp.escape(PROJ.rdoc.dir)}/",
+ "^#{Regexp.escape(PROJ.rcov.dir)}/"]
+
+flatten_arrays = lambda do |this,os|
+ os.instance_variable_get(:@table).each do |key,val|
+ next if key == :dependencies \
+ or key == :development_dependencies
+ case val
+ when Array; val.flatten!
+ when OpenStruct; this.call(this,val)
+ end
+ end
+ end
+flatten_arrays.call(flatten_arrays,PROJ)
+
+PROJ.changes ||= paragraphs_of(PROJ.history_file, 0..1).join("\n\n")
+
+PROJ.description ||= paragraphs_of(PROJ.readme_file, 'description').join("\n\n")
+
+PROJ.summary ||= PROJ.description.split('.').first
+
+PROJ.gem.files ||= manifest
+
+PROJ.gem.executables ||= PROJ.gem.files.find_all {|fn| fn =~ %r/^bin/}
+
+PROJ.rdoc.main ||= PROJ.readme_file
+
+# EOF
diff --git a/tasks/rdoc.rake b/tasks/rdoc.rake
new file mode 100644
index 0000000..98ef3b8
--- /dev/null
+++ b/tasks/rdoc.rake
@@ -0,0 +1,46 @@
+# -*- coding: UTF-8 -*-
+#
+require 'rdoc/task'
+
+namespace :doc do
+ desc 'Generate RDoc documentation'
+ Rake::RDocTask.new do |rd|
+ rdoc = PROJ.rdoc
+ rd.main = rdoc.main
+ rd.rdoc_dir = rdoc.dir
+
+ incl = Regexp.new(rdoc.include.join('|'))
+ excl = Regexp.new(rdoc.exclude.join('|'))
+ files = PROJ.gem.files.find_all do |fn|
+ case fn
+ when excl; false
+ when incl; true
+ else false end
+ end
+ rd.rdoc_files.push(*files)
+ title = "#{PROJ.name}-#{PROJ.version} Documentation"
+ rf_name = PROJ.rubyforge.name
+ title = "#{rf_name}'s " + title if rf_name.valid? and rf_name != title
+ rd.options << "-t #{title}"
+ rd.options.concat(rdoc.opts)
+ end
+
+ desc 'Generate ri locally for testing'
+ task :ri => :clobber_ri do
+ sh "#{RDOC} --ri -o ri ."
+ end
+
+ task :clobber_ri do
+ rm_r 'ri' rescue nil
+ end
+end # namespace :doc
+
+desc 'Alias to doc:rdoc'
+task :doc => 'doc:rdoc'
+
+desc 'Remove all build products'
+task :clobber => %w(doc:clobber_rdoc doc:clobber_ri)
+
+remove_desc_for_task %w(doc:clobber_rdoc)
+
+# EOF
diff --git a/tasks/rubyforge.rake b/tasks/rubyforge.rake
new file mode 100644
index 0000000..73be292
--- /dev/null
+++ b/tasks/rubyforge.rake
@@ -0,0 +1,54 @@
+# -*- coding: UTF-8 -*-
+#
+if PROJ.rubyforge.name.valid? && HAVE_RUBYFORGE
+
+require 'rubyforge'
+require 'rake/contrib/sshpublisher'
+
+namespace :gem do
+ desc 'Package and upload to RubyForge'
+ task :release => [:clobber, 'gem'] do |t|
+ v = ENV['VERSION'] or abort 'Must supply VERSION=x.y.z'
+ abort "Versions don't match #{v} vs #{PROJ.version}" if v != PROJ.version
+ pkg = "pkg/#{PROJ.gem._spec.full_name}"
+
+ if $DEBUG then
+ puts "release_id = rf.add_release #{PROJ.rubyforge.name.inspect}, #{PROJ.name.inspect}, #{PROJ.version.inspect}, \"#{pkg}.tgz\""
+ puts "rf.add_file #{PROJ.rubyforge.name.inspect}, #{PROJ.name.inspect}, release_id, \"#{pkg}.gem\""
+ end
+
+ rf = RubyForge.new
+ rf.configure rescue nil
+ puts 'Logging in'
+ rf.login
+
+ c = rf.userconfig
+ c['release_notes'] = PROJ.description if PROJ.description
+ c['release_changes'] = PROJ.changes if PROJ.changes
+ c['preformatted'] = true
+
+ files = Dir.glob("#{pkg}*.*")
+
+ puts "Releasing #{PROJ.name} v. #{PROJ.version}"
+ rf.add_release PROJ.rubyforge.name, PROJ.name, PROJ.version, *files
+ end
+end # namespace :gem
+
+
+namespace :doc do
+ desc "Publish RDoc to RubyForge"
+ task :release => %w(doc:clobber_rdoc doc:rdoc) do
+ config = YAML.load(File.read(File.expand_path('~/.rubyforge/user-config.yml')))
+
+ host = "#{config['username']}@rubyforge.org"
+ remote_dir = "/var/www/gforge-projects/#{PROJ.rubyforge.name}/"
+ remote_dir << PROJ.rdoc.remote_dir if PROJ.rdoc.remote_dir
+ local_dir = PROJ.rdoc.dir
+
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
+ end
+end # namespace :doc
+
+end # if HAVE_RUBYFORGE
+
+# EOF
diff --git a/tasks/setup.rb b/tasks/setup.rb
new file mode 100644
index 0000000..5e69d02
--- /dev/null
+++ b/tasks/setup.rb
@@ -0,0 +1,129 @@
+# -*- coding: UTF-8 -*-
+#
+require 'rubygems'
+require 'rake'
+require 'rake/clean'
+require 'ostruct'
+
+class OpenStruct; undef :gem; end
+
+# TODO: make my own openstruct type object that includes descriptions
+# TODO: use the descriptions to output help on the available bones options
+
+PROJ = OpenStruct.new(
+ # Project Defaults
+ :name => nil,
+ :summary => nil,
+ :description => nil,
+ :changes => nil,
+ :authors => nil,
+ :email => nil,
+ :url => "\000",
+ :version => ENV['VERSION'] || '0.0.0',
+ :exclude => %w(tmp$ bak$ ~$ CVS \.svn/ \.git/ ^pkg/),
+ :release_name => ENV['RELEASE'],
+
+ # System Defaults
+ :ruby_opts => %w(-w),
+ :libs => [],
+ :history_file => 'Changelog',
+ :readme_file => 'README.rdoc',
+ :ignore_file => '.bnsignore',
+
+ # Announce
+ :ann => OpenStruct.new(
+ :file => 'announcement.txt',
+ :text => nil,
+ :paragraphs => [],
+ :email => {
+ :from => nil,
+ :to => %w(ruby-talk@ruby-lang.org),
+ :server => 'localhost',
+ :port => 25,
+ :domain => ENV['HOSTNAME'],
+ :acct => nil,
+ :passwd => nil,
+ :authtype => :plain,
+ :tls => true,
+ }
+ ),
+
+ # Gem Packaging
+ :gem => OpenStruct.new(
+ :dependencies => [],
+ :development_dependencies => [],
+ :executables => nil,
+ :extensions => FileList['ext/**/extconf.rb'],
+ :files => nil,
+ :need_tar => true,
+ :need_zip => false,
+ :extras => {}
+ ),
+
+ # File Annotations
+ :notes => OpenStruct.new(
+ :exclude => %w(^tasks/setup\.rb$),
+ :extensions => %w(.txt .rb .erb .rdoc) << '',
+ :tags => %w(FIXME OPTIMIZE TODO)
+ ),
+
+ # Rcov
+ :rcov => OpenStruct.new(
+ :dir => 'coverage',
+ :opts => %w[--sort coverage -T -x lib/rcov],
+ :threshold => 90.0,
+ :threshold_exact => false
+ ),
+
+ # Rdoc
+ :rdoc => OpenStruct.new(
+ :opts => [],
+ :include => %w(^lib/ ^bin/ ^ext/ \.txt$ \.rdoc$),
+ :exclude => %w(extconf\.rb$),
+ :main => nil,
+ :dir => 'doc',
+ :remote_dir => nil
+ ),
+
+ # Rubyforge
+ :rubyforge => OpenStruct.new(
+ :name => "\000"
+ ),
+
+ # Rspec
+ :spec => OpenStruct.new(
+ :files => FileList['spec/**/*_spec.rb'],
+ :opts => []
+ ),
+
+ # Subversion Repository
+ :svn => OpenStruct.new(
+ :root => nil,
+ :path => '',
+ :trunk => 'trunk',
+ :tags => 'tags',
+ :branches => 'branches'
+ ),
+
+ # Test::Unit
+ :test => OpenStruct.new(
+ :files => FileList['test/**/test_*.rb'],
+ :file => 'test/all.rb',
+ :opts => []
+ )
+)
+
+# Load the other rake files in the tasks folder
+tasks_dir = File.expand_path(File.dirname(__FILE__))
+post_load_fn = File.join(tasks_dir, 'post_load.rake')
+rakefiles = Dir.glob(File.join(tasks_dir, '*.rake')).sort
+rakefiles.unshift(rakefiles.delete(post_load_fn)).compact!
+import(*rakefiles)
+
+# Setup the project libraries
+%w(lib ext).each {|dir| PROJ.libs << dir if test ?d, dir}
+
+load './tasks/constants.rb'
+load './tasks/helpers.rb'
+
+# EOF
diff --git a/tasks/spec.rake b/tasks/spec.rake
new file mode 100644
index 0000000..7345f5c
--- /dev/null
+++ b/tasks/spec.rake
@@ -0,0 +1,44 @@
+# -*- coding: UTF-8 -*-
+#
+#if HAVE_SPEC_RAKE_SPECTASK and not PROJ.spec.files.to_a.empty?
+require 'rspec/core/rake_task'
+#
+namespace :spec do
+
+ desc 'Run all specs with basic output'
+ RSpec::Core::RakeTask.new(:run) do |t,args|
+ t.ruby_opts = PROJ.ruby_opts
+ t.rspec_opts = PROJ.spec.opts
+ t.pattern = ENV['pattern'] if ENV['pattern']
+ end
+
+ desc 'Run all specs with text output'
+ RSpec::Core::RakeTask.new(:doc) do |t|
+ t.ruby_opts = PROJ.ruby_opts
+ t.rspec_opts = PROJ.spec.opts + ['-fs' ]
+ t.pattern = ENV['pattern'] if ENV['pattern']
+ end
+
+ desc 'Run all specs with html output'
+ RSpec::Core::RakeTask.new(:html) do |t|
+ t.ruby_opts = PROJ.ruby_opts
+ t.rspec_opts = PROJ.spec.opts + ['-fh' ]
+ t.pattern = ENV['pattern'] if ENV['pattern']
+ end
+
+ if HAVE_RCOV
+ desc 'Run all specs with RCov'
+ RSpec::Core::RakeTask.new(:rcov) do |t|
+ t.ruby_opts = PROJ.ruby_opts
+ t.rspec_opts = PROJ.spec.opts
+ t.rcov = true
+ t.rcov_opts = PROJ.rcov.opts + ['--exclude', 'spec']
+ end
+ end
+
+end # namespace :spec
+
+desc 'Alias to spec:run'
+task :spec => 'spec:run'
+
+# EOF
diff --git a/tasks/svn.rake b/tasks/svn.rake
new file mode 100644
index 0000000..b831cbb
--- /dev/null
+++ b/tasks/svn.rake
@@ -0,0 +1,48 @@
+# -*- coding: UTF-8 -*-
+#
+if HAVE_SVN
+
+unless PROJ.svn.root
+ info = %x/svn info ./
+ m = %r/^Repository Root:\s+(.*)$/.match(info)
+ PROJ.svn.root = (m.nil? ? '' : m[1])
+end
+PROJ.svn.root = File.join(PROJ.svn.root, PROJ.svn.path) unless PROJ.svn.path.empty?
+
+namespace :svn do
+
+ # A prerequisites task that all other tasks depend upon
+ task :prereqs
+
+ desc 'Show tags from the SVN repository'
+ task :show_tags => 'svn:prereqs' do |t|
+ tags = %x/svn list #{File.join(PROJ.svn.root, PROJ.svn.tags)}/
+ tags.gsub!(%r/\/$/, '')
+ tags = tags.split("\n").sort {|a,b| b <=> a}
+ puts tags
+ end
+
+ desc 'Create a new tag in the SVN repository'
+ task :create_tag => 'svn:prereqs' do |t|
+ v = ENV['VERSION'] or abort 'Must supply VERSION=x.y.z'
+ abort "Versions don't match #{v} vs #{PROJ.version}" if v != PROJ.version
+
+ svn = PROJ.svn
+ trunk = File.join(svn.root, svn.trunk)
+ tag = "%s-%s" % [PROJ.name, PROJ.version]
+ tag = File.join(svn.root, svn.tags, tag)
+ msg = "Creating tag for #{PROJ.name} version #{PROJ.version}"
+
+ puts "Creating SVN tag '#{tag}'"
+ unless system "svn cp -m '#{msg}' #{trunk} #{tag}"
+ abort "Tag creation failed"
+ end
+ end
+
+end # namespace :svn
+
+task 'gem:release' => 'svn:create_tag'
+
+end # if PROJ.svn.path
+
+# EOF
diff --git a/tasks/test.rake b/tasks/test.rake
new file mode 100644
index 0000000..8257613
--- /dev/null
+++ b/tasks/test.rake
@@ -0,0 +1,41 @@
+# -*- coding: UTF-8 -*-
+#
+if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
+require 'rake/testtask'
+
+namespace :test do
+
+ Rake::TestTask.new(:run) do |t|
+ t.libs = PROJ.libs
+ t.test_files = if test(?f, PROJ.test.file) then [PROJ.test.file]
+ else PROJ.test.files end
+ t.ruby_opts += PROJ.ruby_opts
+ t.ruby_opts += PROJ.test.opts
+ end
+
+ if HAVE_RCOV
+ desc 'Run rcov on the unit tests'
+ task :rcov => :clobber_rcov do
+ opts = PROJ.rcov.opts.dup << '-o' << PROJ.rcov.dir
+ opts = opts.join(' ')
+ files = if test(?f, PROJ.test.file) then [PROJ.test.file]
+ else PROJ.test.files end
+ files = files.join(' ')
+ sh "#{RCOV} #{files} #{opts}"
+ end
+
+ task :clobber_rcov do
+ rm_r 'coverage' rescue nil
+ end
+ end
+
+end # namespace :test
+
+desc 'Alias to test:run'
+task :test => 'test:run'
+
+task :clobber => 'test:clobber_rcov' if HAVE_RCOV
+
+end
+
+# EOF
diff --git a/test/test_evendoors.rb b/test/test_evendoors.rb
new file mode 100644
index 0000000..78f84d7
--- /dev/null
+++ b/test/test_evendoors.rb
@@ -0,0 +1,78 @@
+#! /usr/bin/env ruby
+# -*- coding: UTF-8 -*-
+
+require 'evendoors'
+
+#
+class InputDoor < EvenDoors::Door
+ def start!
+ puts " * start #{self.class.name} #{@path}" if EvenDoors::Twirl.debug
+ @lines = [ "#{name} says : hello", "world ( from #{path} )" ]
+ p = require_p EvenDoors::Particle
+ p.set_dst EvenDoors::ACT_GET, path
+ send_p p
+ end
+ # def stop!
+ # puts " * stop #{self.class.name} #{@path}" if EvenDoors::Twirl.debug
+ # end
+ def receive p
+ puts " * #{self.class.name} receive_p : #{p.action}" if EvenDoors::Twirl.debug
+ if p.action==EvenDoors::ACT_GET
+ p.reset!
+ p.set_data 'line', @lines.shift
+ p.set_data 'f0', 'v0'
+ p.set_data 'f1', 'v1'
+ p.set_data 'f2', 'v2'
+ send_p p
+ if @lines.length>0
+ p = require_p EvenDoors::Particle
+ p.set_dst EvenDoors::ACT_GET, name
+ send_p p
+ end
+ else
+ # we can release it or let the Door do it
+ release_p p
+ end
+ end
+end
+#
+class OutputDoor < EvenDoors::Door
+ # def start!
+ # puts " * start #{self.class.name} #{@path}" if EvenDoors::Twirl.debug
+ # end
+ # def stop!
+ # puts " * stop #{self.class.name} #{@path}" if EvenDoors::Twirl.debug
+ # end
+ def receive p
+ if EvenDoors::Twirl.debug
+ puts " * #{self.class.name} receive_p : #{@path} : DATA #{p.get_data('line')}"
+ else
+ puts p.get_data 'line'
+ end
+ # we do nothing EvenDoors::Twirl.process will detect it and release it
+ end
+end
+#
+space = EvenDoors::Space.new 'space', :debug=>false
+room0 = EvenDoors::Room.new 'room0', space
+room1 = space.add_spot EvenDoors::Room.new 'room1'
+input0 = room0.add_spot InputDoor.new 'input0'
+output0 = room0.add_spot OutputDoor.new 'output0'
+input1 = room1.add_spot InputDoor.new 'input1'
+output1 = room1.add_spot OutputDoor.new 'output1'
+#
+room0.add_link EvenDoors::Link.new('input0', 'output0', nil, nil, nil)
+#
+p0 = EvenDoors::Twirl.require_p EvenDoors::Particle
+p0.set_data EvenDoors::LNK_SRC, 'input1'
+p0.set_data EvenDoors::LNK_DSTS, 'output1'
+p0.set_data EvenDoors::LNK_FIELDS, 'fx,fy,fz'
+p0.set_data EvenDoors::LNK_CONDF, 'f0,f1,f2'
+p0.set_data EvenDoors::LNK_CONDV, 'v0v1v2'
+p0.set_dst EvenDoors::ACT_ADD_LINK, room1.path
+room0.send_sys_p p0 # send_sys_p -> room0 -> space -> room1 -> input1
+#
+space.twirl!
+#
+#
+# EOF