From 2ee48c4f18470e733f244c659221e243ff998416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Zurcher?= Date: Thu, 3 May 2012 22:27:16 +0200 Subject: initial commit --- Changelog | 2 + MIT-LICENSE | 18 +++++ README.rdoc | 26 ++++++ Rakefile | 56 +++++++++++++ TODO | 7 ++ lib/evendoors.rb | 37 +++++++++ lib/evendoors/door.rb | 54 +++++++++++++ lib/evendoors/link.rb | 31 ++++++++ lib/evendoors/particle.rb | 138 ++++++++++++++++++++++++++++++++ lib/evendoors/room.rb | 123 +++++++++++++++++++++++++++++ lib/evendoors/space.rb | 24 ++++++ lib/evendoors/spot.rb | 28 +++++++ lib/evendoors/twirl.rb | 59 ++++++++++++++ spec/evendoors_spec.rb | 12 +++ tasks/ann.rake | 83 ++++++++++++++++++++ tasks/constants.rb | 118 ++++++++++++++++++++++++++++ tasks/gem.rake | 196 ++++++++++++++++++++++++++++++++++++++++++++++ tasks/git.rake | 38 +++++++++ tasks/helpers.rb | 130 ++++++++++++++++++++++++++++++ tasks/notes.rake | 27 +++++++ tasks/post_load.rake | 35 +++++++++ tasks/rdoc.rake | 46 +++++++++++ tasks/rubyforge.rake | 54 +++++++++++++ tasks/setup.rb | 129 ++++++++++++++++++++++++++++++ tasks/spec.rake | 44 +++++++++++ tasks/svn.rake | 48 ++++++++++++ tasks/test.rake | 41 ++++++++++ test/test_evendoors.rb | 78 ++++++++++++++++++ 28 files changed, 1682 insertions(+) create mode 100644 Changelog create mode 100644 MIT-LICENSE create mode 100644 README.rdoc create mode 100644 Rakefile create mode 100644 TODO create mode 100644 lib/evendoors.rb create mode 100644 lib/evendoors/door.rb create mode 100644 lib/evendoors/link.rb create mode 100644 lib/evendoors/particle.rb create mode 100644 lib/evendoors/room.rb create mode 100644 lib/evendoors/space.rb create mode 100644 lib/evendoors/spot.rb create mode 100644 lib/evendoors/twirl.rb create mode 100644 spec/evendoors_spec.rb create mode 100644 tasks/ann.rake create mode 100644 tasks/constants.rb create mode 100644 tasks/gem.rake create mode 100644 tasks/git.rake create mode 100644 tasks/helpers.rb create mode 100644 tasks/notes.rake create mode 100644 tasks/post_load.rake create mode 100644 tasks/rdoc.rake create mode 100644 tasks/rubyforge.rake create mode 100644 tasks/setup.rb create mode 100644 tasks/spec.rake create mode 100644 tasks/svn.rake create mode 100644 tasks/test.rake create mode 100644 test/test_evendoors.rb 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 + * 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 -- cgit v1.1-2-g2b99