[Pkg-ruby-extras-commits] r1092 - in packages-wip: .
libevent-loop-ruby libevent-loop-ruby/branches
libevent-loop-ruby/branches/upstream
libevent-loop-ruby/branches/upstream/current
libevent-loop-ruby/branches/upstream/current/lib
libevent-loop-ruby/branches/upstream/current/lib/event-loop
Florian Ragwitz
florian-guest at alioth.debian.org
Mon Nov 13 14:52:38 CET 2006
Author: florian-guest
Date: 2006-11-13 14:52:29 +0100 (Mon, 13 Nov 2006)
New Revision: 1092
Added:
packages-wip/libevent-loop-ruby/
packages-wip/libevent-loop-ruby/branches/
packages-wip/libevent-loop-ruby/branches/upstream/
packages-wip/libevent-loop-ruby/branches/upstream/current/
packages-wip/libevent-loop-ruby/branches/upstream/current/AUTHORS
packages-wip/libevent-loop-ruby/branches/upstream/current/COPYING
packages-wip/libevent-loop-ruby/branches/upstream/current/ChangeLog
packages-wip/libevent-loop-ruby/branches/upstream/current/GFDL
packages-wip/libevent-loop-ruby/branches/upstream/current/README
packages-wip/libevent-loop-ruby/branches/upstream/current/README.utf-8
packages-wip/libevent-loop-ruby/branches/upstream/current/Rakefile
packages-wip/libevent-loop-ruby/branches/upstream/current/VERSION
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop.rb
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/better-definers.rb
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/event-loop.rb
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/io.rb
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/signal-system.rb
packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/timer.rb
packages-wip/libevent-loop-ruby/branches/upstream/current/setup.rb
packages-wip/libevent-loop-ruby/tags/
Log:
[svn-inject] Installing original source of libevent-loop-ruby
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/AUTHORS
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/AUTHORS 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/AUTHORS 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,2 @@
+Daniel Brockman <daniel at brockman.se>
+Tilman Sauerbeck <tilman at code-monkey.de>
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/COPYING
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/COPYING 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/COPYING 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program 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.
+
+ This program 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/ChangeLog
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/ChangeLog 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/ChangeLog 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,277 @@
+2006-10-19 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.3.
+
+ * Rakefile: No longer depend on rubygems.
+
+ * gemspec.rb: Move contents into `Rakefile'.
+
+ * VERSION: New file.
+
+ * setup.rb: New file (copied from upstream).
+
+ * README.utf-8: Update to reflect changes.
+
+ * lib/event-loop/timer.rb: Make lots and lots of changes without
+ having enough patience to document them here.
+
+ * lib/event-loop/signal-system.rb
+ (SignalEmitterModule#define_signal_handler): Define
+ `prehandle_...' and `posthandle_...' instead of `handle_...' and
+ `after_handle_...'.
+ (SignalEmitter#__signal__): Invoke `prehandle_...' and
+ `posthandle_...'.
+
+2006-04-23 Daniel Brockman <daniel at brockman.se>
+
+ * lib/event-loop/timer.rb: New file extracted from
+ `lib/event-loop/event-loop.rb'.
+ (Timer#initialize): New instance variable `@alarm_handler'.
+ (Timer#replace_alarm_handler): New method.
+ (Timer#initialize, Timer#restart, Timer#start): Use it.
+ (Timer#initialize): Remove option parameter `:start?'.
+ (TimerTest#test_start_monitoring_timer_while_running): Explicitly
+ call `#start'.
+ (Timer#start, Timer#stop): Set `@running'.
+ (Timer#stop): Don't clear `@start_time'.
+ (Timer#stopped?, Timer#running?): Check `@running' instead of
+ `@start_time'.
+ (Timer#started?): New method.
+ (Timer#start_time, Timer#end_time, Timer#time_left, Timer#ready?):
+ Fail if `@start_time' is nil.
+ (Timer#end_time=, Timer#time_left=): New methods.
+ (Timer#initialize): Make `interval' a mandatory
+ positional parameter. Remove `:interval' keyword parameter.
+
+ * lib/event-loop/io.rb: New file extracted from
+ `lib/event-loop/event-loop.rb'.
+
+ * lib/event-loop/event-loop.rb
+ (EventLoop::Utitilies.validate_keyword_arguments): New method
+ extracted from EventLoop::Timer::initialize.
+ (EventLoop#initialize): Document why we have to explicitly extend
+ the notify source with EventLoop::Watchable.
+
+2006-02-06 Daniel Brockman <daniel at brockman.se>
+
+ * lib/event-loop/event-loop.rb (Numeric#seconds, Numeric#minutes)
+ (Numeric#hours, Numeric#days, Numeric#weeks, Numeric#years)
+ (Integer#years): New methods.
+
+2006-02-05 Daniel Brockman <daniel at brockman.se>
+
+ * Rakefile (check): Run tests with `ruby -w'.
+ Suggested by Tilman Sauerbeck.
+
+2006-02-05 Tilman Sauerbeck <tilman at code-monkey.de>
+
+ * lib/event-loop/event-loop.rb (EventLoop#restart)
+ (EventLoop#start): Add parentheses (this avoids warnings).
+
+2006-01-31 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.2.
+
+ This time I won't use Ruby 1.8.3 to build the Gem package.
+ Thanks to Tilman Sauerbeck for pointing out that problem.
+
+ * lib/event-loop/event-loop.rb (Timer#interval=): Only invoke
+ EventLoop#check_timer when the timer is running.
+ (Timer#start, Timer#restart): Accept blocks (for convenience).
+ (EventLoop#monitor_timer): Actually check the timer.
+ (TimerTest): Create new event loops for each test.
+ (TimerTest::test_start_monitoring_timer_while_running): New test.
+ (Time.measure): New method.
+
+ * Rakefile (README): Convert Unicode copyright symbols to
+ conventional ASCII ones. Add note warning that README is
+ automatically generated.
+
+ * GFDL: New file.
+
+ * README.utf-8: License this document under the GFDL.
+ (Table of Contents): New section.
+ (time-stamp-format, time-stamp-start, time-stamp-end):
+ New local variables (for Emacs).
+
+2006-01-30 Daniel Brockman <daniel at brockman.se>
+
+ * README.utf-8 (Event Sources): New section.
+ Suggested by Luke Kanies.
+
+ * lib/event-loop/event-loop.rb (Timer#end_time): Raise an
+ exception if the timer is not running.
+ (TimerTest#test_monitor_unstarted_timer): New test case.
+ (Timer#restart, Timer#sound_alarm, Timer#start, Timer#stop)
+ (EventLoop#wake_up): For convenience, return self.
+
+2006-01-25 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.1.
+
+ * README.utf-8: Improve the documentation, explaining more
+ in-depth how IO objects and timers interact with event loops, and
+ providing some meaningful examples, including one that shows how
+ to use the `IO#will_block' property, and one at the end that
+ provides an outline for the typical application.
+
+ * lib/event-loop/event-loop.rb (Timer.new): Fail if given
+ unrecognized keyword arguments. Suggested by Luke Kanies.
+ (Timer): Add reader for property `event_loop'.
+ (EventLoop): Rename property `sleeping?' to `asleep?', for
+ symmetry with `awake?'.
+ (EventLoop.with_current): Warn the user if `EventLoop.current' is
+ permanently changed within the dynamic extent of `with_current'.
+
+2005-11-17 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.0.20051116.
+
+ * lib/event-loop/signal-system.rb (SignalEmitterModule.extended):
+ Use `fcall' instead of `send' on Ruby 1.9.
+
+2005-09-28 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.0.20050928.
+
+ * lib/event-loop/signal-system.rb
+ (SignalEmitter#__maybe_initialize_signal_emitter): Avoid warnings
+ about using an uninitialized instance variable.
+
+2005-09-04 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.0.20050904.
+
+ * Rakefile (upload): Print an error message if this target is used
+ by someone other than the maintaner.
+ (README): Warn if the Unicode string handling is broken.
+
+2005-09-04 Tilman Sauerbeck <tilman at code-monkey.de>
+
+ * Rakefile: Add `install' target.
+
+2005-08-28 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.0.20050829.0000.
+
+ * lib/event-loop/signal-system.rb (SignalEmitterClass):
+ Rename to SignalEmitterModule.
+ (SignalEmitterModule.extended): New method.
+
+2005-08-26 Daniel Brockman <daniel at brockman.se>
+
+ * lib/event-loop/signal-system.rb
+ (SignalEmitterClass#define_signal_handler): New method.
+ (SignalEmitterClass#define_signal): Use it.
+ (SignalObserver#absorb_signals): New method.
+ (SignalObserver#absorb_signal, SignalObserver#map_signal):
+ New soft aliases.
+
+ * lib/event-loop/better-definers.rb
+ (Module#define_guarded_writers): Pass the block along to
+ `guard_writers' (fixes an obvious bug).
+
+ * lib/event-loop/signal-system.rb
+ (SignalEmitter): New attribute `allow_dynamic_signals?'.
+ (SignalEmitter#__signal__): Fail for undefined signals unless
+ these are explicitly allowed (by the new attribute).
+
+2005-08-25 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.0.20050825.1600.
+
+ * README.utf-8: Update installation instructions, thank Tilman
+ some more for the gem specification, and add a section explaining
+ what timer tolerances are for.
+
+ * lib/event-loop.rb: New file.
+
+ * event-loop.rb, signal-system.rb, better-definers.rb:
+ Move source files to `lib/event-loop/'. Change `require'
+ statements accordingly.
+
+ * lib/, lib/event-loop/: New directories.
+
+ * Rakefile: Add new `upload' target.
+
+ * Makefile: Move `check' target to Rakefile and delete.
+
+ * gemspec.rb: Clean up and copyedit. :-)
+
+2005-08-23 Tilman Sauerbeck <tilman at code-monkey.de>
+
+ * Rakefile: New file.
+
+ * gemspec.rb: New file.
+
+2005-08-23 Daniel Brockman <daniel at brockman.se>
+
+ * event-loop.rb (EventLoop#select, EventLoop#run): Don't call
+ `maybe_initialize_pipe'.
+
+ * event-loop.rb (EventLoop#maybe_initialize_pipe):
+ Remove unused method.
+
+2005-08-21 Daniel Brockman <daniel at brockman.se>
+
+ Release 0.0.20050821.2356.
+
+ * README.utf-8: Fix typos.
+
+ * better-definers.rb (Module#define_soft_aliases):
+ Parenthesize splatted arguments to avoid warnings.
+
+ * event-loop.rb (EventLoop#initialize): Initialize @awake.
+
+2005-08-20 Daniel Brockman <daniel at brockman.se>
+
+ * signal-system.rb (SignalObserver#ignore_signal): Fix fatal bug.
+ (SignalEmitter#__signal__): Don't pass `self' as first argument.
+
+ * better-definers.rb (Module#guard_writers):
+ Use `define_hard_alias' rather than `define_alias'.
+
+ * signal-system.rb: Likewise.
+
+ * event-loop.rb: Use qualified names in `require'.
+ (EventLoop#maybe_initialize_pipe): Use `sysread(256)' instead of
+ `read' to read from the notification pipe.
+
+ * Makefile (check): Pass `-I..' option to `ruby'.
+
+2005-08-19 Daniel Brockman <daniel at brockman.se>
+
+ * README.utf-8 (IO Events): Update documentation to reflect the
+ changes related to Watchable::Automatic.
+
+ * event-loop.rb (Watchable::Automatic): New module.
+ (Watchable#remove_signal_handler, Watchable#add_signal_handler):
+ Move to new module.
+ (Watchable#monitor_events, Watchable#ignore_events): New methods.
+ (Watchable#close_read, Watchable#close_write)
+ (Watchable#close): Use them.
+ (IO#on_readable, IO#on_writable, IO#on_exceptional): Extend with
+ Watchable::Automatic instead of just Watchable.
+ (EventLoop#maybe_initialize_pipe): Use `read' instead of
+ `sysread(1)' to read from the notification pipe.
+ (Symbol#io_state?): New convenience method.
+
+ * event-loop.rb (EventLoop#check_timer): New method.
+ (EventLoop#monitor_timer): Use it.
+ (EventLoop#select): New private method.
+ (EventLoop#iterate): Refactor using new `select' helper method.
+
+ * event-loop.rb (Timer.new): New option `event_loop'. Each timer
+ is now associated with exactly one event loop.
+ (Timer#fire_alarm): Only restart the timer if it is still running
+ when the alarm returns.
+ (Timer#start, Timer#stop): Use @event_loop instead of EventLoop.
+ (Timer#fire_alarm): Rename to `sound_alarm'.
+ (Timer#stopped?, Timer#running?): Instead of checking @running,
+ assume that @start_time is nil iff the timer is running.
+
+ * event-loop.rb (Timer#initialize): Allow a single numeric
+ argument as a shorthand for specifying the interval.
+ (Timer#interval=): New method.
+
+ * ChangeLog: New file.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/GFDL
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/GFDL 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/GFDL 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,397 @@
+ GNU Free Documentation License
+ Version 1.2, November 2002
+
+
+ Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (Thus, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.2
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/README
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/README 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/README 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,708 @@
+------------------------------------------------------------
+ This file was automatically generated from `README.utf-8'.
+------------------------------------------------------------
+
+README for event-loop 0.3
+Copyright (C) 2005, 2006 Daniel Brockman
+
+Author: Daniel Brockman <daniel at brockman.se>
+Written: August 19, 2005
+Updated: October 19, 2006
+
+This document describes a simple signal system and an event
+loop that uses said signal system.
+
+Permission is granted to copy, distribute and/or modify this
+document under the terms of the GNU Free Documentation
+License, Version 1.2 or any later version published by the
+Free Software Foundation; with no Invariant Sections, no
+Front-Cover Texts, and no Back-Cover Texts. You should have
+received a copy of the license along with this document; if
+not, write to the Free Software Foundation, 51 Franklin
+Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+The next screenful of paragraphs is mostly side-chatter.
+If you scroll down a bit, you'll find a table of contents.
+
+If you have any questions, comments, requests, bug reports,
+or patches, please send them to me at <daniel at brockman.se>.
+
+I have a Darcs repository at this URL,
+
+ <http://www.brockman.se/software/ruby-event-loop/>
+
+so you can easily grab a copy of the latest version:
+
+ $ darcs get http://www.brockman.se/software/ruby-event-loop/
+
+If you make any changes, you can send them back to me:
+
+ $ darcs record
+ $ darcs send
+
+You can also reach me on IRC as `dbrock' on Freenode.
+But I'll say that a couple more times below.
+
+This software originated in a Direct Connect client
+codenamed Refusde, but now stands on its own.
+
+Thanks to Tilman Sauerbeck for prompting me to wrap these
+files up in a self-contained package, and then demanding
+documentation. Without him, this file wouldn't exist. :-)
+
+Tilman also helped by creating a RubyGems specification,
+and by translating the Makefile into a Rakefile.
+
+To counteract confusion, I'll say a few words about
+version numbers before I begin.
+
+Prior to version 0.1, this package used to have version
+numbers like 0.0.20051116 --- a kind of self-inflicted FUD.
+Now that it's been used by at least a couple of people other
+than myself, I've decided to switch to a more conventional
+line of version numbers: 0.1, 0.2, ..., 0.10, and so on.
+
+Should a smart person read the code today and come across
+some deeply embedded idiocy, I can point to these other
+people as proof that I'm not the only idiot around here.
+Then I can proceed to fix the idiocy by completely changing
+the API, totally breaking compatibility, and if anyone
+complains I can always tell them they're idiots.
+
+
+Table of Contents
+=================
+
+This document is getting long enough that I'm starting to
+have trouble navigating it myself. I probably should
+convert it into an Info manual. But until that happens,
+maybe this table of contents will help a little.
+
+You will note that there's no section that says anything
+about the file `better-definers.rb'. I don't have any good
+excuse for this; maybe I'll write that section some day.
+In the meantime, the code itself is really easy to follow.
+
+ * Installation
+ * The Signal System: `signal-system.rb'
+ - Emitting Signals
+ - Receiving Signals
+ * The Event Loop: `event-loop.rb'
+ - Creating Event Loops
+ - Event Sources
+ - IO Events: `io.rb'
+ - Timer Events: `timer.rb'
+ - Running the Event Loop
+
+Installation
+============
+
+I suppose the recommended way of using this software is to
+install the gem package. But if you don't want to do that,
+you could simply copy or symlink the files into your project
+directory and require whichever ones you need.
+
+The files in `lib/event-loop/' must reside in a directory
+called `event-loop/' for the dependencies to work, so if you
+are taking anything, take the whole `event-loop/' directory.
+
+While `signal-system.rb' depends on `better-definers.rb',
+and `event-loop.rb' depends on both the aforementioned two,
+it is quite possible to use `signal-system.rb' without
+`event-loop.rb', or `better-definers.rb' without either.
+
+Indeed, `better-definers.rb' and `signal-system.rb' are two
+very general-purpose libraries, and you are likely to be
+able to put them to use for most applications, regardless of
+whether or not they are based on an event loop.
+
+For completeness, `timer.rb' depends on `event-loop.rb',
+and `event-loop.rb' and `io.rb' depend on each other.
+
+
+The Signal System
+=================
+
+This package comes with an intra-process signal system.
+I call these signals "intra-process signals" to distinguish
+them from Unix signals, which are "inter-process signals".
+
+But now that I've made that clear, I'm going to go ahead and
+refer to intra-process signals as just "signals", and to the
+intra-process signal system simply as "the signal system".
+
+Anyway, the signal system is a generic callback mechanism,
+similar in spirit to the `observable' library that comes
+with the standard Ruby distribution. It allows anonymous
+observers to track the events of signal-emitting objects
+(thereby helping to decrease coupling).
+
+The idea is similar to that of the GObject signal system,
+and a myriad of others like it. (If you know INTERCAL,
+signals are to methods as COME FROM is to GO TO.)
+
+
+Emitting Signals
+----------------
+
+If you want your objects to be capable of emitting signals,
+their classes should include the `SignalEmitter' module.
+That will automatically rig the classes with everything you
+need to define signals (the `SignalEmitterModule' module).
+
+For example, this code
+
+ class Dog
+ include SignalEmitter
+ define_signal :bark
+ def bark
+ puts "Bark!"
+ signal :bark
+ end
+ end
+
+ spot = Dog.new
+ spot.on_signal :bark do
+ puts "Hush, Spot!"
+ end
+
+ 3.times { spot.bark }
+
+results in the following output:
+
+ Bark!
+ Hush, Spot!
+ Bark!
+ Hush, Spot!
+ Bark!
+ Hush, Spot!
+
+
+Receiving Signals
+-----------------
+
+The true name for the method that connects a block or proc
+to a signal is `add_signal_handler'. However, that gets
+awfully long when connecting to lots of signals, so there
+are a couple of shortcuts.
+
+First, there are the `on' and `on_signal' aliases.
+These synonyms just delegate to `add_signal_handler'.
+
+Second, every predefined signal FOO gets a shorthand
+connector method called `on_FOO'. So in the above example,
+we could have written this instead:
+
+ spot = Dog.new
+ spot.on_bark { puts "Hush, Spot!" }
+
+The `on_FOO' variant is nice for connecting to signals whose
+names are known statically. Otherwise, `on_signal' is
+usually preferable; these other cases are typically not
+common enough to justify the short form `on'.
+
+Connecting signal handlers is a breeze, but *disconnecting*
+them is actually a minor pain in the ass. Without having an
+explicit reference to the handler, you cannot identify it,
+which means that you cannot tell `remove_signal_handler'
+what handler you want to remove.
+
+An example of the obvious but painful solution follows:
+
+ spot = Dog.new
+ @bark_handler = lambda { puts "Hush, Spot!" }
+ spot.on_bark &@bark_handler
+ # ...
+ spot.remove_signal_handler @bark_handler
+
+The `SignalObserver' module provides a slightly
+better solution:
+
+ class DogOwner
+ include SignalObserver
+
+ def initialize(dog=nil)
+ @dog = dog || Dog.new("Spot")
+ observe_signal @dog, :bark do
+ puts "Shut up, #{@dog.name}"
+ end
+ end
+
+ def stop_yelling
+ ignore_signal @dog, :bark
+ end
+ end
+
+But `SignalObserver' has a few caveats that make it
+insufficient as a general solution (see the source).
+
+In the future, I'd like to provide a more powerful mechanism
+for connecting to signals. Perhaps involving handler tags.
+If you want this feature, nagging me about it on IRC (I'm
+`dbrock' on Freenode) or via e-mail usually works best.
+
+
+The Event Loop
+==============
+
+This section explains how IO multiplexing works in general
+(albeit briefly and not very in-depth), and specifically the
+issues relevant for Ruby applications. You may safely skip
+it if you (a) already know this subject, or (b) don't care.
+
+Plain ol' blocking IO works well when you're reading from
+just a single file descriptor. But when you're interested
+in a whole bunch of FDs, you can't wait for any single one
+of them to become readable or writable, because then you'll
+inevitably miss that happening to the other ones. Instead,
+you need a multiplexer that can wait for them *all at once*.
+
+There are a handful of low-level multiplexing primitives:
+`select', `poll', `epoll', `/dev/poll', and `kqueue'.
+In addition, there are portable low-level wrapper libraries
+such as libevent, which can use any of those primitives.
+The event loop in this package uses the standard `select'
+wrapper shipped with Ruby, `IO::select'. But in the future,
+I'd like to use libevent instead, because that'd be cooler.
+
+Most applications use a higher-level abstraction built on
+top of the low-level multiplexer, usually called a `main
+loop', an `event loop', or an `event source'. There are
+also libraries such as liboop, which generalizes the event
+source and event sink concepts, so that components (event
+sinks) written against liboop become event-source-agnostic.
+
+Actually, the combination of blocking IO and Ruby's green
+threads works well in most cases where you would normally
+use an event loop. When you call `IO#read' on an empty file
+descriptor, for instance, Ruby suspends that thread until
+its internal event loop, known as the scheduler (currently
+based on `select'), determines that the file descriptor has
+become readable. In particular, Ruby never calls the
+low-level `read' function unless it knows that it will not
+block (because `select' said it wouldn't, but see below).
+
+There are several reasons why you would use an event loop
+such as the one implemented by this library instead of
+not-so-plain ol' blocking IO with Ruby's green threads.
+
+First of all, you may consider the event loop API more
+pleasant than Ruby's threads and not-quite-blocking IO.
+Otherwise, don't listen to me; go on using the latter. :-)
+
+Blocking IO can occasionally cause unexpected problems.
+For example, in some cases a blocking read *can* block even
+though select said that the file descriptor was readable.
+This problem may be rare (it can happen, for instance, when
+the checksum of a piece of data fails to match the payload),
+but the bottom line is that non-blocking IO is safer.
+
+Perhaps most importantly, while Ruby's threads are green,
+they are still effectively preemptively scheduled, with all
+the implications thereof --- in a word, synchronization hell.
+By contrast, event handlers are executed in a strictly
+sequential manner; an event loop will never run two event
+handlers simultaneously. (Though, of course, all bets are
+off if you run multiple event loops in separate threads.)
+
+
+Creating Event Loops
+--------------------
+
+You create a new event loop by calling `EventLoop.new'.
+However, if you only need one --- which is likely --- you
+can get it for free by reading from `EventLoop.default'.
+
+If you need multiple event loops in separate threads, put
+them in `EventLoop.current', which will make each of them
+end up in a thread-local variable.
+
+Actually, if you read from `EventLoop.current' before
+writing to it, it defaults to `EventLoop.default', so you
+might as well use `EventLoop.current' in single-threaded
+applications as well.
+
+In fact, `EventLoop.current' is so common that it can be
+shortened to just `EventLoop', if there is no ambiguity.
+So `EventLoop.run' is short for `EventLoop.current.run'.
+
+To dynamically change the "current event loop" for a block
+of code, it is convenient to use `EventLoop.with_current'.
+
+Once you've got the event loop sitting in front of you just
+waiting to be used, you'll want to add some event sources,
+and then finally run the loop. So, first things first:
+What are event sources, and how do you add them to the loop?
+
+
+Event Sources
+-------------
+
+As for the first question, this package currently supports
+two kinds of event sources: watchable IO objects and timers.
+The former kind is used to detect file descriptor activity;
+the latter is used for wall-clock scheduling of execution.
+
+Typically, you don't add event sources to the loop manually.
+Both watchable IO objects and timers provide convenient ways
+of making them add themselves to the "current" event loop.
+
+For example, to add `@io' to the current event loop, you
+might write something like the following:
+
+ @io.monitor_events :readable, :writable
+
+That's short for the following more explicit code:
+
+ EventLoop.current.monitor_io(@io, :readable, :writable)
+
+The call to `EventLoop#monitor_io' causes the event loop to
+wake up --- if it was sleeping in a call to `IO.select' --- and
+adds `@io' to the event loop's set of monitored IO objects.
+For the next event loop iteration, `@io' will be included in
+one or more of the sets passed to `IO.select'.
+
+To add the IO object to another event loop,
+
+ other_loop.monitor_io(@io, :readable, :writable)
+
+you can do it like this,
+
+ EventLoop.with_current(other_loop) do
+ @io.monitor_events :readable, :writable end
+
+which is especially convenient when adding multiple objects.
+
+For timers, it's even easier:
+
+ @timer = EventLoop::PeriodicTimer.new(1.second)
+ @timer.start
+
+That call to `@timer.start' causes the following to happen:
+
+ EventLoop.current.monitor_timer(@timer)
+
+The call to `EventLoop#monitor_timer' may force the event
+loop to wake up, depending on the timer readings and the
+current timeout of the event loop. In any case, the timer
+is added to the event loop's set of monitored timers.
+
+But there's a caveat. This will not work as expected:
+
+ EventLoop.with_current(other_loop) { @timer.start }
+
+That's because timer objects decide in advance which event
+loop they are going to use. Once initialized, timer objects
+no longer care about the value of the current event loop.
+Hence, this code starts a timer in a different event loop:
+
+ @timer = EventLoop.with_current(other_loop) do
+ EventLoop::PeriodicTimer.new(1.second) end
+ @timer.start
+
+Here is another way of writing it:
+
+ @timer = EventLoop::PeriodicTimer.new \
+ 1.second, :event_loop => other_loop
+ @timer.start
+
+In addition, there are quite a few convenient short forms.
+For example, you can write things like this:
+
+ 3.seconds.from_now { puts "Boo!" }
+
+Read on, because the next two sections describe with better
+examples and in more detail how IO and timer events work.
+
+[Actually, there are no examples of using timers at all.
+But it would be nice to have some under "Timer Events".]
+
+
+IO Events
+---------
+
+In Ruby, file descriptors are instances of the class IO.
+Before you can use one of these with the event loop, you
+need to extend it with the module `EventLoop::Watchable'.
+That module defines two signals, `readable' and `writable',
+and a pair of methods for activating and deactivating them.
+
+When you want to start receiving `readable' signals, for
+instance, you call `io.monitor_event :readable'. This makes
+the current event loop monitor `io' for readability, and
+emit the `readable' signal on it when the condition occurs.
+
+ require "socket"
+
+ def initialize (host, port)
+ @socket = TCPSocket.new(host, port)
+ @socket.extend EventLoop::Watchable
+ @socket.will_block = false
+ @socket.on_readable { perform_read }
+ end
+
+ def start_listening
+ @socket.monitor_event :readable
+ end
+
+The `will_block?' property is provided by this package as a
+convenient way of setting up non-blocking IO streams.
+(See `lib/event-loop/io.rb', circa line 81.)
+
+The method that actually performs the reading will probably
+look more or less like so:
+
+ def perform_read
+ process_data @socket.sysread(BUFFER_SIZE, @buffer)
+ rescue EOFError
+ ...
+ rescue Errno::ECONNRESET
+ ...
+ rescue
+ ...
+ end
+
+If you don't want to receive any more `readable' signals,
+you just call `io.ignore_event :readable'.
+
+ def stop_listening
+ @socket.ignore_event :readable
+ end
+
+The "current event loop" is just `EventLoop.current'.
+To make another event loop (say `other_loop') monitor or
+ignore an IO event, either call `other_loop.monitor_io' or
+`other_loop.ignore_io' directly,
+
+ other_loop.monitor_io(io, :readable)
+ other_loop.ignore_io(io, :writable)
+
+or use the `EventLoop.with_current' form,
+
+ EventLoop.with_current(other_loop) do
+ io.monitor_event :readable
+ io.ignore_event :writable
+ end
+
+which implements "dynamic scoping" of `EventLoop.current'.
+
+If you simply want readable signals to be emitted whenever
+there are handlers connected to the `readable' signal (and
+likewise for `writable'), without having to mess around with
+`monitor_event' and `ignore_event', you can extend the IO
+object with the `EventLoop::Watchable::Automatic' module
+instead of `EventLoop::Watchable'.
+
+The `EventLoop::Watchable::Automatic' module sets it up so
+that when you connect a handler to either the `readable' or
+the `writable' signal, the current event loop begins
+monitoring the IO object for the corresponding condition,
+and, inversely, when you remove the last handler, it tells
+the event loop to stop monitoring the condition.
+
+Because this is so often useful, you don't even have to
+extend the IO object yourself. Stub implementations of the
+`on_readable' and `on_writable' methods are provided, which
+automatically bootstrap the IO by extending it with the
+`EventLoop::Watchable::Automatic' module when invoked.
+
+ @socket = TCPSocket.new(host, port)
+ @socket.will_block = false
+
+ # By invoking the stub `on_readable' method,
+ # we implicitly extend the IO object with the
+ # module `EventLoop::Watchable::Automatic'.
+ #
+ # That module hooks into the signal system and
+ # reacts when we start watching the `readable'
+ # signal by starting to monitor that event.
+ @socket.on_readable { perform_read }
+
+Note that once an IO object has been extended with the
+`EventLoop::Watchable::Automatic' module, there is currently
+no way to make it non-automatic (Ruby does not yet allow you
+to un-extend an object with a module). So if you don't want
+the automatic behavior, you *have* to manually extend the
+object with the `EventLoop::Watchable' module before calling
+either of the `or_readable' and `on_writable' methods.
+
+There is actually a third signal: `exceptional', which is
+emitted when `select' reports that the file descriptor is in
+an "exceptional state". You probably don't need to worry
+about this (and if you do, you'll probably know it already).
+
+But in case you're wondering, I think you can use it to
+watch for out-of-band data coming through a socket, provided
+you've set the right socket options. I also believe you can
+use it to determine that a non-blocking connection attempt
+has failed. (When such an attempt succeeds, a writability
+event is fired for the socket.) But that doesn't matter,
+because you can't do non-blocking connects in Ruby. :-)
+
+
+Timer Events
+------------
+
+If you need to do something after a given amount of
+wall-clock time has passed, just do the following:
+
+ 1. Create an `EventLoop::SporadicTimer', passing the
+ timeout (in seconds) to the constructor.
+ 2. Connect to its `alarm' signal (using `on_alarm').
+ 3. Start the timer (using `start').
+
+Sporadic timers only sound their alarm once, and then stop.
+If you want to do something periodically, like every second,
+use `EventLoop::PeriodicTimer' instead.
+
+You can start a sporadic timer as many times as you want,
+but it will still stop itself every time it goes off.
+Periodic timers must be stopped explicitly (using `stop'),
+or they will keep going off as long as the event loop runs.
+
+You can get the effect of an "idle function" by creating an
+periodic timer with a zero-second interval, meaning its
+alarm will sound as often as possible.
+
+If you pass a block to a timer constructor, then that block
+will become the timer's canonical "alarm handler", which is
+just a signal handler for the `alarm' signal, except that
+you can easily replace it, using `replace_alarm_handler'.
+
+Another way to replace the alarm handler is to pass a block
+to the `start' method or to the `restart' method, which will
+just cause `replace_alarm_handler' to be called first.
+
+There are a number of short forms for creating a timer and
+setting its alarm handler. The following statements are
+pairwise equivalent:
+
+ sporadic_timer = EventLoop.after(3.seconds) do ... end
+ sporadic_timer = 3.seconds.from_now { ... }
+
+ periodic_timer = EventLoop.every(3.seconds) do ... end
+ periodic_timer = 3.seconds.from_now_and_repeat { ... }
+
+ sporadic_timer = other_loop.after(3.seconds) do ... end
+ EventLoop.with_current(other_loop) do
+ sporadic_timer = 3.seconds.from_now { ... } end
+
+ periodic_timer = other_loop.every(3.seconds) do ... end
+ EventLoop.with_current(other_loop) do
+ periodic_timer = 3.seconds.from_now_and_repeat { ... } end
+
+ idle_function_timer = EventLoop.every(0) { ... }
+ idle_function_timer = EventLoop.repeat { ... }
+
+ one_shot_idle_function_timer = EventLoop.after(0) { ... }
+ one_shot_idle_function_timer = EventLoop.later { ... }
+
+All of the above forms automatically start the timer.
+
+When a timer is started, the event loop associated with the
+timer is notified and its timeout value updated accordingly.
+(Unlike `Watchable#monitor_event', the `Timer#start' method
+does not depend on the value of `EventLoop.current'.)
+
+By passing `:event_loop => foo' to the timer constructor,
+you can specify which event loop the timer should use;
+otherwise, the "current event loop" (`EventLoop.current')
+will be used as a default.
+
+You can ask a timer for the amount of time left by invoking
+its `time_left' method. When called from an alarm handler,
+it will typically return a negative value, representing the
+amount of time passed since the alarm was supposed to sound.
+
+However, if you specify `:tolerance => 0.1' when creating
+the timer, you are saying it's okay for the alarm to sound
+one-tenth of a second too early. In that case, `time_left'
+can return a positive value even when called from within an
+alarm handler, indicating the alarm sounded too early.
+
+The next section explains why you should set the tolerance
+higher than zero (it is currently 0.001 by default).
+
+
+Timer Tolerances
+................
+
+It is useful for timers to have some amount of tolerance
+because the timeout specified to `select' is an upper bound.
+This means that the process will usually wake up slightly
+earlier than expected. For example, if you start a timer
+set for two seconds and then enter an event loop iteration,
+the call to `select' is likely to return in 1.99 seconds.
+
+If your timer's tolerance is set to zero, that means that
+the alarm must not be sounded yet, and the event loop is
+forced to perform an extra iteration with a 0.01 timeout.
+If, on the other hand, the tolerance of the timer is set to
+at least 0.01 seconds, then that means it's okay for the
+alarm to sound slightly too early; in this case, the need
+for an extra iteration can be avoided.
+
+Currently, the error made by a tolerant timer during one
+iteration is not compensated for during the next iteration.
+Combined with the fact that a tolerant timer will usually
+sound too early rather than too late (because the kernel
+tries hard not to wake the process too late), this means
+that the more tolerant your timer is, the more frequently it
+will sound. In other words, the error accumulates.
+
+This should not be a problem in most cases, simply because
+of the fact that people do not in most cases use Ruby for
+applications that need this kind of precision (the default
+timer tolerance is one millisecond). However, if you would
+like to see this problem addressed, please contact me.
+
+
+Running the Event Loop
+----------------------
+
+To run the current event loop, just call `EventLoop.run'.
+That'll block until an event handler says `EventLoop.quit'.
+
+One typical event loop application looks like this:
+
+ ...
+ @socket.on_writable { ... }
+ @socket.on_readable do
+ ...
+ if something_or_other
+ EventLoop.quit
+ end
+ end
+ ...
+ @timer.start
+ ...
+ EventLoop.run
+
+While the event loop is running, everything that the
+application does takes place in event handlers.
+
+If you want more control, you can run a single iteration of
+the event loop by calling `EventLoop.iterate', which takes
+an optional argument specifying (in seconds) the upper bound
+on the amount of time to block. The default value (`nil')
+means infinity; it causes the upper bound to be the amount
+of time left before the next timer is due. If you're not
+using timers, `EventLoop.iterate' without an argument blocks
+until the first interesting IO event occurs.
+
+That should be all you need to know to use this event loop.
+If it turns out not to be, please bug me on IRC (as I said,
+I'm `dbrock' on Freenode) or send me an e-mail. The source
+shouldn't be particularly hard to understand either.
+
+Thanks for your interest, and happy hacking!
+
+ --- Daniel Brockman
+
+
+## Local Variables:
+## coding: utf-8
+## time-stamp-format: "%:b %:d, %:y"
+## time-stamp-start: "Updated: "
+## time-stamp-end: "$"
+## End:
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/README.utf-8
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/README.utf-8 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/README.utf-8 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,704 @@
+README for event-loop 0.3
+Copyright © 2005, 2006 Daniel Brockman
+
+Author: Daniel Brockman <daniel at brockman.se>
+Written: August 19, 2005
+Updated: October 19, 2006
+
+This document describes a simple signal system and an event
+loop that uses said signal system.
+
+Permission is granted to copy, distribute and/or modify this
+document under the terms of the GNU Free Documentation
+License, Version 1.2 or any later version published by the
+Free Software Foundation; with no Invariant Sections, no
+Front-Cover Texts, and no Back-Cover Texts. You should have
+received a copy of the license along with this document; if
+not, write to the Free Software Foundation, 51 Franklin
+Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+The next screenful of paragraphs is mostly side-chatter.
+If you scroll down a bit, you’ll find a table of contents.
+
+If you have any questions, comments, requests, bug reports,
+or patches, please send them to me at <daniel at brockman.se>.
+
+I have a Darcs repository at this URL,
+
+ <http://www.brockman.se/software/ruby-event-loop/>
+
+so you can easily grab a copy of the latest version:
+
+ $ darcs get http://www.brockman.se/software/ruby-event-loop/
+
+If you make any changes, you can send them back to me:
+
+ $ darcs record
+ $ darcs send
+
+You can also reach me on IRC as ‘dbrock’ on Freenode.
+But I’ll say that a couple more times below.
+
+This software originated in a Direct Connect client
+codenamed Refusde, but now stands on its own.
+
+Thanks to Tilman Sauerbeck for prompting me to wrap these
+files up in a self-contained package, and then demanding
+documentation. Without him, this file wouldn’t exist. :-)
+
+Tilman also helped by creating a RubyGems specification,
+and by translating the Makefile into a Rakefile.
+
+To counteract confusion, I’ll say a few words about
+version numbers before I begin.
+
+Prior to version 0.1, this package used to have version
+numbers like 0.0.20051116 — a kind of self-inflicted FUD.
+Now that it’s been used by at least a couple of people other
+than myself, I’ve decided to switch to a more conventional
+line of version numbers: 0.1, 0.2, ..., 0.10, and so on.
+
+Should a smart person read the code today and come across
+some deeply embedded idiocy, I can point to these other
+people as proof that I’m not the only idiot around here.
+Then I can proceed to fix the idiocy by completely changing
+the API, totally breaking compatibility, and if anyone
+complains I can always tell them they’re idiots.
+
+
+Table of Contents
+=================
+
+This document is getting long enough that I’m starting to
+have trouble navigating it myself. I probably should
+convert it into an Info manual. But until that happens,
+maybe this table of contents will help a little.
+
+You will note that there’s no section that says anything
+about the file ‘better-definers.rb’. I don’t have any good
+excuse for this; maybe I’ll write that section some day.
+In the meantime, the code itself is really easy to follow.
+
+ * Installation
+ * The Signal System: ‘signal-system.rb’
+ - Emitting Signals
+ - Receiving Signals
+ * The Event Loop: ‘event-loop.rb’
+ - Creating Event Loops
+ - Event Sources
+ - IO Events: ‘io.rb’
+ - Timer Events: ‘timer.rb’
+ - Running the Event Loop
+
+Installation
+============
+
+I suppose the recommended way of using this software is to
+install the gem package. But if you don’t want to do that,
+you could simply copy or symlink the files into your project
+directory and require whichever ones you need.
+
+The files in ‘lib/event-loop/’ must reside in a directory
+called ‘event-loop/’ for the dependencies to work, so if you
+are taking anything, take the whole ‘event-loop/’ directory.
+
+While ‘signal-system.rb’ depends on ‘better-definers.rb’,
+and ‘event-loop.rb’ depends on both the aforementioned two,
+it is quite possible to use ‘signal-system.rb’ without
+‘event-loop.rb’, or ‘better-definers.rb’ without either.
+
+Indeed, ‘better-definers.rb’ and ‘signal-system.rb’ are two
+very general-purpose libraries, and you are likely to be
+able to put them to use for most applications, regardless of
+whether or not they are based on an event loop.
+
+For completeness, ‘timer.rb’ depends on ‘event-loop.rb’,
+and ‘event-loop.rb’ and ‘io.rb’ depend on each other.
+
+
+The Signal System
+=================
+
+This package comes with an intra-process signal system.
+I call these signals “intra-process signals” to distinguish
+them from Unix signals, which are “inter-process signals”.
+
+But now that I’ve made that clear, I’m going to go ahead and
+refer to intra-process signals as just “signals”, and to the
+intra-process signal system simply as “the signal system”.
+
+Anyway, the signal system is a generic callback mechanism,
+similar in spirit to the ‘observable’ library that comes
+with the standard Ruby distribution. It allows anonymous
+observers to track the events of signal-emitting objects
+(thereby helping to decrease coupling).
+
+The idea is similar to that of the GObject signal system,
+and a myriad of others like it. (If you know INTERCAL,
+signals are to methods as COME FROM is to GO TO.)
+
+
+Emitting Signals
+----------------
+
+If you want your objects to be capable of emitting signals,
+their classes should include the ‘SignalEmitter’ module.
+That will automatically rig the classes with everything you
+need to define signals (the ‘SignalEmitterModule’ module).
+
+For example, this code
+
+ class Dog
+ include SignalEmitter
+ define_signal :bark
+ def bark
+ puts "Bark!"
+ signal :bark
+ end
+ end
+
+ spot = Dog.new
+ spot.on_signal :bark do
+ puts "Hush, Spot!"
+ end
+
+ 3.times { spot.bark }
+
+results in the following output:
+
+ Bark!
+ Hush, Spot!
+ Bark!
+ Hush, Spot!
+ Bark!
+ Hush, Spot!
+
+
+Receiving Signals
+-----------------
+
+The true name for the method that connects a block or proc
+to a signal is ‘add_signal_handler’. However, that gets
+awfully long when connecting to lots of signals, so there
+are a couple of shortcuts.
+
+First, there are the ‘on’ and ‘on_signal’ aliases.
+These synonyms just delegate to ‘add_signal_handler’.
+
+Second, every predefined signal FOO gets a shorthand
+connector method called ‘on_FOO’. So in the above example,
+we could have written this instead:
+
+ spot = Dog.new
+ spot.on_bark { puts "Hush, Spot!" }
+
+The ‘on_FOO’ variant is nice for connecting to signals whose
+names are known statically. Otherwise, ‘on_signal’ is
+usually preferable; these other cases are typically not
+common enough to justify the short form ‘on’.
+
+Connecting signal handlers is a breeze, but *disconnecting*
+them is actually a minor pain in the ass. Without having an
+explicit reference to the handler, you cannot identify it,
+which means that you cannot tell ‘remove_signal_handler’
+what handler you want to remove.
+
+An example of the obvious but painful solution follows:
+
+ spot = Dog.new
+ @bark_handler = lambda { puts "Hush, Spot!" }
+ spot.on_bark &@bark_handler
+ # ...
+ spot.remove_signal_handler @bark_handler
+
+The ‘SignalObserver’ module provides a slightly
+better solution:
+
+ class DogOwner
+ include SignalObserver
+
+ def initialize(dog=nil)
+ @dog = dog || Dog.new("Spot")
+ observe_signal @dog, :bark do
+ puts "Shut up, #{@dog.name}"
+ end
+ end
+
+ def stop_yelling
+ ignore_signal @dog, :bark
+ end
+ end
+
+But ‘SignalObserver’ has a few caveats that make it
+insufficient as a general solution (see the source).
+
+In the future, I’d like to provide a more powerful mechanism
+for connecting to signals. Perhaps involving handler tags.
+If you want this feature, nagging me about it on IRC (I’m
+‘dbrock’ on Freenode) or via e-mail usually works best.
+
+
+The Event Loop
+==============
+
+This section explains how IO multiplexing works in general
+(albeit briefly and not very in-depth), and specifically the
+issues relevant for Ruby applications. You may safely skip
+it if you (a) already know this subject, or (b) don’t care.
+
+Plain ol’ blocking IO works well when you’re reading from
+just a single file descriptor. But when you’re interested
+in a whole bunch of FDs, you can’t wait for any single one
+of them to become readable or writable, because then you’ll
+inevitably miss that happening to the other ones. Instead,
+you need a multiplexer that can wait for them *all at once*.
+
+There are a handful of low-level multiplexing primitives:
+‘select’, ‘poll’, ‘epoll’, ‘/dev/poll’, and ‘kqueue’.
+In addition, there are portable low-level wrapper libraries
+such as libevent, which can use any of those primitives.
+The event loop in this package uses the standard ‘select’
+wrapper shipped with Ruby, ‘IO::select’. But in the future,
+I’d like to use libevent instead, because that’d be cooler.
+
+Most applications use a higher-level abstraction built on
+top of the low-level multiplexer, usually called a ‘main
+loop’, an ‘event loop’, or an ‘event source’. There are
+also libraries such as liboop, which generalizes the event
+source and event sink concepts, so that components (event
+sinks) written against liboop become event-source-agnostic.
+
+Actually, the combination of blocking IO and Ruby’s green
+threads works well in most cases where you would normally
+use an event loop. When you call ‘IO#read’ on an empty file
+descriptor, for instance, Ruby suspends that thread until
+its internal event loop, known as the scheduler (currently
+based on ‘select’), determines that the file descriptor has
+become readable. In particular, Ruby never calls the
+low-level ‘read’ function unless it knows that it will not
+block (because ‘select’ said it wouldn’t, but see below).
+
+There are several reasons why you would use an event loop
+such as the one implemented by this library instead of
+not-so-plain ol’ blocking IO with Ruby’s green threads.
+
+First of all, you may consider the event loop API more
+pleasant than Ruby’s threads and not-quite-blocking IO.
+Otherwise, don’t listen to me; go on using the latter. :-)
+
+Blocking IO can occasionally cause unexpected problems.
+For example, in some cases a blocking read *can* block even
+though select said that the file descriptor was readable.
+This problem may be rare (it can happen, for instance, when
+the checksum of a piece of data fails to match the payload),
+but the bottom line is that non-blocking IO is safer.
+
+Perhaps most importantly, while Ruby’s threads are green,
+they are still effectively preemptively scheduled, with all
+the implications thereof — in a word, synchronization hell.
+By contrast, event handlers are executed in a strictly
+sequential manner; an event loop will never run two event
+handlers simultaneously. (Though, of course, all bets are
+off if you run multiple event loops in separate threads.)
+
+
+Creating Event Loops
+--------------------
+
+You create a new event loop by calling ‘EventLoop.new’.
+However, if you only need one — which is likely — you
+can get it for free by reading from ‘EventLoop.default’.
+
+If you need multiple event loops in separate threads, put
+them in ‘EventLoop.current’, which will make each of them
+end up in a thread-local variable.
+
+Actually, if you read from ‘EventLoop.current’ before
+writing to it, it defaults to ‘EventLoop.default’, so you
+might as well use ‘EventLoop.current’ in single-threaded
+applications as well.
+
+In fact, ‘EventLoop.current’ is so common that it can be
+shortened to just ‘EventLoop’, if there is no ambiguity.
+So ‘EventLoop.run’ is short for ‘EventLoop.current.run’.
+
+To dynamically change the “current event loop” for a block
+of code, it is convenient to use ‘EventLoop.with_current’.
+
+Once you’ve got the event loop sitting in front of you just
+waiting to be used, you’ll want to add some event sources,
+and then finally run the loop. So, first things first:
+What are event sources, and how do you add them to the loop?
+
+
+Event Sources
+-------------
+
+As for the first question, this package currently supports
+two kinds of event sources: watchable IO objects and timers.
+The former kind is used to detect file descriptor activity;
+the latter is used for wall-clock scheduling of execution.
+
+Typically, you don’t add event sources to the loop manually.
+Both watchable IO objects and timers provide convenient ways
+of making them add themselves to the “current” event loop.
+
+For example, to add ‘@io’ to the current event loop, you
+might write something like the following:
+
+ @io.monitor_events :readable, :writable
+
+That’s short for the following more explicit code:
+
+ EventLoop.current.monitor_io(@io, :readable, :writable)
+
+The call to ‘EventLoop#monitor_io’ causes the event loop to
+wake up — if it was sleeping in a call to ‘IO.select’ — and
+adds ‘@io’ to the event loop’s set of monitored IO objects.
+For the next event loop iteration, ‘@io’ will be included in
+one or more of the sets passed to ‘IO.select’.
+
+To add the IO object to another event loop,
+
+ other_loop.monitor_io(@io, :readable, :writable)
+
+you can do it like this,
+
+ EventLoop.with_current(other_loop) do
+ @io.monitor_events :readable, :writable end
+
+which is especially convenient when adding multiple objects.
+
+For timers, it’s even easier:
+
+ @timer = EventLoop::PeriodicTimer.new(1.second)
+ @timer.start
+
+That call to ‘@timer.start’ causes the following to happen:
+
+ EventLoop.current.monitor_timer(@timer)
+
+The call to ‘EventLoop#monitor_timer’ may force the event
+loop to wake up, depending on the timer readings and the
+current timeout of the event loop. In any case, the timer
+is added to the event loop’s set of monitored timers.
+
+But there’s a caveat. This will not work as expected:
+
+ EventLoop.with_current(other_loop) { @timer.start }
+
+That’s because timer objects decide in advance which event
+loop they are going to use. Once initialized, timer objects
+no longer care about the value of the current event loop.
+Hence, this code starts a timer in a different event loop:
+
+ @timer = EventLoop.with_current(other_loop) do
+ EventLoop::PeriodicTimer.new(1.second) end
+ @timer.start
+
+Here is another way of writing it:
+
+ @timer = EventLoop::PeriodicTimer.new \
+ 1.second, :event_loop => other_loop
+ @timer.start
+
+In addition, there are quite a few convenient short forms.
+For example, you can write things like this:
+
+ 3.seconds.from_now { puts "Boo!" }
+
+Read on, because the next two sections describe with better
+examples and in more detail how IO and timer events work.
+
+[Actually, there are no examples of using timers at all.
+But it would be nice to have some under “Timer Events”.]
+
+
+IO Events
+---------
+
+In Ruby, file descriptors are instances of the class IO.
+Before you can use one of these with the event loop, you
+need to extend it with the module ‘EventLoop::Watchable’.
+That module defines two signals, ‘readable’ and ‘writable’,
+and a pair of methods for activating and deactivating them.
+
+When you want to start receiving ‘readable’ signals, for
+instance, you call ‘io.monitor_event :readable’. This makes
+the current event loop monitor ‘io’ for readability, and
+emit the ‘readable’ signal on it when the condition occurs.
+
+ require "socket"
+
+ def initialize (host, port)
+ @socket = TCPSocket.new(host, port)
+ @socket.extend EventLoop::Watchable
+ @socket.will_block = false
+ @socket.on_readable { perform_read }
+ end
+
+ def start_listening
+ @socket.monitor_event :readable
+ end
+
+The ‘will_block?’ property is provided by this package as a
+convenient way of setting up non-blocking IO streams.
+(See ‘lib/event-loop/io.rb’, circa line 81.)
+
+The method that actually performs the reading will probably
+look more or less like so:
+
+ def perform_read
+ process_data @socket.sysread(BUFFER_SIZE, @buffer)
+ rescue EOFError
+ ...
+ rescue Errno::ECONNRESET
+ ...
+ rescue
+ ...
+ end
+
+If you don’t want to receive any more ‘readable’ signals,
+you just call ‘io.ignore_event :readable’.
+
+ def stop_listening
+ @socket.ignore_event :readable
+ end
+
+The “current event loop” is just ‘EventLoop.current’.
+To make another event loop (say ‘other_loop’) monitor or
+ignore an IO event, either call ‘other_loop.monitor_io’ or
+‘other_loop.ignore_io’ directly,
+
+ other_loop.monitor_io(io, :readable)
+ other_loop.ignore_io(io, :writable)
+
+or use the ‘EventLoop.with_current’ form,
+
+ EventLoop.with_current(other_loop) do
+ io.monitor_event :readable
+ io.ignore_event :writable
+ end
+
+which implements “dynamic scoping” of ‘EventLoop.current’.
+
+If you simply want readable signals to be emitted whenever
+there are handlers connected to the ‘readable’ signal (and
+likewise for ‘writable’), without having to mess around with
+‘monitor_event’ and ‘ignore_event’, you can extend the IO
+object with the ‘EventLoop::Watchable::Automatic’ module
+instead of ‘EventLoop::Watchable’.
+
+The ‘EventLoop::Watchable::Automatic’ module sets it up so
+that when you connect a handler to either the ‘readable’ or
+the ‘writable’ signal, the current event loop begins
+monitoring the IO object for the corresponding condition,
+and, inversely, when you remove the last handler, it tells
+the event loop to stop monitoring the condition.
+
+Because this is so often useful, you don’t even have to
+extend the IO object yourself. Stub implementations of the
+‘on_readable’ and ‘on_writable’ methods are provided, which
+automatically bootstrap the IO by extending it with the
+‘EventLoop::Watchable::Automatic’ module when invoked.
+
+ @socket = TCPSocket.new(host, port)
+ @socket.will_block = false
+
+ # By invoking the stub ‘on_readable’ method,
+ # we implicitly extend the IO object with the
+ # module ‘EventLoop::Watchable::Automatic’.
+ #
+ # That module hooks into the signal system and
+ # reacts when we start watching the ‘readable’
+ # signal by starting to monitor that event.
+ @socket.on_readable { perform_read }
+
+Note that once an IO object has been extended with the
+‘EventLoop::Watchable::Automatic’ module, there is currently
+no way to make it non-automatic (Ruby does not yet allow you
+to un-extend an object with a module). So if you don’t want
+the automatic behavior, you *have* to manually extend the
+object with the ‘EventLoop::Watchable’ module before calling
+either of the ‘or_readable’ and ‘on_writable’ methods.
+
+There is actually a third signal: ‘exceptional’, which is
+emitted when ‘select’ reports that the file descriptor is in
+an “exceptional state”. You probably don’t need to worry
+about this (and if you do, you’ll probably know it already).
+
+But in case you’re wondering, I think you can use it to
+watch for out-of-band data coming through a socket, provided
+you’ve set the right socket options. I also believe you can
+use it to determine that a non-blocking connection attempt
+has failed. (When such an attempt succeeds, a writability
+event is fired for the socket.) But that doesn’t matter,
+because you can’t do non-blocking connects in Ruby. :-)
+
+
+Timer Events
+------------
+
+If you need to do something after a given amount of
+wall-clock time has passed, just do the following:
+
+ 1. Create an ‘EventLoop::SporadicTimer’, passing the
+ timeout (in seconds) to the constructor.
+ 2. Connect to its ‘alarm’ signal (using ‘on_alarm’).
+ 3. Start the timer (using ‘start’).
+
+Sporadic timers only sound their alarm once, and then stop.
+If you want to do something periodically, like every second,
+use ‘EventLoop::PeriodicTimer’ instead.
+
+You can start a sporadic timer as many times as you want,
+but it will still stop itself every time it goes off.
+Periodic timers must be stopped explicitly (using ‘stop’),
+or they will keep going off as long as the event loop runs.
+
+You can get the effect of an “idle function” by creating an
+periodic timer with a zero-second interval, meaning its
+alarm will sound as often as possible.
+
+If you pass a block to a timer constructor, then that block
+will become the timer’s canonical “alarm handler”, which is
+just a signal handler for the ‘alarm’ signal, except that
+you can easily replace it, using ‘replace_alarm_handler’.
+
+Another way to replace the alarm handler is to pass a block
+to the ‘start’ method or to the ‘restart’ method, which will
+just cause ‘replace_alarm_handler’ to be called first.
+
+There are a number of short forms for creating a timer and
+setting its alarm handler. The following statements are
+pairwise equivalent:
+
+ sporadic_timer = EventLoop.after(3.seconds) do ... end
+ sporadic_timer = 3.seconds.from_now { ... }
+
+ periodic_timer = EventLoop.every(3.seconds) do ... end
+ periodic_timer = 3.seconds.from_now_and_repeat { ... }
+
+ sporadic_timer = other_loop.after(3.seconds) do ... end
+ EventLoop.with_current(other_loop) do
+ sporadic_timer = 3.seconds.from_now { ... } end
+
+ periodic_timer = other_loop.every(3.seconds) do ... end
+ EventLoop.with_current(other_loop) do
+ periodic_timer = 3.seconds.from_now_and_repeat { ... } end
+
+ idle_function_timer = EventLoop.every(0) { ... }
+ idle_function_timer = EventLoop.repeat { ... }
+
+ one_shot_idle_function_timer = EventLoop.after(0) { ... }
+ one_shot_idle_function_timer = EventLoop.later { ... }
+
+All of the above forms automatically start the timer.
+
+When a timer is started, the event loop associated with the
+timer is notified and its timeout value updated accordingly.
+(Unlike ‘Watchable#monitor_event’, the ‘Timer#start’ method
+does not depend on the value of ‘EventLoop.current’.)
+
+By passing ‘:event_loop => foo’ to the timer constructor,
+you can specify which event loop the timer should use;
+otherwise, the “current event loop” (‘EventLoop.current’)
+will be used as a default.
+
+You can ask a timer for the amount of time left by invoking
+its ‘time_left’ method. When called from an alarm handler,
+it will typically return a negative value, representing the
+amount of time passed since the alarm was supposed to sound.
+
+However, if you specify ‘:tolerance => 0.1’ when creating
+the timer, you are saying it’s okay for the alarm to sound
+one-tenth of a second too early. In that case, ‘time_left’
+can return a positive value even when called from within an
+alarm handler, indicating the alarm sounded too early.
+
+The next section explains why you should set the tolerance
+higher than zero (it is currently 0.001 by default).
+
+
+Timer Tolerances
+................
+
+It is useful for timers to have some amount of tolerance
+because the timeout specified to ‘select’ is an upper bound.
+This means that the process will usually wake up slightly
+earlier than expected. For example, if you start a timer
+set for two seconds and then enter an event loop iteration,
+the call to ‘select’ is likely to return in 1.99 seconds.
+
+If your timer’s tolerance is set to zero, that means that
+the alarm must not be sounded yet, and the event loop is
+forced to perform an extra iteration with a 0.01 timeout.
+If, on the other hand, the tolerance of the timer is set to
+at least 0.01 seconds, then that means it’s okay for the
+alarm to sound slightly too early; in this case, the need
+for an extra iteration can be avoided.
+
+Currently, the error made by a tolerant timer during one
+iteration is not compensated for during the next iteration.
+Combined with the fact that a tolerant timer will usually
+sound too early rather than too late (because the kernel
+tries hard not to wake the process too late), this means
+that the more tolerant your timer is, the more frequently it
+will sound. In other words, the error accumulates.
+
+This should not be a problem in most cases, simply because
+of the fact that people do not in most cases use Ruby for
+applications that need this kind of precision (the default
+timer tolerance is one millisecond). However, if you would
+like to see this problem addressed, please contact me.
+
+
+Running the Event Loop
+----------------------
+
+To run the current event loop, just call ‘EventLoop.run’.
+That’ll block until an event handler says ‘EventLoop.quit’.
+
+One typical event loop application looks like this:
+
+ ...
+ @socket.on_writable { ... }
+ @socket.on_readable do
+ ...
+ if something_or_other
+ EventLoop.quit
+ end
+ end
+ ...
+ @timer.start
+ ...
+ EventLoop.run
+
+While the event loop is running, everything that the
+application does takes place in event handlers.
+
+If you want more control, you can run a single iteration of
+the event loop by calling ‘EventLoop.iterate’, which takes
+an optional argument specifying (in seconds) the upper bound
+on the amount of time to block. The default value (‘nil’)
+means infinity; it causes the upper bound to be the amount
+of time left before the next timer is due. If you’re not
+using timers, ‘EventLoop.iterate’ without an argument blocks
+until the first interesting IO event occurs.
+
+That should be all you need to know to use this event loop.
+If it turns out not to be, please bug me on IRC (as I said,
+I’m ‘dbrock’ on Freenode) or send me an e-mail. The source
+shouldn’t be particularly hard to understand either.
+
+Thanks for your interest, and happy hacking!
+
+ — Daniel Brockman
+
+
+## Local Variables:
+## coding: utf-8
+## time-stamp-format: "%:b %:d, %:y"
+## time-stamp-start: "Updated: "
+## time-stamp-end: "$"
+## End:
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/Rakefile
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/Rakefile 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/Rakefile 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,82 @@
+require "rake/packagetask"
+
+begin
+ require "rubygems"
+ require "rake/gempackagetask"
+ $have_rubygems = true
+rescue
+ $have_rubygems = false
+end
+
+PKG_VERSION = File.open("VERSION").read.chomp
+PKG_FILES = FileList[*%w{[A-Z]* README setup.rb lib/**/*.rb}]
+
+MAINTAINER_MAILNAME = "wigwam.brockman.se"
+SCP_TARGET = "teepee:/var/www/www.brockman.se/software/ruby-event-loop/"
+
+task :default => ["README", :check]
+
+file "README" => ["README.utf-8", "Rakefile"] do |t|
+ # I'm paranoid and I don't trust Ruby with Unicode.
+ if "a—b".gsub(/—/, "---") != "a---b"
+ warn "Can't build `README': broken Unicode support."
+ else
+ File.open("README", "w") do |out|
+ out << "\
+------------------------------------------------------------
+ This file was automatically generated from `README.utf-8'.
+------------------------------------------------------------\n\n"
+ IO.foreach("README.utf-8") do |line|
+ out << line.
+ gsub(/©/, "(C)").
+ gsub(/—/, "---").
+ gsub(/‘/, "`").
+ gsub(/’/, "'").
+ gsub(/“/, "\"").
+ gsub(/”/, "\"")
+ end
+ end
+ end
+end
+
+task :check do
+ Dir.foreach("lib/event-loop/") do |basename|
+ next if basename[0] == ?.
+ system %{ruby -w -Ilib "lib/event-loop/#{basename}"}
+ end
+end
+
+if $have_rubygems
+ GEMSPEC = Gem::Specification.new do |gem|
+ gem.name = "event-loop"
+ gem.version = PKG_VERSION
+ gem.summary = "Simple and usable event loop and signal system"
+ gem.description =
+ "This package contains a simple select-based event loop " +
+ "featuring IO (file descriptor) and timer event sources. " +
+ "It comes with a signal system inspired by that of GLib."
+ gem.files = PKG_FILES.to_a
+ gem.require_path = "lib"
+ gem.author = "Daniel Brockman"
+ gem.email = "daniel at brockman.se"
+ gem.homepage = "http://www.brockman.se/software/ruby-event-loop/"
+ end
+
+ Rake::GemPackageTask.new(GEMSPEC) do |task|
+ task.need_tar_gz = true
+ end
+else
+ Rake::PackageTask.new("event-loop", PKG_VERSION) do |task|
+ task.need_tar_gz = true
+ task.package_files = PKG_FILES
+ end
+end
+
+task :upload => :package do
+ mailname = File.read("/etc/mailname").chomp rescue "(none)"
+ if mailname == MAINTAINER_MAILNAME
+ sh %{scp -r pkg/*#{PKG_VERSION}* #{SCP_TARGET}}
+ else
+ warn "The `upload' target is only meant for the maintainer."
+ end
+end
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/VERSION
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/VERSION 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/VERSION 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1 @@
+0.3
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/better-definers.rb
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/better-definers.rb 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/better-definers.rb 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,369 @@
+## better-definers.rb --- better attribute and method definers
+# Copyright (C) 2005 Daniel Brockman
+
+# This program 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.
+
+# This file 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.
+
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the Free
+# Software Foundation, 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+class Symbol
+ def predicate?
+ to_s.include? "?" end
+ def imperative?
+ to_s.include? "!" end
+ def writer?
+ to_s.include? "=" end
+
+ def punctuated?
+ predicate? or imperative? or writer? end
+ def without_punctuation
+ to_s.delete("?!=").to_sym end
+
+ def predicate
+ without_punctuation.to_s + "?" end
+ def imperative
+ without_punctuation.to_s + "!" end
+ def writer
+ without_punctuation.to_s + "=" end
+end
+
+class Hash
+ def collect! (&block)
+ replace Hash[*collect(&block).flatten]
+ end
+
+ def flatten
+ to_a.flatten
+ end
+end
+
+module Kernel
+ def returning (value)
+ yield value ; value
+ end
+end
+
+class Module
+ def define_hard_aliases (name_pairs)
+ for new_aliases, existing_name in name_pairs do
+ new_aliases.kind_of? Array or new_aliases = [new_aliases]
+ for new_alias in new_aliases do
+ alias_method(new_alias, existing_name)
+ end
+ end
+ end
+
+ def define_soft_aliases (name_pairs)
+ for new_aliases, existing_name in name_pairs do
+ new_aliases.kind_of? Array or new_aliases = [new_aliases]
+ for new_alias in new_aliases do
+ class_eval %{def #{new_alias}(*args, &block)
+ #{existing_name}(*args, &block) end}
+ end
+ end
+ end
+
+ define_soft_aliases \
+ :define_hard_alias => :define_hard_aliases,
+ :define_soft_alias => :define_soft_aliases
+
+ # This method lets you define predicates like :foo?,
+ # which will be defined to return the value of @foo.
+ def define_readers (*names)
+ for name in names.map { |x| x.to_sym } do
+ if name.punctuated?
+ # There's no way to define an efficient reader whose
+ # name is different from the instance variable.
+ class_eval %{def #{name} ; @#{name.without_punctuation} end}
+ else
+ # Use `attr_reader' to define an efficient method.
+ attr_reader(name)
+ end
+ end
+ end
+
+ def writer_defined? (name)
+ method_defined? name.to_sym.writer
+ end
+
+ # If you pass a predicate symbol :foo? to this method, it'll first
+ # define a regular writer method :foo, without a question mark.
+ # Then it'll define an imperative writer method :foo! as a shorthand
+ # for setting the property to true.
+ def define_writers (*names, &body)
+ for name in names.map { |x| x.to_sym } do
+ if block_given?
+ define_method(name.writer, &body)
+ else
+ attr_writer(name.without_punctuation)
+ end
+ if name.predicate?
+ class_eval %{def #{name.imperative}
+ self.#{name.writer} true end}
+ end
+ end
+ end
+
+ define_soft_aliases \
+ :define_reader => :define_readers,
+ :define_writer => :define_writers
+
+ # We don't need a singular alias for `define_accessors',
+ # because it always defines at least two methods.
+
+ def define_accessors (*names)
+ define_readers(*names)
+ define_writers(*names)
+ end
+
+ def define_opposite_readers (name_pairs)
+ name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] }
+ for opposite_name, name in name_pairs do
+ define_reader(name) unless method_defined? name
+ class_eval %{def #{opposite_name} ; not #{name} end}
+ end
+ end
+
+ def define_opposite_writers (name_pairs)
+ name_pairs.collect! { |k, v| [k.to_sym, v.to_sym] }
+ for opposite_name, name in name_pairs do
+ define_writer(name) unless writer_defined? name
+ class_eval %{def #{opposite_name.writer} x
+ self.#{name.writer} !x end}
+ class_eval %{def #{opposite_name.imperative}
+ self.#{name.writer} false end}
+ end
+ end
+
+ define_soft_aliases \
+ :define_opposite_reader => :define_opposite_readers,
+ :define_opposite_writer => :define_opposite_writers
+
+ def define_opposite_accessors (name_pairs)
+ define_opposite_readers name_pairs
+ define_opposite_writers name_pairs
+ end
+
+ def define_reader_with_opposite (name_pair, &body)
+ name, opposite_name = name_pair.flatten.collect { |x| x.to_sym }
+ define_method(name, &body)
+ define_opposite_reader(opposite_name => name)
+ end
+
+ def define_writer_with_opposite (name_pair, &body)
+ name, opposite_name = name_pair.flatten.collect { |x| x.to_sym }
+ define_writer(name, &body)
+ define_opposite_writer(opposite_name => name)
+ end
+
+ public :define_method
+
+ def define_methods (*names, &body)
+ names.each { |name| define_method(name, &body) }
+ end
+
+ def define_private_methods (*names, &body)
+ define_methods(*names, &body)
+ names.each { |name| private name }
+ end
+
+ def define_protected_methods (*names, &body)
+ define_methods(*names, &body)
+ names.each { |name| protected name }
+ end
+
+ def define_private_method (name, &body)
+ define_method(name, &body)
+ private name
+ end
+
+ def define_protected_method (name, &body)
+ define_method(name, &body)
+ protected name
+ end
+end
+
+class ImmutableAttributeError < StandardError
+ def initialize (attribute=nil, message=nil)
+ super message
+ @attribute = attribute
+ end
+
+ define_accessors :attribute
+
+ def to_s
+ if @attribute and @message
+ "cannot change the value of `#@attribute': #@message"
+ elsif @attribute
+ "cannot change the value of `#@attribute'"
+ elsif @message
+ "cannot change the value of attribute: #@message"
+ else
+ "cannot change the value of attribute"
+ end
+ end
+end
+
+class Module
+ # Guard each of the specified attributes by replacing the writer
+ # method with a proxy that asks the supplied block before proceeding
+ # with the change.
+ #
+ # If it's okay to change the attribute, the block should return
+ # either nil or the symbol :mutable. If it isn't okay, the block
+ # should return a string saying why the attribute can't be changed.
+ # If you don't want to provide a reason, you can have the block
+ # return just the symbol :immutable.
+ def guard_writers(*names, &predicate)
+ for name in names.map { |x| x.to_sym } do
+ define_hard_alias("__unguarded_#{name.writer}" => name.writer)
+ define_method(name.writer) do |new_value|
+ case result = predicate.call
+ when :mutable, nil
+ __send__("__unguarded_#{name.writer}", new_value)
+ when :immutable
+ raise ImmutableAttributeError.new(name)
+ else
+ raise ImmutableAttributeError.new(name, result)
+ end
+ end
+ end
+ end
+
+ def define_guarded_writers (*names, &block)
+ define_writers(*names)
+ guard_writers(*names, &block)
+ end
+
+ define_soft_alias :guard_writer => :guard_writers
+ define_soft_alias :define_guarded_writer => :define_guarded_writers
+end
+
+if __FILE__ == $0
+ require "test/unit"
+
+ class DefineAccessorsTest < Test::Unit::TestCase
+ def setup
+ @X = Class.new
+ @Y = Class.new @X
+ @x = @X.new
+ @y = @Y.new
+ end
+
+ def test_define_hard_aliases
+ @X.define_method(:foo) { 123 }
+ @X.define_method(:baz) { 321 }
+ @X.define_hard_aliases :bar => :foo, :quux => :baz
+ assert_equal @x.foo, 123
+ assert_equal @x.bar, 123
+ assert_equal @y.foo, 123
+ assert_equal @y.bar, 123
+ assert_equal @x.baz, 321
+ assert_equal @x.quux, 321
+ assert_equal @y.baz, 321
+ assert_equal @y.quux, 321
+ @Y.define_method(:foo) { 456 }
+ assert_equal @y.foo, 456
+ assert_equal @y.bar, 123
+ @Y.define_method(:quux) { 654 }
+ assert_equal @y.baz, 321
+ assert_equal @y.quux, 654
+ end
+
+ def test_define_soft_aliases
+ @X.define_method(:foo) { 123 }
+ @X.define_method(:baz) { 321 }
+ @X.define_soft_aliases :bar => :foo, :quux => :baz
+ assert_equal @x.foo, 123
+ assert_equal @x.bar, 123
+ assert_equal @y.foo, 123
+ assert_equal @y.bar, 123
+ assert_equal @x.baz, 321
+ assert_equal @x.quux, 321
+ assert_equal @y.baz, 321
+ assert_equal @y.quux, 321
+ @Y.define_method(:foo) { 456 }
+ assert_equal @y.foo, @y.bar, 456
+ @Y.define_method(:quux) { 654 }
+ assert_equal @y.baz, 321
+ assert_equal @y.quux, 654
+ end
+
+ def test_define_readers
+ @X.define_readers :foo, :bar
+ assert !@x.respond_to?(:foo=)
+ assert !@x.respond_to?(:bar=)
+ @x.instance_eval { @foo = 123 ; @bar = 456 }
+ assert_equal @x.foo, 123
+ assert_equal @x.bar, 456
+ @X.define_readers :baz?, :quux?
+ assert !@x.respond_to?(:baz=)
+ assert !@x.respond_to?(:quux=)
+ @x.instance_eval { @baz = false ; @quux = true }
+ assert !@x.baz?
+ assert @x.quux?
+ end
+
+ def test_define_writers
+ assert !@X.writer_defined?(:foo)
+ assert !@X.writer_defined?(:bar)
+ @X.define_writers :foo, :bar
+ assert @X.writer_defined?(:foo)
+ assert @X.writer_defined?(:bar)
+ assert @X.writer_defined?(:foo=)
+ assert @X.writer_defined?(:bar=)
+ assert @X.writer_defined?(:foo?)
+ assert @X.writer_defined?(:bar?)
+ assert !@x.respond_to?(:foo)
+ assert !@x.respond_to?(:bar)
+ @x.foo = 123
+ @x.bar = 456
+ assert_equal @x.instance_eval { @foo }, 123
+ assert_equal @x.instance_eval { @bar }, 456
+ @X.define_writers :baz?, :quux?
+ assert !@x.respond_to?(:baz?)
+ assert !@x.respond_to?(:quux?)
+ @x.baz = true
+ @x.quux = false
+ assert_equal @x.instance_eval { @baz }, true
+ assert_equal @x.instance_eval { @quux }, false
+ end
+
+ def test_define_accessors
+ @X.define_accessors :foo, :bar
+ @x.foo = 123 ; @x.bar = 456
+ assert_equal @x.foo, 123
+ assert_equal @x.bar, 456
+ end
+
+ def test_define_opposite_readers
+ @X.define_opposite_readers :foo? => :bar?, :baz? => :quux?
+ assert !@x.respond_to?(:foo=)
+ assert !@x.respond_to?(:bar=)
+ assert !@x.respond_to?(:baz=)
+ assert !@x.respond_to?(:quux=)
+ @x.instance_eval { @bar = true ; @quux = false }
+ assert !@x.foo?
+ assert @x.bar?
+ assert @x.baz?
+ assert !@x.quux?
+ end
+
+ def test_define_opposite_writers
+ @X.define_opposite_writers :foo? => :bar?, :baz => :quux
+ end
+ end
+end
+
+## better-definers.rb ends here.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/event-loop.rb
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/event-loop.rb 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/event-loop.rb 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,202 @@
+## event-loop.rb --- high-level IO multiplexer
+# Copyright (C) 2005, 2006 Daniel Brockman
+
+# This program 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.
+
+# This file 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.
+
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the Free
+# Software Foundation, 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+require "event-loop/better-definers"
+require "event-loop/signal-system"
+
+class EventLoop ; end
+
+require "event-loop/io"
+
+class EventLoop
+ module Utilities
+ def self.validate_keyword_arguments (actual, allowed)
+ (unknown_keys = actual - allowed).empty? or
+ fail "unrecognized keyword argument" +
+ "#{"s" if unknown_keys.size > 1}: " +
+ unknown_keys.map { |x| "`#{x}'" }.join(", ")
+ end
+ end
+end
+
+class EventLoop
+ include SignalEmitter
+
+ IO_STATES = [:readable, :writable, :exceptional]
+
+ class << self
+ def default ; @default ||= new end
+ def default= x ; @default = x end
+
+ def current
+ Thread.current["event-loop::current"] || default end
+ def current= x
+ Thread.current["event-loop::current"] = x end
+
+ def with_current (new)
+ # Be sure to return the value of the block.
+ if current == new
+ yield
+ else
+ begin
+ old = self.current
+ self.current = new
+ yield
+ ensure
+ current == new or warn "uncontained change " +
+ "to `EventLoop.current' within dynamic " +
+ "extent of `EventLoop.with_current'"
+ self.current = old
+ end
+ end
+ end
+
+ def method_missing (name, *args, &block)
+ if current.respond_to? name
+ current.__send__(name, *args, &block)
+ else
+ super
+ end
+ end
+ end
+
+ define_signals :before_sleep, :after_sleep
+
+ def initialize
+ @running = false
+ @awake = false
+ @wakeup_time = nil
+ @timers = []
+
+ @io_arrays = [[], [], []]
+ @ios = Hash.new do |h, k| raise ArgumentError,
+ "invalid IO event: #{k}", caller(2) end
+ IO_STATES.each_with_index { |x, i| @ios[x] = @io_arrays[i] }
+
+ @notify_src, @notify_snk = IO.pipe
+
+ @notify_src.will_block = false
+ @notify_snk.will_block = false
+
+ # For bootstrapping reasons, we can't let the stub
+ # implementation of IO#on_readable set this up.
+ monitor_io(@notify_src, :readable)
+ @notify_src.extend(Watchable)
+ # Each time a byte is sent through the notification pipe
+ # we need to read it, or IO.select will keep returning.
+ @notify_src.on_readable do
+ begin
+ @notify_src.sysread(256)
+ rescue Errno::EAGAIN
+ # The pipe wasn't readable after all.
+ end
+ end
+ end
+
+ define_opposite_accessors \
+ :stopped? => :running?,
+ :asleep? => :awake?
+
+ # This is an old name for the property.
+ define_hard_alias :sleeping? => :asleep?
+
+ def run
+ if block_given?
+ thread = Thread.new { run }
+ yield ; quit ; thread.join
+ else
+ running!
+ iterate while running?
+ end
+ ensure
+ quit
+ end
+
+ def iterate (user_timeout=nil)
+ t1, t2 = user_timeout, max_timeout
+ timeout = t1 && t2 ? [t1, t2].min : t1 || t2
+ select(timeout).zip(IO_STATES) do |ios, state|
+ ios.each { |x| x.signal(state) } if ios
+ end
+ end
+
+ private
+
+ def select (timeout)
+ @wakeup_time = timeout ? Time.now + timeout : nil
+ # puts "waiting: #{timeout} seconds"
+ signal :before_sleep ; asleep!
+ IO.select(*@io_arrays + [timeout]) || []
+ ensure
+ awake! ; signal :after_sleep
+ @timers.each { |x| x.sound_alarm if x.ready? }
+ end
+
+ public
+
+ def quit ; stopped! ; wake_up ; self end
+
+ def monitoring_io? (io, event)
+ @ios[event].include? io end
+ def monitoring_timer? (timer)
+ @timers.include? timer end
+
+ def monitor_io (io, *events)
+ for event in events do
+ unless monitoring_io?(io, event)
+ @ios[event] << io ; wake_up
+ end
+ end
+ end
+
+ def monitor_timer (timer)
+ @timers << timer unless monitoring_timer? timer
+ check_timer(timer)
+ end
+
+ def check_timer (timer)
+ wake_up if running? and asleep? and
+ timer.end_time < @wakeup_time
+ end
+
+ def ignore_io (io, *events)
+ events = IO_STATES if events.empty?
+ for event in events do
+ wake_up if @ios[event].delete(io)
+ end
+ end
+
+ def ignore_timer (timer)
+ # Don't need to wake up for this.
+ @timers.delete(timer)
+ end
+
+ def max_timeout
+ return nil if @timers.empty?
+ [@timers.collect { |x| x.time_left }.min, 0].max
+ end
+
+ def wake_up
+ returning self do
+ @notify_snk.write('.') if asleep?
+ end
+ end
+end
+
+## event-loop.rb ends here.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/io.rb
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/io.rb 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/io.rb 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,98 @@
+## io.rb --- convenience features for IO objects
+# Copyright (C) 2005, 2006 Daniel Brockman
+
+# This program 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.
+
+# This file 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.
+
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the Free
+# Software Foundation, 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+require "event-loop"
+require "fcntl"
+
+class Symbol
+ def io_state?
+ EventLoop::IO_STATES.include? self
+ end
+end
+
+module EventLoop::Watchable
+ include SignalEmitter
+
+ define_signals :readable, :writable, :exceptional
+
+ def monitor_events (*events)
+ EventLoop.monitor_io(self, *events) end
+ def ignore_events (*events)
+ EventLoop.ignore_io(self, *events) end
+
+ define_soft_aliases \
+ :monitor_event => :monitor_events,
+ :ignore_event => :ignore_events
+
+ def close ; super
+ ignore_events end
+ def close_read ; super
+ ignore_event :readable end
+ def close_write ; super
+ ignore_event :writable end
+
+ module Automatic
+ include EventLoop::Watchable
+
+ def add_signal_handler (name, &handler) super
+ monitor_event(name) if name.io_state?
+ end
+
+ def remove_signal_handler (name, handler) super
+ if @signal_handlers[name].empty?
+ ignore_event(name) if name.io_state?
+ end
+ end
+ end
+end
+
+class IO
+ def on_readable &block
+ extend EventLoop::Watchable::Automatic
+ on_readable(&block)
+ end
+
+ def on_writable &block
+ extend EventLoop::Watchable::Automatic
+ on_writable(&block)
+ end
+
+ def on_exceptional &block
+ extend EventLoop::Watchable::Automatic
+ on_exceptional(&block)
+ end
+
+ def will_block?
+ require "fcntl"
+ fcntl(Fcntl::F_GETFL, 0) & Fcntl::O_NONBLOCK == 0
+ end
+
+ def will_block= (wants_blocking)
+ require "fcntl"
+ flags = fcntl(Fcntl::F_GETFL, 0)
+ if wants_blocking
+ flags &= ~Fcntl::O_NONBLOCK
+ else
+ flags |= Fcntl::O_NONBLOCK
+ end
+ fcntl(Fcntl::F_SETFL, flags)
+ end
+end
+
+## io.rb ends here.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/signal-system.rb
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/signal-system.rb 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/signal-system.rb 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,226 @@
+## signal-system.rb --- simple intra-process signal system
+# Copyright (C) 2005, 2006 Daniel Brockman
+
+# This program 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.
+
+# This file 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.
+
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the Free
+# Software Foundation, 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+require "event-loop/better-definers"
+
+module SignalEmitterModule
+ def self.extended (object)
+ if object.kind_of? Module and not object < SignalEmitter
+ if object.respond_to? :fcall
+ # This is the way to call private methods
+ # in Ruby 1.9 as of November 16.
+ object.fcall :include, SignalEmitter
+ else
+ object.__send__ :include, SignalEmitter
+ end
+ end
+ end
+
+ def define_signal (name, slot=:before, &body)
+ # Can't use `define_method' and take a block pre-1.9.
+ class_eval %{ def on_#{name} &block
+ add_signal_handler(:#{name}, &block) end }
+ define_signal_handler(name, :before, &lambda {|*a|})
+ define_signal_handler(name, :after, &lambda {|*a|})
+ define_signal_handler(name, slot, &body) if block_given?
+ end
+
+ def define_signals (*names, &body)
+ names.each { |x| define_signal(x, &body) }
+ end
+
+ def define_signal_handler (name, slot=:before, &body)
+ case slot
+ when :before
+ define_protected_method "prehandle_#{name}", &body
+ when :after
+ define_protected_method "posthandle_#{name}", &body
+ else
+ raise ArgumentError, "invalid slot `#{slot.inspect}'; " +
+ "should be `:before' or `:after'", caller(1)
+ end
+ end
+end
+
+# This is an old name for the same thing.
+SignalEmitterClass = SignalEmitterModule
+
+module SignalEmitter
+ def self.included (includer)
+ if not includer.kind_of? SignalEmitterClass
+ includer.extend SignalEmitterClass
+ end
+ end
+
+ def __maybe_initialize_signal_emitter
+ @signal_handlers ||= Hash.new { |h, k| h[k] = Array.new }
+ @allow_dynamic_signals ||= false
+ end
+
+ define_accessors :allow_dynamic_signals?
+
+ def add_signal_handler (name, &handler)
+ __maybe_initialize_signal_emitter
+ @signal_handlers[name] << handler
+ return handler
+ end
+
+ define_soft_aliases [:on, :on_signal] => :add_signal_handler
+
+ def remove_signal_handler (name, handler)
+ __maybe_initialize_signal_emitter
+ @signal_handlers[name].delete(handler)
+ end
+
+ def __signal__ (name, *args, &block)
+ __maybe_initialize_signal_emitter
+ respond_to? "on_#{name}" or allow_dynamic_signals? or
+ fail "undefined signal `#{name}' for #{self}:#{self.class}"
+ __send__("prehandle_#{name}", *args, &block) if
+ respond_to? "prehandle_#{name}"
+ # This is an old name for the same thing:
+ __send__("handle_#{name}", *args, &block) if
+ respond_to? "handle_#{name}"
+ @signal_handlers[name].each { |x| x.call(*args, &block) }
+ __send__("posthandle_#{name}", *args, &block) if
+ respond_to? "posthandle_#{name}"
+ # This is an old name for the same thing:
+ __send__("after_handle_#{name}", *args, &block) if
+ respond_to? "after_handle_#{name}"
+ end
+
+ define_soft_alias :signal => :__signal__
+end
+
+# This module is indended to be a convenience mixin to be used by
+# classes whose objects need to observe foreign signals. That is,
+# if you want to observe some signals coming from an object, *you*
+# should mix in this module.
+#
+# You cannot use this module at two different places of the same
+# inheritance chain to observe signals coming from the same object.
+#
+# XXX: This has not seen much use, and I'd like to provide a
+# better solution for the problem in the future.
+module SignalObserver
+ def __maybe_initialize_signal_observer
+ @observed_signals ||= Hash.new do |signals, object|
+ signals[object] = Hash.new do |handlers, name|
+ handlers[name] = Array.new
+ end
+ end
+ end
+
+ def observe_signal (subject, name, &handler)
+ __maybe_initialize_signal_observer
+ @observed_signals[subject][name] << handler
+ subject.add_signal_handler(name, &handler)
+ end
+
+ def map_signals (source, pairs={})
+ pairs.each do |src_name, dst_name|
+ observe_signal(source, src_name) do |*args|
+ __signal__(dst_name, *args)
+ end
+ end
+ end
+
+ def absorb_signals (subject, *names)
+ names.each do |name|
+ observe_signal(subject, name) do |*args|
+ __signal__(name, *args)
+ end
+ end
+ end
+
+ define_soft_aliases \
+ :map_signal => :map_signals,
+ :absorb_signal => :absorb_signals
+
+ def ignore_signal (subject, name)
+ __maybe_initialize_signal_observer
+ __ignore_signal_1(subject, name)
+ @observed_signals.delete(subject) if
+ @observed_signals[subject].empty?
+ end
+
+ def ignore_signals (subject, *names)
+ __maybe_initialize_signal_observer
+ names = @observed_signals[subject] if names.empty?
+ names.each { |x| __ignore_signal_1(subject, x) }
+ end
+
+ private
+
+ def __ignore_signal_1(subject, name)
+ @observed_signals[subject][name].each do |handler|
+ subject.remove_signal_handler(name, handler) end
+ @observed_signals[subject].delete(name)
+ end
+end
+
+if __FILE__ == $0
+ require "test/unit"
+ class SignalEmitterTest < Test::Unit::TestCase
+ class X
+ include SignalEmitter
+ define_signal :foo
+ end
+
+ def setup
+ @x = X.new
+ end
+
+ def test_on_signal
+ moomin = 0
+ @x.on_signal(:foo) { moomin = 1 }
+ @x.signal :foo
+ assert moomin == 1
+ end
+
+ def test_on_foo
+ moomin = 0
+ @x.on_foo { moomin = 1 }
+ @x.signal :foo
+ assert moomin == 1
+ end
+
+ def test_multiple_on_signal
+ moomin = 0
+ @x.on_signal(:foo) { moomin += 1 }
+ @x.on_signal(:foo) { moomin += 2 }
+ @x.on_signal(:foo) { moomin += 4 }
+ @x.on_signal(:foo) { moomin += 8 }
+ @x.signal :foo
+ assert moomin == 15
+ end
+
+ def test_multiple_on_foo
+ moomin = 0
+ @x.on_foo { moomin += 1 }
+ @x.on_foo { moomin += 2 }
+ @x.on_foo { moomin += 4 }
+ @x.on_foo { moomin += 8 }
+ @x.signal :foo
+ assert moomin == 15
+ end
+ end
+end
+
+## signal-system.rb ends here.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/timer.rb
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/timer.rb 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop/timer.rb 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,288 @@
+## timer.rb --- timer implementations for the event loop
+# Copyright (C) 2005, 2006 Daniel Brockman
+
+# This program 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.
+
+# This file 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.
+
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the Free
+# Software Foundation, 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+require "event-loop"
+
+class EventLoop
+ def every (interval, options={}, &body)
+ options[:event_loop] ||= self
+ PeriodicTimer.new(interval, options, &body).start
+ end
+
+ def after (interval, options={}, &body)
+ options[:event_loop] ||= self
+ SporadicTimer.new(interval, options, &body).start
+ end
+
+ def repeat (&body)
+ every(0, &body)
+ end
+
+ def later (&body)
+ after(0, &body)
+ end
+end
+
+class EventLoop::Timer
+ include SignalEmitter
+
+ DEFAULT_TOLERANCE = 0.001
+
+ define_opposite_readers :stopped? => :running?
+ define_readers :interval, :tolerance, :event_loop
+ define_signal :alarm
+
+ def initialize (interval, options={}, &alarm_handler)
+ EventLoop::Utilities.validate_keyword_arguments options.keys,
+ [:tolerance, :event_loop]
+
+ @running = false
+ @start_time = nil
+
+ @interval = interval
+ @event_loop = options[:event_loop] || EventLoop.current
+ @alarm_handler = alarm_handler and
+ replace_alarm_handler(&@alarm_handler)
+
+ if options[:tolerance]
+ @tolerance = options[:tolerance].to_f
+ elsif DEFAULT_TOLERANCE < @interval
+ @tolerance = DEFAULT_TOLERANCE
+ else
+ @tolerance = 0.0
+ end
+ end
+
+ def start_time ; @start_time or
+ fail "the timer has not been started" end
+ def end_time ; start_time + @interval end
+ def time_left ; end_time - Time.now end
+ def ready? ; time_left <= @tolerance end
+
+ def interval= (new_interval)
+ old_interval = @interval
+ @interval = new_interval
+ if running? and new_interval < old_interval
+ @event_loop.check_timer(self)
+ end
+ end
+
+ def end_time= (new_end_time)
+ self.interval = new_end_time - start_time end
+ def time_left= (new_time_left)
+ self.end_time = Time.now + new_time_left end
+
+ def replace_alarm_handler (&block)
+ remove_signal_handler(:alarm, @alarm_handler) if @alarm_handler
+ add_signal_handler(:alarm, &block)
+ @alarm_handler = block
+ end
+
+ def restart (&block)
+ running? or raise "the timer is not running"
+ replace_alarm_handler(&block) if block_given?
+ returning self do
+ @start_time = Time.now
+ end
+ end
+
+ def start (&block)
+ replace_alarm_handler(&block) if block_given?
+ returning self do
+ @start_time = Time.now
+ @event_loop.monitor_timer(self)
+ @running = true
+ end
+ end
+
+ def stop
+ returning self do
+ @event_loop.ignore_timer(self)
+ @running = false
+ end
+ end
+
+ class << self
+ define_hard_alias :regular_new => :new
+
+ def new (*a, &b)
+ warn "event-loop: As of event-loop 0.3, `EventLoop::Timer.new' " +
+ "is deprecated in favor of `EventLoop#every' and " +
+ "`EventLoop#after'; see the documentation for more information."
+ new!(*a, &b)
+ end
+
+ def new! (options={}, &body)
+ if options.kind_of? Numeric
+ interval = options
+ options = {}
+ elsif options.include? :interval
+ interval = options[:interval].to_f
+ options.delete(:interval)
+ else
+ interval = 0.0
+ end
+
+ EventLoop::Utilities.validate_keyword_arguments options.keys,
+ [:interval, :tolerance, :start?, :event_loop]
+
+ if options.include? :start?
+ start = options.delete(:start?)
+ else
+ start = block_given?
+ end
+
+ timer = EventLoop::PeriodicTimer.new(interval, options)
+ timer.on_alarm(&body) if block_given?
+ timer.start if start
+ return timer
+ end
+ end
+end
+
+class EventLoop::PeriodicTimer < EventLoop::Timer
+ class << self
+ define_soft_alias :new => :regular_new end
+ def sound_alarm
+ signal :alarm ; restart if running? end
+end
+
+class EventLoop::SporadicTimer < EventLoop::Timer
+ class << self
+ define_soft_alias :new => :regular_new end
+ def sound_alarm
+ stop ; signal :alarm end
+end
+
+class Numeric
+ def nanoseconds ; self / 1_000_000_000.0 end
+ def microseconds ; self / 1_000_000.0 end
+ def milliseconds ; self / 1_000.0 end
+ def seconds ; self end
+ def minutes ; self * 60.seconds end
+ def hours ; self * 60.minutes end
+ def days ; self * 24.hours end
+ def weeks ; self * 7.days end
+ def years ; self * 365.24.days end
+
+ define_hard_aliases \
+ :nanosecond => :nanoseconds,
+ :microsecond => :microseconds,
+ :millisecond => :milliseconds,
+ :second => :seconds,
+ :minute => :minutes,
+ :hour => :hours,
+ :day => :days,
+ :week => :weeks,
+ :year => :years
+
+ define_hard_aliases \
+ :ns => :nanoseconds,
+ :ms => :milliseconds
+
+ def half ; self / 2.0 end
+ def quarter ; self / 4.0 end
+
+ def from_now (&block)
+ EventLoop.after(self, &block)
+ end
+
+ def from_now_and_repeat (&block)
+ EventLoop.every(self, &block)
+ end
+end
+
+class Integer
+ # It turns out whole numbers of years are
+ # always whole numbers of seconds.
+ def years ; super.to_i end
+ define_hard_alias :year => :years
+end
+
+def Time.measure
+ t0 = now ; yield ; now - t0
+end
+
+if __FILE__ == $0
+ require "test/unit"
+
+ class TimerTest < Test::Unit::TestCase
+ def setup
+ EventLoop.current = EventLoop.new
+ @timer = EventLoop::Timer.new!(:interval => 1.ms)
+ end
+
+ def test_monitor_unstarted_timer
+ assert_raise RuntimeError do
+ EventLoop.monitor_timer(@timer)
+ EventLoop.run
+ end
+ end
+
+ def test_start_monitoring_timer_while_running
+ EventLoop.later { 1.ms.from_now { EventLoop.quit } }
+ 1.second.from_now { EventLoop.quit }
+ assert Time.measure { EventLoop.run } < 1.half.second
+ end
+
+ def test_start_monitoring_timer_while_running_deprecated
+ @timer.start { EventLoop::Timer.new!(1.ms) { EventLoop.quit } }
+ EventLoop::Timer.new!(1.second) { EventLoop.quit }
+ assert Time.measure { EventLoop.run } < 1.half.second
+ end
+
+ def test_timer_tolerance
+ timer = EventLoop::SporadicTimer.new(10.milliseconds) do
+ puts "[#{timer.time_left * 1000} milliseconds left on alarm]"
+ EventLoop.quit end
+ 8.times do
+ dt = Time.measure { timer.start ; EventLoop.run }
+ assert(dt > timer.interval - timer.tolerance)
+ end
+ end
+ end
+
+ class SporadicTimerTest < Test::Unit::TestCase
+ def setup
+ EventLoop.current = EventLoop.new
+ end
+
+ def test_sporadicity
+ counter = 0
+ 1.nanosecond.from_now { counter += 1 }
+ 5.times { EventLoop.iterate(10.milliseconds) }
+ assert counter == 1
+ end
+ end
+
+ class PeriodicTimerTest < Test::Unit::TestCase
+ def setup
+ EventLoop.current = EventLoop.new
+ end
+
+ def test_periodicity
+ counter = 0
+ EventLoop.every(1.nanosecond) { counter += 1 }
+ 5.times { EventLoop.iterate(10.milliseconds) }
+ assert counter == 5
+ end
+ end
+end
+
+## timer.rb ends here.
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop.rb
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop.rb 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/lib/event-loop.rb 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1 @@
+require "event-loop/event-loop"
Added: packages-wip/libevent-loop-ruby/branches/upstream/current/setup.rb
===================================================================
--- packages-wip/libevent-loop-ruby/branches/upstream/current/setup.rb 2006-11-12 23:30:03 UTC (rev 1091)
+++ packages-wip/libevent-loop-ruby/branches/upstream/current/setup.rb 2006-11-13 13:52:29 UTC (rev 1092)
@@ -0,0 +1,1587 @@
+#
+# setup.rb
+#
+# Copyright (c) 2000-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+#
+
+unless Enumerable.method_defined?(:map) # Ruby 1.4.6
+ module Enumerable
+ alias map collect
+ end
+end
+
+unless File.respond_to?(:read) # Ruby 1.6
+ def File.read(fname)
+ open(fname) {|f|
+ return f.read
+ }
+ end
+end
+
+unless Errno.const_defined?(:ENOTEMPTY) # Windows?
+ module Errno
+ class ENOTEMPTY
+ # We do not raise this exception, implementation is not needed.
+ end
+ end
+end
+
+def File.binread(fname)
+ open(fname, 'rb') {|f|
+ return f.read
+ }
+end
+
+# for corrupted Windows' stat(2)
+def File.dir?(path)
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
+end
+
+
+class ConfigTable
+
+ include Enumerable
+
+ def initialize(rbconfig)
+ @rbconfig = rbconfig
+ @items = []
+ @table = {}
+ # options
+ @install_prefix = nil
+ @config_opt = nil
+ @verbose = true
+ @no_harm = false
+ end
+
+ attr_accessor :install_prefix
+ attr_accessor :config_opt
+
+ attr_writer :verbose
+
+ def verbose?
+ @verbose
+ end
+
+ attr_writer :no_harm
+
+ def no_harm?
+ @no_harm
+ end
+
+ def [](key)
+ lookup(key).resolve(self)
+ end
+
+ def []=(key, val)
+ lookup(key).set val
+ end
+
+ def names
+ @items.map {|i| i.name }
+ end
+
+ def each(&block)
+ @items.each(&block)
+ end
+
+ def key?(name)
+ @table.key?(name)
+ end
+
+ def lookup(name)
+ @table[name] or setup_rb_error "no such config item: #{name}"
+ end
+
+ def add(item)
+ @items.push item
+ @table[item.name] = item
+ end
+
+ def remove(name)
+ item = lookup(name)
+ @items.delete_if {|i| i.name == name }
+ @table.delete_if {|name, i| i.name == name }
+ item
+ end
+
+ def load_script(path, inst = nil)
+ if File.file?(path)
+ MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
+ end
+ end
+
+ def savefile
+ '.config'
+ end
+
+ def load_savefile
+ begin
+ File.foreach(savefile()) do |line|
+ k, v = *line.split(/=/, 2)
+ self[k] = v.strip
+ end
+ rescue Errno::ENOENT
+ setup_rb_error $!.message + "\n#{File.basename($0)} config first"
+ end
+ end
+
+ def save
+ @items.each {|i| i.value }
+ File.open(savefile(), 'w') {|f|
+ @items.each do |i|
+ f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
+ end
+ }
+ end
+
+ def load_standard_entries
+ standard_entries(@rbconfig).each do |ent|
+ add ent
+ end
+ end
+
+ def standard_entries(rbconfig)
+ c = rbconfig
+
+ rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
+
+ major = c['MAJOR'].to_i
+ minor = c['MINOR'].to_i
+ teeny = c['TEENY'].to_i
+ version = "#{major}.#{minor}"
+
+ # ruby ver. >= 1.4.4?
+ newpath_p = ((major >= 2) or
+ ((major == 1) and
+ ((minor >= 5) or
+ ((minor == 4) and (teeny >= 4)))))
+
+ if c['rubylibdir']
+ # V > 1.6.3
+ libruby = "#{c['prefix']}/lib/ruby"
+ librubyver = c['rubylibdir']
+ librubyverarch = c['archdir']
+ siteruby = c['sitedir']
+ siterubyver = c['sitelibdir']
+ siterubyverarch = c['sitearchdir']
+ elsif newpath_p
+ # 1.4.4 <= V <= 1.6.3
+ libruby = "#{c['prefix']}/lib/ruby"
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+ siteruby = c['sitedir']
+ siterubyver = "$siteruby/#{version}"
+ siterubyverarch = "$siterubyver/#{c['arch']}"
+ else
+ # V < 1.4.4
+ libruby = "#{c['prefix']}/lib/ruby"
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+ siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
+ siterubyver = siteruby
+ siterubyverarch = "$siterubyver/#{c['arch']}"
+ end
+ parameterize = lambda {|path|
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
+ }
+
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+ else
+ makeprog = 'make'
+ end
+
+ [
+ ExecItem.new('installdirs', 'std/site/home',
+ 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
+ {|val, table|
+ case val
+ when 'std'
+ table['rbdir'] = '$librubyver'
+ table['sodir'] = '$librubyverarch'
+ when 'site'
+ table['rbdir'] = '$siterubyver'
+ table['sodir'] = '$siterubyverarch'
+ when 'home'
+ setup_rb_error '$HOME was not set' unless ENV['HOME']
+ table['prefix'] = ENV['HOME']
+ table['rbdir'] = '$libdir/ruby'
+ table['sodir'] = '$libdir/ruby'
+ end
+ },
+ PathItem.new('prefix', 'path', c['prefix'],
+ 'path prefix of target environment'),
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+ 'the directory for commands'),
+ PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
+ 'the directory for libraries'),
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+ 'the directory for shared data'),
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+ 'the directory for man pages'),
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+ 'the directory for system configuration files'),
+ PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
+ 'the directory for local state data'),
+ PathItem.new('libruby', 'path', libruby,
+ 'the directory for ruby libraries'),
+ PathItem.new('librubyver', 'path', librubyver,
+ 'the directory for standard ruby libraries'),
+ PathItem.new('librubyverarch', 'path', librubyverarch,
+ 'the directory for standard ruby extensions'),
+ PathItem.new('siteruby', 'path', siteruby,
+ 'the directory for version-independent aux ruby libraries'),
+ PathItem.new('siterubyver', 'path', siterubyver,
+ 'the directory for aux ruby libraries'),
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
+ 'the directory for aux ruby binaries'),
+ PathItem.new('rbdir', 'path', '$siterubyver',
+ 'the directory for ruby scripts'),
+ PathItem.new('sodir', 'path', '$siterubyverarch',
+ 'the directory for ruby extentions'),
+ PathItem.new('rubypath', 'path', rubypath,
+ 'the path to set to #! line'),
+ ProgramItem.new('rubyprog', 'name', rubypath,
+ 'the ruby program using for installation'),
+ ProgramItem.new('makeprog', 'name', makeprog,
+ 'the make program to compile ruby extentions'),
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+ 'shebang line (#!) editing mode'),
+ BoolItem.new('without-ext', 'yes/no', 'no',
+ 'does not compile/install ruby extentions')
+ ]
+ end
+ private :standard_entries
+
+ def load_multipackage_entries
+ multipackage_entries().each do |ent|
+ add ent
+ end
+ end
+
+ def multipackage_entries
+ [
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+ 'package names that you want to install'),
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+ 'package names that you do not want to install')
+ ]
+ end
+ private :multipackage_entries
+
+ ALIASES = {
+ 'std-ruby' => 'librubyver',
+ 'stdruby' => 'librubyver',
+ 'rubylibdir' => 'librubyver',
+ 'archdir' => 'librubyverarch',
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
+ 'site-ruby' => 'siterubyver', # For backward compatibility
+ 'bin-dir' => 'bindir',
+ 'bin-dir' => 'bindir',
+ 'rb-dir' => 'rbdir',
+ 'so-dir' => 'sodir',
+ 'data-dir' => 'datadir',
+ 'ruby-path' => 'rubypath',
+ 'ruby-prog' => 'rubyprog',
+ 'ruby' => 'rubyprog',
+ 'make-prog' => 'makeprog',
+ 'make' => 'makeprog'
+ }
+
+ def fixup
+ ALIASES.each do |ali, name|
+ @table[ali] = @table[name]
+ end
+ end
+
+ def options_re
+ /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
+ end
+
+ def parse_opt(opt)
+ m = options_re().match(opt) or setup_rb_error "config: unknown option #{opt}"
+ m.to_a[1,2]
+ end
+
+ def dllext
+ @rbconfig['DLEXT']
+ end
+
+ def value_config?(name)
+ lookup(name).value?
+ end
+
+ class Item
+ def initialize(name, template, default, desc)
+ @name = name.freeze
+ @template = template
+ @value = default
+ @default = default
+ @description = desc
+ end
+
+ attr_reader :name
+ attr_reader :description
+
+ attr_accessor :default
+ alias help_default default
+
+ def help_opt
+ "--#{@name}=#{@template}"
+ end
+
+ def value?
+ true
+ end
+
+ def value
+ @value
+ end
+
+ def resolve(table)
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
+ end
+
+ def set(val)
+ @value = check(val)
+ end
+
+ private
+
+ def check(val)
+ setup_rb_error "config: --#{name} requires argument" unless val
+ val
+ end
+ end
+
+ class BoolItem < Item
+ def config_type
+ 'bool'
+ end
+
+ def help_opt
+ "--#{@name}"
+ end
+
+ private
+
+ def check(val)
+ return 'yes' unless val
+ case val
+ when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
+ when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
+ else
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
+ end
+ end
+ end
+
+ class PathItem < Item
+ def config_type
+ 'path'
+ end
+
+ private
+
+ def check(path)
+ setup_rb_error "config: --#{@name} requires argument" unless path
+ path[0,1] == '$' ? path : File.expand_path(path)
+ end
+ end
+
+ class ProgramItem < Item
+ def config_type
+ 'program'
+ end
+ end
+
+ class SelectItem < Item
+ def initialize(name, selection, default, desc)
+ super
+ @ok = selection.split('/')
+ end
+
+ def config_type
+ 'select'
+ end
+
+ private
+
+ def check(val)
+ unless @ok.include?(val.strip)
+ setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
+ end
+ val.strip
+ end
+ end
+
+ class ExecItem < Item
+ def initialize(name, selection, desc, &block)
+ super name, selection, nil, desc
+ @ok = selection.split('/')
+ @action = block
+ end
+
+ def config_type
+ 'exec'
+ end
+
+ def value?
+ false
+ end
+
+ def resolve(table)
+ setup_rb_error "$#{name()} wrongly used as option value"
+ end
+
+ undef set
+
+ def evaluate(val, table)
+ v = val.strip.downcase
+ unless @ok.include?(v)
+ setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
+ end
+ @action.call v, table
+ end
+ end
+
+ class PackageSelectionItem < Item
+ def initialize(name, template, default, help_default, desc)
+ super name, template, default, desc
+ @help_default = help_default
+ end
+
+ attr_reader :help_default
+
+ def config_type
+ 'package'
+ end
+
+ private
+
+ def check(val)
+ unless File.dir?("packages/#{val}")
+ setup_rb_error "config: no such package: #{val}"
+ end
+ val
+ end
+ end
+
+ class MetaConfigEnvironment
+ def initialize(config, installer)
+ @config = config
+ @installer = installer
+ end
+
+ def config_names
+ @config.names
+ end
+
+ def config?(name)
+ @config.key?(name)
+ end
+
+ def bool_config?(name)
+ @config.lookup(name).config_type == 'bool'
+ end
+
+ def path_config?(name)
+ @config.lookup(name).config_type == 'path'
+ end
+
+ def value_config?(name)
+ @config.lookup(name).config_type != 'exec'
+ end
+
+ def add_config(item)
+ @config.add item
+ end
+
+ def add_bool_config(name, default, desc)
+ @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+ end
+
+ def add_path_config(name, default, desc)
+ @config.add PathItem.new(name, 'path', default, desc)
+ end
+
+ def set_config_default(name, default)
+ @config.lookup(name).default = default
+ end
+
+ def remove_config(name)
+ @config.remove(name)
+ end
+
+ # For only multipackage
+ def packages
+ raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
+ @installer.packages
+ end
+
+ # For only multipackage
+ def declare_packages(list)
+ raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
+ @installer.packages = list
+ end
+ end
+
+end # class ConfigTable
+
+
+# This module requires: #verbose?, #no_harm?
+module FileOperations
+
+ def mkdir_p(dirname, prefix = nil)
+ dirname = prefix + File.expand_path(dirname) if prefix
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
+ return if no_harm?
+
+ # Does not check '/', it's too abnormal.
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
+ if /\A[a-z]:\z/i =~ dirs[0]
+ disk = dirs.shift
+ dirs[0] = disk + dirs[0]
+ end
+ dirs.each_index do |idx|
+ path = dirs[0..idx].join('')
+ Dir.mkdir path unless File.dir?(path)
+ end
+ end
+
+ def rm_f(path)
+ $stderr.puts "rm -f #{path}" if verbose?
+ return if no_harm?
+ force_remove_file path
+ end
+
+ def rm_rf(path)
+ $stderr.puts "rm -rf #{path}" if verbose?
+ return if no_harm?
+ remove_tree path
+ end
+
+ def remove_tree(path)
+ if File.symlink?(path)
+ remove_file path
+ elsif File.dir?(path)
+ remove_tree0 path
+ else
+ force_remove_file path
+ end
+ end
+
+ def remove_tree0(path)
+ Dir.foreach(path) do |ent|
+ next if ent == '.'
+ next if ent == '..'
+ entpath = "#{path}/#{ent}"
+ if File.symlink?(entpath)
+ remove_file entpath
+ elsif File.dir?(entpath)
+ remove_tree0 entpath
+ else
+ force_remove_file entpath
+ end
+ end
+ begin
+ Dir.rmdir path
+ rescue Errno::ENOTEMPTY
+ # directory may not be empty
+ end
+ end
+
+ def move_file(src, dest)
+ force_remove_file dest
+ begin
+ File.rename src, dest
+ rescue
+ File.open(dest, 'wb') {|f|
+ f.write File.binread(src)
+ }
+ File.chmod File.stat(src).mode, dest
+ File.unlink src
+ end
+ end
+
+ def force_remove_file(path)
+ begin
+ remove_file path
+ rescue
+ end
+ end
+
+ def remove_file(path)
+ File.chmod 0777, path
+ File.unlink path
+ end
+
+ def install(from, dest, mode, prefix = nil)
+ $stderr.puts "install #{from} #{dest}" if verbose?
+ return if no_harm?
+
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
+ str = File.binread(from)
+ if diff?(str, realdest)
+ verbose_off {
+ rm_f realdest if File.exist?(realdest)
+ }
+ File.open(realdest, 'wb') {|f|
+ f.write str
+ }
+ File.chmod mode, realdest
+
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
+ if prefix
+ f.puts realdest.sub(prefix, '')
+ else
+ f.puts realdest
+ end
+ }
+ end
+ end
+
+ def diff?(new_content, path)
+ return true unless File.exist?(path)
+ new_content != File.binread(path)
+ end
+
+ def command(*args)
+ $stderr.puts args.join(' ') if verbose?
+ system(*args) or raise RuntimeError,
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
+ end
+
+ def ruby(*args)
+ command config('rubyprog'), *args
+ end
+
+ def make(task = nil)
+ command(*[config('makeprog'), task].compact)
+ end
+
+ def extdir?(dir)
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
+ end
+
+ def files_of(dir)
+ Dir.open(dir) {|d|
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
+ }
+ end
+
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
+
+ def directories_of(dir)
+ Dir.open(dir) {|d|
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
+ }
+ end
+
+end
+
+
+# This module requires: #srcdir_root, #objdir_root, #relpath
+module HookScriptAPI
+
+ def get_config(key)
+ @config[key]
+ end
+
+ alias config get_config
+
+ # obsolete: use metaconfig to change configuration
+ def set_config(key, val)
+ @config[key] = val
+ end
+
+ #
+ # srcdir/objdir (works only in the package directory)
+ #
+
+ def curr_srcdir
+ "#{srcdir_root()}/#{relpath()}"
+ end
+
+ def curr_objdir
+ "#{objdir_root()}/#{relpath()}"
+ end
+
+ def srcfile(path)
+ "#{curr_srcdir()}/#{path}"
+ end
+
+ def srcexist?(path)
+ File.exist?(srcfile(path))
+ end
+
+ def srcdirectory?(path)
+ File.dir?(srcfile(path))
+ end
+
+ def srcfile?(path)
+ File.file?(srcfile(path))
+ end
+
+ def srcentries(path = '.')
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
+ return d.to_a - %w(. ..)
+ }
+ end
+
+ def srcfiles(path = '.')
+ srcentries(path).select {|fname|
+ File.file?(File.join(curr_srcdir(), path, fname))
+ }
+ end
+
+ def srcdirectories(path = '.')
+ srcentries(path).select {|fname|
+ File.dir?(File.join(curr_srcdir(), path, fname))
+ }
+ end
+
+end
+
+
+class ToplevelInstaller
+
+ Version = '3.4.1'
+ Copyright = 'Copyright (c) 2000-2006 Minero Aoki'
+
+ TASKS = [
+ [ 'all', 'do config, setup, then install' ],
+ [ 'config', 'saves your configurations' ],
+ [ 'show', 'shows current configuration' ],
+ [ 'setup', 'compiles ruby extentions and others' ],
+ [ 'install', 'installs files' ],
+ [ 'test', 'run all tests in test/' ],
+ [ 'clean', "does `make clean' for each extention" ],
+ [ 'distclean',"does `make distclean' for each extention" ]
+ ]
+
+ def ToplevelInstaller.invoke
+ config = ConfigTable.new(load_rbconfig())
+ config.load_standard_entries
+ config.load_multipackage_entries if multipackage?
+ config.fixup
+ klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
+ klass.new(File.dirname($0), config).invoke
+ end
+
+ def ToplevelInstaller.multipackage?
+ File.dir?(File.dirname($0) + '/packages')
+ end
+
+ def ToplevelInstaller.load_rbconfig
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+ ARGV.delete(arg)
+ load File.expand_path(arg.split(/=/, 2)[1])
+ $".push 'rbconfig.rb'
+ else
+ require 'rbconfig'
+ end
+ ::Config::CONFIG
+ end
+
+ def initialize(ardir_root, config)
+ @ardir = File.expand_path(ardir_root)
+ @config = config
+ # cache
+ @valid_task_re = nil
+ end
+
+ def config(key)
+ @config[key]
+ end
+
+ def inspect
+ "#<#{self.class} #{__id__()}>"
+ end
+
+ def invoke
+ run_metaconfigs
+ case task = parsearg_global()
+ when nil, 'all'
+ parsearg_config
+ init_installers
+ exec_config
+ exec_setup
+ exec_install
+ else
+ case task
+ when 'config', 'test'
+ ;
+ when 'clean', 'distclean'
+ @config.load_savefile if File.exist?(@config.savefile)
+ else
+ @config.load_savefile
+ end
+ __send__ "parsearg_#{task}"
+ init_installers
+ __send__ "exec_#{task}"
+ end
+ end
+
+ def run_metaconfigs
+ @config.load_script "#{@ardir}/metaconfig"
+ end
+
+ def init_installers
+ @installer = Installer.new(@config, @ardir, File.expand_path('.'))
+ end
+
+ #
+ # Hook Script API bases
+ #
+
+ def srcdir_root
+ @ardir
+ end
+
+ def objdir_root
+ '.'
+ end
+
+ def relpath
+ '.'
+ end
+
+ #
+ # Option Parsing
+ #
+
+ def parsearg_global
+ while arg = ARGV.shift
+ case arg
+ when /\A\w+\z/
+ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
+ return arg
+ when '-q', '--quiet'
+ @config.verbose = false
+ when '--verbose'
+ @config.verbose = true
+ when '--help'
+ print_usage $stdout
+ exit 0
+ when '--version'
+ puts "#{File.basename($0)} version #{Version}"
+ exit 0
+ when '--copyright'
+ puts Copyright
+ exit 0
+ else
+ setup_rb_error "unknown global option '#{arg}'"
+ end
+ end
+ nil
+ end
+
+ def valid_task?(t)
+ valid_task_re() =~ t
+ end
+
+ def valid_task_re
+ @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
+ end
+
+ def parsearg_no_options
+ unless ARGV.empty?
+ task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
+ setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
+ end
+ end
+
+ alias parsearg_show parsearg_no_options
+ alias parsearg_setup parsearg_no_options
+ alias parsearg_test parsearg_no_options
+ alias parsearg_clean parsearg_no_options
+ alias parsearg_distclean parsearg_no_options
+
+ def parsearg_config
+ evalopt = []
+ set = []
+ @config.config_opt = []
+ while i = ARGV.shift
+ if /\A--?\z/ =~ i
+ @config.config_opt = ARGV.dup
+ break
+ end
+ name, value = *@config.parse_opt(i)
+ if @config.value_config?(name)
+ @config[name] = value
+ else
+ evalopt.push [name, value]
+ end
+ set.push name
+ end
+ evalopt.each do |name, value|
+ @config.lookup(name).evaluate value, @config
+ end
+ # Check if configuration is valid
+ set.each do |n|
+ @config[n] if @config.value_config?(n)
+ end
+ end
+
+ def parsearg_install
+ @config.no_harm = false
+ @config.install_prefix = ''
+ while a = ARGV.shift
+ case a
+ when '--no-harm'
+ @config.no_harm = true
+ when /\A--prefix=/
+ path = a.split(/=/, 2)[1]
+ path = File.expand_path(path) unless path[0,1] == '/'
+ @config.install_prefix = path
+ else
+ setup_rb_error "install: unknown option #{a}"
+ end
+ end
+ end
+
+ def print_usage(out)
+ out.puts 'Typical Installation Procedure:'
+ out.puts " $ ruby #{File.basename $0} config"
+ out.puts " $ ruby #{File.basename $0} setup"
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
+ out.puts
+ out.puts 'Detailed Usage:'
+ out.puts " ruby #{File.basename $0} <global option>"
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
+
+ fmt = " %-24s %s\n"
+ out.puts
+ out.puts 'Global options:'
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
+ out.printf fmt, ' --verbose', 'output messages verbosely'
+ out.printf fmt, ' --help', 'print this message'
+ out.printf fmt, ' --version', 'print version and quit'
+ out.printf fmt, ' --copyright', 'print copyright and quit'
+ out.puts
+ out.puts 'Tasks:'
+ TASKS.each do |name, desc|
+ out.printf fmt, name, desc
+ end
+
+ fmt = " %-24s %s [%s]\n"
+ out.puts
+ out.puts 'Options for CONFIG or ALL:'
+ @config.each do |item|
+ out.printf fmt, item.help_opt, item.description, item.help_default
+ end
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
+ out.puts
+ out.puts 'Options for INSTALL:'
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
+ out.printf fmt, '--prefix=path', 'install path prefix', ''
+ out.puts
+ end
+
+ #
+ # Task Handlers
+ #
+
+ def exec_config
+ @installer.exec_config
+ @config.save # must be final
+ end
+
+ def exec_setup
+ @installer.exec_setup
+ end
+
+ def exec_install
+ @installer.exec_install
+ end
+
+ def exec_test
+ @installer.exec_test
+ end
+
+ def exec_show
+ @config.each do |i|
+ printf "%-20s %s\n", i.name, i.value if i.value?
+ end
+ end
+
+ def exec_clean
+ @installer.exec_clean
+ end
+
+ def exec_distclean
+ @installer.exec_distclean
+ end
+
+end # class ToplevelInstaller
+
+
+class ToplevelInstallerMulti < ToplevelInstaller
+
+ include FileOperations
+
+ def initialize(ardir_root, config)
+ super
+ @packages = directories_of("#{@ardir}/packages")
+ raise 'no package exists' if @packages.empty?
+ @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
+ end
+
+ def run_metaconfigs
+ @config.load_script "#{@ardir}/metaconfig", self
+ @packages.each do |name|
+ @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
+ end
+ end
+
+ attr_reader :packages
+
+ def packages=(list)
+ raise 'package list is empty' if list.empty?
+ list.each do |name|
+ raise "directory packages/#{name} does not exist"\
+ unless File.dir?("#{@ardir}/packages/#{name}")
+ end
+ @packages = list
+ end
+
+ def init_installers
+ @installers = {}
+ @packages.each do |pack|
+ @installers[pack] = Installer.new(@config,
+ "#{@ardir}/packages/#{pack}",
+ "packages/#{pack}")
+ end
+ with = extract_selection(config('with'))
+ without = extract_selection(config('without'))
+ @selected = @installers.keys.select {|name|
+ (with.empty? or with.include?(name)) \
+ and not without.include?(name)
+ }
+ end
+
+ def extract_selection(list)
+ a = list.split(/,/)
+ a.each do |name|
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
+ end
+ a
+ end
+
+ def print_usage(f)
+ super
+ f.puts 'Inluded packages:'
+ f.puts ' ' + @packages.sort.join(' ')
+ f.puts
+ end
+
+ #
+ # Task Handlers
+ #
+
+ def exec_config
+ run_hook 'pre-config'
+ each_selected_installers {|inst| inst.exec_config }
+ run_hook 'post-config'
+ @config.save # must be final
+ end
+
+ def exec_setup
+ run_hook 'pre-setup'
+ each_selected_installers {|inst| inst.exec_setup }
+ run_hook 'post-setup'
+ end
+
+ def exec_install
+ run_hook 'pre-install'
+ each_selected_installers {|inst| inst.exec_install }
+ run_hook 'post-install'
+ end
+
+ def exec_test
+ run_hook 'pre-test'
+ each_selected_installers {|inst| inst.exec_test }
+ run_hook 'post-test'
+ end
+
+ def exec_clean
+ rm_f @config.savefile
+ run_hook 'pre-clean'
+ each_selected_installers {|inst| inst.exec_clean }
+ run_hook 'post-clean'
+ end
+
+ def exec_distclean
+ rm_f @config.savefile
+ run_hook 'pre-distclean'
+ each_selected_installers {|inst| inst.exec_distclean }
+ run_hook 'post-distclean'
+ end
+
+ #
+ # lib
+ #
+
+ def each_selected_installers
+ Dir.mkdir 'packages' unless File.dir?('packages')
+ @selected.each do |pack|
+ $stderr.puts "Processing the package `#{pack}' ..." if verbose?
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
+ Dir.chdir "packages/#{pack}"
+ yield @installers[pack]
+ Dir.chdir '../..'
+ end
+ end
+
+ def run_hook(id)
+ @root_installer.run_hook id
+ end
+
+ # module FileOperations requires this
+ def verbose?
+ @config.verbose?
+ end
+
+ # module FileOperations requires this
+ def no_harm?
+ @config.no_harm?
+ end
+
+end # class ToplevelInstallerMulti
+
+
+class Installer
+
+ FILETYPES = %w( bin lib ext data conf man )
+
+ include FileOperations
+ include HookScriptAPI
+
+ def initialize(config, srcroot, objroot)
+ @config = config
+ @srcdir = File.expand_path(srcroot)
+ @objdir = File.expand_path(objroot)
+ @currdir = '.'
+ end
+
+ def inspect
+ "#<#{self.class} #{File.basename(@srcdir)}>"
+ end
+
+ def noop(rel)
+ end
+
+ #
+ # Hook Script API base methods
+ #
+
+ def srcdir_root
+ @srcdir
+ end
+
+ def objdir_root
+ @objdir
+ end
+
+ def relpath
+ @currdir
+ end
+
+ #
+ # Config Access
+ #
+
+ # module FileOperations requires this
+ def verbose?
+ @config.verbose?
+ end
+
+ # module FileOperations requires this
+ def no_harm?
+ @config.no_harm?
+ end
+
+ def verbose_off
+ begin
+ save, @config.verbose = @config.verbose?, false
+ yield
+ ensure
+ @config.verbose = save
+ end
+ end
+
+ #
+ # TASK config
+ #
+
+ def exec_config
+ exec_task_traverse 'config'
+ end
+
+ alias config_dir_bin noop
+ alias config_dir_lib noop
+
+ def config_dir_ext(rel)
+ extconf if extdir?(curr_srcdir())
+ end
+
+ alias config_dir_data noop
+ alias config_dir_conf noop
+ alias config_dir_man noop
+
+ def extconf
+ ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
+ end
+
+ #
+ # TASK setup
+ #
+
+ def exec_setup
+ exec_task_traverse 'setup'
+ end
+
+ def setup_dir_bin(rel)
+ files_of(curr_srcdir()).each do |fname|
+ update_shebang_line "#{curr_srcdir()}/#{fname}"
+ end
+ end
+
+ alias setup_dir_lib noop
+
+ def setup_dir_ext(rel)
+ make if extdir?(curr_srcdir())
+ end
+
+ alias setup_dir_data noop
+ alias setup_dir_conf noop
+ alias setup_dir_man noop
+
+ def update_shebang_line(path)
+ return if no_harm?
+ return if config('shebang') == 'never'
+ old = Shebang.load(path)
+ if old
+ $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
+ new = new_shebang(old)
+ return if new.to_s == old.to_s
+ else
+ return unless config('shebang') == 'all'
+ new = Shebang.new(config('rubypath'))
+ end
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
+ open_atomic_writer(path) {|output|
+ File.open(path, 'rb') {|f|
+ f.gets if old # discard
+ output.puts new.to_s
+ output.print f.read
+ }
+ }
+ end
+
+ def new_shebang(old)
+ if /\Aruby/ =~ File.basename(old.cmd)
+ Shebang.new(config('rubypath'), old.args)
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
+ Shebang.new(config('rubypath'), old.args[1..-1])
+ else
+ return old unless config('shebang') == 'all'
+ Shebang.new(config('rubypath'))
+ end
+ end
+
+ def open_atomic_writer(path, &block)
+ tmpfile = File.basename(path) + '.tmp'
+ begin
+ File.open(tmpfile, 'wb', &block)
+ File.rename tmpfile, File.basename(path)
+ ensure
+ File.unlink tmpfile if File.exist?(tmpfile)
+ end
+ end
+
+ class Shebang
+ def Shebang.load(path)
+ line = nil
+ File.open(path) {|f|
+ line = f.gets
+ }
+ return nil unless /\A#!/ =~ line
+ parse(line)
+ end
+
+ def Shebang.parse(line)
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
+ new(cmd, args)
+ end
+
+ def initialize(cmd, args = [])
+ @cmd = cmd
+ @args = args
+ end
+
+ attr_reader :cmd
+ attr_reader :args
+
+ def to_s
+ "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
+ end
+ end
+
+ #
+ # TASK install
+ #
+
+ def exec_install
+ rm_f 'InstalledFiles'
+ exec_task_traverse 'install'
+ end
+
+ def install_dir_bin(rel)
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
+ end
+
+ def install_dir_lib(rel)
+ install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
+ end
+
+ def install_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ install_files rubyextentions('.'),
+ "#{config('sodir')}/#{File.dirname(rel)}",
+ 0555
+ end
+
+ def install_dir_data(rel)
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
+ end
+
+ def install_dir_conf(rel)
+ # FIXME: should not remove current config files
+ # (rename previous file to .old/.org)
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
+ end
+
+ def install_dir_man(rel)
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
+ end
+
+ def install_files(list, dest, mode)
+ mkdir_p dest, @config.install_prefix
+ list.each do |fname|
+ install fname, dest, mode, @config.install_prefix
+ end
+ end
+
+ def libfiles
+ glob_reject(%w(*.y *.output), targetfiles())
+ end
+
+ def rubyextentions(dir)
+ ents = glob_select("*.#{@config.dllext}", targetfiles())
+ if ents.empty?
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
+ end
+ ents
+ end
+
+ def targetfiles
+ mapdir(existfiles() - hookfiles())
+ end
+
+ def mapdir(ents)
+ ents.map {|ent|
+ if File.exist?(ent)
+ then ent # objdir
+ else "#{curr_srcdir()}/#{ent}" # srcdir
+ end
+ }
+ end
+
+ # picked up many entries from cvs-1.11.1/src/ignore.c
+ JUNK_FILES = %w(
+ core RCSLOG tags TAGS .make.state
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
+
+ *.org *.in .*
+ )
+
+ def existfiles
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
+ end
+
+ def hookfiles
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
+ %w( config setup install clean distclean ).map {|t| sprintf(fmt, t) }
+ }.flatten
+ end
+
+ def glob_select(pat, ents)
+ re = globs2re([pat])
+ ents.select {|ent| re =~ ent }
+ end
+
+ def glob_reject(pats, ents)
+ re = globs2re(pats)
+ ents.reject {|ent| re =~ ent }
+ end
+
+ GLOB2REGEX = {
+ '.' => '\.',
+ '$' => '\$',
+ '#' => '\#',
+ '*' => '.*'
+ }
+
+ def globs2re(pats)
+ /\A(?:#{
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
+ })\z/
+ end
+
+ #
+ # TASK test
+ #
+
+ TESTDIR = 'test'
+
+ def exec_test
+ unless File.directory?('test')
+ $stderr.puts 'no test in this package' if verbose?
+ return
+ end
+ $stderr.puts 'Running tests...' if verbose?
+ begin
+ require 'test/unit'
+ rescue LoadError
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
+ end
+ runner = Test::Unit::AutoRunner.new(true)
+ runner.to_run << TESTDIR
+ runner.run
+ end
+
+ #
+ # TASK clean
+ #
+
+ def exec_clean
+ exec_task_traverse 'clean'
+ rm_f @config.savefile
+ rm_f 'InstalledFiles'
+ end
+
+ alias clean_dir_bin noop
+ alias clean_dir_lib noop
+ alias clean_dir_data noop
+ alias clean_dir_conf noop
+ alias clean_dir_man noop
+
+ def clean_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ make 'clean' if File.file?('Makefile')
+ end
+
+ #
+ # TASK distclean
+ #
+
+ def exec_distclean
+ exec_task_traverse 'distclean'
+ rm_f @config.savefile
+ rm_f 'InstalledFiles'
+ end
+
+ alias distclean_dir_bin noop
+ alias distclean_dir_lib noop
+
+ def distclean_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ make 'distclean' if File.file?('Makefile')
+ end
+
+ alias distclean_dir_data noop
+ alias distclean_dir_conf noop
+ alias distclean_dir_man noop
+
+ #
+ # Traversing
+ #
+
+ def exec_task_traverse(task)
+ run_hook "pre-#{task}"
+ FILETYPES.each do |type|
+ if type == 'ext' and config('without-ext') == 'yes'
+ $stderr.puts 'skipping ext/* by user option' if verbose?
+ next
+ end
+ traverse task, type, "#{task}_dir_#{type}"
+ end
+ run_hook "post-#{task}"
+ end
+
+ def traverse(task, rel, mid)
+ dive_into(rel) {
+ run_hook "pre-#{task}"
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
+ directories_of(curr_srcdir()).each do |d|
+ traverse task, "#{rel}/#{d}", mid
+ end
+ run_hook "post-#{task}"
+ }
+ end
+
+ def dive_into(rel)
+ return unless File.dir?("#{@srcdir}/#{rel}")
+
+ dir = File.basename(rel)
+ Dir.mkdir dir unless File.dir?(dir)
+ prevdir = Dir.pwd
+ Dir.chdir dir
+ $stderr.puts '---> ' + rel if verbose?
+ @currdir = rel
+ yield
+ Dir.chdir prevdir
+ $stderr.puts '<--- ' + rel if verbose?
+ @currdir = File.dirname(rel)
+ end
+
+ def run_hook(id)
+ path = [ "#{curr_srcdir()}/#{id}",
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
+ return unless path
+ $stderr.puts "invoking hook script #{path}" if verbose?
+ begin
+ instance_eval File.read(path), path, 1
+ rescue
+ raise if $DEBUG
+ setup_rb_error "hook #{path} failed:\n" + $!.message
+ end
+ end
+
+end # class Installer
+
+
+class SetupError < StandardError; end
+
+def setup_rb_error(msg)
+ raise SetupError, msg
+end
+
+if $0 == __FILE__
+ begin
+ ToplevelInstaller.invoke
+ rescue SetupError
+ raise if $DEBUG
+ $stderr.puts $!.message
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
+ exit 1
+ end
+end
More information about the Pkg-ruby-extras-commits
mailing list