[macruby-changes] [1889] MacRuby/branches/experimental/lib
source_changes at macosforge.org
source_changes at macosforge.org
Fri Jun 19 15:42:24 PDT 2009
Revision: 1889
http://trac.macosforge.org/projects/ruby/changeset/1889
Author: lsansonetti at apple.com
Date: 2009-06-19 15:42:24 -0700 (Fri, 19 Jun 2009)
Log Message:
-----------
merged stdlib (including hotcocoa) from trunk
Modified Paths:
--------------
MacRuby/branches/experimental/lib/.document
MacRuby/branches/experimental/lib/README
MacRuby/branches/experimental/lib/cgi/session/pstore.rb
MacRuby/branches/experimental/lib/cgi/session.rb
MacRuby/branches/experimental/lib/cgi.rb
MacRuby/branches/experimental/lib/cmath.rb
MacRuby/branches/experimental/lib/complex.rb
MacRuby/branches/experimental/lib/csv.rb
MacRuby/branches/experimental/lib/date/format.rb
MacRuby/branches/experimental/lib/date.rb
MacRuby/branches/experimental/lib/debug.rb
MacRuby/branches/experimental/lib/delegate.rb
MacRuby/branches/experimental/lib/drb/drb.rb
MacRuby/branches/experimental/lib/e2mmap.rb
MacRuby/branches/experimental/lib/erb.rb
MacRuby/branches/experimental/lib/fileutils.rb
MacRuby/branches/experimental/lib/find.rb
MacRuby/branches/experimental/lib/forwardable.rb
MacRuby/branches/experimental/lib/gserver.rb
MacRuby/branches/experimental/lib/hotcocoa/delegate_builder.rb
MacRuby/branches/experimental/lib/hotcocoa/graphics/canvas.rb
MacRuby/branches/experimental/lib/hotcocoa/kernel_ext.rb
MacRuby/branches/experimental/lib/hotcocoa/kvo_accessors.rb
MacRuby/branches/experimental/lib/hotcocoa/layout_view.rb
MacRuby/branches/experimental/lib/hotcocoa/mapper.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/alert.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/application.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/array_controller.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/box.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/button.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/collection_view.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/column.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/combo_box.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/label.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/movie_view.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/scroll_view.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/table_view.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/text_field.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/web_view.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/window.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings.rb
MacRuby/branches/experimental/lib/ipaddr.rb
MacRuby/branches/experimental/lib/irb/completion.rb
MacRuby/branches/experimental/lib/irb/context.rb
MacRuby/branches/experimental/lib/irb/ext/change-ws.rb
MacRuby/branches/experimental/lib/irb/ext/multi-irb.rb
MacRuby/branches/experimental/lib/irb/ext/save-history.rb
MacRuby/branches/experimental/lib/irb/extend-command.rb
MacRuby/branches/experimental/lib/irb/help.rb
MacRuby/branches/experimental/lib/irb/init.rb
MacRuby/branches/experimental/lib/irb/input-method.rb
MacRuby/branches/experimental/lib/irb/lc/help-message
MacRuby/branches/experimental/lib/irb/lc/ja/error.rb
MacRuby/branches/experimental/lib/irb/lc/ja/help-message
MacRuby/branches/experimental/lib/irb/locale.rb
MacRuby/branches/experimental/lib/irb/notifier.rb
MacRuby/branches/experimental/lib/irb/ruby-lex.rb
MacRuby/branches/experimental/lib/irb/slex.rb
MacRuby/branches/experimental/lib/irb/xmp.rb
MacRuby/branches/experimental/lib/irb.rb
MacRuby/branches/experimental/lib/logger.rb
MacRuby/branches/experimental/lib/mathn.rb
MacRuby/branches/experimental/lib/matrix.rb
MacRuby/branches/experimental/lib/mkmf.rb
MacRuby/branches/experimental/lib/net/ftp.rb
MacRuby/branches/experimental/lib/net/http.rb
MacRuby/branches/experimental/lib/net/https.rb
MacRuby/branches/experimental/lib/net/imap.rb
MacRuby/branches/experimental/lib/net/pop.rb
MacRuby/branches/experimental/lib/net/smtp.rb
MacRuby/branches/experimental/lib/net/telnet.rb
MacRuby/branches/experimental/lib/open-uri.rb
MacRuby/branches/experimental/lib/open3.rb
MacRuby/branches/experimental/lib/optparse/version.rb
MacRuby/branches/experimental/lib/optparse.rb
MacRuby/branches/experimental/lib/ostruct.rb
MacRuby/branches/experimental/lib/pathname.rb
MacRuby/branches/experimental/lib/pp.rb
MacRuby/branches/experimental/lib/prettyprint.rb
MacRuby/branches/experimental/lib/profile.rb
MacRuby/branches/experimental/lib/profiler.rb
MacRuby/branches/experimental/lib/pstore.rb
MacRuby/branches/experimental/lib/racc/parser.rb
MacRuby/branches/experimental/lib/rake/gempackagetask.rb
MacRuby/branches/experimental/lib/rake/loaders/makefile.rb
MacRuby/branches/experimental/lib/rake/packagetask.rb
MacRuby/branches/experimental/lib/rake/rdoctask.rb
MacRuby/branches/experimental/lib/rake/tasklib.rb
MacRuby/branches/experimental/lib/rake/testtask.rb
MacRuby/branches/experimental/lib/rake.rb
MacRuby/branches/experimental/lib/rational.rb
MacRuby/branches/experimental/lib/rdoc/README
MacRuby/branches/experimental/lib/rdoc/code_objects.rb
MacRuby/branches/experimental/lib/rdoc/diagram.rb
MacRuby/branches/experimental/lib/rdoc/generator/chm/chm.rb
MacRuby/branches/experimental/lib/rdoc/generator/html/hefss.rb
MacRuby/branches/experimental/lib/rdoc/generator/html/html.rb
MacRuby/branches/experimental/lib/rdoc/generator/html/kilmer.rb
MacRuby/branches/experimental/lib/rdoc/generator/html/one_page_html.rb
MacRuby/branches/experimental/lib/rdoc/generator/html.rb
MacRuby/branches/experimental/lib/rdoc/generator/ri.rb
MacRuby/branches/experimental/lib/rdoc/generator/xml/xml.rb
MacRuby/branches/experimental/lib/rdoc/generator/xml.rb
MacRuby/branches/experimental/lib/rdoc/generator.rb
MacRuby/branches/experimental/lib/rdoc/markup/attribute_manager.rb
MacRuby/branches/experimental/lib/rdoc/markup/fragments.rb
MacRuby/branches/experimental/lib/rdoc/markup/inline.rb
MacRuby/branches/experimental/lib/rdoc/markup/lines.rb
MacRuby/branches/experimental/lib/rdoc/markup/preprocess.rb
MacRuby/branches/experimental/lib/rdoc/markup/to_html.rb
MacRuby/branches/experimental/lib/rdoc/markup/to_html_crossref.rb
MacRuby/branches/experimental/lib/rdoc/markup.rb
MacRuby/branches/experimental/lib/rdoc/options.rb
MacRuby/branches/experimental/lib/rdoc/rdoc.rb
MacRuby/branches/experimental/lib/rdoc/ri/cache.rb
MacRuby/branches/experimental/lib/rdoc/ri/descriptions.rb
MacRuby/branches/experimental/lib/rdoc/ri/display.rb
MacRuby/branches/experimental/lib/rdoc/ri/driver.rb
MacRuby/branches/experimental/lib/rdoc/ri/formatter.rb
MacRuby/branches/experimental/lib/rdoc/ri/paths.rb
MacRuby/branches/experimental/lib/rdoc/ri/reader.rb
MacRuby/branches/experimental/lib/rdoc/ri/util.rb
MacRuby/branches/experimental/lib/rdoc/ri.rb
MacRuby/branches/experimental/lib/rdoc/stats.rb
MacRuby/branches/experimental/lib/rdoc.rb
MacRuby/branches/experimental/lib/resolv-replace.rb
MacRuby/branches/experimental/lib/resolv.rb
MacRuby/branches/experimental/lib/rexml/attlistdecl.rb
MacRuby/branches/experimental/lib/rexml/attribute.rb
MacRuby/branches/experimental/lib/rexml/cdata.rb
MacRuby/branches/experimental/lib/rexml/child.rb
MacRuby/branches/experimental/lib/rexml/comment.rb
MacRuby/branches/experimental/lib/rexml/document.rb
MacRuby/branches/experimental/lib/rexml/dtd/attlistdecl.rb
MacRuby/branches/experimental/lib/rexml/dtd/dtd.rb
MacRuby/branches/experimental/lib/rexml/dtd/elementdecl.rb
MacRuby/branches/experimental/lib/rexml/dtd/entitydecl.rb
MacRuby/branches/experimental/lib/rexml/dtd/notationdecl.rb
MacRuby/branches/experimental/lib/rexml/element.rb
MacRuby/branches/experimental/lib/rexml/encodings/CP-1252.rb
MacRuby/branches/experimental/lib/rexml/encodings/ISO-8859-15.rb
MacRuby/branches/experimental/lib/rexml/entity.rb
MacRuby/branches/experimental/lib/rexml/formatters/pretty.rb
MacRuby/branches/experimental/lib/rexml/formatters/transitive.rb
MacRuby/branches/experimental/lib/rexml/functions.rb
MacRuby/branches/experimental/lib/rexml/instruction.rb
MacRuby/branches/experimental/lib/rexml/light/node.rb
MacRuby/branches/experimental/lib/rexml/namespace.rb
MacRuby/branches/experimental/lib/rexml/node.rb
MacRuby/branches/experimental/lib/rexml/output.rb
MacRuby/branches/experimental/lib/rexml/parsers/lightparser.rb
MacRuby/branches/experimental/lib/rexml/parsers/sax2parser.rb
MacRuby/branches/experimental/lib/rexml/parsers/ultralightparser.rb
MacRuby/branches/experimental/lib/rexml/parsers/xpathparser.rb
MacRuby/branches/experimental/lib/rexml/quickpath.rb
MacRuby/branches/experimental/lib/rexml/sax2listener.rb
MacRuby/branches/experimental/lib/rexml/streamlistener.rb
MacRuby/branches/experimental/lib/rexml/text.rb
MacRuby/branches/experimental/lib/rexml/validation/relaxng.rb
MacRuby/branches/experimental/lib/rexml/xmldecl.rb
MacRuby/branches/experimental/lib/rexml/xmltokens.rb
MacRuby/branches/experimental/lib/rexml/xpath.rb
MacRuby/branches/experimental/lib/rexml/xpath_parser.rb
MacRuby/branches/experimental/lib/rinda/ring.rb
MacRuby/branches/experimental/lib/rinda/tuplespace.rb
MacRuby/branches/experimental/lib/rss/converter.rb
MacRuby/branches/experimental/lib/rss/maker/base.rb
MacRuby/branches/experimental/lib/rss/maker/itunes.rb
MacRuby/branches/experimental/lib/rss/parser.rb
MacRuby/branches/experimental/lib/rss/rss.rb
MacRuby/branches/experimental/lib/rss/utils.rb
MacRuby/branches/experimental/lib/rubygems/command.rb
MacRuby/branches/experimental/lib/rubygems/command_manager.rb
MacRuby/branches/experimental/lib/rubygems/commands/cert_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/check_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/contents_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/dependency_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/environment_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/fetch_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/help_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/install_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/list_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/lock_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/outdated_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/pristine_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/query_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/rdoc_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/sources_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/specification_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/unpack_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/update_command.rb
MacRuby/branches/experimental/lib/rubygems/commands/which_command.rb
MacRuby/branches/experimental/lib/rubygems/config_file.rb
MacRuby/branches/experimental/lib/rubygems/custom_require.rb
MacRuby/branches/experimental/lib/rubygems/defaults.rb
MacRuby/branches/experimental/lib/rubygems/dependency.rb
MacRuby/branches/experimental/lib/rubygems/dependency_installer.rb
MacRuby/branches/experimental/lib/rubygems/dependency_list.rb
MacRuby/branches/experimental/lib/rubygems/doc_manager.rb
MacRuby/branches/experimental/lib/rubygems/ext/builder.rb
MacRuby/branches/experimental/lib/rubygems/ext/rake_builder.rb
MacRuby/branches/experimental/lib/rubygems/gem_openssl.rb
MacRuby/branches/experimental/lib/rubygems/gem_path_searcher.rb
MacRuby/branches/experimental/lib/rubygems/indexer.rb
MacRuby/branches/experimental/lib/rubygems/install_update_options.rb
MacRuby/branches/experimental/lib/rubygems/installer.rb
MacRuby/branches/experimental/lib/rubygems/local_remote_options.rb
MacRuby/branches/experimental/lib/rubygems/package/tar_reader.rb
MacRuby/branches/experimental/lib/rubygems/platform.rb
MacRuby/branches/experimental/lib/rubygems/remote_fetcher.rb
MacRuby/branches/experimental/lib/rubygems/requirement.rb
MacRuby/branches/experimental/lib/rubygems/rubygems_version.rb
MacRuby/branches/experimental/lib/rubygems/security.rb
MacRuby/branches/experimental/lib/rubygems/server.rb
MacRuby/branches/experimental/lib/rubygems/source_index.rb
MacRuby/branches/experimental/lib/rubygems/source_info_cache.rb
MacRuby/branches/experimental/lib/rubygems/specification.rb
MacRuby/branches/experimental/lib/rubygems/uninstaller.rb
MacRuby/branches/experimental/lib/rubygems/user_interaction.rb
MacRuby/branches/experimental/lib/rubygems/validator.rb
MacRuby/branches/experimental/lib/rubygems/version.rb
MacRuby/branches/experimental/lib/rubygems.rb
MacRuby/branches/experimental/lib/scanf.rb
MacRuby/branches/experimental/lib/shell/command-processor.rb
MacRuby/branches/experimental/lib/shell/process-controller.rb
MacRuby/branches/experimental/lib/shell.rb
MacRuby/branches/experimental/lib/singleton.rb
MacRuby/branches/experimental/lib/sync.rb
MacRuby/branches/experimental/lib/tempfile.rb
MacRuby/branches/experimental/lib/test/unit/assertions.rb
MacRuby/branches/experimental/lib/test/unit/testcase.rb
MacRuby/branches/experimental/lib/test/unit.rb
MacRuby/branches/experimental/lib/thwait.rb
MacRuby/branches/experimental/lib/time.rb
MacRuby/branches/experimental/lib/tmpdir.rb
MacRuby/branches/experimental/lib/tsort.rb
MacRuby/branches/experimental/lib/un.rb
MacRuby/branches/experimental/lib/uri/common.rb
MacRuby/branches/experimental/lib/uri/generic.rb
MacRuby/branches/experimental/lib/uri/mailto.rb
MacRuby/branches/experimental/lib/webrick/cgi.rb
MacRuby/branches/experimental/lib/webrick/httpauth/digestauth.rb
MacRuby/branches/experimental/lib/webrick/httpproxy.rb
MacRuby/branches/experimental/lib/webrick/httprequest.rb
MacRuby/branches/experimental/lib/webrick/httpresponse.rb
MacRuby/branches/experimental/lib/webrick/httpservlet/abstract.rb
MacRuby/branches/experimental/lib/webrick/httpservlet/cgi_runner.rb
MacRuby/branches/experimental/lib/webrick/httpservlet/cgihandler.rb
MacRuby/branches/experimental/lib/webrick/httpservlet/filehandler.rb
MacRuby/branches/experimental/lib/webrick/httputils.rb
MacRuby/branches/experimental/lib/webrick/server.rb
MacRuby/branches/experimental/lib/webrick/utils.rb
MacRuby/branches/experimental/lib/xmlrpc/client.rb
MacRuby/branches/experimental/lib/xmlrpc/create.rb
MacRuby/branches/experimental/lib/xmlrpc/httpserver.rb
MacRuby/branches/experimental/lib/xmlrpc/parser.rb
MacRuby/branches/experimental/lib/xmlrpc/server.rb
MacRuby/branches/experimental/lib/xmlrpc/utils.rb
MacRuby/branches/experimental/lib/yaml/baseemitter.rb
MacRuby/branches/experimental/lib/yaml/rubytypes.rb
MacRuby/branches/experimental/lib/yaml/yamlnode.rb
MacRuby/branches/experimental/lib/yaml.rb
Added Paths:
-----------
MacRuby/branches/experimental/lib/base64.rb
MacRuby/branches/experimental/lib/cgi/cookie.rb
MacRuby/branches/experimental/lib/cgi/core.rb
MacRuby/branches/experimental/lib/cgi/html.rb
MacRuby/branches/experimental/lib/cgi/util.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/progress_indicator.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/search_field.rb
MacRuby/branches/experimental/lib/hotcocoa/mappings/sort_descriptor.rb
MacRuby/branches/experimental/lib/hotcocoa/mvc.rb
MacRuby/branches/experimental/lib/irb/lc/ja/encoding_aliases.rb
MacRuby/branches/experimental/lib/irb/magic-file.rb
MacRuby/branches/experimental/lib/irb/src_encoding.rb
MacRuby/branches/experimental/lib/minitest/
MacRuby/branches/experimental/lib/minitest/autorun.rb
MacRuby/branches/experimental/lib/minitest/mock.rb
MacRuby/branches/experimental/lib/minitest/spec.rb
MacRuby/branches/experimental/lib/minitest/unit.rb
MacRuby/branches/experimental/lib/prime.rb
MacRuby/branches/experimental/lib/rake/win32.rb
MacRuby/branches/experimental/lib/rdoc/generator/html/common.rb
MacRuby/branches/experimental/lib/rdoc/generator/html/frameless.rb
MacRuby/branches/experimental/lib/rdoc/generator/html/kilmerfactory.rb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/class.texinfo.erb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/file.texinfo.erb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/method.texinfo.erb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/texinfo.erb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo.rb
MacRuby/branches/experimental/lib/rdoc/known_classes.rb
MacRuby/branches/experimental/lib/rdoc/markup/to_texinfo.rb
MacRuby/branches/experimental/lib/rdoc/parser/
MacRuby/branches/experimental/lib/rdoc/parser/c.rb
MacRuby/branches/experimental/lib/rdoc/parser/f95.rb
MacRuby/branches/experimental/lib/rdoc/parser/perl.rb
MacRuby/branches/experimental/lib/rdoc/parser/ruby.rb
MacRuby/branches/experimental/lib/rdoc/parser/simple.rb
MacRuby/branches/experimental/lib/rdoc/parser.rb
MacRuby/branches/experimental/lib/rubygems/commands/stale_command.rb
MacRuby/branches/experimental/lib/rubygems/spec_fetcher.rb
MacRuby/branches/experimental/lib/rubygems/test_utilities.rb
Removed Paths:
-------------
MacRuby/branches/experimental/lib/minitest/autorun.rb
MacRuby/branches/experimental/lib/minitest/mock.rb
MacRuby/branches/experimental/lib/minitest/spec.rb
MacRuby/branches/experimental/lib/minitest/unit.rb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/class.texinfo.erb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/file.texinfo.erb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/method.texinfo.erb
MacRuby/branches/experimental/lib/rdoc/generator/texinfo/texinfo.erb
MacRuby/branches/experimental/lib/rdoc/parser/c.rb
MacRuby/branches/experimental/lib/rdoc/parser/f95.rb
MacRuby/branches/experimental/lib/rdoc/parser/perl.rb
MacRuby/branches/experimental/lib/rdoc/parser/ruby.rb
MacRuby/branches/experimental/lib/rdoc/parser/simple.rb
MacRuby/branches/experimental/lib/rdoc/parsers/
MacRuby/branches/experimental/lib/test/unit/assertionfailederror.rb
MacRuby/branches/experimental/lib/test/unit/autorunner.rb
MacRuby/branches/experimental/lib/test/unit/collector/
MacRuby/branches/experimental/lib/test/unit/collector.rb
MacRuby/branches/experimental/lib/test/unit/error.rb
MacRuby/branches/experimental/lib/test/unit/failure.rb
MacRuby/branches/experimental/lib/test/unit/testresult.rb
MacRuby/branches/experimental/lib/test/unit/testsuite.rb
MacRuby/branches/experimental/lib/test/unit/ui/
MacRuby/branches/experimental/lib/test/unit/util/
Modified: MacRuby/branches/experimental/lib/.document
===================================================================
--- MacRuby/branches/experimental/lib/.document 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/.document 2009-06-19 22:42:24 UTC (rev 1889)
@@ -59,6 +59,7 @@
ping.rb
pp.rb
prettyprint.rb
+prime.rb
profile.rb
profiler.rb
pstore.rb
Modified: MacRuby/branches/experimental/lib/README
===================================================================
--- MacRuby/branches/experimental/lib/README 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/README 2009-06-19 22:42:24 UTC (rev 1889)
@@ -42,6 +42,7 @@
pathname.rb Object-Oriented Pathname Class
pp.rb pretty print objects
prettyprint.rb pretty printing algorithm
+prime.rb prime numbers and factorization
profile.rb runs ruby profiler
profiler.rb ruby profiler module
pstore.rb persistent object strage using marshal
Copied: MacRuby/branches/experimental/lib/base64.rb (from rev 1886, MacRuby/trunk/lib/base64.rb)
===================================================================
--- MacRuby/branches/experimental/lib/base64.rb (rev 0)
+++ MacRuby/branches/experimental/lib/base64.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,91 @@
+#
+# = base64.rb: methods for base64-encoding and -decoding stings
+#
+
+# The Base64 module provides for the encoding (#encode64, #strict_encode64,
+# #urlsafe_encode64) and decoding (#decode64, #strict_decode64,
+# #urlsafe_decode64) of binary data using a Base64 representation.
+#
+# == Example
+#
+# A simple encoding and decoding.
+#
+# require "base64"
+#
+# enc = Base64.encode64('Send reinforcements')
+# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
+# plain = Base64.decode64(enc)
+# # -> "Send reinforcements"
+#
+# The purpose of using base64 to encode data is that it translates any
+# binary data into purely printable characters.
+
+module Base64
+ module_function
+
+ # Returns the Base64-encoded version of +bin+.
+ # This method complies with RFC 2045.
+ # Line feeds are added to every 60 encoded charactors.
+ #
+ # require 'base64'
+ # Base64.encode64("Now is the time for all good coders\nto learn Ruby")
+ #
+ # <i>Generates:</i>
+ #
+ # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g
+ # UnVieQ==
+ def encode64(bin)
+ [bin].pack("m")
+ end
+
+ # Returns the Base64-decoded version of +str+.
+ # This method complies with RFC 2045.
+ # Characters outside the base alphabet are ignored.
+ #
+ # require 'base64'
+ # str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' +
+ # 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' +
+ # 'ZSB0aHJlZQpBbmQgc28gb24uLi4K'
+ # puts Base64.decode64(str)
+ #
+ # <i>Generates:</i>
+ #
+ # This is line one
+ # This is line two
+ # This is line three
+ # And so on...
+ def decode64(str)
+ str.unpack("m").first
+ end
+
+ # Returns the Base64-encoded version of +bin+.
+ # This method complies with RFC 4648.
+ # No line feeds are added.
+ def strict_encode64(bin)
+ [bin].pack("m0")
+ end
+
+ # Returns the Base64-decoded version of +str+.
+ # This method complies with RFC 4648.
+ # ArgumentError is raised if +str+ is incorrectly padded or contains
+ # non-alphabet characters. Note that CR or LF are also rejected.
+ def strict_decode64(str)
+ str.unpack("m0").first
+ end
+
+ # Returns the Base64-encoded version of +bin+.
+ # This method complies with ``Base 64 Encoding with URL and Filename Safe
+ # Alphabet'' in RFC 4648.
+ # The alphabet uses '-' instead of '+' and '_' instead of '/'.
+ def urlsafe_encode64(bin)
+ strict_encode64(bin).tr("+/", "-_")
+ end
+
+ # Returns the Base64-decoded version of +str+.
+ # This method complies with ``Base 64 Encoding with URL and Filename Safe
+ # Alphabet'' in RFC 4648.
+ # The alphabet uses '-' instead of '+' and '_' instead of '/'.
+ def urlsafe_decode64(str)
+ strict_decode64(str.tr("-_", "+/"))
+ end
+end
Copied: MacRuby/branches/experimental/lib/cgi/cookie.rb (from rev 1886, MacRuby/trunk/lib/cgi/cookie.rb)
===================================================================
--- MacRuby/branches/experimental/lib/cgi/cookie.rb (rev 0)
+++ MacRuby/branches/experimental/lib/cgi/cookie.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,137 @@
+ # Class representing an HTTP cookie.
+ #
+ # In addition to its specific fields and methods, a Cookie instance
+ # is a delegator to the array of its values.
+ #
+ # See RFC 2965.
+ #
+ # == Examples of use
+ # cookie1 = CGI::Cookie::new("name", "value1", "value2", ...)
+ # cookie1 = CGI::Cookie::new("name" => "name", "value" => "value")
+ # cookie1 = CGI::Cookie::new('name' => 'name',
+ # 'value' => ['value1', 'value2', ...],
+ # 'path' => 'path', # optional
+ # 'domain' => 'domain', # optional
+ # 'expires' => Time.now, # optional
+ # 'secure' => true # optional
+ # )
+ #
+ # cgi.out("cookie" => [cookie1, cookie2]) { "string" }
+ #
+ # name = cookie1.name
+ # values = cookie1.value
+ # path = cookie1.path
+ # domain = cookie1.domain
+ # expires = cookie1.expires
+ # secure = cookie1.secure
+ #
+ # cookie1.name = 'name'
+ # cookie1.value = ['value1', 'value2', ...]
+ # cookie1.path = 'path'
+ # cookie1.domain = 'domain'
+ # cookie1.expires = Time.now + 30
+ # cookie1.secure = true
+class CGI
+ class Cookie < Array
+
+ # Create a new CGI::Cookie object.
+ #
+ # The contents of the cookie can be specified as a +name+ and one
+ # or more +value+ arguments. Alternatively, the contents can
+ # be specified as a single hash argument. The possible keywords of
+ # this hash are as follows:
+ #
+ # name:: the name of the cookie. Required.
+ # value:: the cookie's value or list of values.
+ # path:: the path for which this cookie applies. Defaults to the
+ # base directory of the CGI script.
+ # domain:: the domain for which this cookie applies.
+ # expires:: the time at which this cookie expires, as a +Time+ object.
+ # secure:: whether this cookie is a secure cookie or not (default to
+ # false). Secure cookies are only transmitted to HTTPS
+ # servers.
+ #
+ # These keywords correspond to attributes of the cookie object.
+ def initialize(name = "", *value)
+ if name.kind_of?(String)
+ @name = name
+ @value = value
+ %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
+ @path = ($1 or "")
+ @secure = false
+ return super(@value)
+ end
+
+ options = name
+ unless options.has_key?("name")
+ raise ArgumentError, "`name' required"
+ end
+
+ @name = options["name"]
+ @value = Array(options["value"])
+ # simple support for IE
+ if options["path"]
+ @path = options["path"]
+ else
+ %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
+ @path = ($1 or "")
+ end
+ @domain = options["domain"]
+ @expires = options["expires"]
+ @secure = options["secure"] == true ? true : false
+
+ super(@value)
+ end
+
+ attr_accessor("name", "value", "path", "domain", "expires")
+ attr_reader("secure")
+
+ # Set whether the Cookie is a secure cookie or not.
+ #
+ # +val+ must be a boolean.
+ def secure=(val)
+ @secure = val if val == true or val == false
+ @secure
+ end
+
+ # Convert the Cookie to its string representation.
+ def to_s
+ val = @value.kind_of?(String) ? CGI::escape(@value) : @value.collect{|v| CGI::escape(v) }.join("&")
+ buf = "#{@name}=#{val}"
+ buf << "; domain=#{@domain}" if @domain
+ buf << "; path=#{@path}" if @path
+ buf << "; expires=#{CGI::rfc1123_date(@expires)}" if @expires
+ buf << "; secure" if @secure == true
+ buf
+ end
+
+ end # class Cookie
+
+
+ # Parse a raw cookie string into a hash of cookie-name=>Cookie
+ # pairs.
+ #
+ # cookies = CGI::Cookie::parse("raw_cookie_string")
+ # # { "name1" => cookie1, "name2" => cookie2, ... }
+ #
+ def Cookie::parse(raw_cookie)
+ cookies = Hash.new([])
+ return cookies unless raw_cookie
+
+ raw_cookie.split(/[;,]\s?/).each do |pairs|
+ name, values = pairs.split('=',2)
+ next unless name and values
+ name = CGI::unescape(name)
+ values ||= ""
+ values = values.split('&').collect{|v| CGI::unescape(v) }
+ if cookies.has_key?(name)
+ values = cookies[name].value + values
+ end
+ cookies[name] = Cookie::new(name, *values)
+ end
+
+ cookies
+ end
+end
+
+
Copied: MacRuby/branches/experimental/lib/cgi/core.rb (from rev 1886, MacRuby/trunk/lib/cgi/core.rb)
===================================================================
--- MacRuby/branches/experimental/lib/cgi/core.rb (rev 0)
+++ MacRuby/branches/experimental/lib/cgi/core.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,786 @@
+class CGI
+
+ $CGI_ENV = ENV # for FCGI support
+
+ # String for carriage return
+ CR = "\015"
+
+ # String for linefeed
+ LF = "\012"
+
+ # Standard internet newline sequence
+ EOL = CR + LF
+
+ REVISION = '$Id: core.rb 21825 2009-01-28 09:21:49Z yugui $' #:nodoc:
+
+ NEEDS_BINMODE = true if /WIN/i.match(RUBY_PLATFORM)
+
+ # Path separators in different environments.
+ PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
+
+ # HTTP status codes.
+ HTTP_STATUS = {
+ "OK" => "200 OK",
+ "PARTIAL_CONTENT" => "206 Partial Content",
+ "MULTIPLE_CHOICES" => "300 Multiple Choices",
+ "MOVED" => "301 Moved Permanently",
+ "REDIRECT" => "302 Found",
+ "NOT_MODIFIED" => "304 Not Modified",
+ "BAD_REQUEST" => "400 Bad Request",
+ "AUTH_REQUIRED" => "401 Authorization Required",
+ "FORBIDDEN" => "403 Forbidden",
+ "NOT_FOUND" => "404 Not Found",
+ "METHOD_NOT_ALLOWED" => "405 Method Not Allowed",
+ "NOT_ACCEPTABLE" => "406 Not Acceptable",
+ "LENGTH_REQUIRED" => "411 Length Required",
+ "PRECONDITION_FAILED" => "412 Rrecondition Failed",
+ "SERVER_ERROR" => "500 Internal Server Error",
+ "NOT_IMPLEMENTED" => "501 Method Not Implemented",
+ "BAD_GATEWAY" => "502 Bad Gateway",
+ "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
+ }
+
+ # Abbreviated day-of-week names specified by RFC 822
+ RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
+
+ # Abbreviated month names specified by RFC 822
+ RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
+
+ # :startdoc:
+
+ def env_table
+ ENV
+ end
+
+ def stdinput
+ $stdin
+ end
+
+ def stdoutput
+ $stdout
+ end
+
+ private :env_table, :stdinput, :stdoutput
+
+
+ # Create an HTTP header block as a string.
+ #
+ # Includes the empty line that ends the header block.
+ #
+ # +options+ can be a string specifying the Content-Type (defaults
+ # to text/html), or a hash of header key/value pairs. The following
+ # header keys are recognized:
+ #
+ # type:: the Content-Type header. Defaults to "text/html"
+ # charset:: the charset of the body, appended to the Content-Type header.
+ # nph:: a boolean value. If true, prepend protocol string and status code, and
+ # date; and sets default values for "server" and "connection" if not
+ # explicitly set.
+ # status:: the HTTP status code, returned as the Status header. See the
+ # list of available status codes below.
+ # server:: the server software, returned as the Server header.
+ # connection:: the connection type, returned as the Connection header (for
+ # instance, "close".
+ # length:: the length of the content that will be sent, returned as the
+ # Content-Length header.
+ # language:: the language of the content, returned as the Content-Language
+ # header.
+ # expires:: the time on which the current content expires, as a +Time+
+ # object, returned as the Expires header.
+ # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
+ # The value can be the literal string of the cookie; a CGI::Cookie
+ # object; an Array of literal cookie strings or Cookie objects; or a
+ # hash all of whose values are literal cookie strings or Cookie objects.
+ # These cookies are in addition to the cookies held in the
+ # @output_cookies field.
+ #
+ # Other header lines can also be set; they are appended as key: value.
+ #
+ # header
+ # # Content-Type: text/html
+ #
+ # header("text/plain")
+ # # Content-Type: text/plain
+ #
+ # header("nph" => true,
+ # "status" => "OK", # == "200 OK"
+ # # "status" => "200 GOOD",
+ # "server" => ENV['SERVER_SOFTWARE'],
+ # "connection" => "close",
+ # "type" => "text/html",
+ # "charset" => "iso-2022-jp",
+ # # Content-Type: text/html; charset=iso-2022-jp
+ # "length" => 103,
+ # "language" => "ja",
+ # "expires" => Time.now + 30,
+ # "cookie" => [cookie1, cookie2],
+ # "my_header1" => "my_value"
+ # "my_header2" => "my_value")
+ #
+ # The status codes are:
+ #
+ # "OK" --> "200 OK"
+ # "PARTIAL_CONTENT" --> "206 Partial Content"
+ # "MULTIPLE_CHOICES" --> "300 Multiple Choices"
+ # "MOVED" --> "301 Moved Permanently"
+ # "REDIRECT" --> "302 Found"
+ # "NOT_MODIFIED" --> "304 Not Modified"
+ # "BAD_REQUEST" --> "400 Bad Request"
+ # "AUTH_REQUIRED" --> "401 Authorization Required"
+ # "FORBIDDEN" --> "403 Forbidden"
+ # "NOT_FOUND" --> "404 Not Found"
+ # "METHOD_NOT_ALLOWED" --> "405 Method Not Allowed"
+ # "NOT_ACCEPTABLE" --> "406 Not Acceptable"
+ # "LENGTH_REQUIRED" --> "411 Length Required"
+ # "PRECONDITION_FAILED" --> "412 Precondition Failed"
+ # "SERVER_ERROR" --> "500 Internal Server Error"
+ # "NOT_IMPLEMENTED" --> "501 Method Not Implemented"
+ # "BAD_GATEWAY" --> "502 Bad Gateway"
+ # "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
+ #
+ # This method does not perform charset conversion.
+ def header(options='text/html')
+ if options.is_a?(String)
+ content_type = options
+ buf = _header_for_string(content_type)
+ elsif options.is_a?(Hash)
+ if options.size == 1 && options.has_key?('type')
+ content_type = options['type']
+ buf = _header_for_string(content_type)
+ else
+ buf = _header_for_hash(options.dup)
+ end
+ else
+ raise ArgumentError.new("expected String or Hash but got #{options.class}")
+ end
+ if defined?(MOD_RUBY)
+ _header_for_modruby(buf)
+ return ''
+ else
+ buf << EOL # empty line of separator
+ return buf
+ end
+ end # header()
+
+ def _header_for_string(content_type) #:nodoc:
+ buf = ''
+ if nph?()
+ buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}"
+ buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
+ buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}"
+ buf << "Connection: close#{EOL}"
+ end
+ buf << "Content-Type: #{content_type}#{EOL}"
+ if @output_cookies
+ @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" }
+ end
+ return buf
+ end # _header_for_string
+ private :_header_for_string
+
+ def _header_for_hash(options) #:nodoc:
+ buf = ''
+ ## add charset to option['type']
+ options['type'] ||= 'text/html'
+ charset = options.delete('charset')
+ options['type'] += "; charset=#{charset}" if charset
+ ## NPH
+ options.delete('nph') if defined?(MOD_RUBY)
+ if options.delete('nph') || nph?()
+ protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'
+ status = options.delete('status')
+ status = HTTP_STATUS[status] || status || '200 OK'
+ buf << "#{protocol} #{status}#{EOL}"
+ buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
+ options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || ''
+ options['connection'] ||= 'close'
+ end
+ ## common headers
+ status = options.delete('status')
+ buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status
+ server = options.delete('server')
+ buf << "Server: #{server}#{EOL}" if server
+ connection = options.delete('connection')
+ buf << "Connection: #{connection}#{EOL}" if connection
+ type = options.delete('type')
+ buf << "Content-Type: #{type}#{EOL}" #if type
+ length = options.delete('length')
+ buf << "Content-Length: #{length}#{EOL}" if length
+ language = options.delete('language')
+ buf << "Content-Language: #{language}#{EOL}" if language
+ expires = options.delete('expires')
+ buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires
+ ## cookie
+ if cookie = options.delete('cookie')
+ case cookie
+ when String, Cookie
+ buf << "Set-Cookie: #{cookie}#{EOL}"
+ when Array
+ arr = cookie
+ arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ when Hash
+ hash = cookie
+ hash.each {|name, c| buf << "Set-Cookie: #{c}#{EOL}" }
+ end
+ end
+ if @output_cookies
+ @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ end
+ ## other headers
+ options.each do |key, value|
+ buf << "#{key}: #{value}#{EOL}"
+ end
+ return buf
+ end # _header_for_hash
+ private :_header_for_hash
+
+ def nph? #:nodoc:
+ return /IIS\/(\d+)/.match($CGI_ENV['SERVER_SOFTWARE']) && $1.to_i < 5
+ end
+
+ def _header_for_modruby(buf) #:nodoc:
+ request = Apache::request
+ buf.scan(/([^:]+): (.+)#{EOL}/o) do |name, value|
+ warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
+ case name
+ when 'Set-Cookie'
+ request.headers_out.add(name, value)
+ when /^status$/i
+ request.status_line = value
+ request.status = value.to_i
+ when /^content-type$/i
+ request.content_type = value
+ when /^content-encoding$/i
+ request.content_encoding = value
+ when /^location$/i
+ request.status = 302 if request.status == 200
+ request.headers_out[name] = value
+ else
+ request.headers_out[name] = value
+ end
+ end
+ request.send_http_header
+ return ''
+ end
+ private :_header_for_modruby
+ #
+
+ # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
+ #
+ # The header is provided by +options+, as for #header().
+ # The body of the document is that returned by the passed-
+ # in block. This block takes no arguments. It is required.
+ #
+ # cgi = CGI.new
+ # cgi.out{ "string" }
+ # # Content-Type: text/html
+ # # Content-Length: 6
+ # #
+ # # string
+ #
+ # cgi.out("text/plain") { "string" }
+ # # Content-Type: text/plain
+ # # Content-Length: 6
+ # #
+ # # string
+ #
+ # cgi.out("nph" => true,
+ # "status" => "OK", # == "200 OK"
+ # "server" => ENV['SERVER_SOFTWARE'],
+ # "connection" => "close",
+ # "type" => "text/html",
+ # "charset" => "iso-2022-jp",
+ # # Content-Type: text/html; charset=iso-2022-jp
+ # "language" => "ja",
+ # "expires" => Time.now + (3600 * 24 * 30),
+ # "cookie" => [cookie1, cookie2],
+ # "my_header1" => "my_value",
+ # "my_header2" => "my_value") { "string" }
+ #
+ # Content-Length is automatically calculated from the size of
+ # the String returned by the content block.
+ #
+ # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
+ # is outputted (the content block is still required, but it
+ # is ignored).
+ #
+ # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
+ # the content is converted to this charset, and the language is set
+ # to "ja".
+ def out(options = "text/html") # :yield:
+
+ options = { "type" => options } if options.kind_of?(String)
+ content = yield
+ options["length"] = content.bytesize.to_s
+ output = stdoutput
+ output.binmode if defined? output.binmode
+ output.print header(options)
+ output.print content unless "HEAD" == env_table['REQUEST_METHOD']
+ end
+
+
+ # Print an argument or list of arguments to the default output stream
+ #
+ # cgi = CGI.new
+ # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print
+ def print(*options)
+ stdoutput.print(*options)
+ end
+
+ # Parse an HTTP query string into a hash of key=>value pairs.
+ #
+ # params = CGI::parse("query_string")
+ # # {"name1" => ["value1", "value2", ...],
+ # # "name2" => ["value1", "value2", ...], ... }
+ #
+ def CGI::parse(query)
+ params = {}
+ query.split(/[&;]/).each do |pairs|
+ key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
+ if key && value
+ params.has_key?(key) ? params[key].push(value) : params[key] = [value]
+ elsif key
+ params[key]=[]
+ end
+ end
+ params.default=[].freeze
+ params
+ end
+
+ # Maximum content length of post data
+ ##MAX_CONTENT_LENGTH = 2 * 1024 * 1024
+
+ # Maximum content length of multipart data
+ MAX_MULTIPART_LENGTH = 128 * 1024 * 1024
+
+ # Maximum number of request parameters when multipart
+ MAX_MULTIPART_COUNT = 128
+
+ # Mixin module. It provides the follow functionality groups:
+ #
+ # 1. Access to CGI environment variables as methods. See
+ # documentation to the CGI class for a list of these variables.
+ #
+ # 2. Access to cookies, including the cookies attribute.
+ #
+ # 3. Access to parameters, including the params attribute, and overloading
+ # [] to perform parameter value lookup by key.
+ #
+ # 4. The initialize_query method, for initialising the above
+ # mechanisms, handling multipart forms, and allowing the
+ # class to be used in "offline" mode.
+ #
+ module QueryExtension
+
+ %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
+ define_method(env.sub(/^HTTP_/, '').downcase) do
+ (val = env_table[env]) && Integer(val)
+ end
+ end
+
+ %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
+ PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
+ REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
+ SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
+
+ HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
+ HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
+ define_method(env.sub(/^HTTP_/, '').downcase) do
+ env_table[env]
+ end
+ end
+
+ # Get the raw cookies as a string.
+ def raw_cookie
+ env_table["HTTP_COOKIE"]
+ end
+
+ # Get the raw RFC2965 cookies as a string.
+ def raw_cookie2
+ env_table["HTTP_COOKIE2"]
+ end
+
+ # Get the cookies as a hash of cookie-name=>Cookie pairs.
+ attr_accessor :cookies
+
+ # Get the parameters as a hash of name=>values pairs, where
+ # values is an Array.
+ attr_reader :params
+
+ # Get the uploaed files as a hash of name=>values pairs
+ attr_reader :files
+
+ # Set all the parameters.
+ def params=(hash)
+ @params.clear
+ @params.update(hash)
+ end
+
+ def read_multipart(boundary, content_length)
+ ## read first boundary
+ stdin = $stdin
+ first_line = "--#{boundary}#{EOL}"
+ content_length -= first_line.bytesize
+ status = stdin.read(first_line.bytesize)
+ raise EOFError.new("no content body") unless status
+ raise EOFError.new("bad content body") unless first_line == status
+ ## parse and set params
+ params = {}
+ @files = {}
+ boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/
+ boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize
+ boundary_end = nil
+ buf = ''
+ bufsize = 10 * 1024
+ max_count = MAX_MULTIPART_COUNT
+ n = 0
+ while true
+ (n += 1) < max_count or raise StandardError.new("too many parameters.")
+ ## create body (StringIO or Tempfile)
+ body = create_body(bufsize < content_length)
+ class << body
+ alias local_path path
+ attr_reader :original_filename, :content_type
+ end
+ ## find head and boundary
+ head = nil
+ separator = EOL * 2
+ until head && matched = boundary_rexp.match(buf)
+ if !head && pos = buf.index(separator)
+ len = pos + EOL.bytesize
+ head = buf[0, len]
+ buf = buf[(pos+separator.bytesize)..-1]
+ else
+ if head && buf.size > boundary_size
+ len = buf.size - boundary_size
+ body.print(buf[0, len])
+ buf[0, len] = ''
+ end
+ c = stdin.read(bufsize < content_length ? bufsize : content_length)
+ raise EOFError.new("bad content body") if c.nil? || c.empty?
+ buf << c
+ content_length -= c.bytesize
+ end
+ end
+ ## read to end of boundary
+ m = matched
+ len = m.begin(0)
+ s = buf[0, len]
+ if s =~ /(\r?\n)\z/
+ s = buf[0, len - $1.bytesize]
+ end
+ body.print(s)
+ buf = buf[m.end(0)..-1]
+ boundary_end = m[1]
+ content_length = -1 if boundary_end == '--'
+ ## reset file cursor position
+ body.rewind
+ ## original filename
+ /Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
+ filename = $1 || $2 || ''
+ filename = CGI.unescape(filename) if unescape_filename?()
+ body.instance_variable_set('@original_filename', filename.taint)
+ ## content type
+ /Content-Type: (.*)/i.match(head)
+ (content_type = $1 || '').chomp!
+ body.instance_variable_set('@content_type', content_type.taint)
+ ## query parameter name
+ /Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
+ name = $1 || $2 || ''
+ if body.original_filename.empty?
+ value=body.read.dup.force_encoding(@accept_charset)
+ (params[name] ||= []) << value
+ unless value.valid_encoding?
+ if @accept_charset_error_block
+ @accept_charset_error_block.call(name,value)
+ else
+ raise InvalidEncoding,"Accept-Charset encoding error"
+ end
+ end
+ class << params[name].last;self;end.class_eval do
+ define_method(:read){self}
+ define_method(:original_filename){""}
+ define_method(:content_type){""}
+ end
+ else
+ (params[name] ||= []) << body
+ @files[name]=body
+ end
+ ## break loop
+ break if buf.size == 0
+ break if content_length == -1
+ end
+ raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/
+ params.default = []
+ params
+ end # read_multipart
+ private :read_multipart
+ def create_body(is_large) #:nodoc:
+ if is_large
+ require 'tempfile'
+ body = Tempfile.new('CGI', encoding: "ascii-8bit")
+ else
+ begin
+ require 'stringio'
+ body = StringIO.new("".force_encoding("ascii-8bit"))
+ rescue LoadError
+ require 'tempfile'
+ body = Tempfile.new('CGI', encoding: "ascii-8bit")
+ end
+ end
+ body.binmode if defined? body.binmode
+ return body
+ end
+ def unescape_filename? #:nodoc:
+ user_agent = $CGI_ENV['HTTP_USER_AGENT']
+ return /Mac/i.match(user_agent) && /Mozilla/i.match(user_agent) && !/MSIE/i.match(user_agent)
+ end
+
+ # offline mode. read name=value pairs on standard input.
+ def read_from_cmdline
+ require "shellwords"
+
+ string = unless ARGV.empty?
+ ARGV.join(' ')
+ else
+ if STDIN.tty?
+ STDERR.print(
+ %|(offline mode: enter name=value pairs on standard input)\n|
+ )
+ end
+ readlines.join(' ').gsub(/\n/, '')
+ end.gsub(/\\=/, '%3D').gsub(/\\&/, '%26')
+
+ words = Shellwords.shellwords(string)
+
+ if words.find{|x| /=/.match(x) }
+ words.join('&')
+ else
+ words.join('+')
+ end
+ end
+ private :read_from_cmdline
+
+ # A wrapper class to use a StringIO object as the body and switch
+ # to a TempFile when the passed threshold is passed.
+ # Initialize the data from the query.
+ #
+ # Handles multipart forms (in particular, forms that involve file uploads).
+ # Reads query parameters in the @params field, and cookies into @cookies.
+ def initialize_query()
+ if ("POST" == env_table['REQUEST_METHOD']) and
+ %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|.match(env_table['CONTENT_TYPE'])
+ raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > MAX_MULTIPART_LENGTH
+ boundary = $1.dup
+ @multipart = true
+ @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
+ else
+ @multipart = false
+ @params = CGI::parse(
+ case env_table['REQUEST_METHOD']
+ when "GET", "HEAD"
+ if defined?(MOD_RUBY)
+ Apache::request.args or ""
+ else
+ env_table['QUERY_STRING'] or ""
+ end
+ when "POST"
+ stdinput.binmode if defined? stdinput.binmode
+ stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
+ else
+ read_from_cmdline
+ end.dup.force_encoding(@accept_charset)
+ )
+ unless Encoding.find(@accept_charset) == Encoding::ASCII_8BIT
+ @params.each do |key,values|
+ values.each do |value|
+ unless value.valid_encoding?
+ if @accept_charset_error_block
+ @accept_charset_error_block.call(key,value)
+ else
+ raise InvalidEncoding,"Accept-Charset encoding error"
+ end
+ end
+ end
+ end
+ end
+ end
+
+ @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
+ end
+ private :initialize_query
+
+ def multipart?
+ @multipart
+ end
+
+ # Get the value for the parameter with a given key.
+ #
+ # If the parameter has multiple values, only the first will be
+ # retrieved; use #params() to get the array of values.
+ def [](key)
+ params = @params[key]
+ return '' unless params
+ value = params[0]
+ if @multipart
+ if value
+ return value
+ elsif defined? StringIO
+ StringIO.new("".force_encoding("ascii-8bit"))
+ else
+ Tempfile.new("CGI",encoding:"ascii-8bit")
+ end
+ else
+ str = if value then value.dup else "" end
+ str
+ end
+ end
+
+ # Return all parameter keys as an array.
+ def keys(*args)
+ @params.keys(*args)
+ end
+
+ # Returns true if a given parameter key exists in the query.
+ def has_key?(*args)
+ @params.has_key?(*args)
+ end
+ alias key? has_key?
+ alias include? has_key?
+
+ end # QueryExtension
+
+ # InvalidEncoding Exception class
+ class InvalidEncoding < Exception; end
+
+ # @@accept_charset is default accept character set.
+ # This default value default is "UTF-8"
+ # If you want to change the default accept character set
+ # when create a new CGI instance, set this:
+ #
+ # CGI.accept_charset = "EUC-JP"
+ #
+
+ @@accept_charset="UTF-8"
+
+ def self.accept_charset
+ @@accept_charset
+ end
+
+ def self.accept_charset=(accept_charset)
+ @@accept_charset=accept_charset
+ end
+
+ # Create a new CGI instance.
+ #
+ # CGI accept constructor parameters either in a hash, string as a block.
+ # But string is as same as using :tag_maker of hash.
+ #
+ # CGI.new("html3") #=> CGI.new(:tag_maker=>"html3")
+ #
+ # And, if you specify string, @accept_charset cannot be changed.
+ # Instead, please use hash parameter.
+ #
+ # == accept_charset
+ #
+ # :accept_charset specifies encoding of received query string.
+ # ( Default value is @@accept_charset. )
+ # If not valid, raise CGI::InvalidEncoding
+ #
+ # Example. Suppose @@accept_charset # => "UTF-8"
+ #
+ # when not specified:
+ #
+ # cgi=CGI.new # @accept_charset # => "UTF-8"
+ #
+ # when specified "EUC-JP":
+ #
+ # cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP"
+ #
+ # == block
+ #
+ # When you use a block, you can write a process
+ # that query encoding is invalid. Example:
+ #
+ # encoding_error={}
+ # cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value|
+ # encoding_error[key] = value
+ # end
+ #
+ # == tag_maker
+ #
+ # :tag_maker specifies which version of HTML to load the HTML generation
+ # methods for. The following versions of HTML are supported:
+ #
+ # html3:: HTML 3.x
+ # html4:: HTML 4.0
+ # html4Tr:: HTML 4.0 Transitional
+ # html4Fr:: HTML 4.0 with Framesets
+ #
+ # If not specified, no HTML generation methods will be loaded.
+ #
+ # If the CGI object is not created in a standard CGI call environment
+ # (that is, it can't locate REQUEST_METHOD in its environment), then
+ # it will run in "offline" mode. In this mode, it reads its parameters
+ # from the command line or (failing that) from standard input. Otherwise,
+ # cookies and other parameters are parsed automatically from the standard
+ # CGI locations, which varies according to the REQUEST_METHOD. It works this:
+ #
+ # CGI.new(:tag_maker=>"html3")
+ #
+ # This will be obsolete:
+ #
+ # CGI.new("html3")
+ #
+ attr_reader :accept_charset
+ def initialize(options = {},&block)
+ @accept_charset_error_block=block if block_given?
+ @options={:accept_charset=>@@accept_charset}
+ case options
+ when Hash
+ @options.merge!(options)
+ when String
+ @options[:tag_maker]=options
+ end
+ @accept_charset=@options[:accept_charset]
+ if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
+ Apache.request.setup_cgi_env
+ end
+
+ extend QueryExtension
+ @multipart = false
+
+ initialize_query() # set @params, @cookies
+ @output_cookies = nil
+ @output_hidden = nil
+
+ case @options[:tag_maker]
+ when "html3"
+ require 'cgi/html'
+ extend Html3
+ element_init()
+ extend HtmlExtension
+ when "html4"
+ require 'cgi/html'
+ extend Html4
+ element_init()
+ extend HtmlExtension
+ when "html4Tr"
+ require 'cgi/html'
+ extend Html4Tr
+ element_init()
+ extend HtmlExtension
+ when "html4Fr"
+ require 'cgi/html'
+ extend Html4Tr
+ element_init()
+ extend Html4Fr
+ element_init()
+ extend HtmlExtension
+ end
+ end
+
+end # class CGI
+
+
Copied: MacRuby/branches/experimental/lib/cgi/html.rb (from rev 1886, MacRuby/trunk/lib/cgi/html.rb)
===================================================================
--- MacRuby/branches/experimental/lib/cgi/html.rb (rev 0)
+++ MacRuby/branches/experimental/lib/cgi/html.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,1021 @@
+ # Base module for HTML-generation mixins.
+ #
+ # Provides methods for code generation for tags following
+ # the various DTD element types.
+class CGI
+ module TagMaker # :nodoc:
+
+ # Generate code for an element with required start and end tags.
+ #
+ # - -
+ def nn_element_def(element)
+ nOE_element_def(element, <<-END)
+ if block_given?
+ yield.to_s
+ else
+ ""
+ end +
+ "</#{element.upcase}>"
+ END
+ end
+
+ # Generate code for an empty element.
+ #
+ # - O EMPTY
+ def nOE_element_def(element, append = nil)
+ s = <<-END
+ attributes={attributes=>nil} if attributes.kind_of?(String)
+ "<#{element.upcase}" + attributes.collect{|name, value|
+ next unless value
+ " " + CGI::escapeHTML(name.to_s) +
+ if true == value
+ ""
+ else
+ '="' + CGI::escapeHTML(value.to_s) + '"'
+ end
+ }.join + ">"
+ END
+ s.sub!(/\Z/, " +") << append if append
+ s
+ end
+
+ # Generate code for an element for which the end (and possibly the
+ # start) tag is optional.
+ #
+ # O O or - O
+ def nO_element_def(element)
+ nOE_element_def(element, <<-END)
+ if block_given?
+ yield.to_s + "</#{element.upcase}>"
+ else
+ ""
+ end
+ END
+ end
+
+ end # TagMaker
+
+
+ #
+ # Mixin module providing HTML generation methods.
+ #
+ # For example,
+ # cgi.a("http://www.example.com") { "Example" }
+ # # => "<A HREF=\"http://www.example.com\">Example</A>"
+ #
+ # Modules Http3, Http4, etc., contain more basic HTML-generation methods
+ # (:title, :center, etc.).
+ #
+ # See class CGI for a detailed example.
+ #
+ module HtmlExtension
+
+
+ # Generate an Anchor element as a string.
+ #
+ # +href+ can either be a string, giving the URL
+ # for the HREF attribute, or it can be a hash of
+ # the element's attributes.
+ #
+ # The body of the element is the string returned by the no-argument
+ # block passed in.
+ #
+ # a("http://www.example.com") { "Example" }
+ # # => "<A HREF=\"http://www.example.com\">Example</A>"
+ #
+ # a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
+ # # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
+ #
+ def a(href = "") # :yield:
+ attributes = if href.kind_of?(String)
+ { "HREF" => href }
+ else
+ href
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+ # Generate a Document Base URI element as a String.
+ #
+ # +href+ can either by a string, giving the base URL for the HREF
+ # attribute, or it can be a has of the element's attributes.
+ #
+ # The passed-in no-argument block is ignored.
+ #
+ # base("http://www.example.com/cgi")
+ # # => "<BASE HREF=\"http://www.example.com/cgi\">"
+ def base(href = "") # :yield:
+ attributes = if href.kind_of?(String)
+ { "HREF" => href }
+ else
+ href
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+ # Generate a BlockQuote element as a string.
+ #
+ # +cite+ can either be a string, give the URI for the source of
+ # the quoted text, or a hash, giving all attributes of the element,
+ # or it can be omitted, in which case the element has no attributes.
+ #
+ # The body is provided by the passed-in no-argument block
+ #
+ # blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
+ # #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
+ def blockquote(cite = {}) # :yield:
+ attributes = if cite.kind_of?(String)
+ { "CITE" => cite }
+ else
+ cite
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+
+ # Generate a Table Caption element as a string.
+ #
+ # +align+ can be a string, giving the alignment of the caption
+ # (one of top, bottom, left, or right). It can be a hash of
+ # all the attributes of the element. Or it can be omitted.
+ #
+ # The body of the element is provided by the passed-in no-argument block.
+ #
+ # caption("left") { "Capital Cities" }
+ # # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
+ def caption(align = {}) # :yield:
+ attributes = if align.kind_of?(String)
+ { "ALIGN" => align }
+ else
+ align
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+
+ # Generate a Checkbox Input element as a string.
+ #
+ # The attributes of the element can be specified as three arguments,
+ # +name+, +value+, and +checked+. +checked+ is a boolean value;
+ # if true, the CHECKED attribute will be included in the element.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # checkbox("name")
+ # # = checkbox("NAME" => "name")
+ #
+ # checkbox("name", "value")
+ # # = checkbox("NAME" => "name", "VALUE" => "value")
+ #
+ # checkbox("name", "value", true)
+ # # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
+ def checkbox(name = "", value = nil, checked = nil)
+ attributes = if name.kind_of?(String)
+ { "TYPE" => "checkbox", "NAME" => name,
+ "VALUE" => value, "CHECKED" => checked }
+ else
+ name["TYPE"] = "checkbox"
+ name
+ end
+ input(attributes)
+ end
+
+ # Generate a sequence of checkbox elements, as a String.
+ #
+ # The checkboxes will all have the same +name+ attribute.
+ # Each checkbox is followed by a label.
+ # There will be one checkbox for each value. Each value
+ # can be specified as a String, which will be used both
+ # as the value of the VALUE attribute and as the label
+ # for that checkbox. A single-element array has the
+ # same effect.
+ #
+ # Each value can also be specified as a three-element array.
+ # The first element is the VALUE attribute; the second is the
+ # label; and the third is a boolean specifying whether this
+ # checkbox is CHECKED.
+ #
+ # Each value can also be specified as a two-element
+ # array, by omitting either the value element (defaults
+ # to the same as the label), or the boolean checked element
+ # (defaults to false).
+ #
+ # checkbox_group("name", "foo", "bar", "baz")
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
+ #
+ # checkbox_group("name", ["foo"], ["bar", true], "baz")
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
+ #
+ # checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
+ # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
+ #
+ # checkbox_group("NAME" => "name",
+ # "VALUES" => ["foo", "bar", "baz"])
+ #
+ # checkbox_group("NAME" => "name",
+ # "VALUES" => [["foo"], ["bar", true], "baz"])
+ #
+ # checkbox_group("NAME" => "name",
+ # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
+ def checkbox_group(name = "", *values)
+ if name.kind_of?(Hash)
+ values = name["VALUES"]
+ name = name["NAME"]
+ end
+ values.collect{|value|
+ if value.kind_of?(String)
+ checkbox(name, value) + value
+ else
+ if value[-1] == true || value[-1] == false
+ checkbox(name, value[0], value[-1]) +
+ value[-2]
+ else
+ checkbox(name, value[0]) +
+ value[-1]
+ end
+ end
+ }.join
+ end
+
+
+ # Generate an File Upload Input element as a string.
+ #
+ # The attributes of the element can be specified as three arguments,
+ # +name+, +size+, and +maxlength+. +maxlength+ is the maximum length
+ # of the file's _name_, not of the file's _contents_.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # See #multipart_form() for forms that include file uploads.
+ #
+ # file_field("name")
+ # # <INPUT TYPE="file" NAME="name" SIZE="20">
+ #
+ # file_field("name", 40)
+ # # <INPUT TYPE="file" NAME="name" SIZE="40">
+ #
+ # file_field("name", 40, 100)
+ # # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
+ #
+ # file_field("NAME" => "name", "SIZE" => 40)
+ # # <INPUT TYPE="file" NAME="name" SIZE="40">
+ def file_field(name = "", size = 20, maxlength = nil)
+ attributes = if name.kind_of?(String)
+ { "TYPE" => "file", "NAME" => name,
+ "SIZE" => size.to_s }
+ else
+ name["TYPE"] = "file"
+ name
+ end
+ attributes["MAXLENGTH"] = maxlength.to_s if maxlength
+ input(attributes)
+ end
+
+
+ # Generate a Form element as a string.
+ #
+ # +method+ should be either "get" or "post", and defaults to the latter.
+ # +action+ defaults to the current CGI script name. +enctype+
+ # defaults to "application/x-www-form-urlencoded".
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # See also #multipart_form() for forms that include file uploads.
+ #
+ # form{ "string" }
+ # # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
+ #
+ # form("get") { "string" }
+ # # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
+ #
+ # form("get", "url") { "string" }
+ # # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
+ #
+ # form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
+ # # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
+ def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
+ attributes = if method.kind_of?(String)
+ { "METHOD" => method, "ACTION" => action,
+ "ENCTYPE" => enctype }
+ else
+ unless method.has_key?("METHOD")
+ method["METHOD"] = "post"
+ end
+ unless method.has_key?("ENCTYPE")
+ method["ENCTYPE"] = enctype
+ end
+ method
+ end
+ if block_given?
+ body = yield
+ else
+ body = ""
+ end
+ if @output_hidden
+ body += @output_hidden.collect{|k,v|
+ "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
+ }.join
+ end
+ super(attributes){body}
+ end
+
+ # Generate a Hidden Input element as a string.
+ #
+ # The attributes of the element can be specified as two arguments,
+ # +name+ and +value+.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # hidden("name")
+ # # <INPUT TYPE="hidden" NAME="name">
+ #
+ # hidden("name", "value")
+ # # <INPUT TYPE="hidden" NAME="name" VALUE="value">
+ #
+ # hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
+ # # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
+ def hidden(name = "", value = nil)
+ attributes = if name.kind_of?(String)
+ { "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
+ else
+ name["TYPE"] = "hidden"
+ name
+ end
+ input(attributes)
+ end
+
+ # Generate a top-level HTML element as a string.
+ #
+ # The attributes of the element are specified as a hash. The
+ # pseudo-attribute "PRETTY" can be used to specify that the generated
+ # HTML string should be indented. "PRETTY" can also be specified as
+ # a string as the sole argument to this method. The pseudo-attribute
+ # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
+ # should include the entire text of this tag, including angle brackets.
+ #
+ # The body of the html element is supplied as a block.
+ #
+ # html{ "string" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
+ #
+ # html("LANG" => "ja") { "string" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
+ #
+ # html("DOCTYPE" => false) { "string" }
+ # # <HTML>string</HTML>
+ #
+ # html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
+ # # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
+ #
+ # html("PRETTY" => " ") { "<BODY></BODY>" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ # html("PRETTY" => "\t") { "<BODY></BODY>" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ # html("PRETTY") { "<BODY></BODY>" }
+ # # = html("PRETTY" => " ") { "<BODY></BODY>" }
+ #
+ # html(if $VERBOSE then "PRETTY" end) { "HTML string" }
+ #
+ def html(attributes = {}) # :yield:
+ if nil == attributes
+ attributes = {}
+ elsif "PRETTY" == attributes
+ attributes = { "PRETTY" => true }
+ end
+ pretty = attributes.delete("PRETTY")
+ pretty = " " if true == pretty
+ buf = ""
+
+ if attributes.has_key?("DOCTYPE")
+ if attributes["DOCTYPE"]
+ buf += attributes.delete("DOCTYPE")
+ else
+ attributes.delete("DOCTYPE")
+ end
+ else
+ buf += doctype
+ end
+
+ if block_given?
+ buf += super(attributes){ yield }
+ else
+ buf += super(attributes)
+ end
+
+ if pretty
+ CGI::pretty(buf, pretty)
+ else
+ buf
+ end
+
+ end
+
+ # Generate an Image Button Input element as a string.
+ #
+ # +src+ is the URL of the image to use for the button. +name+
+ # is the input name. +alt+ is the alternative text for the image.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # image_button("url")
+ # # <INPUT TYPE="image" SRC="url">
+ #
+ # image_button("url", "name", "string")
+ # # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
+ #
+ # image_button("SRC" => "url", "ATL" => "strng")
+ # # <INPUT TYPE="image" SRC="url" ALT="string">
+ def image_button(src = "", name = nil, alt = nil)
+ attributes = if src.kind_of?(String)
+ { "TYPE" => "image", "SRC" => src, "NAME" => name,
+ "ALT" => alt }
+ else
+ src["TYPE"] = "image"
+ src["SRC"] ||= ""
+ src
+ end
+ input(attributes)
+ end
+
+
+ # Generate an Image element as a string.
+ #
+ # +src+ is the URL of the image. +alt+ is the alternative text for
+ # the image. +width+ is the width of the image, and +height+ is
+ # its height.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # img("src", "alt", 100, 50)
+ # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
+ #
+ # img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
+ # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
+ def img(src = "", alt = "", width = nil, height = nil)
+ attributes = if src.kind_of?(String)
+ { "SRC" => src, "ALT" => alt }
+ else
+ src
+ end
+ attributes["WIDTH"] = width.to_s if width
+ attributes["HEIGHT"] = height.to_s if height
+ super(attributes)
+ end
+
+
+ # Generate a Form element with multipart encoding as a String.
+ #
+ # Multipart encoding is used for forms that include file uploads.
+ #
+ # +action+ is the action to perform. +enctype+ is the encoding
+ # type, which defaults to "multipart/form-data".
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # multipart_form{ "string" }
+ # # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
+ #
+ # multipart_form("url") { "string" }
+ # # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
+ def multipart_form(action = nil, enctype = "multipart/form-data")
+ attributes = if action == nil
+ { "METHOD" => "post", "ENCTYPE" => enctype }
+ elsif action.kind_of?(String)
+ { "METHOD" => "post", "ACTION" => action,
+ "ENCTYPE" => enctype }
+ else
+ unless action.has_key?("METHOD")
+ action["METHOD"] = "post"
+ end
+ unless action.has_key?("ENCTYPE")
+ action["ENCTYPE"] = enctype
+ end
+ action
+ end
+ if block_given?
+ form(attributes){ yield }
+ else
+ form(attributes)
+ end
+ end
+
+
+ # Generate a Password Input element as a string.
+ #
+ # +name+ is the name of the input field. +value+ is its default
+ # value. +size+ is the size of the input field display. +maxlength+
+ # is the maximum length of the inputted password.
+ #
+ # Alternatively, attributes can be specified as a hash.
+ #
+ # password_field("name")
+ # # <INPUT TYPE="password" NAME="name" SIZE="40">
+ #
+ # password_field("name", "value")
+ # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
+ #
+ # password_field("password", "value", 80, 200)
+ # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
+ #
+ # password_field("NAME" => "name", "VALUE" => "value")
+ # # <INPUT TYPE="password" NAME="name" VALUE="value">
+ def password_field(name = "", value = nil, size = 40, maxlength = nil)
+ attributes = if name.kind_of?(String)
+ { "TYPE" => "password", "NAME" => name,
+ "VALUE" => value, "SIZE" => size.to_s }
+ else
+ name["TYPE"] = "password"
+ name
+ end
+ attributes["MAXLENGTH"] = maxlength.to_s if maxlength
+ input(attributes)
+ end
+
+ # Generate a Select element as a string.
+ #
+ # +name+ is the name of the element. The +values+ are the options that
+ # can be selected from the Select menu. Each value can be a String or
+ # a one, two, or three-element Array. If a String or a one-element
+ # Array, this is both the value of that option and the text displayed for
+ # it. If a three-element Array, the elements are the option value, displayed
+ # text, and a boolean value specifying whether this option starts as selected.
+ # The two-element version omits either the option value (defaults to the same
+ # as the display text) or the boolean selected specifier (defaults to false).
+ #
+ # The attributes and options can also be specified as a hash. In this
+ # case, options are specified as an array of values as described above,
+ # with the hash key of "VALUES".
+ #
+ # popup_menu("name", "foo", "bar", "baz")
+ # # <SELECT NAME="name">
+ # # <OPTION VALUE="foo">foo</OPTION>
+ # # <OPTION VALUE="bar">bar</OPTION>
+ # # <OPTION VALUE="baz">baz</OPTION>
+ # # </SELECT>
+ #
+ # popup_menu("name", ["foo"], ["bar", true], "baz")
+ # # <SELECT NAME="name">
+ # # <OPTION VALUE="foo">foo</OPTION>
+ # # <OPTION VALUE="bar" SELECTED>bar</OPTION>
+ # # <OPTION VALUE="baz">baz</OPTION>
+ # # </SELECT>
+ #
+ # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
+ # # <SELECT NAME="name">
+ # # <OPTION VALUE="1">Foo</OPTION>
+ # # <OPTION SELECTED VALUE="2">Bar</OPTION>
+ # # <OPTION VALUE="Baz">Baz</OPTION>
+ # # </SELECT>
+ #
+ # popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
+ # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
+ # # <SELECT NAME="name" MULTIPLE SIZE="2">
+ # # <OPTION VALUE="1">Foo</OPTION>
+ # # <OPTION SELECTED VALUE="2">Bar</OPTION>
+ # # <OPTION VALUE="Baz">Baz</OPTION>
+ # # </SELECT>
+ def popup_menu(name = "", *values)
+
+ if name.kind_of?(Hash)
+ values = name["VALUES"]
+ size = name["SIZE"].to_s if name["SIZE"]
+ multiple = name["MULTIPLE"]
+ name = name["NAME"]
+ else
+ size = nil
+ multiple = nil
+ end
+
+ select({ "NAME" => name, "SIZE" => size,
+ "MULTIPLE" => multiple }){
+ values.collect{|value|
+ if value.kind_of?(String)
+ option({ "VALUE" => value }){ value }
+ else
+ if value[value.size - 1] == true
+ option({ "VALUE" => value[0], "SELECTED" => true }){
+ value[value.size - 2]
+ }
+ else
+ option({ "VALUE" => value[0] }){
+ value[value.size - 1]
+ }
+ end
+ end
+ }.join
+ }
+
+ end
+
+ # Generates a radio-button Input element.
+ #
+ # +name+ is the name of the input field. +value+ is the value of
+ # the field if checked. +checked+ specifies whether the field
+ # starts off checked.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # radio_button("name", "value")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value">
+ #
+ # radio_button("name", "value", true)
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
+ #
+ # radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
+ def radio_button(name = "", value = nil, checked = nil)
+ attributes = if name.kind_of?(String)
+ { "TYPE" => "radio", "NAME" => name,
+ "VALUE" => value, "CHECKED" => checked }
+ else
+ name["TYPE"] = "radio"
+ name
+ end
+ input(attributes)
+ end
+
+ # Generate a sequence of radio button Input elements, as a String.
+ #
+ # This works the same as #checkbox_group(). However, it is not valid
+ # to have more than one radiobutton in a group checked.
+ #
+ # radio_group("name", "foo", "bar", "baz")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
+ #
+ # radio_group("name", ["foo"], ["bar", true], "baz")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
+ #
+ # radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
+ # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
+ # # <INPUT TYPE="radio" NAME="name" VALUE="Baz">Baz
+ #
+ # radio_group("NAME" => "name",
+ # "VALUES" => ["foo", "bar", "baz"])
+ #
+ # radio_group("NAME" => "name",
+ # "VALUES" => [["foo"], ["bar", true], "baz"])
+ #
+ # radio_group("NAME" => "name",
+ # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
+ def radio_group(name = "", *values)
+ if name.kind_of?(Hash)
+ values = name["VALUES"]
+ name = name["NAME"]
+ end
+ values.collect{|value|
+ if value.kind_of?(String)
+ radio_button(name, value) + value
+ else
+ if value[-1] == true || value[-1] == false
+ radio_button(name, value[0], value[-1]) +
+ value[-2]
+ else
+ radio_button(name, value[0]) +
+ value[-1]
+ end
+ end
+ }.join
+ end
+
+ # Generate a reset button Input element, as a String.
+ #
+ # This resets the values on a form to their initial values. +value+
+ # is the text displayed on the button. +name+ is the name of this button.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # reset
+ # # <INPUT TYPE="reset">
+ #
+ # reset("reset")
+ # # <INPUT TYPE="reset" VALUE="reset">
+ #
+ # reset("VALUE" => "reset", "ID" => "foo")
+ # # <INPUT TYPE="reset" VALUE="reset" ID="foo">
+ def reset(value = nil, name = nil)
+ attributes = if (not value) or value.kind_of?(String)
+ { "TYPE" => "reset", "VALUE" => value, "NAME" => name }
+ else
+ value["TYPE"] = "reset"
+ value
+ end
+ input(attributes)
+ end
+
+ alias scrolling_list popup_menu
+
+ # Generate a submit button Input element, as a String.
+ #
+ # +value+ is the text to display on the button. +name+ is the name
+ # of the input.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # submit
+ # # <INPUT TYPE="submit">
+ #
+ # submit("ok")
+ # # <INPUT TYPE="submit" VALUE="ok">
+ #
+ # submit("ok", "button1")
+ # # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
+ #
+ # submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo")
+ # # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
+ def submit(value = nil, name = nil)
+ attributes = if (not value) or value.kind_of?(String)
+ { "TYPE" => "submit", "VALUE" => value, "NAME" => name }
+ else
+ value["TYPE"] = "submit"
+ value
+ end
+ input(attributes)
+ end
+
+ # Generate a text field Input element, as a String.
+ #
+ # +name+ is the name of the input field. +value+ is its initial
+ # value. +size+ is the size of the input area. +maxlength+
+ # is the maximum length of input accepted.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # text_field("name")
+ # # <INPUT TYPE="text" NAME="name" SIZE="40">
+ #
+ # text_field("name", "value")
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
+ #
+ # text_field("name", "value", 80)
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
+ #
+ # text_field("name", "value", 80, 200)
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
+ #
+ # text_field("NAME" => "name", "VALUE" => "value")
+ # # <INPUT TYPE="text" NAME="name" VALUE="value">
+ def text_field(name = "", value = nil, size = 40, maxlength = nil)
+ attributes = if name.kind_of?(String)
+ { "TYPE" => "text", "NAME" => name, "VALUE" => value,
+ "SIZE" => size.to_s }
+ else
+ name["TYPE"] = "text"
+ name
+ end
+ attributes["MAXLENGTH"] = maxlength.to_s if maxlength
+ input(attributes)
+ end
+
+ # Generate a TextArea element, as a String.
+ #
+ # +name+ is the name of the textarea. +cols+ is the number of
+ # columns and +rows+ is the number of rows in the display.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # The body is provided by the passed-in no-argument block
+ #
+ # textarea("name")
+ # # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
+ #
+ # textarea("name", 40, 5)
+ # # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5)
+ def textarea(name = "", cols = 70, rows = 10) # :yield:
+ attributes = if name.kind_of?(String)
+ { "NAME" => name, "COLS" => cols.to_s,
+ "ROWS" => rows.to_s }
+ else
+ name
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+ end # HtmlExtension
+
+
+ # Mixin module for HTML version 3 generation methods.
+ module Html3 # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ extend TagMaker
+ methods = ""
+ # - -
+ for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
+ DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
+ APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
+ STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
+ CAPTION ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
+ ISINDEX META ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # O O or - O
+ for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
+ th td ]
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html3
+
+
+ # Mixin module for HTML version 4 generation methods.
+ module Html4 # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ extend TagMaker
+ methods = ""
+ # - -
+ for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD
+ VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT
+ H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP
+ FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT
+ TEXTAREA FORM A BLOCKQUOTE CAPTION ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # O O or - O
+ for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
+ COLGROUP TR TH TD HEAD]
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html4
+
+
+ # Mixin module for HTML version 4 transitional generation methods.
+ module Html4Tr # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ extend TagMaker
+ methods = ""
+ # - -
+ for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN
+ CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO
+ ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q
+ INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET
+ LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT
+ NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
+ COL ISINDEX META ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # O O or - O
+ for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
+ COLGROUP TR TH TD HEAD ]
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html4Tr
+
+
+ # Mixin module for generating HTML version 4 with framesets.
+ module Html4Fr # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ methods = ""
+ # - -
+ for element in %w[ FRAMESET ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ FRAME ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html4Fr
+end
+
+
Modified: MacRuby/branches/experimental/lib/cgi/session/pstore.rb
===================================================================
--- MacRuby/branches/experimental/lib/cgi/session/pstore.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/cgi/session/pstore.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -30,7 +30,7 @@
# characters; automatically generated session ids observe
# this requirement.
#
- # +option+ is a hash of options for the initialiser. The
+ # +option+ is a hash of options for the initializer. The
# following options are recognised:
#
# tmpdir:: the directory to use for storing the PStore
@@ -43,55 +43,55 @@
# This session's PStore file will be created if it does
# not exist, or opened if it does.
def initialize(session, option={})
- dir = option['tmpdir'] || Dir::tmpdir
- prefix = option['prefix'] || ''
- id = session.session_id
+ dir = option['tmpdir'] || Dir::tmpdir
+ prefix = option['prefix'] || ''
+ id = session.session_id
require 'digest/md5'
md5 = Digest::MD5.hexdigest(id)[0,16]
- path = dir+"/"+prefix+md5
- path.untaint
- if File::exist?(path)
- @hash = nil
- else
+ path = dir+"/"+prefix+md5
+ path.untaint
+ if File::exist?(path)
+ @hash = nil
+ else
unless session.new_session
raise CGI::Session::NoSession, "uninitialized session"
end
- @hash = {}
- end
- @p = ::PStore.new(path)
- @p.transaction do |p|
- File.chmod(0600, p.path)
- end
+ @hash = {}
+ end
+ @p = ::PStore.new(path)
+ @p.transaction do |p|
+ File.chmod(0600, p.path)
+ end
end
# Restore session state from the session's PStore file.
#
# Returns the session state as a hash.
def restore
- unless @hash
- @p.transaction do
+ unless @hash
+ @p.transaction do
@hash = @p['hash'] || {}
- end
- end
- @hash
+ end
+ end
+ @hash
end
# Save session state to the session's PStore file.
def update
- @p.transaction do
- @p['hash'] = @hash
- end
+ @p.transaction do
+ @p['hash'] = @hash
+ end
end
# Update and close the session's PStore file.
def close
- update
+ update
end
# Close and delete the session's PStore file.
def delete
- path = @p.path
- File::unlink path
+ path = @p.path
+ File::unlink path
end
end
Modified: MacRuby/branches/experimental/lib/cgi/session.rb
===================================================================
--- MacRuby/branches/experimental/lib/cgi/session.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/cgi/session.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -163,7 +163,7 @@
def Session::callback(dbman) #:nodoc:
Proc.new{
- dbman[0].close unless dbman.empty?
+ dbman[0].close unless dbman.empty?
}
end
@@ -188,7 +188,6 @@
md5.update('foobar')
session_id = md5.hexdigest
end
- @new_session = true
session_id
end
private :create_new_id
@@ -230,7 +229,7 @@
# session_path:: the path for which this session applies. Defaults
# to the directory of the CGI script.
#
- # +option+ is also passed on to the session storage class initialiser; see
+ # +option+ is also passed on to the session storage class initializer; see
# the documentation for each session storage class for the options
# they support.
#
@@ -254,24 +253,26 @@
session_key = option['session_key'] || '_session_id'
session_id = option['session_id']
unless session_id
- if option['new_session']
- session_id = create_new_id
- end
+ if option['new_session']
+ session_id = create_new_id
+ @new_session = true
+ end
end
unless session_id
- if request.key?(session_key)
- session_id = request[session_key]
- session_id = session_id.read if session_id.respond_to?(:read)
- end
- unless session_id
- session_id, = request.cookies[session_key]
- end
- unless session_id
- unless option.fetch('new_session', true)
- raise ArgumentError, "session_key `%s' should be supplied"%session_key
- end
- session_id = create_new_id
- end
+ if request.key?(session_key)
+ session_id = request[session_key]
+ session_id = session_id.read if session_id.respond_to?(:read)
+ end
+ unless session_id
+ session_id, = request.cookies[session_key]
+ end
+ unless session_id
+ unless option.fetch('new_session', true)
+ raise ArgumentError, "session_key `%s' should be supplied"%session_key
+ end
+ session_id = create_new_id
+ @new_session = true
+ end
end
@session_id = session_id
dbman = option['database_manager'] || FileStore
@@ -281,24 +282,26 @@
unless option.fetch('new_session', true)
raise ArgumentError, "invalid session_id `%s'"%session_id
end
- session_id = @session_id = create_new_id
+ session_id = @session_id = create_new_id unless session_id
+ @new_session=true
retry
end
request.instance_eval do
- @output_hidden = {session_key => session_id} unless option['no_hidden']
- @output_cookies = [
+ @output_hidden = {session_key => session_id} unless option['no_hidden']
+ @output_cookies = [
Cookie::new("name" => session_key,
- "value" => session_id,
- "expires" => option['session_expires'],
- "domain" => option['session_domain'],
- "secure" => option['session_secure'],
- "path" => if option['session_path'] then
- option['session_path']
- elsif ENV["SCRIPT_NAME"] then
- File::dirname(ENV["SCRIPT_NAME"])
- else
- ""
- end)
+ "value" => session_id,
+ "expires" => option['session_expires'],
+ "domain" => option['session_domain'],
+ "secure" => option['session_secure'],
+ "path" =>
+ if option['session_path']
+ option['session_path']
+ elsif ENV["SCRIPT_NAME"]
+ File::dirname(ENV["SCRIPT_NAME"])
+ else
+ ""
+ end)
] unless option['no_cookies']
end
@dbprot = [@dbman]
@@ -357,7 +360,7 @@
# characters; automatically generated session ids observe
# this requirement.
#
- # +option+ is a hash of options for the initialiser. The
+ # +option+ is a hash of options for the initializer. The
# following options are recognised:
#
# tmpdir:: the directory to use for storing the FileStore
@@ -373,56 +376,56 @@
# This session's FileStore file will be created if it does
# not exist, or opened if it does.
def initialize(session, option={})
- dir = option['tmpdir'] || Dir::tmpdir
- prefix = option['prefix'] || 'cgi_sid_'
- suffix = option['suffix'] || ''
- id = session.session_id
+ dir = option['tmpdir'] || Dir::tmpdir
+ prefix = option['prefix'] || 'cgi_sid_'
+ suffix = option['suffix'] || ''
+ id = session.session_id
require 'digest/md5'
md5 = Digest::MD5.hexdigest(id)[0,16]
- @path = dir+"/"+prefix+md5+suffix
- if File::exist? @path
- @hash = nil
- else
+ @path = dir+"/"+prefix+md5+suffix
+ if File::exist? @path
+ @hash = nil
+ else
unless session.new_session
raise CGI::Session::NoSession, "uninitialized session"
end
- @hash = {}
- end
+ @hash = {}
+ end
end
# Restore session state from the session's FileStore file.
#
# Returns the session state as a hash.
def restore
- unless @hash
- @hash = {}
+ unless @hash
+ @hash = {}
begin
lockf = File.open(@path+".lock", "r")
lockf.flock File::LOCK_SH
- f = File.open(@path, 'r')
- for line in f
- line.chomp!
- k, v = line.split('=',2)
- @hash[CGI::unescape(k)] = CGI::unescape(v)
- end
+ f = File.open(@path, 'r')
+ for line in f
+ line.chomp!
+ k, v = line.split('=',2)
+ @hash[CGI::unescape(k)] = Marshal.restore(CGI::unescape(v))
+ end
ensure
- f.close unless f.nil?
+ f.close unless f.nil?
lockf.close if lockf
end
- end
- @hash
+ end
+ @hash
end
# Save session state to the session's FileStore file.
def update
- return unless @hash
+ return unless @hash
begin
lockf = File.open(@path+".lock", File::CREAT|File::RDWR, 0600)
- lockf.flock File::LOCK_EX
+ lockf.flock File::LOCK_EX
f = File.open(@path+".new", File::CREAT|File::TRUNC|File::WRONLY, 0600)
- for k,v in @hash
- f.printf "%s=%s\n", CGI::escape(k), CGI::escape(String(v))
- end
+ for k,v in @hash
+ f.printf "%s=%s\n", CGI::escape(k), CGI::escape(String(Marshal.dump(v)))
+ end
f.close
File.rename @path+".new", @path
ensure
@@ -433,15 +436,14 @@
# Update and close the session's FileStore file.
def close
- update
+ update
end
# Close and delete the session's FileStore file.
def delete
File::unlink @path+".lock" rescue nil
File::unlink @path+".new" rescue nil
- File::unlink @path
- rescue Errno::ENOENT
+ File::unlink @path rescue Errno::ENOENT
end
end
@@ -459,7 +461,7 @@
# +option+ is a list of initialisation options. None are
# currently recognised.
def initialize(session, option=nil)
- @session_id = session.session_id
+ @session_id = session.session_id
unless GLOBAL_HASH_TABLE.key?(@session_id)
unless session.new_session
raise CGI::Session::NoSession, "uninitialized session"
@@ -472,26 +474,26 @@
#
# Returns session data as a hash.
def restore
- GLOBAL_HASH_TABLE[@session_id]
+ GLOBAL_HASH_TABLE[@session_id]
end
# Update session state.
#
# A no-op.
def update
- # don't need to update; hash is shared
+ # don't need to update; hash is shared
end
# Close session storage.
#
# A no-op.
def close
- # don't need to close
+ # don't need to close
end
# Delete the session state.
def delete
- GLOBAL_HASH_TABLE.delete(@session_id)
+ GLOBAL_HASH_TABLE.delete(@session_id)
end
end
Copied: MacRuby/branches/experimental/lib/cgi/util.rb (from rev 1886, MacRuby/trunk/lib/cgi/util.rb)
===================================================================
--- MacRuby/branches/experimental/lib/cgi/util.rb (rev 0)
+++ MacRuby/branches/experimental/lib/cgi/util.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,181 @@
+class CGI
+ # URL-encode a string.
+ # url_encoded_string = CGI::escape("'Stop!' said Fred")
+ # # => "%27Stop%21%27+said+Fred"
+ def CGI::escape(string)
+ string.gsub(/([^ a-zA-Z0-9_.-]+)/) do
+ '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
+ end.tr(' ', '+')
+ end
+
+
+ # URL-decode a string.
+ # string = CGI::unescape("%27Stop%21%27+said+Fred")
+ # # => "'Stop!' said Fred"
+ def CGI::unescape(string)
+ enc = string.encoding
+ string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/) do
+ [$1.delete('%')].pack('H*').force_encoding(enc)
+ end
+ end
+
+ TABLE_FOR_ESCAPE_HTML__ = {
+ '&' => '&',
+ '"' => '"',
+ '<' => '<',
+ '>' => '>',
+ }
+
+ # Escape special characters in HTML, namely &\"<>
+ # CGI::escapeHTML('Usage: foo "bar" <baz>')
+ # # => "Usage: foo "bar" <baz>"
+ def CGI::escapeHTML(string)
+ string.gsub(/[&\"<>]/, TABLE_FOR_ESCAPE_HTML__)
+ end
+
+
+ # Unescape a string that has been HTML-escaped
+ # CGI::unescapeHTML("Usage: foo "bar" <baz>")
+ # # => "Usage: foo \"bar\" <baz>"
+ def CGI::unescapeHTML(string)
+ enc = string.encoding
+ if [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].include?(enc)
+ return string.gsub(Regexp.new('&(amp|quot|gt|lt|#[0-9]+|#x[0-9A-Fa-f]+);'.encode(enc))) do
+ case $1.encode("US-ASCII")
+ when 'amp' then '&'.encode(enc)
+ when 'quot' then '"'.encode(enc)
+ when 'gt' then '>'.encode(enc)
+ when 'lt' then '<'.encode(enc)
+ when /\A#0*(\d+)\z/ then $1.to_i.chr(enc)
+ when /\A#x([0-9a-f]+)\z/i then $1.hex.chr(enc)
+ end
+ end
+ end
+ asciicompat = Encoding.compatible?(string, "a")
+ string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/) do
+ match = $1.dup
+ case match
+ when 'amp' then '&'
+ when 'quot' then '"'
+ when 'gt' then '>'
+ when 'lt' then '<'
+ when /\A#0*(\d+)\z/
+ n = $1.to_i
+ if enc == Encoding::UTF_8 or
+ enc == Encoding::ISO_8859_1 && n < 256 or
+ asciicompat && n < 128
+ n.chr(enc)
+ else
+ "&##{$1};"
+ end
+ when /\A#x([0-9a-f]+)\z/i
+ n = $1.hex
+ if enc == Encoding::UTF_8 or
+ enc == Encoding::ISO_8859_1 && n < 256 or
+ asciicompat && n < 128
+ n.chr(enc)
+ else
+ "&#x#{$1};"
+ end
+ else
+ "&#{match};"
+ end
+ end
+ end
+ def CGI::escape_html(str)
+ escapeHTML(str)
+ end
+ def CGI::unescape_html(str)
+ unescapeHTML(str)
+ end
+
+ # Escape only the tags of certain HTML elements in +string+.
+ #
+ # Takes an element or elements or array of elements. Each element
+ # is specified by the name of the element, without angle brackets.
+ # This matches both the start and the end tag of that element.
+ # The attribute list of the open tag will also be escaped (for
+ # instance, the double-quotes surrounding attribute values).
+ #
+ # print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
+ # # "<BR><A HREF="url"></A>"
+ #
+ # print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
+ # # "<BR><A HREF="url"></A>"
+ def CGI::escapeElement(string, *elements)
+ elements = elements[0] if elements[0].kind_of?(Array)
+ unless elements.empty?
+ string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do
+ CGI::escapeHTML($&)
+ end
+ else
+ string
+ end
+ end
+
+
+ # Undo escaping such as that done by CGI::escapeElement()
+ #
+ # print CGI::unescapeElement(
+ # CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
+ # # "<BR><A HREF="url"></A>"
+ #
+ # print CGI::unescapeElement(
+ # CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
+ # # "<BR><A HREF="url"></A>"
+ def CGI::unescapeElement(string, *elements)
+ elements = elements[0] if elements[0].kind_of?(Array)
+ unless elements.empty?
+ string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do
+ CGI::unescapeHTML($&)
+ end
+ else
+ string
+ end
+ end
+ def CGI::escape_element(str)
+ escapeElement(str)
+ end
+ def CGI::unescape_element(str)
+ unescapeElement(str)
+ end
+
+ # Format a +Time+ object as a String using the format specified by RFC 1123.
+ #
+ # CGI::rfc1123_date(Time.now)
+ # # Sat, 01 Jan 2000 00:00:00 GMT
+ def CGI::rfc1123_date(time)
+ t = time.clone.gmtime
+ return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
+ RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
+ t.hour, t.min, t.sec)
+ end
+
+ # Prettify (indent) an HTML string.
+ #
+ # +string+ is the HTML string to indent. +shift+ is the indentation
+ # unit to use; it defaults to two spaces.
+ #
+ # print CGI::pretty("<HTML><BODY></BODY></HTML>")
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ # print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ def CGI::pretty(string, shift = " ")
+ lines = string.gsub(/(?!\A)<(?:.|\n)*?>/, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/, "\\0\n")
+ end_pos = 0
+ while end_pos = lines.index(/^<\/(\w+)/, end_pos)
+ element = $1.dup
+ start_pos = lines.rindex(/^\s*<#{element}/i, end_pos)
+ lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/, "\n" + shift) + "__"
+ end
+ lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/, '\1')
+ end
+end
Modified: MacRuby/branches/experimental/lib/cgi.rb
===================================================================
--- MacRuby/branches/experimental/lib/cgi.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/cgi.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -30,10 +30,8 @@
# See http://www.w3.org/CGI/ for more information on the CGI
# protocol.
-raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4"
+raise "Please, use ruby 1.9.0 or later." if RUBY_VERSION < "1.9.0"
-require 'English'
-
# CGI class. See documentation for the file cgi.rb for an overview
# of the CGI protocol.
#
@@ -271,2063 +269,6 @@
# CGI.new("html4Tr") # html4.01 Transitional
# CGI.new("html4Fr") # html4.01 Frameset
#
-class CGI
-
- # :stopdoc:
-
- # String for carriage return
- CR = "\015"
-
- # String for linefeed
- LF = "\012"
-
- # Standard internet newline sequence
- EOL = CR + LF
-
- REVISION = '$Id: cgi.rb 15781 2008-03-14 08:08:51Z matz $' #:nodoc:
-
- NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM)
-
- # Path separators in different environments.
- PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
-
- # HTTP status codes.
- HTTP_STATUS = {
- "OK" => "200 OK",
- "PARTIAL_CONTENT" => "206 Partial Content",
- "MULTIPLE_CHOICES" => "300 Multiple Choices",
- "MOVED" => "301 Moved Permanently",
- "REDIRECT" => "302 Found",
- "NOT_MODIFIED" => "304 Not Modified",
- "BAD_REQUEST" => "400 Bad Request",
- "AUTH_REQUIRED" => "401 Authorization Required",
- "FORBIDDEN" => "403 Forbidden",
- "NOT_FOUND" => "404 Not Found",
- "METHOD_NOT_ALLOWED" => "405 Method Not Allowed",
- "NOT_ACCEPTABLE" => "406 Not Acceptable",
- "LENGTH_REQUIRED" => "411 Length Required",
- "PRECONDITION_FAILED" => "412 Rrecondition Failed",
- "SERVER_ERROR" => "500 Internal Server Error",
- "NOT_IMPLEMENTED" => "501 Method Not Implemented",
- "BAD_GATEWAY" => "502 Bad Gateway",
- "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
- }
-
- # Abbreviated day-of-week names specified by RFC 822
- RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
-
- # Abbreviated month names specified by RFC 822
- RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
-
- # :startdoc:
-
- def env_table
- ENV
- end
-
- def stdinput
- $stdin
- end
-
- def stdoutput
- $DEFAULT_OUTPUT
- end
-
- private :env_table, :stdinput, :stdoutput
-
- # URL-encode a string.
- # url_encoded_string = CGI::escape("'Stop!' said Fred")
- # # => "%27Stop%21%27+said+Fred"
- def CGI::escape(string)
- string.gsub(/([^ a-zA-Z0-9_.-]+)/) do
- '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
- end.tr(' ', '+')
- end
-
-
- # URL-decode a string.
- # string = CGI::unescape("%27Stop%21%27+said+Fred")
- # # => "'Stop!' said Fred"
- def CGI::unescape(string)
- enc = string.encoding
- string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/) do
- [$1.delete('%')].pack('H*').force_encoding(enc)
- end
- end
-
- TABLE_FOR_ESCAPE_HTML__ = {
- '&' => '&',
- '"' => '"',
- '<' => '<',
- '>' => '>',
- }
-
- # Escape special characters in HTML, namely &\"<>
- # CGI::escapeHTML('Usage: foo "bar" <baz>')
- # # => "Usage: foo "bar" <baz>"
- def CGI::escapeHTML(string)
- string.gsub(/[&\"<>]/, TABLE_FOR_ESCAPE_HTML__)
- end
-
-
- # Unescape a string that has been HTML-escaped
- # CGI::unescapeHTML("Usage: foo "bar" <baz>")
- # # => "Usage: foo \"bar\" <baz>"
- def CGI::unescapeHTML(string)
- enc = string.encoding
- string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/) do
- match = $1.dup
- case match
- when 'amp' then '&'
- when 'quot' then '"'
- when 'gt' then '>'
- when 'lt' then '<'
- when /\A#0*(\d+)\z/ then
- if Integer($1) < 256
- Integer($1).chr.force_encoding(enc)
- else
- "&##{$1};"
- end
- when /\A#x([0-9a-f]+)\z/i then
- if $1.hex < 256
- $1.hex.chr.force_encoding(enc)
- else
- "&#x#{$1};"
- end
- else
- "&#{match};"
- end
- end
- end
- def CGI::escape_html(str)
- escapeHTML(str)
- end
- def CGI::unescape_html(str)
- unescapeHTML(str)
- end
-
- # Escape only the tags of certain HTML elements in +string+.
- #
- # Takes an element or elements or array of elements. Each element
- # is specified by the name of the element, without angle brackets.
- # This matches both the start and the end tag of that element.
- # The attribute list of the open tag will also be escaped (for
- # instance, the double-quotes surrounding attribute values).
- #
- # print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
- # # "<BR><A HREF="url"></A>"
- #
- # print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
- # # "<BR><A HREF="url"></A>"
- def CGI::escapeElement(string, *elements)
- elements = elements[0] if elements[0].kind_of?(Array)
- unless elements.empty?
- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
- CGI::escapeHTML($&)
- end
- else
- string
- end
- end
-
-
- # Undo escaping such as that done by CGI::escapeElement()
- #
- # print CGI::unescapeElement(
- # CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
- # # "<BR><A HREF="url"></A>"
- #
- # print CGI::unescapeElement(
- # CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
- # # "<BR><A HREF="url"></A>"
- def CGI::unescapeElement(string, *elements)
- elements = elements[0] if elements[0].kind_of?(Array)
- unless elements.empty?
- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
- CGI::unescapeHTML($&)
- end
- else
- string
- end
- end
- def CGI::escape_element(str)
- escapeElement(str)
- end
- def CGI::unescape_element(str)
- unescapeElement(str)
- end
-
- # Format a +Time+ object as a String using the format specified by RFC 1123.
- #
- # CGI::rfc1123_date(Time.now)
- # # Sat, 01 Jan 2000 00:00:00 GMT
- def CGI::rfc1123_date(time)
- t = time.clone.gmtime
- return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
- RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
- t.hour, t.min, t.sec)
- end
-
-
- # Create an HTTP header block as a string.
- #
- # Includes the empty line that ends the header block.
- #
- # +options+ can be a string specifying the Content-Type (defaults
- # to text/html), or a hash of header key/value pairs. The following
- # header keys are recognized:
- #
- # type:: the Content-Type header. Defaults to "text/html"
- # charset:: the charset of the body, appended to the Content-Type header.
- # nph:: a boolean value. If true, prepend protocol string and status code, and
- # date; and sets default values for "server" and "connection" if not
- # explicitly set.
- # status:: the HTTP status code, returned as the Status header. See the
- # list of available status codes below.
- # server:: the server software, returned as the Server header.
- # connection:: the connection type, returned as the Connection header (for
- # instance, "close".
- # length:: the length of the content that will be sent, returned as the
- # Content-Length header.
- # language:: the language of the content, returned as the Content-Language
- # header.
- # expires:: the time on which the current content expires, as a +Time+
- # object, returned as the Expires header.
- # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
- # The value can be the literal string of the cookie; a CGI::Cookie
- # object; an Array of literal cookie strings or Cookie objects; or a
- # hash all of whose values are literal cookie strings or Cookie objects.
- # These cookies are in addition to the cookies held in the
- # @output_cookies field.
- #
- # Other header lines can also be set; they are appended as key: value.
- #
- # header
- # # Content-Type: text/html
- #
- # header("text/plain")
- # # Content-Type: text/plain
- #
- # header("nph" => true,
- # "status" => "OK", # == "200 OK"
- # # "status" => "200 GOOD",
- # "server" => ENV['SERVER_SOFTWARE'],
- # "connection" => "close",
- # "type" => "text/html",
- # "charset" => "iso-2022-jp",
- # # Content-Type: text/html; charset=iso-2022-jp
- # "length" => 103,
- # "language" => "ja",
- # "expires" => Time.now + 30,
- # "cookie" => [cookie1, cookie2],
- # "my_header1" => "my_value"
- # "my_header2" => "my_value")
- #
- # The status codes are:
- #
- # "OK" --> "200 OK"
- # "PARTIAL_CONTENT" --> "206 Partial Content"
- # "MULTIPLE_CHOICES" --> "300 Multiple Choices"
- # "MOVED" --> "301 Moved Permanently"
- # "REDIRECT" --> "302 Found"
- # "NOT_MODIFIED" --> "304 Not Modified"
- # "BAD_REQUEST" --> "400 Bad Request"
- # "AUTH_REQUIRED" --> "401 Authorization Required"
- # "FORBIDDEN" --> "403 Forbidden"
- # "NOT_FOUND" --> "404 Not Found"
- # "METHOD_NOT_ALLOWED" --> "405 Method Not Allowed"
- # "NOT_ACCEPTABLE" --> "406 Not Acceptable"
- # "LENGTH_REQUIRED" --> "411 Length Required"
- # "PRECONDITION_FAILED" --> "412 Precondition Failed"
- # "SERVER_ERROR" --> "500 Internal Server Error"
- # "NOT_IMPLEMENTED" --> "501 Method Not Implemented"
- # "BAD_GATEWAY" --> "502 Bad Gateway"
- # "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
- #
- # This method does not perform charset conversion.
- #
- def header(options = "text/html")
-
- buf = ""
-
- case options
- when String
- options = { "type" => options }
- when Hash
- options = options.dup
- end
-
- unless options.has_key?("type")
- options["type"] = "text/html"
- end
-
- if options.has_key?("charset")
- options["type"] += "; charset=" + options.delete("charset")
- end
-
- options.delete("nph") if defined?(MOD_RUBY)
- if options.delete("nph") or
- (/IIS\/(\d+)/n.match(env_table['SERVER_SOFTWARE']) and $1.to_i < 5)
- buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0") + " " +
- (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") +
- EOL +
- "Date: " + CGI::rfc1123_date(Time.now) + EOL
-
- unless options.has_key?("server")
- options["server"] = (env_table['SERVER_SOFTWARE'] or "")
- end
-
- unless options.has_key?("connection")
- options["connection"] = "close"
- end
-
- options.delete("status")
- end
-
- if options.has_key?("status")
- buf += "Status: " +
- (HTTP_STATUS[options["status"]] or options["status"]) + EOL
- options.delete("status")
- end
-
- if options.has_key?("server")
- buf += "Server: " + options.delete("server") + EOL
- end
-
- if options.has_key?("connection")
- buf += "Connection: " + options.delete("connection") + EOL
- end
-
- buf += "Content-Type: " + options.delete("type") + EOL
-
- if options.has_key?("length")
- buf += "Content-Length: " + options.delete("length").to_s + EOL
- end
-
- if options.has_key?("language")
- buf += "Content-Language: " + options.delete("language") + EOL
- end
-
- if options.has_key?("expires")
- buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
- end
-
- if options.has_key?("cookie")
- if options["cookie"].kind_of?(String) or
- options["cookie"].kind_of?(Cookie)
- buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL
- elsif options["cookie"].kind_of?(Array)
- options.delete("cookie").each{|cookie|
- buf += "Set-Cookie: " + cookie.to_s + EOL
- }
- elsif options["cookie"].kind_of?(Hash)
- options.delete("cookie").each_value{|cookie|
- buf += "Set-Cookie: " + cookie.to_s + EOL
- }
- end
- end
- if @output_cookies
- for cookie in @output_cookies
- buf += "Set-Cookie: " + cookie.to_s + EOL
- end
- end
-
- options.each{|key, value|
- buf += key + ": " + value.to_s + EOL
- }
-
- if defined?(MOD_RUBY)
- table = Apache::request.headers_out
- buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
- warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
- case name
- when 'Set-Cookie'
- table.add(name, value)
- when /^status$/ni
- Apache::request.status_line = value
- Apache::request.status = value.to_i
- when /^content-type$/ni
- Apache::request.content_type = value
- when /^content-encoding$/ni
- Apache::request.content_encoding = value
- when /^location$/ni
- if Apache::request.status == 200
- Apache::request.status = 302
- end
- Apache::request.headers_out[name] = value
- else
- Apache::request.headers_out[name] = value
- end
- }
- Apache::request.send_http_header
- ''
- else
- buf + EOL
- end
-
- end # header()
-
-
- # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
- #
- # The header is provided by +options+, as for #header().
- # The body of the document is that returned by the passed-
- # in block. This block takes no arguments. It is required.
- #
- # cgi = CGI.new
- # cgi.out{ "string" }
- # # Content-Type: text/html
- # # Content-Length: 6
- # #
- # # string
- #
- # cgi.out("text/plain") { "string" }
- # # Content-Type: text/plain
- # # Content-Length: 6
- # #
- # # string
- #
- # cgi.out("nph" => true,
- # "status" => "OK", # == "200 OK"
- # "server" => ENV['SERVER_SOFTWARE'],
- # "connection" => "close",
- # "type" => "text/html",
- # "charset" => "iso-2022-jp",
- # # Content-Type: text/html; charset=iso-2022-jp
- # "language" => "ja",
- # "expires" => Time.now + (3600 * 24 * 30),
- # "cookie" => [cookie1, cookie2],
- # "my_header1" => "my_value",
- # "my_header2" => "my_value") { "string" }
- #
- # Content-Length is automatically calculated from the size of
- # the String returned by the content block.
- #
- # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
- # is outputted (the content block is still required, but it
- # is ignored).
- #
- # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
- # the content is converted to this charset, and the language is set
- # to "ja".
- def out(options = "text/html") # :yield:
-
- options = { "type" => options } if options.kind_of?(String)
- content = yield
-
- if options.has_key?("charset")
- require "nkf"
- case options["charset"]
- when /iso-2022-jp/ni
- content = NKF::nkf('-j -m0 -x', content)
- options["language"] = "ja" unless options.has_key?("language")
- when /euc-jp/ni
- content = NKF::nkf('-e -m0 -x', content)
- options["language"] = "ja" unless options.has_key?("language")
- when /shift_jis/ni
- content = NKF::nkf('-s -m0 -x', content)
- options["language"] = "ja" unless options.has_key?("language")
- end
- end
-
- options["length"] = content.length.to_s
- output = stdoutput
- output.binmode if defined? output.binmode
- output.print header(options)
- output.print content unless "HEAD" == env_table['REQUEST_METHOD']
- end
-
-
- # Print an argument or list of arguments to the default output stream
- #
- # cgi = CGI.new
- # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print
- def print(*options)
- stdoutput.print(*options)
- end
-
- require "delegate"
-
- # Class representing an HTTP cookie.
- #
- # In addition to its specific fields and methods, a Cookie instance
- # is a delegator to the array of its values.
- #
- # See RFC 2965.
- #
- # == Examples of use
- # cookie1 = CGI::Cookie::new("name", "value1", "value2", ...)
- # cookie1 = CGI::Cookie::new("name" => "name", "value" => "value")
- # cookie1 = CGI::Cookie::new('name' => 'name',
- # 'value' => ['value1', 'value2', ...],
- # 'path' => 'path', # optional
- # 'domain' => 'domain', # optional
- # 'expires' => Time.now, # optional
- # 'secure' => true # optional
- # )
- #
- # cgi.out("cookie" => [cookie1, cookie2]) { "string" }
- #
- # name = cookie1.name
- # values = cookie1.value
- # path = cookie1.path
- # domain = cookie1.domain
- # expires = cookie1.expires
- # secure = cookie1.secure
- #
- # cookie1.name = 'name'
- # cookie1.value = ['value1', 'value2', ...]
- # cookie1.path = 'path'
- # cookie1.domain = 'domain'
- # cookie1.expires = Time.now + 30
- # cookie1.secure = true
- class Cookie < DelegateClass(Array)
-
- # Create a new CGI::Cookie object.
- #
- # The contents of the cookie can be specified as a +name+ and one
- # or more +value+ arguments. Alternatively, the contents can
- # be specified as a single hash argument. The possible keywords of
- # this hash are as follows:
- #
- # name:: the name of the cookie. Required.
- # value:: the cookie's value or list of values.
- # path:: the path for which this cookie applies. Defaults to the
- # base directory of the CGI script.
- # domain:: the domain for which this cookie applies.
- # expires:: the time at which this cookie expires, as a +Time+ object.
- # secure:: whether this cookie is a secure cookie or not (default to
- # false). Secure cookies are only transmitted to HTTPS
- # servers.
- #
- # These keywords correspond to attributes of the cookie object.
- def initialize(name = "", *value)
- if name.kind_of?(String)
- @name = name
- @value = value
- %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
- @path = ($1 or "")
- @secure = false
- return super(@value)
- end
-
- options = name
- unless options.has_key?("name")
- raise ArgumentError, "`name' required"
- end
-
- @name = options["name"]
- @value = Array(options["value"])
- # simple support for IE
- if options["path"]
- @path = options["path"]
- else
- %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
- @path = ($1 or "")
- end
- @domain = options["domain"]
- @expires = options["expires"]
- @secure = options["secure"] == true ? true : false
-
- super(@value)
- end
-
- attr_accessor("name", "value", "path", "domain", "expires")
- attr_reader("secure")
-
- # Set whether the Cookie is a secure cookie or not.
- #
- # +val+ must be a boolean.
- def secure=(val)
- @secure = val if val == true or val == false
- @secure
- end
-
- # Convert the Cookie to its string representation.
- def to_s
- buf = ""
- buf += @name + '='
-
- if @value.kind_of?(String)
- buf += CGI::escape(@value)
- else
- buf += @value.collect{|v| CGI::escape(v) }.join("&")
- end
-
- if @domain
- buf += '; domain=' + @domain
- end
-
- if @path
- buf += '; path=' + @path
- end
-
- if @expires
- buf += '; expires=' + CGI::rfc1123_date(@expires)
- end
-
- if @secure == true
- buf += '; secure'
- end
-
- buf
- end
-
- end # class Cookie
-
-
- # Parse a raw cookie string into a hash of cookie-name=>Cookie
- # pairs.
- #
- # cookies = CGI::Cookie::parse("raw_cookie_string")
- # # { "name1" => cookie1, "name2" => cookie2, ... }
- #
- def Cookie::parse(raw_cookie)
- cookies = Hash.new([])
- return cookies unless raw_cookie
-
- raw_cookie.split(/[;,]\s?/).each do |pairs|
- name, values = pairs.split('=',2)
- next unless name and values
- name = CGI::unescape(name)
- values ||= ""
- values = values.split('&').collect{|v| CGI::unescape(v) }
- if cookies.has_key?(name)
- values = cookies[name].value + values
- end
- cookies[name] = Cookie::new(name, *values)
- end
-
- cookies
- end
-
- # Parse an HTTP query string into a hash of key=>value pairs.
- #
- # params = CGI::parse("query_string")
- # # {"name1" => ["value1", "value2", ...],
- # # "name2" => ["value1", "value2", ...], ... }
- #
- def CGI::parse(query)
- params = Hash.new([].freeze)
-
- query.split(/[&;]/n).each do |pairs|
- key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
- if params.has_key?(key)
- params[key].push(value)
- else
- params[key] = [value]
- end
- end
-
- params
- end
-
- # Mixin module. It provides the follow functionality groups:
- #
- # 1. Access to CGI environment variables as methods. See
- # documentation to the CGI class for a list of these variables.
- #
- # 2. Access to cookies, including the cookies attribute.
- #
- # 3. Access to parameters, including the params attribute, and overloading
- # [] to perform parameter value lookup by key.
- #
- # 4. The initialize_query method, for initialising the above
- # mechanisms, handling multipart forms, and allowing the
- # class to be used in "offline" mode.
- #
- module QueryExtension
-
- %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
- define_method(env.sub(/^HTTP_/n, '').downcase) do
- (val = env_table[env]) && Integer(val)
- end
- end
-
- %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
- PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
- REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
- SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
-
- HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
- HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
- HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
- define_method(env.sub(/^HTTP_/n, '').downcase) do
- env_table[env]
- end
- end
-
- # Get the raw cookies as a string.
- def raw_cookie
- env_table["HTTP_COOKIE"]
- end
-
- # Get the raw RFC2965 cookies as a string.
- def raw_cookie2
- env_table["HTTP_COOKIE2"]
- end
-
- # Get the cookies as a hash of cookie-name=>Cookie pairs.
- attr_accessor :cookies
-
- # Get the parameters as a hash of name=>values pairs, where
- # values is an Array.
- attr_reader :params
-
- # Set all the parameters.
- def params=(hash)
- @params.clear
- @params.update(hash)
- end
-
- def read_multipart(boundary, content_length)
- params = Hash.new([])
- boundary = "--" + boundary
- quoted_boundary = Regexp.quote(boundary)
- buf = ""
- bufsize = 10 * 1024
- boundary_end=""
-
- # start multipart/form-data
- stdinput.binmode if defined? stdinput.binmode
- boundary_size = boundary.size + EOL.size
- content_length -= boundary_size
- status = stdinput.read(boundary_size)
- if nil == status
- raise EOFError, "no content body"
- elsif boundary + EOL != status
- raise EOFError, "bad content body"
- end
-
- loop do
- head = nil
- body = MorphingBody.new
-
- until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
- if (not head) and /#{EOL}#{EOL}/n.match(buf)
- buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
- head = $1.dup
- ""
- end
- next
- end
-
- if head and ( (EOL + boundary + EOL).size < buf.size )
- body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
- buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
- end
-
- c = if bufsize < content_length
- stdinput.read(bufsize)
- else
- stdinput.read(content_length)
- end
- if c.nil? || c.empty?
- raise EOFError, "bad content body"
- end
- buf.concat(c)
- content_length -= c.size
- end
-
- buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
- body.print $1
- if "--" == $2
- content_length = -1
- end
- boundary_end = $2.dup
- ""
- end
-
- body.rewind
-
- /Content-Disposition:.* filename=(?:"((?:\\.|[^\"\s])*)"|([^;\s]*))/ni.match(head)
- filename = ($1 or $2 or "")
- if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
- /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
- (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
- filename = CGI::unescape(filename)
- end
-
- /Content-Type: ([^\s]*)/ni.match(head)
- content_type = ($1 or "")
-
- (class << body; self; end).class_eval do
- alias local_path path
- define_method(:original_filename) {filename.dup.taint}
- define_method(:content_type) {content_type.dup.taint}
- end
-
- /Content-Disposition:.* name="?([^\";\s]*)"?/ni.match(head)
- name = ($1 || "").dup
-
- if params.has_key?(name)
- params[name].push(body)
- else
- params[name] = [body]
- end
- break if buf.size == 0
- break if content_length == -1
- end
- raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
-
- params
- end # read_multipart
- private :read_multipart
-
- # offline mode. read name=value pairs on standard input.
- def read_from_cmdline
- require "shellwords"
-
- string = unless ARGV.empty?
- ARGV.join(' ')
- else
- if STDIN.tty?
- STDERR.print(
- %|(offline mode: enter name=value pairs on standard input)\n|
- )
- end
- readlines.join(' ').gsub(/\n/n, '')
- end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
-
- words = Shellwords.shellwords(string)
-
- if words.find{|x| /=/n.match(x) }
- words.join('&')
- else
- words.join('+')
- end
- end
- private :read_from_cmdline
-
- # A wrapper class to use a StringIO object as the body and switch
- # to a TempFile when the passed threshold is passed.
- class MorphingBody
- begin
- require "stringio"
- @@small_buffer = lambda{StringIO.new}
- rescue LoadError
- require "tempfile"
- @@small_buffer = lambda{
- n = Tempfile.new("CGI")
- n.binmode
- n
- }
- end
-
- def initialize(morph_threshold = 10240)
- @threshold = morph_threshold
- @body = @@small_buffer.call
- @cur_size = 0
- @morph_check = true
- end
-
- def print(data)
- if @morph_check && (@cur_size + data.size > @threshold)
- convert_body
- end
- @body.print data
- end
- def rewind
- @body.rewind
- end
- def path
- @body.path
- end
-
- # returns the true body object.
- def extract
- @body
- end
-
- private
- def convert_body
- new_body = TempFile.new("CGI")
- new_body.binmode if defined? @body.binmode
- new_body.binmode if defined? new_body.binmode
-
- @body.rewind
- new_body.print @body.read
- @body = new_body
- @morph_check = false
- end
- end
-
- # Initialize the data from the query.
- #
- # Handles multipart forms (in particular, forms that involve file uploads).
- # Reads query parameters in the @params field, and cookies into @cookies.
- def initialize_query()
- if ("POST" == env_table['REQUEST_METHOD']) and
- %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
- boundary = $1.dup
- @multipart = true
- @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
- else
- @multipart = false
- @params = CGI::parse(
- case env_table['REQUEST_METHOD']
- when "GET", "HEAD"
- if defined?(MOD_RUBY)
- Apache::request.args or ""
- else
- env_table['QUERY_STRING'] or ""
- end
- when "POST"
- stdinput.binmode if defined? stdinput.binmode
- stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
- else
- read_from_cmdline
- end
- )
- end
-
- @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
- end
- private :initialize_query
-
- def multipart?
- @multipart
- end
-
- # Get the value for the parameter with a given key.
- #
- # If the parameter has multiple values, only the first will be
- # retrieved; use #params() to get the array of values.
- def [](key)
- params = @params[key]
- return '' unless params
- value = params[0]
- if @multipart
- if value
- return value
- elsif defined? StringIO
- StringIO.new("")
- else
- Tempfile.new("CGI")
- end
- else
- str = if value then value.dup else "" end
- str
- end
- end
-
- # Return all parameter keys as an array.
- def keys(*args)
- @params.keys(*args)
- end
-
- # Returns true if a given parameter key exists in the query.
- def has_key?(*args)
- @params.has_key?(*args)
- end
- alias key? has_key?
- alias include? has_key?
-
- end # QueryExtension
-
-
- # Prettify (indent) an HTML string.
- #
- # +string+ is the HTML string to indent. +shift+ is the indentation
- # unit to use; it defaults to two spaces.
- #
- # print CGI::pretty("<HTML><BODY></BODY></HTML>")
- # # <HTML>
- # # <BODY>
- # # </BODY>
- # # </HTML>
- #
- # print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
- # # <HTML>
- # # <BODY>
- # # </BODY>
- # # </HTML>
- #
- def CGI::pretty(string, shift = " ")
- lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
- end_pos = 0
- while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
- element = $1.dup
- start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
- lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
- end
- lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
- end
-
-
- # Base module for HTML-generation mixins.
- #
- # Provides methods for code generation for tags following
- # the various DTD element types.
- module TagMaker # :nodoc:
-
- # Generate code for an element with required start and end tags.
- #
- # - -
- def nn_element_def(element)
- nOE_element_def(element, <<-END)
- if block_given?
- yield.to_s
- else
- ""
- end +
- "</#{element.upcase}>"
- END
- end
-
- # Generate code for an empty element.
- #
- # - O EMPTY
- def nOE_element_def(element, append = nil)
- s = <<-END
- "<#{element.upcase}" + attributes.collect{|name, value|
- next unless value
- " " + CGI::escapeHTML(name) +
- if true == value
- ""
- else
- '="' + CGI::escapeHTML(value) + '"'
- end
- }.join + ">"
- END
- s.sub!(/\Z/, " +") << append if append
- s
- end
-
- # Generate code for an element for which the end (and possibly the
- # start) tag is optional.
- #
- # O O or - O
- def nO_element_def(element)
- nOE_element_def(element, <<-END)
- if block_given?
- yield.to_s + "</#{element.upcase}>"
- else
- ""
- end
- END
- end
-
- end # TagMaker
-
-
- #
- # Mixin module providing HTML generation methods.
- #
- # For example,
- # cgi.a("http://www.example.com") { "Example" }
- # # => "<A HREF=\"http://www.example.com\">Example</A>"
- #
- # Modules Http3, Http4, etc., contain more basic HTML-generation methods
- # (:title, :center, etc.).
- #
- # See class CGI for a detailed example.
- #
- module HtmlExtension
-
-
- # Generate an Anchor element as a string.
- #
- # +href+ can either be a string, giving the URL
- # for the HREF attribute, or it can be a hash of
- # the element's attributes.
- #
- # The body of the element is the string returned by the no-argument
- # block passed in.
- #
- # a("http://www.example.com") { "Example" }
- # # => "<A HREF=\"http://www.example.com\">Example</A>"
- #
- # a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
- # # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
- #
- def a(href = "") # :yield:
- attributes = if href.kind_of?(String)
- { "HREF" => href }
- else
- href
- end
- if block_given?
- super(attributes){ yield }
- else
- super(attributes)
- end
- end
-
- # Generate a Document Base URI element as a String.
- #
- # +href+ can either by a string, giving the base URL for the HREF
- # attribute, or it can be a has of the element's attributes.
- #
- # The passed-in no-argument block is ignored.
- #
- # base("http://www.example.com/cgi")
- # # => "<BASE HREF=\"http://www.example.com/cgi\">"
- def base(href = "") # :yield:
- attributes = if href.kind_of?(String)
- { "HREF" => href }
- else
- href
- end
- if block_given?
- super(attributes){ yield }
- else
- super(attributes)
- end
- end
-
- # Generate a BlockQuote element as a string.
- #
- # +cite+ can either be a string, give the URI for the source of
- # the quoted text, or a hash, giving all attributes of the element,
- # or it can be omitted, in which case the element has no attributes.
- #
- # The body is provided by the passed-in no-argument block
- #
- # blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
- # #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
- def blockquote(cite = nil) # :yield:
- attributes = if cite.kind_of?(String)
- { "CITE" => cite }
- else
- cite or ""
- end
- if block_given?
- super(attributes){ yield }
- else
- super(attributes)
- end
- end
-
-
- # Generate a Table Caption element as a string.
- #
- # +align+ can be a string, giving the alignment of the caption
- # (one of top, bottom, left, or right). It can be a hash of
- # all the attributes of the element. Or it can be omitted.
- #
- # The body of the element is provided by the passed-in no-argument block.
- #
- # caption("left") { "Capital Cities" }
- # # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
- def caption(align = nil) # :yield:
- attributes = if align.kind_of?(String)
- { "ALIGN" => align }
- else
- align or ""
- end
- if block_given?
- super(attributes){ yield }
- else
- super(attributes)
- end
- end
-
-
- # Generate a Checkbox Input element as a string.
- #
- # The attributes of the element can be specified as three arguments,
- # +name+, +value+, and +checked+. +checked+ is a boolean value;
- # if true, the CHECKED attribute will be included in the element.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # checkbox("name")
- # # = checkbox("NAME" => "name")
- #
- # checkbox("name", "value")
- # # = checkbox("NAME" => "name", "VALUE" => "value")
- #
- # checkbox("name", "value", true)
- # # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
- def checkbox(name = "", value = nil, checked = nil)
- attributes = if name.kind_of?(String)
- { "TYPE" => "checkbox", "NAME" => name,
- "VALUE" => value, "CHECKED" => checked }
- else
- name["TYPE"] = "checkbox"
- name
- end
- input(attributes)
- end
-
- # Generate a sequence of checkbox elements, as a String.
- #
- # The checkboxes will all have the same +name+ attribute.
- # Each checkbox is followed by a label.
- # There will be one checkbox for each value. Each value
- # can be specified as a String, which will be used both
- # as the value of the VALUE attribute and as the label
- # for that checkbox. A single-element array has the
- # same effect.
- #
- # Each value can also be specified as a three-element array.
- # The first element is the VALUE attribute; the second is the
- # label; and the third is a boolean specifying whether this
- # checkbox is CHECKED.
- #
- # Each value can also be specified as a two-element
- # array, by omitting either the value element (defaults
- # to the same as the label), or the boolean checked element
- # (defaults to false).
- #
- # checkbox_group("name", "foo", "bar", "baz")
- # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
- # # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
- # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
- #
- # checkbox_group("name", ["foo"], ["bar", true], "baz")
- # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
- # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
- # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
- #
- # checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
- # # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
- # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
- # # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
- #
- # checkbox_group("NAME" => "name",
- # "VALUES" => ["foo", "bar", "baz"])
- #
- # checkbox_group("NAME" => "name",
- # "VALUES" => [["foo"], ["bar", true], "baz"])
- #
- # checkbox_group("NAME" => "name",
- # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
- def checkbox_group(name = "", *values)
- if name.kind_of?(Hash)
- values = name["VALUES"]
- name = name["NAME"]
- end
- values.collect{|value|
- if value.kind_of?(String)
- checkbox(name, value) + value
- else
- if value[value.size - 1] == true
- checkbox(name, value[0], true) +
- value[value.size - 2]
- else
- checkbox(name, value[0]) +
- value[value.size - 1]
- end
- end
- }.join
- end
-
-
- # Generate an File Upload Input element as a string.
- #
- # The attributes of the element can be specified as three arguments,
- # +name+, +size+, and +maxlength+. +maxlength+ is the maximum length
- # of the file's _name_, not of the file's _contents_.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # See #multipart_form() for forms that include file uploads.
- #
- # file_field("name")
- # # <INPUT TYPE="file" NAME="name" SIZE="20">
- #
- # file_field("name", 40)
- # # <INPUT TYPE="file" NAME="name" SIZE="40">
- #
- # file_field("name", 40, 100)
- # # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
- #
- # file_field("NAME" => "name", "SIZE" => 40)
- # # <INPUT TYPE="file" NAME="name" SIZE="40">
- def file_field(name = "", size = 20, maxlength = nil)
- attributes = if name.kind_of?(String)
- { "TYPE" => "file", "NAME" => name,
- "SIZE" => size.to_s }
- else
- name["TYPE"] = "file"
- name
- end
- attributes["MAXLENGTH"] = maxlength.to_s if maxlength
- input(attributes)
- end
-
-
- # Generate a Form element as a string.
- #
- # +method+ should be either "get" or "post", and defaults to the latter.
- # +action+ defaults to the current CGI script name. +enctype+
- # defaults to "application/x-www-form-urlencoded".
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # See also #multipart_form() for forms that include file uploads.
- #
- # form{ "string" }
- # # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
- #
- # form("get") { "string" }
- # # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
- #
- # form("get", "url") { "string" }
- # # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
- #
- # form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
- # # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
- def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
- attributes = if method.kind_of?(String)
- { "METHOD" => method, "ACTION" => action,
- "ENCTYPE" => enctype }
- else
- unless method.has_key?("METHOD")
- method["METHOD"] = "post"
- end
- unless method.has_key?("ENCTYPE")
- method["ENCTYPE"] = enctype
- end
- method
- end
- if block_given?
- body = yield
- else
- body = ""
- end
- if @output_hidden
- body += @output_hidden.collect{|k,v|
- "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
- }.join
- end
- super(attributes){body}
- end
-
- # Generate a Hidden Input element as a string.
- #
- # The attributes of the element can be specified as two arguments,
- # +name+ and +value+.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # hidden("name")
- # # <INPUT TYPE="hidden" NAME="name">
- #
- # hidden("name", "value")
- # # <INPUT TYPE="hidden" NAME="name" VALUE="value">
- #
- # hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
- # # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
- def hidden(name = "", value = nil)
- attributes = if name.kind_of?(String)
- { "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
- else
- name["TYPE"] = "hidden"
- name
- end
- input(attributes)
- end
-
- # Generate a top-level HTML element as a string.
- #
- # The attributes of the element are specified as a hash. The
- # pseudo-attribute "PRETTY" can be used to specify that the generated
- # HTML string should be indented. "PRETTY" can also be specified as
- # a string as the sole argument to this method. The pseudo-attribute
- # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
- # should include the entire text of this tag, including angle brackets.
- #
- # The body of the html element is supplied as a block.
- #
- # html{ "string" }
- # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
- #
- # html("LANG" => "ja") { "string" }
- # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
- #
- # html("DOCTYPE" => false) { "string" }
- # # <HTML>string</HTML>
- #
- # html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
- # # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
- #
- # html("PRETTY" => " ") { "<BODY></BODY>" }
- # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
- # # <HTML>
- # # <BODY>
- # # </BODY>
- # # </HTML>
- #
- # html("PRETTY" => "\t") { "<BODY></BODY>" }
- # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
- # # <HTML>
- # # <BODY>
- # # </BODY>
- # # </HTML>
- #
- # html("PRETTY") { "<BODY></BODY>" }
- # # = html("PRETTY" => " ") { "<BODY></BODY>" }
- #
- # html(if $VERBOSE then "PRETTY" end) { "HTML string" }
- #
- def html(attributes = {}) # :yield:
- if nil == attributes
- attributes = {}
- elsif "PRETTY" == attributes
- attributes = { "PRETTY" => true }
- end
- pretty = attributes.delete("PRETTY")
- pretty = " " if true == pretty
- buf = ""
-
- if attributes.has_key?("DOCTYPE")
- if attributes["DOCTYPE"]
- buf += attributes.delete("DOCTYPE")
- else
- attributes.delete("DOCTYPE")
- end
- else
- buf += doctype
- end
-
- if block_given?
- buf += super(attributes){ yield }
- else
- buf += super(attributes)
- end
-
- if pretty
- CGI::pretty(buf, pretty)
- else
- buf
- end
-
- end
-
- # Generate an Image Button Input element as a string.
- #
- # +src+ is the URL of the image to use for the button. +name+
- # is the input name. +alt+ is the alternative text for the image.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # image_button("url")
- # # <INPUT TYPE="image" SRC="url">
- #
- # image_button("url", "name", "string")
- # # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
- #
- # image_button("SRC" => "url", "ATL" => "strng")
- # # <INPUT TYPE="image" SRC="url" ALT="string">
- def image_button(src = "", name = nil, alt = nil)
- attributes = if src.kind_of?(String)
- { "TYPE" => "image", "SRC" => src, "NAME" => name,
- "ALT" => alt }
- else
- src["TYPE"] = "image"
- src["SRC"] ||= ""
- src
- end
- input(attributes)
- end
-
-
- # Generate an Image element as a string.
- #
- # +src+ is the URL of the image. +alt+ is the alternative text for
- # the image. +width+ is the width of the image, and +height+ is
- # its height.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # img("src", "alt", 100, 50)
- # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
- #
- # img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
- # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
- def img(src = "", alt = "", width = nil, height = nil)
- attributes = if src.kind_of?(String)
- { "SRC" => src, "ALT" => alt }
- else
- src
- end
- attributes["WIDTH"] = width.to_s if width
- attributes["HEIGHT"] = height.to_s if height
- super(attributes)
- end
-
-
- # Generate a Form element with multipart encoding as a String.
- #
- # Multipart encoding is used for forms that include file uploads.
- #
- # +action+ is the action to perform. +enctype+ is the encoding
- # type, which defaults to "multipart/form-data".
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # multipart_form{ "string" }
- # # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
- #
- # multipart_form("url") { "string" }
- # # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
- def multipart_form(action = nil, enctype = "multipart/form-data")
- attributes = if action == nil
- { "METHOD" => "post", "ENCTYPE" => enctype }
- elsif action.kind_of?(String)
- { "METHOD" => "post", "ACTION" => action,
- "ENCTYPE" => enctype }
- else
- unless action.has_key?("METHOD")
- action["METHOD"] = "post"
- end
- unless action.has_key?("ENCTYPE")
- action["ENCTYPE"] = enctype
- end
- action
- end
- if block_given?
- form(attributes){ yield }
- else
- form(attributes)
- end
- end
-
-
- # Generate a Password Input element as a string.
- #
- # +name+ is the name of the input field. +value+ is its default
- # value. +size+ is the size of the input field display. +maxlength+
- # is the maximum length of the inputted password.
- #
- # Alternatively, attributes can be specified as a hash.
- #
- # password_field("name")
- # # <INPUT TYPE="password" NAME="name" SIZE="40">
- #
- # password_field("name", "value")
- # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
- #
- # password_field("password", "value", 80, 200)
- # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
- #
- # password_field("NAME" => "name", "VALUE" => "value")
- # # <INPUT TYPE="password" NAME="name" VALUE="value">
- def password_field(name = "", value = nil, size = 40, maxlength = nil)
- attributes = if name.kind_of?(String)
- { "TYPE" => "password", "NAME" => name,
- "VALUE" => value, "SIZE" => size.to_s }
- else
- name["TYPE"] = "password"
- name
- end
- attributes["MAXLENGTH"] = maxlength.to_s if maxlength
- input(attributes)
- end
-
- # Generate a Select element as a string.
- #
- # +name+ is the name of the element. The +values+ are the options that
- # can be selected from the Select menu. Each value can be a String or
- # a one, two, or three-element Array. If a String or a one-element
- # Array, this is both the value of that option and the text displayed for
- # it. If a three-element Array, the elements are the option value, displayed
- # text, and a boolean value specifying whether this option starts as selected.
- # The two-element version omits either the option value (defaults to the same
- # as the display text) or the boolean selected specifier (defaults to false).
- #
- # The attributes and options can also be specified as a hash. In this
- # case, options are specified as an array of values as described above,
- # with the hash key of "VALUES".
- #
- # popup_menu("name", "foo", "bar", "baz")
- # # <SELECT NAME="name">
- # # <OPTION VALUE="foo">foo</OPTION>
- # # <OPTION VALUE="bar">bar</OPTION>
- # # <OPTION VALUE="baz">baz</OPTION>
- # # </SELECT>
- #
- # popup_menu("name", ["foo"], ["bar", true], "baz")
- # # <SELECT NAME="name">
- # # <OPTION VALUE="foo">foo</OPTION>
- # # <OPTION VALUE="bar" SELECTED>bar</OPTION>
- # # <OPTION VALUE="baz">baz</OPTION>
- # # </SELECT>
- #
- # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
- # # <SELECT NAME="name">
- # # <OPTION VALUE="1">Foo</OPTION>
- # # <OPTION SELECTED VALUE="2">Bar</OPTION>
- # # <OPTION VALUE="Baz">Baz</OPTION>
- # # </SELECT>
- #
- # popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
- # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
- # # <SELECT NAME="name" MULTIPLE SIZE="2">
- # # <OPTION VALUE="1">Foo</OPTION>
- # # <OPTION SELECTED VALUE="2">Bar</OPTION>
- # # <OPTION VALUE="Baz">Baz</OPTION>
- # # </SELECT>
- def popup_menu(name = "", *values)
-
- if name.kind_of?(Hash)
- values = name["VALUES"]
- size = name["SIZE"].to_s if name["SIZE"]
- multiple = name["MULTIPLE"]
- name = name["NAME"]
- else
- size = nil
- multiple = nil
- end
-
- select({ "NAME" => name, "SIZE" => size,
- "MULTIPLE" => multiple }){
- values.collect{|value|
- if value.kind_of?(String)
- option({ "VALUE" => value }){ value }
- else
- if value[value.size - 1] == true
- option({ "VALUE" => value[0], "SELECTED" => true }){
- value[value.size - 2]
- }
- else
- option({ "VALUE" => value[0] }){
- value[value.size - 1]
- }
- end
- end
- }.join
- }
-
- end
-
- # Generates a radio-button Input element.
- #
- # +name+ is the name of the input field. +value+ is the value of
- # the field if checked. +checked+ specifies whether the field
- # starts off checked.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # radio_button("name", "value")
- # # <INPUT TYPE="radio" NAME="name" VALUE="value">
- #
- # radio_button("name", "value", true)
- # # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
- #
- # radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo")
- # # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
- def radio_button(name = "", value = nil, checked = nil)
- attributes = if name.kind_of?(String)
- { "TYPE" => "radio", "NAME" => name,
- "VALUE" => value, "CHECKED" => checked }
- else
- name["TYPE"] = "radio"
- name
- end
- input(attributes)
- end
-
- # Generate a sequence of radio button Input elements, as a String.
- #
- # This works the same as #checkbox_group(). However, it is not valid
- # to have more than one radiobutton in a group checked.
- #
- # radio_group("name", "foo", "bar", "baz")
- # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
- # # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
- # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
- #
- # radio_group("name", ["foo"], ["bar", true], "baz")
- # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
- # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
- # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
- #
- # radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
- # # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
- # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
- # # <INPUT TYPE="radio" NAME="name" VALUE="Baz">Baz
- #
- # radio_group("NAME" => "name",
- # "VALUES" => ["foo", "bar", "baz"])
- #
- # radio_group("NAME" => "name",
- # "VALUES" => [["foo"], ["bar", true], "baz"])
- #
- # radio_group("NAME" => "name",
- # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
- def radio_group(name = "", *values)
- if name.kind_of?(Hash)
- values = name["VALUES"]
- name = name["NAME"]
- end
- values.collect{|value|
- if value.kind_of?(String)
- radio_button(name, value) + value
- else
- if value[value.size - 1] == true
- radio_button(name, value[0], true) +
- value[value.size - 2]
- else
- radio_button(name, value[0]) +
- value[value.size - 1]
- end
- end
- }.join
- end
-
- # Generate a reset button Input element, as a String.
- #
- # This resets the values on a form to their initial values. +value+
- # is the text displayed on the button. +name+ is the name of this button.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # reset
- # # <INPUT TYPE="reset">
- #
- # reset("reset")
- # # <INPUT TYPE="reset" VALUE="reset">
- #
- # reset("VALUE" => "reset", "ID" => "foo")
- # # <INPUT TYPE="reset" VALUE="reset" ID="foo">
- def reset(value = nil, name = nil)
- attributes = if (not value) or value.kind_of?(String)
- { "TYPE" => "reset", "VALUE" => value, "NAME" => name }
- else
- value["TYPE"] = "reset"
- value
- end
- input(attributes)
- end
-
- alias scrolling_list popup_menu
-
- # Generate a submit button Input element, as a String.
- #
- # +value+ is the text to display on the button. +name+ is the name
- # of the input.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # submit
- # # <INPUT TYPE="submit">
- #
- # submit("ok")
- # # <INPUT TYPE="submit" VALUE="ok">
- #
- # submit("ok", "button1")
- # # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
- #
- # submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo")
- # # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
- def submit(value = nil, name = nil)
- attributes = if (not value) or value.kind_of?(String)
- { "TYPE" => "submit", "VALUE" => value, "NAME" => name }
- else
- value["TYPE"] = "submit"
- value
- end
- input(attributes)
- end
-
- # Generate a text field Input element, as a String.
- #
- # +name+ is the name of the input field. +value+ is its initial
- # value. +size+ is the size of the input area. +maxlength+
- # is the maximum length of input accepted.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # text_field("name")
- # # <INPUT TYPE="text" NAME="name" SIZE="40">
- #
- # text_field("name", "value")
- # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
- #
- # text_field("name", "value", 80)
- # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
- #
- # text_field("name", "value", 80, 200)
- # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
- #
- # text_field("NAME" => "name", "VALUE" => "value")
- # # <INPUT TYPE="text" NAME="name" VALUE="value">
- def text_field(name = "", value = nil, size = 40, maxlength = nil)
- attributes = if name.kind_of?(String)
- { "TYPE" => "text", "NAME" => name, "VALUE" => value,
- "SIZE" => size.to_s }
- else
- name["TYPE"] = "text"
- name
- end
- attributes["MAXLENGTH"] = maxlength.to_s if maxlength
- input(attributes)
- end
-
- # Generate a TextArea element, as a String.
- #
- # +name+ is the name of the textarea. +cols+ is the number of
- # columns and +rows+ is the number of rows in the display.
- #
- # Alternatively, the attributes can be specified as a hash.
- #
- # The body is provided by the passed-in no-argument block
- #
- # textarea("name")
- # # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
- #
- # textarea("name", 40, 5)
- # # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5)
- def textarea(name = "", cols = 70, rows = 10) # :yield:
- attributes = if name.kind_of?(String)
- { "NAME" => name, "COLS" => cols.to_s,
- "ROWS" => rows.to_s }
- else
- name
- end
- if block_given?
- super(attributes){ yield }
- else
- super(attributes)
- end
- end
-
- end # HtmlExtension
-
-
- # Mixin module for HTML version 3 generation methods.
- module Html3 # :nodoc:
-
- # The DOCTYPE declaration for this version of HTML
- def doctype
- %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
- end
-
- # Initialise the HTML generation methods for this version.
- def element_init
- extend TagMaker
- methods = ""
- # - -
- for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
- DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
- APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
- STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
- CAPTION ]
- methods += <<-BEGIN + nn_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
-
- # - O EMPTY
- for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
- ISINDEX META ]
- methods += <<-BEGIN + nOE_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
-
- # O O or - O
- for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
- th td ]
- methods += <<-BEGIN + nO_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
- eval(methods)
- end
-
- end # Html3
-
-
- # Mixin module for HTML version 4 generation methods.
- module Html4 # :nodoc:
-
- # The DOCTYPE declaration for this version of HTML
- def doctype
- %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
- end
-
- # Initialise the HTML generation methods for this version.
- def element_init
- extend TagMaker
- methods = ""
- # - -
- for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD
- VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT
- H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP
- FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT
- TEXTAREA FORM A BLOCKQUOTE CAPTION ]
- methods += <<-BEGIN + nn_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
-
- # - O EMPTY
- for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
- methods += <<-BEGIN + nOE_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
-
- # O O or - O
- for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
- COLGROUP TR TH TD HEAD]
- methods += <<-BEGIN + nO_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
- eval(methods)
- end
-
- end # Html4
-
-
- # Mixin module for HTML version 4 transitional generation methods.
- module Html4Tr # :nodoc:
-
- # The DOCTYPE declaration for this version of HTML
- def doctype
- %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
- end
-
- # Initialise the HTML generation methods for this version.
- def element_init
- extend TagMaker
- methods = ""
- # - -
- for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN
- CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO
- ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q
- INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET
- LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT
- NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]
- methods += <<-BEGIN + nn_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
-
- # - O EMPTY
- for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
- COL ISINDEX META ]
- methods += <<-BEGIN + nOE_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
-
- # O O or - O
- for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
- COLGROUP TR TH TD HEAD ]
- methods += <<-BEGIN + nO_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
- eval(methods)
- end
-
- end # Html4Tr
-
-
- # Mixin module for generating HTML version 4 with framesets.
- module Html4Fr # :nodoc:
-
- # The DOCTYPE declaration for this version of HTML
- def doctype
- %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
- end
-
- # Initialise the HTML generation methods for this version.
- def element_init
- methods = ""
- # - -
- for element in %w[ FRAMESET ]
- methods += <<-BEGIN + nn_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
-
- # - O EMPTY
- for element in %w[ FRAME ]
- methods += <<-BEGIN + nOE_element_def(element) + <<-END
- def #{element.downcase}(attributes = {})
- BEGIN
- end
- END
- end
- eval(methods)
- end
-
- end # Html4Fr
-
-
- # Creates a new CGI instance.
- #
- # +type+ specifies which version of HTML to load the HTML generation
- # methods for. The following versions of HTML are supported:
- #
- # html3:: HTML 3.x
- # html4:: HTML 4.0
- # html4Tr:: HTML 4.0 Transitional
- # html4Fr:: HTML 4.0 with Framesets
- #
- # If not specified, no HTML generation methods will be loaded.
- #
- # If the CGI object is not created in a standard CGI call environment
- # (that is, it can't locate REQUEST_METHOD in its environment), then
- # it will run in "offline" mode. In this mode, it reads its parameters
- # from the command line or (failing that) from standard input. Otherwise,
- # cookies and other parameters are parsed automatically from the standard
- # CGI locations, which varies according to the REQUEST_METHOD.
- def initialize(type = "query")
- if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
- Apache.request.setup_cgi_env
- end
-
- (class << self; self; end).class_eval do
- const_set(:CGI_PARAMS, [1])
- const_set(:CGI_COOKIES, [2])
- end
-
- extend QueryExtension
- @multipart = false
-
- initialize_query() # set @params, @cookies
- @output_cookies = nil
- @output_hidden = nil
-
- case type
- when "html3"
- extend Html3
- element_init()
- extend HtmlExtension
- when "html4"
- extend Html4
- element_init()
- extend HtmlExtension
- when "html4Tr"
- extend Html4Tr
- element_init()
- extend HtmlExtension
- when "html4Fr"
- extend Html4Tr
- element_init()
- extend Html4Fr
- element_init()
- extend HtmlExtension
- end
- end
-
-end # class CGI
+require 'cgi/core'
+require 'cgi/cookie'
+require 'cgi/util'
Modified: MacRuby/branches/experimental/lib/cmath.rb
===================================================================
--- MacRuby/branches/experimental/lib/cmath.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/cmath.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -25,17 +25,17 @@
alias atanh! atanh
def exp(z)
- if Complex.generic?(z)
+ if z.real?
exp!(z)
else
- Complex(exp!(z.real) * cos!(z.image),
- exp!(z.real) * sin!(z.image))
+ Complex(exp!(z.real) * cos!(z.imag),
+ exp!(z.real) * sin!(z.imag))
end
end
def log(*args)
z, b = args
- if Complex.generic?(z) and z >= 0 and (b.nil? or b >= 0)
+ if z.real? and z >= 0 and (b.nil? or b >= 0)
log!(*args)
else
r, theta = z.polar
@@ -48,7 +48,7 @@
end
def log10(z)
- if Complex.generic?(z)
+ if z.real?
log10!(z)
else
log(z) / log!(10)
@@ -56,14 +56,14 @@
end
def sqrt(z)
- if Complex.generic?(z)
- if z >= 0
+ if z.real?
+ if z < 0
+ Complex(0, sqrt!(-z))
+ else
sqrt!(z)
- else
- Complex(0,sqrt!(-z))
end
else
- if z.image < 0
+ if z.imag < 0
sqrt(z.conjugate).conjugate
else
r = z.abs
@@ -74,25 +74,25 @@
end
def sin(z)
- if Complex.generic?(z)
+ if z.real?
sin!(z)
else
- Complex(sin!(z.real) * cosh!(z.image),
- cos!(z.real) * sinh!(z.image))
+ Complex(sin!(z.real) * cosh!(z.imag),
+ cos!(z.real) * sinh!(z.imag))
end
end
def cos(z)
- if Complex.generic?(z)
+ if z.real?
cos!(z)
else
- Complex(cos!(z.real) * cosh!(z.image),
- -sin!(z.real) * sinh!(z.image))
+ Complex(cos!(z.real) * cosh!(z.imag),
+ -sin!(z.real) * sinh!(z.imag))
end
end
def tan(z)
- if Complex.generic?(z)
+ if z.real?
tan!(z)
else
sin(z)/cos(z)
@@ -100,25 +100,25 @@
end
def sinh(z)
- if Complex.generic?(z)
+ if z.real?
sinh!(z)
else
- Complex(sinh!(z.real) * cos!(z.image),
- cosh!(z.real) * sin!(z.image))
+ Complex(sinh!(z.real) * cos!(z.imag),
+ cosh!(z.real) * sin!(z.imag))
end
end
def cosh(z)
- if Complex.generic?(z)
+ if z.real?
cosh!(z)
else
- Complex(cosh!(z.real) * cos!(z.image),
- sinh!(z.real) * sin!(z.image))
+ Complex(cosh!(z.real) * cos!(z.imag),
+ sinh!(z.real) * sin!(z.imag))
end
end
def tanh(z)
- if Complex.generic?(z)
+ if z.real?
tanh!(z)
else
sinh(z) / cosh(z)
@@ -126,39 +126,39 @@
end
def asin(z)
- if Complex.generic?(z) and z >= -1 and z <= 1
+ if z.real? and z >= -1 and z <= 1
asin!(z)
else
- -1.0.im * log(1.0.im * z + sqrt(1.0 - z * z))
+ Complex(0, -1.0) * log(Complex(0, 1.0) * z + sqrt(1.0 - z * z))
end
end
def acos(z)
- if Complex.generic?(z) and z >= -1 and z <= 1
+ if z.real? and z >= -1 and z <= 1
acos!(z)
else
- -1.0.im * log(z + 1.0.im * sqrt(1.0 - z * z))
+ Complex(0, -1.0) * log(z + Complex(0, 1.0) * sqrt(1.0 - z * z))
end
end
def atan(z)
- if Complex.generic?(z)
+ if z.real?
atan!(z)
else
- 1.0.im * log((1.0.im + z) / (1.0.im - z)) / 2.0
+ Complex(0, 1.0) * log((Complex(0, 1.0) + z) / (Complex(0, 1.0) - z)) / 2.0
end
end
def atan2(y,x)
- if Complex.generic?(y) and Complex.generic?(x)
+ if y.real? and x.real?
atan2!(y,x)
else
- -1.0.im * log((x + 1.0.im * y) / sqrt(x * x + y * y))
+ Complex(0, -1.0) * log((x + Complex(0, 1.0) * y) / sqrt(x * x + y * y))
end
end
def acosh(z)
- if Complex.generic?(z) and z >= 1
+ if z.real? and z >= 1
acosh!(z)
else
log(z + sqrt(z * z - 1.0))
@@ -166,7 +166,7 @@
end
def asinh(z)
- if Complex.generic?(z)
+ if z.real?
asinh!(z)
else
log(z + sqrt(1.0 + z * z))
@@ -174,7 +174,7 @@
end
def atanh(z)
- if Complex.generic?(z) and z >= -1 and z <= 1
+ if z.real? and z >= -1 and z <= 1
atanh!(z)
else
log((1.0 + z) / (1.0 - z)) / 2.0
@@ -220,4 +220,14 @@
module_function :atanh!
module_function :atanh
+ module_function :log2
+ module_function :cbrt
+ module_function :frexp
+ module_function :ldexp
+ module_function :hypot
+ module_function :erf
+ module_function :erfc
+ module_function :gamma
+ module_function :lgamma
+
end
Modified: MacRuby/branches/experimental/lib/complex.rb
===================================================================
--- MacRuby/branches/experimental/lib/complex.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/complex.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,4 +1,24 @@
require 'cmath'
-Object.instance_eval{remove_const :Math}
-Math = CMath
+unless defined?(Math.exp!)
+ Object.instance_eval{remove_const :Math}
+ Math = CMath
+end
+
+def Complex.generic? (other)
+ other.kind_of?(Integer) ||
+ other.kind_of?(Float) ||
+ other.kind_of?(Rational)
+end
+
+class Complex
+
+ alias image imag
+
+end
+
+class Numeric
+
+ def im() Complex(0, self) end
+
+end
Modified: MacRuby/branches/experimental/lib/csv.rb
===================================================================
--- MacRuby/branches/experimental/lib/csv.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/csv.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,5 +1,5 @@
-#!/usr/local/bin/ruby -w
-
+#!/usr/bin/env ruby -w
+# encoding: UTF-8
# = csv.rb -- CSV Reading and Writing
#
# Created by James Edward Gray II on 2005-10-31.
@@ -37,6 +37,7 @@
#
# === CSV Parsing
#
+# * This parser is m17n aware. See CSV for full details.
# * This library has a stricter parser and will throw MalformedCSVErrors on
# problematic data.
# * This library has a less liberal idea of a line ending than CSV. What you
@@ -91,7 +92,6 @@
require "forwardable"
require "English"
-require "enumerator"
require "date"
require "stringio"
@@ -130,7 +130,7 @@
#
# === To a File
#
-# CSV.open("path/to/file.csv", "w") do |csv|
+# CSV.open("path/to/file.csv", "wb") do |csv|
# csv << ["row", "of", "CSV", "data"]
# csv << ["another", "row"]
# # ...
@@ -155,9 +155,51 @@
# CSV(csv = "") { |csv_str| csv_str << %w{my data here} } # to a String
# CSV($stderr) { |csv_err| csv_err << %w{my data here} } # to $stderr
#
+# == CSV and Character Encodings (M17n or Multilingualization)
+#
+# This new CSV parser is m17n savvy. The parser works in the Encoding of the IO
+# or String object being read from or written to. Your data is never transcoded
+# (unless you ask Ruby to transcode it for you) and will literally be parsed in
+# the Encoding it is in. Thus CSV will return Arrays or Rows of Strings in the
+# Encoding of your data. This is accomplished by transcoding the parser itself
+# into your Encoding.
+#
+# Some transcoding must take place, of course, to accomplish this multiencoding
+# support. For example, <tt>:col_sep</tt>, <tt>:row_sep</tt>, and
+# <tt>:quote_char</tt> must be transcoded to match your data. Hopefully this
+# makes the entire process feel transparent, since CSV's defaults should just
+# magically work for you data. However, you can set these values manually in
+# the target Encoding to avoid the translation.
+#
+# It's also important to note that while all of CSV's core parser is now
+# Encoding agnostic, some features are not. For example, the built-in
+# converters will try to transcode data to UTF-8 before making conversions.
+# Again, you can provide custom converters that are aware of your Encodings to
+# avoid this translation. It's just too hard for me to support native
+# conversions in all of Ruby's Encodings.
+#
+# Anyway, the practical side of this is simple: make sure IO and String objects
+# passed into CSV have the proper Encoding set and everything should just work.
+# CSV methods that allow you to open IO objects (CSV::foreach(), CSV::open(),
+# CSV::read(), and CSV::readlines()) do allow you to specify the Encoding.
+#
+# One minor exception comes when generating CSV into a String with an Encoding
+# that is not ASCII compatible. There's no existing data for CSV to use to
+# prepare itself and thus you will probably need to manually specify the desired
+# Encoding for most of those cases. It will try to guess using the fields in a
+# row of output though, when using CSV::generate_line() or Array#to_csv().
+#
+# I try to point out any other Encoding issues in the documentation of methods
+# as they come up.
+#
+# This has been tested to the best of my ability with all non-"dummy" Encodings
+# Ruby ships with. However, it is brave new code and may have some bugs.
+# Please feel free to {report}[mailto:james at grayproductions.net] any issues you
+# find with it.
+#
class CSV
# The version of the installed library.
- VERSION = "2.0.0".freeze
+ VERSION = "2.4.5".freeze
#
# A CSV::Row is part Array and part Hash. It retains an order for the fields
@@ -188,9 +230,9 @@
# handle extra headers or fields
@row = if headers.size > fields.size
- headers.each_with_index.map { |header, i| [header, fields[i]] }
+ headers.zip(fields)
else
- fields.each_with_index.map { |field, i| [headers[i], field] }
+ fields.zip(headers).map { |pair| pair.reverse }
end
end
@@ -444,6 +486,24 @@
fields.to_csv(options)
end
alias_method :to_s, :to_csv
+
+ # A summary of fields, by header, in an ASCII compatible String.
+ def inspect
+ str = ["#<", self.class.to_s]
+ each do |header, field|
+ str << " " << (header.is_a?(Symbol) ? header.to_s : header.inspect) <<
+ ":" << field.inspect
+ end
+ str << ">"
+ begin
+ str.join
+ rescue # any encoding error
+ str.map do |s|
+ e = Encoding::Converter.asciicompat_encoding(s.encoding)
+ e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
+ end.join
+ end
+ end
end
#
@@ -775,6 +835,11 @@
end.join
end
alias_method :to_s, :to_csv
+
+ # Shows the mode and size of this table in a US-ASCII String.
+ def inspect
+ "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
+ end
end
# The error thrown when the parser encounters illegal CSV formatting.
@@ -799,6 +864,10 @@
DateTimeMatcher =
/ \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} |
\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} )\z /x
+
+ # The encoding used by all converters.
+ ConverterEncoding = Encoding.find("UTF-8")
+
#
# This Hash holds the built-in converters of CSV that can be accessed by name.
# You can select Converters with CSV.convert() or through the +options+ Hash
@@ -813,22 +882,40 @@
# <b><tt>:all</tt></b>:: All built-in converters. A combination of
# <tt>:date_time</tt> and <tt>:numeric</tt>.
#
- # This Hash is intetionally left unfrozen and users should feel free to add
+ # All built-in converters transcode field data to UTF-8 before attempting a
+ # conversion. If your data cannot be transcoded to UTF-8 the conversion will
+ # fail and the field will remain unchanged.
+ #
+ # This Hash is intentionally left unfrozen and users should feel free to add
# values to it that can be accessed by all CSV objects.
#
# To add a combo field, the value should be an Array of names. Combo fields
# can be nested with other combo fields.
#
- Converters = { :integer => lambda { |f| Integer(f) rescue f },
- :float => lambda { |f| Float(f) rescue f },
- :numeric => [:integer, :float],
- :date => lambda { |f|
- f =~ DateMatcher ? (Date.parse(f) rescue f) : f
+ Converters = { integer: lambda { |f|
+ Integer(f.encode(ConverterEncoding)) rescue f
},
- :date_time => lambda { |f|
- f =~ DateTimeMatcher ? (DateTime.parse(f) rescue f) : f
+ float: lambda { |f|
+ Float(f.encode(ConverterEncoding)) rescue f
},
- :all => [:date_time, :numeric] }
+ numeric: [:integer, :float],
+ date: lambda { |f|
+ begin
+ e = f.encode(ConverterEncoding)
+ e =~ DateMatcher ? Date.parse(e) : f
+ rescue # encoding conversion or date parse errors
+ f
+ end
+ },
+ date_time: lambda { |f|
+ begin
+ e = f.encode(ConverterEncoding)
+ e =~ DateTimeMatcher ? DateTime.parse(e) : f
+ rescue # encoding conversion or date parse errors
+ f
+ end
+ },
+ all: [:date_time, :numeric] }
#
# This Hash holds the built-in header converters of CSV that can be accessed
@@ -840,6 +927,10 @@
# replaced with underscores, non-word characters
# are dropped, and finally to_sym() is called.
#
+ # All built-in header converters transcode header data to UTF-8 before
+ # attempting a conversion. If your data cannot be transcoded to UTF-8 the
+ # conversion will fail and the header will remain unchanged.
+ #
# This Hash is intetionally left unfrozen and users should feel free to add
# values to it that can be accessed by all CSV objects.
#
@@ -847,9 +938,10 @@
# can be nested with other combo fields.
#
HeaderConverters = {
- :downcase => lambda { |h| h.downcase },
- :symbol => lambda { |h|
- h.downcase.tr(" ", "_").delete("^a-z0-9_").to_sym
+ downcase: lambda { |h| h.encode(ConverterEncoding).downcase },
+ symbol: lambda { |h|
+ h.encode(ConverterEncoding).downcase.gsub(/\s+/, "_").
+ gsub(/\W+/, "").to_sym
}
}
@@ -859,6 +951,7 @@
# <b><tt>:col_sep</tt></b>:: <tt>","</tt>
# <b><tt>:row_sep</tt></b>:: <tt>:auto</tt>
# <b><tt>:quote_char</tt></b>:: <tt>'"'</tt>
+ # <b><tt>:field_size_limit</tt></b>:: +nil+
# <b><tt>:converters</tt></b>:: +nil+
# <b><tt>:unconverted_fields</tt></b>:: +nil+
# <b><tt>:headers</tt></b>:: +false+
@@ -867,18 +960,44 @@
# <b><tt>:skip_blanks</tt></b>:: +false+
# <b><tt>:force_quotes</tt></b>:: +false+
#
- DEFAULT_OPTIONS = { :col_sep => ",",
- :row_sep => :auto,
- :quote_char => '"',
- :converters => nil,
- :unconverted_fields => nil,
- :headers => false,
- :return_headers => false,
- :header_converters => nil,
- :skip_blanks => false,
- :force_quotes => false }.freeze
+ DEFAULT_OPTIONS = { col_sep: ",",
+ row_sep: :auto,
+ quote_char: '"',
+ field_size_limit: nil,
+ converters: nil,
+ unconverted_fields: nil,
+ headers: false,
+ return_headers: false,
+ header_converters: nil,
+ skip_blanks: false,
+ force_quotes: false }.freeze
#
+ # This method will return a CSV instance, just like CSV::new(), but the
+ # instance will be cached and returned for all future calls to this method for
+ # the same +data+ object (tested by Object#object_id()) with the same
+ # +options+.
+ #
+ # If a block is given, the instance is passed to the block and the return
+ # value becomes the return value of the block.
+ #
+ def self.instance(data = $stdout, options = Hash.new)
+ # create a _signature_ for this method call, data object and options
+ sig = [data.object_id] +
+ options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s })
+
+ # fetch or create the instance for this signature
+ @@instances ||= Hash.new
+ instance = (@@instances[sig] ||= new(data, options))
+
+ if block_given?
+ yield instance # run block, if given, returning result
+ else
+ instance # or return the instance
+ end
+ end
+
+ #
# This method allows you to serialize an Array of Ruby objects to a String or
# File of CSV data. This is not as powerful as Marshal or YAML, but perhaps
# useful for spreadsheet and database interaction.
@@ -959,6 +1078,53 @@
end
#
+ # This method is the reading counterpart to CSV::dump(). See that method for
+ # a detailed description of the process.
+ #
+ # You can customize loading by adding a class method called csv_load() which
+ # will be passed a Hash of meta information, an Array of headers, and an Array
+ # of fields for the object the method is expected to return.
+ #
+ # Remember that all fields will be Strings after this load. If you need
+ # something else, use +options+ to setup converters or provide a custom
+ # csv_load() implementation.
+ #
+ def self.load(io_or_str, options = Hash.new)
+ csv = new(io_or_str, options)
+
+ # load meta information
+ meta = Hash[*csv.shift]
+ cls = meta["class".encode(csv.encoding)].split("::".encode(csv.encoding)).
+ inject(Object) do |c, const|
+ c.const_get(const)
+ end
+
+ # load headers
+ headers = csv.shift
+
+ # unserialize each object stored in the file
+ results = csv.inject(Array.new) do |all, row|
+ begin
+ obj = cls.csv_load(meta, headers, row)
+ rescue NoMethodError
+ obj = cls.allocate
+ headers.zip(row) do |name, value|
+ if name[0] == ?@
+ obj.instance_variable_set(name, value)
+ else
+ obj.send(name, value)
+ end
+ end
+ end
+ all << obj
+ end
+
+ csv.close unless io_or_str.is_a? String
+
+ results
+ end
+
+ #
# :call-seq:
# filter( options = Hash.new ) { |row| ... }
# filter( input, options = Hash.new ) { |row| ... }
@@ -984,7 +1150,7 @@
#
def self.filter(*args)
# parse options for input, output, or both
- in_options, out_options = Hash.new, {:row_sep => $INPUT_RECORD_SEPARATOR}
+ in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR}
if args.last.is_a? Hash
args.pop.each do |key, value|
case key.to_s
@@ -1014,10 +1180,20 @@
# pass a +path+ and any +options+ you wish to set for the read. Each row of
# file will be passed to the provided +block+ in turn.
#
- # The +options+ parameter can be anything CSV::new() understands.
+ # The +options+ parameter can be anything CSV::new() understands. This method
+ # also understands an additional <tt>:encoding</tt> parameter that you can use
+ # to specify the Encoding of the data in the file to be read. You must provide
+ # this unless your data is in Encoding::default_external(). CSV will use this
+ # to deterime how to parse the data. You may provide a second Encoding to
+ # have the data transcoded as it is read. For example,
+ # <tt>encoding: "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file
+ # but transcode it to UTF-8 before CSV parses it.
#
def self.foreach(path, options = Hash.new, &block)
- open(path, options) do |csv|
+ encoding = options.delete(:encoding)
+ mode = "rb"
+ mode << ":#{encoding}" if encoding
+ open(path, mode, options) do |csv|
csv.each(&block)
end
end
@@ -1035,7 +1211,10 @@
# Note that a passed String *is* modfied by this method. Call dup() before
# passing if you need a new String.
#
- # The +options+ parameter can be anthing CSV::new() understands.
+ # The +options+ parameter can be anthing CSV::new() understands. This method
+ # understands an additional <tt>:encoding</tt> parameter when not passed a
+ # String to set the base Encoding for the output. CSV needs this hint if you
+ # plan to output non-ASCII compatible data.
#
def self.generate(*args)
# add a default empty String, if none was given
@@ -1044,7 +1223,10 @@
io.seek(0, IO::SEEK_END)
args.unshift(io)
else
- args.unshift("")
+ encoding = args.last.is_a?(Hash) ? args.last.delete(:encoding) : nil
+ str = ""
+ str.encode!(encoding) if encoding
+ args.unshift(str)
end
csv = new(*args) # wrap
yield csv # yield for appending
@@ -1055,122 +1237,79 @@
# This method is a shortcut for converting a single row (Array) into a CSV
# String.
#
- # The +options+ parameter can be anthing CSV::new() understands.
+ # The +options+ parameter can be anthing CSV::new() understands. This method
+ # understands an additional <tt>:encoding</tt> parameter to set the base
+ # Encoding for the output. This method will try to guess your Encoding from
+ # the first non-+nil+ field in +row+, if possible, but you may need to use
+ # this parameter as a backup plan.
#
# The <tt>:row_sep</tt> +option+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>
# (<tt>$/</tt>) when calling this method.
#
def self.generate_line(row, options = Hash.new)
- options = {:row_sep => $INPUT_RECORD_SEPARATOR}.merge(options)
- (new("", options) << row).string
- end
-
- #
- # This method will return a CSV instance, just like CSV::new(), but the
- # instance will be cached and returned for all future calls to this method for
- # the same +data+ object (tested by Object#object_id()) with the same
- # +options+.
- #
- # If a block is given, the instance is passed to the block and the return
- # value becomes the return value of the block.
- #
- def self.instance(data = $stdout, options = Hash.new)
- # create a _signature_ for this method call, data object and options
- sig = [data.object_id] +
- options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s })
-
- # fetch or create the instance for this signature
- @@instances ||= Hash.new
- instance = (@@instances[sig] ||= new(data, options))
-
- if block_given?
- yield instance # run block, if given, returning result
- else
- instance # or return the instance
+ options = {row_sep: $INPUT_RECORD_SEPARATOR}.merge(options)
+ encoding = options.delete(:encoding)
+ str = ""
+ if encoding
+ str.force_encoding(encoding)
+ elsif field = row.find { |f| not f.nil? }
+ str.force_encoding(String(field).encoding)
end
+ (new(str, options) << row).string
end
#
- # This method is the reading counterpart to CSV::dump(). See that method for
- # a detailed description of the process.
- #
- # You can customize loading by adding a class method called csv_load() which
- # will be passed a Hash of meta information, an Array of headers, and an Array
- # of fields for the object the method is expected to return.
- #
- # Remember that all fields will be Strings after this load. If you need
- # something else, use +options+ to setup converters or provide a custom
- # csv_load() implementation.
- #
- def self.load(io_or_str, options = Hash.new)
- csv = new(io_or_str, options)
-
- # load meta information
- meta = Hash[*csv.shift]
- cls = meta["class"].split("::").inject(Object) do |c, const|
- c.const_get(const)
- end
-
- # load headers
- headers = csv.shift
-
- # unserialize each object stored in the file
- results = csv.inject(Array.new) do |all, row|
- begin
- obj = cls.csv_load(meta, headers, row)
- rescue NoMethodError
- obj = cls.allocate
- headers.zip(row) do |name, value|
- if name[0] == ?@
- obj.instance_variable_set(name, value)
- else
- obj.send(name, value)
- end
- end
- end
- all << obj
- end
-
- csv.close unless io_or_str.is_a? String
-
- results
- end
-
- #
# :call-seq:
- # open( filename, mode="r", options = Hash.new ) { |csv| ... }
- # open( filename, mode="r", options = Hash.new )
+ # open( filename, mode = "rb", options = Hash.new ) { |faster_csv| ... }
+ # open( filename, options = Hash.new ) { |faster_csv| ... }
+ # open( filename, mode = "rb", options = Hash.new )
+ # open( filename, options = Hash.new )
#
# This method opens an IO object, and wraps that with CSV. This is intended
# as the primary interface for writing a CSV file.
#
- # You may pass any +args+ Ruby's open() understands followed by an optional
- # Hash containing any +options+ CSV::new() understands.
+ # You must pass a +filename+ and may optionally add a +mode+ for Ruby's
+ # open(). You may also pass an optional Hash containing any +options+
+ # CSV::new() understands as the final argument.
#
# This method works like Ruby's open() call, in that it will pass a CSV object
- # to a provided block and close it when the block termminates, or it will
+ # to a provided block and close it when the block terminates, or it will
# return the CSV object when no block is provided. (*Note*: This is different
# from the Ruby 1.8 CSV library which passed rows to the block. Use
# CSV::foreach() for that behavior.)
#
- # An opened CSV object will delegate to many IO methods, for convenience. You
+ # You must provide a +mode+ with an embedded Encoding designator unless your
+ # data is in Encoding::default_external(). CSV will check the Encoding of the
+ # underlying IO object (set by the +mode+ you pass) to deterime how to parse
+ # the data. You may provide a second Encoding to have the data transcoded as
+ # it is read just as you can with a normal call to IO::open(). For example,
+ # <tt>"rb:UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file but
+ # transcode it to UTF-8 before CSV parses it.
+ #
+ # An opened CSV object will delegate to many IO methods for convenience. You
# may call:
#
# * binmode()
+ # * binmode?()
# * close()
# * close_read()
# * close_write()
# * closed?()
# * eof()
# * eof?()
+ # * external_encoding()
# * fcntl()
# * fileno()
+ # * flock()
# * flush()
# * fsync()
+ # * internal_encoding()
# * ioctl()
# * isatty()
+ # * path()
# * pid()
# * pos()
+ # * pos=()
# * reopen()
# * seek()
# * stat()
@@ -1179,11 +1318,14 @@
# * tell()
# * to_i()
# * to_io()
+ # * truncate()
# * tty?()
#
def self.open(*args)
# find the +options+ Hash
options = if args.last.is_a? Hash then args.pop else Hash.new end
+ # default to a binary open mode
+ args << "rb" if args.size == 1
# wrap a File opened with the remaining +args+
csv = new(File.open(*args), options)
@@ -1237,10 +1379,20 @@
#
# Use to slurp a CSV file into an Array of Arrays. Pass the +path+ to the
- # file and any +options+ CSV::new() understands.
+ # file and any +options+ CSV::new() understands. This method also understands
+ # an additional <tt>:encoding</tt> parameter that you can use to specify the
+ # Encoding of the data in the file to be read. You must provide this unless
+ # your data is in Encoding::default_external(). CSV will use this to deterime
+ # how to parse the data. You may provide a second Encoding to have the data
+ # transcoded as it is read. For example,
+ # <tt>encoding: "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file
+ # but transcode it to UTF-8 before CSV parses it.
#
def self.read(path, options = Hash.new)
- open(path, options) { |csv| csv.read }
+ encoding = options.delete(:encoding)
+ mode = "rb"
+ mode << ":#{encoding}" if encoding
+ open(path, mode, options) { |csv| csv.read }
end
# Alias for CSV::read().
@@ -1251,14 +1403,14 @@
#
# A shortcut for:
#
- # CSV.read( path, { :headers => true,
- # :converters => :numeric,
- # :header_converters => :symbol }.merge(options) )
+ # CSV.read( path, { headers: true,
+ # converters: :numeric,
+ # header_converters: :symbol }.merge(options) )
#
def self.table(path, options = Hash.new)
- read( path, { :headers => true,
- :converters => :numeric,
- :header_converters => :symbol }.merge(options) )
+ read( path, { headers: true,
+ converters: :numeric,
+ header_converters: :symbol }.merge(options) )
end
#
@@ -1276,6 +1428,8 @@
# Available options are:
#
# <b><tt>:col_sep</tt></b>:: The String placed between each field.
+ # This String will be transcoded into
+ # the data's Encoding before parsing.
# <b><tt>:row_sep</tt></b>:: The String appended to the end of each
# row. This can be set to the special
# <tt>:auto</tt> setting, which requests
@@ -1295,7 +1449,16 @@
# <tt>$INPUT_RECORD_SEPARATOR</tt>
# (<tt>$/</tt>) is used. Obviously,
# discovery takes a little time. Set
- # manually if speed is important.
+ # manually if speed is important. Also
+ # note that IO objects should be opened
+ # in binary mode on Windows if this
+ # feature will be used as the
+ # line-ending translation can cause
+ # problems with resetting the document
+ # position to where it was before the
+ # read ahead. This String will be
+ # transcoded into the data's Encoding
+ # before parsing.
# <b><tt>:quote_char</tt></b>:: The character used to quote fields.
# This has to be a single character
# String. This is useful for
@@ -1304,16 +1467,36 @@
# instead of the correct <tt>"</tt>.
# CSV will always consider a double
# sequence this character to be an
- # escaped quote.
+ # escaped quote. This String will be
+ # transcoded into the data's Encoding
+ # before parsing.
+ # <b><tt>:field_size_limit</tt></b>:: This is a maximum size CSV will read
+ # ahead looking for the closing quote
+ # for a field. (In truth, it reads to
+ # the first line ending beyond this
+ # size.) If a quote cannot be found
+ # within the limit CSV will raise a
+ # MalformedCSVError, assuming the data
+ # is faulty. You can use this limit to
+ # prevent what are effectively DoS
+ # attacks on the parser. However, this
+ # limit can cause a legitimate parse to
+ # fail and thus is set to +nil+, or off,
+ # by default.
# <b><tt>:converters</tt></b>:: An Array of names from the Converters
# Hash and/or lambdas that handle custom
# conversion. A single converter
- # doesn't have to be in an Array.
+ # doesn't have to be in an Array. All
+ # built-in converters try to transcode
+ # fields to UTF-8 before converting.
+ # The conversion will fail if the data
+ # cannot be transcoded, leaving the
+ # field unchanged.
# <b><tt>:unconverted_fields</tt></b>:: If set to +true+, an
# unconverted_fields() method will be
# added to all returned rows (Array or
# CSV::Row) that will return the fields
- # as they were before convertion. Note
+ # as they were before conversion. Note
# that <tt>:headers</tt> supplied by
# Array or String were not fields of the
# document and thus will have an empty
@@ -1324,11 +1507,14 @@
# headers. If set to an Array, the
# contents will be used as the headers.
# If set to a String, the String is run
- # through a call of CSV::parse_line() to
- # produce an Array of headers. This
- # setting causes CSV.shift() to return
+ # through a call of CSV::parse_line()
+ # with the same <tt>:col_sep</tt>,
+ # <tt>:row_sep</tt>, and
+ # <tt>:quote_char</tt> as this instance
+ # to produce an Array of headers. This
+ # setting causes CSV#shift() to return
# rows as CSV::Row objects instead of
- # Arrays and CSV.read() to return
+ # Arrays and CSV#read() to return
# CSV::Table objects instead of an Array
# of Arrays.
# <b><tt>:return_headers</tt></b>:: When +false+, header rows are silently
@@ -1337,10 +1523,17 @@
# with identical headers and
# fields (save that the fields do not go
# through the converters).
+ # <b><tt>:write_headers</tt></b>:: When +true+ and <tt>:headers</tt> is
+ # set, a header row will be added to the
+ # output.
# <b><tt>:header_converters</tt></b>:: Identical in functionality to
# <tt>:converters</tt> save that the
# conversions are only made to header
- # rows.
+ # rows. All built-in converters try to
+ # transcode headers to UTF-8 before
+ # converting. The conversion will fail
+ # if the data cannot be transcoded,
+ # leaving the header unchanged.
# <b><tt>:skip_blanks</tt></b>:: When set to a +true+ value, CSV will
# skip over any rows with no content.
# <b><tt>:force_quotes</tt></b>:: When set to a +true+ value, CSV will
@@ -1356,8 +1549,24 @@
options = DEFAULT_OPTIONS.merge(options)
# create the IO object we will read from
- @io = if data.is_a? String then StringIO.new(data) else data end
-
+ @io = if data.is_a? String then StringIO.new(data) else data end
+ # honor the IO encoding if we can, otherwise default to ASCII-8BIT
+ @encoding = if @io.respond_to? :internal_encoding
+ @io.internal_encoding || @io.external_encoding
+ elsif @io.is_a? StringIO
+ @io.string.encoding
+ end
+ @encoding ||= Encoding.default_internal || Encoding.default_external
+ #
+ # prepare for building safe regular expressions in the target encoding,
+ # if we can transcode the needed characters
+ #
+ @re_esc = "\\".encode(@encoding) rescue ""
+ @re_chars = %w[ \\ . [ ] - ^ $ ?
+ * + { } ( ) | #
+ \ \r \n \t \f \v ].
+ map { |s| s.encode(@encoding) rescue nil }.compact
+
init_separators(options)
init_parsers(options)
init_converters(options)
@@ -1372,6 +1581,79 @@
end
#
+ # The encoded <tt>:col_sep</tt> used in parsing and writing. See CSV::new
+ # for details.
+ #
+ attr_reader :col_sep
+ #
+ # The encoded <tt>:row_sep</tt> used in parsing and writing. See CSV::new
+ # for details.
+ #
+ attr_reader :row_sep
+ #
+ # The encoded <tt>:quote_char</tt> used in parsing and writing. See CSV::new
+ # for details.
+ #
+ attr_reader :quote_char
+ # The limit for field size, if any. See CSV::new for details.
+ attr_reader :field_size_limit
+ #
+ # Returns the current list of converters in effect. See CSV::new for details.
+ # Built-in converters will be returned by name, while others will be returned
+ # as is.
+ #
+ def converters
+ @converters.map do |converter|
+ name = Converters.rassoc(converter)
+ name ? name.first : converter
+ end
+ end
+ #
+ # Returns +true+ if unconverted_fields() to parsed results. See CSV::new
+ # for details.
+ #
+ def unconverted_fields?() @unconverted_fields end
+ #
+ # Returns +nil+ if headers will not be used, +true+ if they will but have not
+ # yet been read, or the actual headers after they have been read. See
+ # CSV::new for details.
+ #
+ def headers
+ @headers || true if @use_headers
+ end
+ #
+ # Returns +true+ if headers will be returned as a row of results.
+ # See CSV::new for details.
+ #
+ def return_headers?() @return_headers end
+ # Returns +true+ if headers are written in output. See CSV::new for details.
+ def write_headers?() @write_headers end
+ #
+ # Returns the current list of converters in effect for headers. See CSV::new
+ # for details. Built-in converters will be returned by name, while others
+ # will be returned as is.
+ #
+ def header_converters
+ @header_converters.map do |converter|
+ name = HeaderConverters.rassoc(converter)
+ name ? name.first : converter
+ end
+ end
+ #
+ # Returns +true+ blank lines are skipped by the parser. See CSV::new
+ # for details.
+ #
+ def skip_blanks?() @skip_blanks end
+ # Returns +true+ if all output fields are quoted. See CSV::new for details.
+ def force_quotes?() @force_quotes end
+
+ #
+ # The Encoding CSV is parsing or writing in. This will be the Encoding you
+ # receive parsed data in and/or the Encoding data will be written in.
+ #
+ attr_reader :encoding
+
+ #
# The line number of the last row read from this file. Fields with nested
# line-end characters will not affect this count.
#
@@ -1380,10 +1662,12 @@
### IO and StringIO Delegation ###
extend Forwardable
- def_delegators :@io, :binmode, :close, :close_read, :close_write, :closed?,
- :eof, :eof?, :fcntl, :fileno, :flush, :fsync, :ioctl,
- :isatty, :pid, :pos, :reopen, :seek, :stat, :string,
- :sync, :sync=, :tell, :to_i, :to_io, :tty?
+ def_delegators :@io, :binmode, :binmode?, :close, :close_read, :close_write,
+ :closed?, :eof, :eof?, :external_encoding, :fcntl,
+ :fileno, :flock, :flush, :fsync, :internal_encoding,
+ :ioctl, :isatty, :path, :pid, :pos, :pos=, :reopen,
+ :seek, :stat, :string, :sync, :sync=, :tell, :to_i,
+ :to_io, :truncate, :tty?
# Rewinds the underlying IO object and resets CSV's lineno() counter.
def rewind
@@ -1403,12 +1687,18 @@
# The data source must be open for writing.
#
def <<(row)
+ # make sure headers have been assigned
+ if header_row? and [Array, String].include? @use_headers.class
+ parse_headers # won't read data for Array or String
+ self << @headers if @write_headers
+ end
+
# handle CSV::Row objects and Hashes
row = case row
- when self.class::Row then row.fields
- when Hash then @headers.map { |header| row[header] }
- else row
- end
+ when self.class::Row then row.fields
+ when Hash then @headers.map { |header| row[header] }
+ else row
+ end
@headers = row if header_row?
@lineno += 1
@@ -1431,7 +1721,7 @@
#
# If you provide a block that takes one argument, it will be passed the field
# and is expected to return the converted value or the field itself. If your
- # block takes two arguments, it will also be passed a FieldInfo Struct,
+ # block takes two arguments, it will also be passed a CSV::FieldInfo Struct,
# containing details about the field. Again, the block should return a
# converted field or the field itself.
#
@@ -1445,7 +1735,7 @@
# header_convert { |field| ... }
# header_convert { |field, field_info| ... }
#
- # Identical to CSV.convert(), but for header rows.
+ # Identical to CSV#convert(), but for header rows.
#
# Note that this method must be called before header rows are read to have any
# effect.
@@ -1526,7 +1816,7 @@
# add another read to the line
(line += @io.gets(@row_sep)) rescue return nil
# copy the line so we can chop it up in parsing
- parse = line.dup
+ parse = line.dup
parse.sub!(@parsers[:line_end], "")
#
@@ -1566,7 +1856,7 @@
nil # for Ruby 1.8 CSV compatibility
else
# I decided to take a strict approach to CSV parsing...
- if $2.count("\r\n").zero? # verify correctness of field...
+ if $2.count(@parsers[:return_newline]).zero? # verify correctness
$2
else
# or throw an Exception
@@ -1603,6 +1893,10 @@
# if we're not empty?() but at eof?(), a quoted field wasn't closed...
if @io.eof?
raise MalformedCSVError, "Unclosed quoted field on line #{lineno + 1}."
+ elsif parse =~ @parsers[:bad_field]
+ raise MalformedCSVError, "Illegal quoting on line #{lineno + 1}."
+ elsif @field_size_limit and parse.length >= @field_size_limit
+ raise MalformedCSVError, "Field size exceeded on line #{lineno + 1}."
end
# otherwise, we need to loop and pull some more data to complete the row
end
@@ -1610,6 +1904,45 @@
alias_method :gets, :shift
alias_method :readline, :shift
+ #
+ # Returns a simplified description of the key FasterCSV attributes in an
+ # ASCII compatible String.
+ #
+ def inspect
+ str = ["<#", self.class.to_s, " io_type:"]
+ # show type of wrapped IO
+ if @io == $stdout then str << "$stdout"
+ elsif @io == $stdin then str << "$stdin"
+ elsif @io == $stderr then str << "$stderr"
+ else str << @io.class.to_s
+ end
+ # show IO.path(), if available
+ if @io.respond_to?(:path) and (p = @io.path)
+ str << " io_path:" << p.inspect
+ end
+ # show encoding
+ str << " encoding:" << @encoding.name
+ # show other attributes
+ %w[ lineno col_sep row_sep
+ quote_char skip_blanks ].each do |attr_name|
+ if a = instance_variable_get("@#{attr_name}")
+ str << " " << attr_name << ":" << a.inspect
+ end
+ end
+ if @use_headers
+ str << " headers:" << headers.inspect
+ end
+ str << ">"
+ begin
+ str.join
+ rescue # any encoding error
+ str.map do |s|
+ e = Encoding::Converter.asciicompat_encoding(s.encoding)
+ e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
+ end.join
+ end
+ end
+
private
#
@@ -1624,15 +1957,18 @@
#
def init_separators(options)
# store the selected separators
- @col_sep = options.delete(:col_sep)
- @row_sep = options.delete(:row_sep)
- @quote_char = options.delete(:quote_char)
+ @col_sep = options.delete(:col_sep).to_s.encode(@encoding)
+ @row_sep = options.delete(:row_sep) # encode after resolving :auto
+ @quote_char = options.delete(:quote_char).to_s.encode(@encoding)
if @quote_char.length != 1
raise ArgumentError, ":quote_char has to be a single character String"
end
+ #
# automatically discover row separator when requested
+ # (not fully encoding safe)
+ #
if @row_sep == :auto
if [ARGF, STDIN, STDOUT, STDERR].include?(@io) or
(defined?(Zlib) and @io.class == Zlib::GzipWriter)
@@ -1651,11 +1987,12 @@
end
# read ahead a bit
- sample = @io.read(1024)
- sample += @io.read(1) if sample[-1..-1] == "\r" and not @io.eof?
+ sample = read_to_char(1024)
+ sample += read_to_char(1) if sample[-1..-1] == encode_str("\r") and
+ not @io.eof?
# try to find a standard separator
- if sample =~ /\r\n?|\n/
+ if sample =~ encode_re("\r\n?|\n")
@row_sep = $&
break
end
@@ -1673,14 +2010,17 @@
end
end
end
+ @row_sep = @row_sep.to_s.encode(@encoding)
# establish quoting rules
- do_quote = lambda do |field|
+ @force_quotes = options.delete(:force_quotes)
+ do_quote = lambda do |field|
@quote_char +
String(field).gsub(@quote_char, @quote_char * 2) +
@quote_char
end
- @quote = if options.delete(:force_quotes)
+ quotable_chars = encode_str("\r\n", @col_sep, @quote_char)
+ @quote = if @force_quotes
do_quote
else
lambda do |field|
@@ -1690,7 +2030,7 @@
field = String(field) # Stringify fields
# represent empty fields as empty quoted fields
if field.empty? or
- field.count("\r\n#{@col_sep}#{@quote_char}").nonzero?
+ field.count(quotable_chars).nonzero?
do_quote.call(field)
else
field # unquoted field
@@ -1703,27 +2043,45 @@
# Pre-compiles parsers and stores them by name for access during reads.
def init_parsers(options)
# store the parser behaviors
- @skip_blanks = options.delete(:skip_blanks)
+ @skip_blanks = options.delete(:skip_blanks)
+ @field_size_limit = options.delete(:field_size_limit)
# prebuild Regexps for faster parsing
- esc_col_sep = Regexp.escape(@col_sep)
- esc_row_sep = Regexp.escape(@row_sep)
- esc_quote = Regexp.escape(@quote_char)
+ esc_col_sep = escape_re(@col_sep)
+ esc_row_sep = escape_re(@row_sep)
+ esc_quote = escape_re(@quote_char)
@parsers = {
- :leading_fields =>
- /\A(?:#{esc_col_sep})+/, # for empty leading fields
- :csv_row =>
- ### The Primary Parser ###
- / \G(?:^|#{esc_col_sep}) # anchor the match
- (?: #{esc_quote}( (?>[^#{esc_quote}]*) # find quoted fields
- (?> #{esc_quote*2}
- [^#{esc_quote}]* )* )#{esc_quote}
- | # ... or ...
- ([^#{esc_quote}#{esc_col_sep}]*) # unquoted fields
- )/x,
- ### End Primary Parser ###
- :line_end =>
- /#{esc_row_sep}\z/ # safer than chomp!()
+ # for empty leading fields
+ leading_fields: encode_re("\\A(?:", esc_col_sep, ")+"),
+ # The Primary Parser
+ csv_row: encode_re(
+ "\\G(?:\\A|", esc_col_sep, ")", # anchor the match
+ "(?:", esc_quote, # find quoted fields
+ "((?>[^", esc_quote, "]*)", # "unrolling the loop"
+ "(?>", esc_quote * 2, # double for escaping
+ "[^", esc_quote, "]*)*)",
+ esc_quote,
+ "|", # ... or ...
+ "([^", esc_quote, esc_col_sep, "]*))", # unquoted fields
+ "(?=", esc_col_sep, "|\\z)" # ensure field is ended
+ ),
+ # a test for unescaped quotes
+ bad_field: encode_re(
+ "\\A", esc_col_sep, "?", # an optional comma
+ "(?:", esc_quote, # a quoted field
+ "(?>[^", esc_quote, "]*)", # "unrolling the loop"
+ "(?>", esc_quote * 2, # double for escaping
+ "[^", esc_quote, "]*)*",
+ esc_quote, # the closing quote
+ "[^", esc_quote, "]", # an extra character
+ "|", # ... or ...
+ "[^", esc_quote, esc_col_sep, "]+", # an unquoted field
+ esc_quote, ")" # an extra quote
+ ),
+ # safer than chomp!()
+ line_end: encode_re(esc_row_sep, "\\z"),
+ # illegal unquoted characters
+ return_newline: encode_str("\r\n")
}
end
@@ -1744,7 +2102,7 @@
instance_variable_set("@#{field_name}", Array.new)
- # find the correct method to add the coverters
+ # find the correct method to add the converters
convert = method(field_name.to_s.sub(/ers\Z/, ""))
# load converters
@@ -1770,6 +2128,7 @@
def init_headers(options)
@use_headers = options.delete(:headers)
@return_headers = options.delete(:return_headers)
+ @write_headers = options.delete(:write_headers)
# headers must be delayed until shift(), in case they need a row of content
@headers = nil
@@ -1812,7 +2171,7 @@
# see if we are converting headers or fields
converters = headers ? @header_converters : @converters
- fields.each_with_index.map do |field, index| # map_with_index
+ fields.map.with_index do |field, index|
converters.each do |converter|
field = if converter.arity == 1 # straight field converter
converter[field]
@@ -1839,10 +2198,17 @@
def parse_headers(row = nil)
if @headers.nil? # header row
@headers = case @use_headers # save headers
- when Array then @use_headers # Array of headers
- when String then self.class.parse_line(@use_headers) # CSV header String
- else row # first row headers
- end
+ # Array of headers
+ when Array then @use_headers
+ # CSV header String
+ when String
+ self.class.parse_line( @use_headers,
+ col_sep: @col_sep,
+ row_sep: @row_sep,
+ quote_char: @quote_char )
+ # first row is headers
+ else row
+ end
# prepare converted and unconverted copies
row = @headers if row.nil?
@@ -1870,6 +2236,59 @@
row.instance_eval { @unconverted_fields = fields }
row
end
+
+ #
+ # This method is an encoding safe version of Regexp::escape(). I will escape
+ # any characters that would change the meaning of a regular expression in the
+ # encoding of +str+. Regular expression characters that cannot be transcoded
+ # to the target encodign will be skipped and no escaping will be performed if
+ # a backslash cannot be transcoded.
+ #
+ def escape_re(str)
+ str.chars.map { |c| @re_chars.include?(c) ? @re_esc + c : c }.join
+ end
+
+ #
+ # Builds a regular expression in <tt>@encoding</tt>. All +chunks+ will be
+ # transcoded to that encoding.
+ #
+ def encode_re(*chunks)
+ Regexp.new(encode_str(*chunks))
+ end
+
+ #
+ # Builds a String in <tt>@encoding</tt>. All +chunks+ will be transcoded to
+ # that encoding.
+ #
+ def encode_str(*chunks)
+ chunks.map { |chunk| chunk.encode(@encoding.name) }.join
+ end
+
+ #
+ # Reads at least +bytes+ from <tt>@io</tt>, but will read up 10 bytes ahead if
+ # needed to ensure the data read is valid in the ecoding of that data. This
+ # should ensure that it is safe to use regular expressions on the read data,
+ # unless it is actually a broken encoding. The read data will be returned in
+ # <tt>@encoding</tt>.
+ #
+ def read_to_char(bytes)
+ return "" if @io.eof?
+ data = @io.read(bytes)
+ begin
+ encoded = encode_str(data)
+ raise unless encoded.valid_encoding?
+ return encoded
+ rescue # encoding error or my invalid data raise
+ if @io.eof? or data.size >= bytes + 10
+ return data
+ else
+ data += @io.read(1) until data.valid_encoding? or
+ @io.eof? or
+ data.size >= bytes + 10
+ retry
+ end
+ end
+ end
end
# Another name for CSV::instance().
Modified: MacRuby/branches/experimental/lib/date/format.rb
===================================================================
--- MacRuby/branches/experimental/lib/date/format.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/date/format.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,8 +1,6 @@
# format.rb: Written by Tadayoshi Funaba 1999-2008
# $Id: format.rb,v 2.43 2008-01-17 20:16:31+09 tadf Exp $
-require 'rational'
-
class Date
module Format # :nodoc:
@@ -257,13 +255,19 @@
when 'j'; emit_n(yday, 3, f)
when 'k'; emit_a(hour, 2, f)
when 'L'
- emit_n((sec_fraction / MILLISECONDS_IN_SECOND).floor, 3, f)
+ f[:p] = nil
+ w = f[:w] || 3
+ u = 10**w
+ emit_n((sec_fraction * u).floor, w, f)
when 'l'; emit_a((hour % 12).nonzero? || 12, 2, f)
when 'M', 'OM'; emit_n(min, 2, f)
when 'm', 'Om'; emit_n(mon, 2, f)
when 'N'
- emit_n((sec_fraction / NANOSECONDS_IN_SECOND).floor, 9, f)
- when 'n'; "\n"
+ f[:p] = nil
+ w = f[:w] || 9
+ u = 10**w
+ emit_n((sec_fraction * u).floor, w, f)
+ when 'n'; emit_a("\n", 0, f)
when 'P'; emit_ad(strftime('%p').downcase, 0, f)
when 'p'; emit_au(if hour < 12 then 'AM' else 'PM' end, 0, f)
when 'Q'
@@ -281,7 +285,7 @@
else
emit_a(strftime('%H:%M:%S'), 0, f)
end
- when 't'; "\t"
+ when 't'; emit_a("\t", 0, f)
when 'U', 'W', 'OU', 'OW'
emit_n(if c[-1,1] == 'U' then wnum0 else wnum1 end, 2, f)
when 'u', 'Ou'; emit_n(cwday, 1, f)
@@ -665,11 +669,11 @@
private_class_method :s3e
def self._parse_day(str, e) # :nodoc:
- if str.sub!(/\b(#{Format::ABBR_DAYS.keys.join('|')})[^-\d\s]*/ino, ' ')
+ if str.sub!(/\b(#{Format::ABBR_DAYS.keys.join('|')})[^-\d\s]*/io, ' ')
e.wday = Format::ABBR_DAYS[$1.downcase]
true
=begin
- elsif str.sub!(/\b(?!\dth)(su|mo|tu|we|th|fr|sa)\b/in, ' ')
+ elsif str.sub!(/\b(?!\dth)(su|mo|tu|we|th|fr|sa)\b/i, ' ')
e.wday = %w(su mo tu we th fr sa).index($1.downcase)
true
=end
@@ -704,7 +708,7 @@
[[:alpha:]]+(?:\sdst)?\b
)
)?
- /inx,
+ /ix,
' ')
t = $1
@@ -716,7 +720,7 @@
\s*:?\s*(\d+)(?:[,.](\d+))?s?
)?
)?
- (?:\s*([ap])(?:m\b|\.m\.))?/inx
+ (?:\s*([ap])(?:m\b|\.m\.))?/ix
e.hour = $1.to_i
e.min = $2.to_i if $2
@@ -761,7 +765,7 @@
\s*
('?-?\d+(?:(?:st|nd|rd|th)\b)?)
)?
- /inox,
+ /iox,
' ') # '
s3e(e, $4, Format::ABBR_MONTHS[$2.downcase], $1,
$3 && $3[0,1].downcase == 'b')
@@ -780,7 +784,7 @@
\s*
('?-?\d+)
)?
- /inox,
+ /iox,
' ') # '
s3e(e, $4, Format::ABBR_MONTHS[$1.downcase], $2,
$3 && $3[0,1].downcase == 'b')
@@ -789,43 +793,43 @@
end
def self._parse_iso(str, e) # :nodoc:
- if str.sub!(/('?[-+]?\d+)-(\d+)-('?-?\d+)/n, ' ')
+ if str.sub!(/('?[-+]?\d+)-(\d+)-('?-?\d+)/, ' ')
s3e(e, $1, $2, $3)
true
end
end
def self._parse_iso2(str, e) # :nodoc:
- if str.sub!(/\b(\d{2}|\d{4})?-?w(\d{2})(?:-?(\d))?\b/in, ' ')
+ if str.sub!(/\b(\d{2}|\d{4})?-?w(\d{2})(?:-?(\d))?\b/i, ' ')
e.cwyear = $1.to_i if $1
e.cweek = $2.to_i
e.cwday = $3.to_i if $3
true
- elsif str.sub!(/-w-(\d)\b/in, ' ')
+ elsif str.sub!(/-w-(\d)\b/i, ' ')
e.cwday = $1.to_i
true
- elsif str.sub!(/--(\d{2})?-(\d{2})\b/n, ' ')
+ elsif str.sub!(/--(\d{2})?-(\d{2})\b/, ' ')
e.mon = $1.to_i if $1
e.mday = $2.to_i
true
- elsif str.sub!(/--(\d{2})(\d{2})?\b/n, ' ')
+ elsif str.sub!(/--(\d{2})(\d{2})?\b/, ' ')
e.mon = $1.to_i
e.mday = $2.to_i if $2
true
- elsif /[,.](\d{2}|\d{4})-\d{3}\b/n !~ str &&
- str.sub!(/\b(\d{2}|\d{4})-(\d{3})\b/n, ' ')
+ elsif /[,.](\d{2}|\d{4})-\d{3}\b/ !~ str &&
+ str.sub!(/\b(\d{2}|\d{4})-(\d{3})\b/, ' ')
e.year = $1.to_i
e.yday = $2.to_i
true
- elsif /\d-\d{3}\b/n !~ str &&
- str.sub!(/\b-(\d{3})\b/n, ' ')
+ elsif /\d-\d{3}\b/ !~ str &&
+ str.sub!(/\b-(\d{3})\b/, ' ')
e.yday = $1.to_i
true
end
end
def self._parse_jis(str, e) # :nodoc:
- if str.sub!(/\b([mtsh])(\d+)\.(\d+)\.(\d+)/in, ' ')
+ if str.sub!(/\b([mtsh])(\d+)\.(\d+)\.(\d+)/i, ' ')
era = { 'm'=>1867,
't'=>1911,
's'=>1925,
@@ -840,46 +844,46 @@
def self._parse_vms(str, e) # :nodoc:
if str.sub!(/('?-?\d+)-(#{Format::ABBR_MONTHS.keys.join('|')})[^-]*
- -('?-?\d+)/inox, ' ')
+ -('?-?\d+)/iox, ' ')
s3e(e, $3, Format::ABBR_MONTHS[$2.downcase], $1)
true
elsif str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})[^-]*
- -('?-?\d+)(?:-('?-?\d+))?/inox, ' ')
+ -('?-?\d+)(?:-('?-?\d+))?/iox, ' ')
s3e(e, $3, Format::ABBR_MONTHS[$1.downcase], $2)
true
end
end
def self._parse_sla(str, e) # :nodoc:
- if str.sub!(%r|('?-?\d+)/\s*('?\d+)(?:\D\s*('?-?\d+))?|n, ' ') # '
+ if str.sub!(%r|('?-?\d+)/\s*('?\d+)(?:\D\s*('?-?\d+))?|, ' ') # '
s3e(e, $1, $2, $3)
true
end
end
def self._parse_dot(str, e) # :nodoc:
- if str.sub!(%r|('?-?\d+)\.\s*('?\d+)\.\s*('?-?\d+)|n, ' ') # '
+ if str.sub!(%r|('?-?\d+)\.\s*('?\d+)\.\s*('?-?\d+)|, ' ') # '
s3e(e, $1, $2, $3)
true
end
end
def self._parse_year(str, e) # :nodoc:
- if str.sub!(/'(\d+)\b/n, ' ')
+ if str.sub!(/'(\d+)\b/, ' ')
e.year = $1.to_i
true
end
end
def self._parse_mon(str, e) # :nodoc:
- if str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})\S*/ino, ' ')
+ if str.sub!(/\b(#{Format::ABBR_MONTHS.keys.join('|')})\S*/io, ' ')
e.mon = Format::ABBR_MONTHS[$1.downcase]
true
end
end
def self._parse_mday(str, e) # :nodoc:
- if str.sub!(/(\d+)(st|nd|rd|th)\b/in, ' ')
+ if str.sub!(/(\d+)(st|nd|rd|th)\b/i, ' ')
e.mday = $1.to_i
true
end
@@ -904,7 +908,7 @@
\[[-+]?\d[^\]]*\]
)
)?
- /inx,
+ /ix,
' ')
case $2.size
when 2
@@ -1030,7 +1034,7 @@
e._comp = comp
- str.gsub!(/[^-+',.\/:@[:alnum:]\[\]\x80-\xff]+/n, ' ')
+ str.gsub!(/[^-+',.\/:@[:alnum:]\[\]]+/, ' ')
_parse_time(str, e) # || _parse_beat(str, e)
_parse_day(str, e)
@@ -1048,13 +1052,13 @@
_parse_mday(str, e) ||
_parse_ddd(str, e)
- if str.sub!(/\b(bc\b|bce\b|b\.c\.|b\.c\.e\.)/in, ' ')
+ if str.sub!(/\b(bc\b|bce\b|b\.c\.|b\.c\.e\.)/i, ' ')
if e.year
e.year = -e.year + 1
end
end
- if str.sub!(/\A\s*(\d{1,2})\s*\z/n, ' ')
+ if str.sub!(/\A\s*(\d{1,2})\s*\z/, ' ')
if e.hour && !e.mday
v = $1.to_i
if (1..31) === v
@@ -1096,20 +1100,20 @@
-w-\d)
(t
\d{2}:\d{2}(:\d{2}([,.]\d+)?)?
- (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/inx =~ str
+ (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/ix =~ str
_parse(str)
elsif /\A\s*(([-+]?(\d{2}|\d{4})|--)\d{2}\d{2}|
([-+]?(\d{2}|\d{4}))?\d{3}|-\d{3}|
(\d{2}|\d{4})?w\d{2}\d)
(t?
\d{2}\d{2}(\d{2}([,.]\d+)?)?
- (z|[-+]\d{2}(\d{2})?)?)?\s*\z/inx =~ str
+ (z|[-+]\d{2}(\d{2})?)?)?\s*\z/ix =~ str
_parse(str)
elsif /\A\s*(\d{2}:\d{2}(:\d{2}([,.]\d+)?)?
- (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/inx =~ str
+ (z|[-+]\d{2}(:?\d{2})?)?)?\s*\z/ix =~ str
_parse(str)
elsif /\A\s*(\d{2}\d{2}(\d{2}([,.]\d+)?)?
- (z|[-+]\d{2}(\d{2})?)?)?\s*\z/inx =~ str
+ (z|[-+]\d{2}(\d{2})?)?)?\s*\z/ix =~ str
_parse(str)
end
end
@@ -1118,7 +1122,7 @@
if /\A\s*-?\d{4}-\d{2}-\d{2} # allow minus, anyway
(t|\s)
\d{2}:\d{2}:\d{2}(\.\d+)?
- (z|[-+]\d{2}:\d{2})\s*\z/inx =~ str
+ (z|[-+]\d{2}:\d{2})\s*\z/ix =~ str
_parse(str)
end
end
@@ -1127,7 +1131,7 @@
if /\A\s*(-?\d{4,})(?:-(\d{2})(?:-(\d{2}))?)?
(?:t
(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?)?
- (z|[-+]\d{2}:\d{2})?\s*\z/inx =~ str
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
e = Format::Bag.new
e.year = $1.to_i
e.mon = $2.to_i if $2
@@ -1142,7 +1146,7 @@
end
e.to_hash
elsif /\A\s*(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?
- (z|[-+]\d{2}:\d{2})?\s*\z/inx =~ str
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
e = Format::Bag.new
e.hour = $1.to_i if $1
e.min = $2.to_i if $2
@@ -1154,7 +1158,7 @@
end
e.to_hash
elsif /\A\s*(?:--(\d{2})(?:-(\d{2}))?|---(\d{2}))
- (z|[-+]\d{2}:\d{2})?\s*\z/inx =~ str
+ (z|[-+]\d{2}:\d{2})?\s*\z/ix =~ str
e = Format::Bag.new
e.mon = $1.to_i if $1
e.mday = $2.to_i if $2
@@ -1173,7 +1177,7 @@
(?:#{Format::ABBR_MONTHS.keys.join('|')})\s+
-?(\d{2,})\s+ # allow minus, anyway
\d{2}:\d{2}(:\d{2})?\s*
- (?:[-+]\d{4}|ut|gmt|e[sd]t|c[sd]t|m[sd]t|p[sd]t|[a-ik-z])\s*\z/inox =~ str
+ (?:[-+]\d{4}|ut|gmt|e[sd]t|c[sd]t|m[sd]t|p[sd]t|[a-ik-z])\s*\z/iox =~ str
e = _parse(str, false)
if $1.size < 4
if e[:year] < 50
@@ -1194,20 +1198,20 @@
(#{Format::ABBR_MONTHS.keys.join('|')})\s+
-?\d{4}\s+ # allow minus, anyway
\d{2}:\d{2}:\d{2}\s+
- gmt\s*\z/inox =~ str
+ gmt\s*\z/iox =~ str
_rfc2822(str)
elsif /\A\s*(#{Format::DAYS.keys.join('|')})\s*,\s+
\d{2}\s*-\s*
(#{Format::ABBR_MONTHS.keys.join('|')})\s*-\s*
\d{2}\s+
\d{2}:\d{2}:\d{2}\s+
- gmt\s*\z/inox =~ str
+ gmt\s*\z/iox =~ str
_parse(str)
elsif /\A\s*(#{Format::ABBR_DAYS.keys.join('|')})\s+
(#{Format::ABBR_MONTHS.keys.join('|')})\s+
\d{1,2}\s+
\d{2}:\d{2}:\d{2}\s+
- \d{4}\s*\z/inox =~ str
+ \d{4}\s*\z/iox =~ str
_parse(str)
end
end
@@ -1216,7 +1220,7 @@
if /\A\s*[mtsh]?\d{2}\.\d{2}\.\d{2}
(t
(\d{2}:\d{2}(:\d{2}([,.]\d*)?)?
- (z|[-+]\d{2}(:?\d{2})?)?)?)?\s*\z/inx =~ str
+ (z|[-+]\d{2}(:?\d{2})?)?)?)?\s*\z/ix =~ str
if /\A\s*\d/ =~ str
_parse(str.sub(/\A\s*(\d)/, 'h\1'))
else
Modified: MacRuby/branches/experimental/lib/date.rb
===================================================================
--- MacRuby/branches/experimental/lib/date.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/date.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -193,7 +193,6 @@
#
# puts secs_to_new_year()
-require 'rational'
require 'date/format'
# Class representing a date.
@@ -1471,7 +1470,9 @@
def hash() @ajd.hash end
# Return internal object state as a programmer-readable string.
- def inspect() format('#<%s: %s,%s,%s>', self.class, @ajd, @of, @sg) end
+ def inspect
+ format('#<%s: %s (%s,%s,%s)>', self.class, to_s, @ajd, @of, @sg)
+ end
# Return the date as a human-readable string.
#
@@ -1791,12 +1792,23 @@
# Create a new Date object representing today.
#
# +sg+ specifies the Day of Calendar Reform.
- def self.today(sg=ITALY) Time.now.to_date .new_start(sg) end
+ def self.today(sg=ITALY)
+ t = Time.now
+ jd = civil_to_jd(t.year, t.mon, t.mday, sg)
+ new!(jd_to_ajd(jd, 0, 0), 0, sg)
+ end
# Create a new DateTime object representing the current time.
#
# +sg+ specifies the Day of Calendar Reform.
- def self.now (sg=ITALY) Time.now.to_datetime.new_start(sg) end
+ def self.now(sg=ITALY)
+ t = Time.now
+ jd = civil_to_jd(t.year, t.mon, t.mday, sg)
+ fr = time_to_day_fraction(t.hour, t.min, [t.sec, 59].min) +
+ Rational(t.nsec, 86400_000_000_000)
+ of = Rational(t.utc_offset, 86400)
+ new!(jd_to_ajd(jd, fr, of), of, sg)
+ end
private_class_method :now
Modified: MacRuby/branches/experimental/lib/debug.rb
===================================================================
--- MacRuby/branches/experimental/lib/debug.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/debug.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -711,9 +711,6 @@
end
@frames.shift
- when 'end'
- @frames.shift
-
when 'raise'
excn_handle(file, line, id, binding)
@@ -901,10 +898,10 @@
stdout.printf "Debug.rb\n"
stdout.printf "Emacs support available.\n\n"
+RubyVM::InstructionSequence.compile_option = {
+ trace_instruction: true
+}
set_trace_func proc { |event, file, line, id, binding, klass, *rest|
DEBUGGER__.context.trace_func event, file, line, id, binding, klass
}
-VM::InstructionSequence.compile_option = {
- trace_instruction: true
-}
end
Modified: MacRuby/branches/experimental/lib/delegate.rb
===================================================================
--- MacRuby/branches/experimental/lib/delegate.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/delegate.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -115,102 +115,87 @@
# implementation, see SimpleDelegator.
#
class Delegator
- preserved = [
- :__id__, :object_id, :__send__, :public_send, :respond_to?, :send,
- :instance_eval, :instance_exec, :extend, :initialize
- ]
- instance_methods.each do |m|
- next if preserved.include?(m)
+ [:to_s,:inspect,:=~,:!~,:===].each do |m|
undef_method m
end
- module MethodDelegation
- #
- # Pass in the _obj_ to delegate method calls to. All methods supported by
- # _obj_ will be delegated to.
- #
- def initialize(obj)
- __setobj__(obj)
- end
+ #
+ # Pass in the _obj_ to delegate method calls to. All methods supported by
+ # _obj_ will be delegated to.
+ #
+ def initialize(obj)
+ __setobj__(obj)
+ end
- # Handles the magic of delegation through \_\_getobj\_\_.
- def method_missing(m, *args, &block)
- begin
- target = self.__getobj__
- unless target.respond_to?(m)
- super(m, *args, &block)
- else
- target.__send__(m, *args, &block)
- end
- rescue Exception
- $@.delete_if{|s| %r"\A#{__FILE__}:\d+:in `method_missing'\z"o =~ s}
- ::Kernel::raise
+ # Handles the magic of delegation through \_\_getobj\_\_.
+ def method_missing(m, *args, &block)
+ begin
+ target = self.__getobj__
+ unless target.respond_to?(m)
+ super(m, *args, &block)
+ else
+ target.__send__(m, *args, &block)
end
+ rescue Exception
+ $@.delete_if{|s| %r"\A#{__FILE__}:\d+:in `method_missing'\z"o =~ s}
+ ::Kernel::raise
end
+ end
- #
- # Checks for a method provided by this the delegate object by fowarding the
- # call through \_\_getobj\_\_.
- #
- def respond_to?(m, include_private = false)
- return true if super
- return self.__getobj__.respond_to?(m, include_private)
- end
+ #
+ # Checks for a method provided by this the delegate object by fowarding the
+ # call through \_\_getobj\_\_.
+ #
+ def respond_to?(m, include_private = false)
+ return true if super
+ return self.__getobj__.respond_to?(m, include_private)
+ end
- #
- # Returns true if two objects are considered same.
- #
- def ==(obj)
- return true if obj.equal?(self)
- self.__getobj__ == obj
- end
+ #
+ # Returns true if two objects are considered same.
+ #
+ def ==(obj)
+ return true if obj.equal?(self)
+ self.__getobj__ == obj
+ end
- #
- # Returns true only if two objects are identical.
- #
- def equal?(obj)
- self.object_id == obj.object_id
- end
+ #
+ # This method must be overridden by subclasses and should return the object
+ # method calls are being delegated to.
+ #
+ def __getobj__
+ raise NotImplementedError, "need to define `__getobj__'"
+ end
- #
- # This method must be overridden by subclasses and should return the object
- # method calls are being delegated to.
- #
- def __getobj__
- raise NotImplementedError, "need to define `__getobj__'"
- end
+ #
+ # This method must be overridden by subclasses and change the object delegate
+ # to _obj_.
+ #
+ def __setobj__(obj)
+ raise NotImplementedError, "need to define `__setobj__'"
+ end
- #
- # This method must be overridden by subclasses and change the object delegate
- # to _obj_.
- #
- def __setobj__(obj)
- raise NotImplementedError, "need to define `__setobj__'"
- end
+ # Serialization support for the object returned by \_\_getobj\_\_.
+ def marshal_dump
+ __getobj__
+ end
+ # Reinitializes delegation from a serialized object.
+ def marshal_load(obj)
+ __setobj__(obj)
+ end
- # Serialization support for the object returned by \_\_getobj\_\_.
- def marshal_dump
- __getobj__
- end
- # Reinitializes delegation from a serialized object.
- def marshal_load(obj)
- __setobj__(obj)
- end
-
- # Clone support for the object returned by \_\_getobj\_\_.
- def clone
- new = super
- new.__setobj__(__getobj__.clone)
- new
- end
- # Duplication support for the object returned by \_\_getobj\_\_.
- def dup
- new = super
- new.__setobj__(__getobj__.dup)
- new
- end
+ # Clone support for the object returned by \_\_getobj\_\_.
+ def clone
+ new = super
+ new.__setobj__(__getobj__.clone)
+ new
end
- include MethodDelegation
+ # Duplication support for the object returned by \_\_getobj\_\_.
+ def dup
+ new = super
+ new.__setobj__(__getobj__.dup)
+ new
+ end
end
#
@@ -249,7 +234,7 @@
def Delegator.delegating_block(mid)
lambda do |*args, &block|
begin
- @delegate_dc_obj.__send__(mid, *args, &block)
+ __getobj__.__send__(mid, *args, &block)
rescue
re = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}:/o
$!.backtrace.delete_if {|t| re =~ t}
@@ -264,22 +249,17 @@
# your class.
#
# class MyClass < DelegateClass( ClassToDelegateTo ) # Step 1
-# def initiaize
+# def initialize
# super(obj_of_ClassToDelegateTo) # Step 2
# end
# end
#
def DelegateClass(superclass)
- klass = Class.new
+ klass = Class.new(Delegator)
methods = superclass.public_instance_methods(true)
- methods -= [
- :__id__, :object_id, :__send__, :public_send, :respond_to?, :send,
- :==, :equal?, :initialize, :method_missing, :__getobj__, :__setobj__,
- :clone, :dup, :marshal_dump, :marshal_load, :instance_eval, :instance_exec,
- :extend,
- ]
+ methods -= ::Delegator.public_instance_methods
+ methods -= [:to_s,:inspect,:=~,:!~,:===]
klass.module_eval {
- include Delegator::MethodDelegation
def __getobj__ # :nodoc:
@delegate_dc_obj
end
Modified: MacRuby/branches/experimental/lib/drb/drb.rb
===================================================================
--- MacRuby/branches/experimental/lib/drb/drb.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/drb/drb.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1208,6 +1208,7 @@
end
def alive? # :nodoc:
+ return false unless @protocol
@protocol.alive?
end
end
Modified: MacRuby/branches/experimental/lib/e2mmap.rb
===================================================================
--- MacRuby/branches/experimental/lib/e2mmap.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/e2mmap.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -105,7 +105,7 @@
# {[class, exp] => message, ...}
@MessageMap = {}
- # E2MM.def_exception(k, e, m)
+ # E2MM.def_e2message(k, e, m)
# k: class to define exception under.
# e: exception
# m: message_form
Modified: MacRuby/branches/experimental/lib/erb.rb
===================================================================
--- MacRuby/branches/experimental/lib/erb.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/erb.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -10,237 +10,255 @@
#
# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# = ERB -- Ruby Templating
-#
-# == Introduction
-#
-# ERB provides an easy to use but powerful templating system for Ruby. Using
-# ERB, actual Ruby code can be added to any plain text document for the
-# purposes of generating document information details and/or flow control.
-#
-# A very simple example is this:
-#
-# require 'erb'
-#
-# x = 42
-# template = ERB.new <<-EOF
-# The value of x is: <%= x %>
-# EOF
-# puts template.result(binding)
-#
-# <em>Prints:</em> The value of x is: 42
-#
-# More complex examples are given below.
-#
-#
-# == Recognized Tags
-#
-# ERB recognizes certain tags in the provided template and converts them based
-# on the rules below:
-#
-# <% Ruby code -- inline with output %>
-# <%= Ruby expression -- replace with result %>
-# <%# comment -- ignored -- useful in testing %>
-# % a line of Ruby code -- treated as <% line %> (optional -- see ERB.new)
-# %% replaced with % if first thing on a line and % processing is used
-# <%% or %%> -- replace with <% or %> respectively
-#
-# All other text is passed through ERB filtering unchanged.
-#
-#
-# == Options
-#
-# There are several settings you can change when you use ERB:
-# * the nature of the tags that are recognized;
-# * the value of <tt>$SAFE</tt> under which the template is run;
-# * the binding used to resolve local variables in the template.
-#
-# See the ERB.new and ERB#result methods for more detail.
-#
-#
-# == Examples
-#
-# === Plain Text
-#
-# ERB is useful for any generic templating situation. Note that in this example, we use the
-# convenient "% at start of line" tag, and we quote the template literally with
-# <tt>%q{...}</tt> to avoid trouble with the backslash.
-#
-# require "erb"
-#
-# # Create template.
-# template = %q{
-# From: James Edward Gray II <james at grayproductions.net>
-# To: <%= to %>
-# Subject: Addressing Needs
-#
-# <%= to[/\w+/] %>:
-#
-# Just wanted to send a quick note assuring that your needs are being
-# addressed.
-#
-# I want you to know that my team will keep working on the issues,
-# especially:
-#
-# <%# ignore numerous minor requests -- focus on priorities %>
-# % priorities.each do |priority|
-# * <%= priority %>
-# % end
-#
-# Thanks for your patience.
-#
-# James Edward Gray II
-# }.gsub(/^ /, '')
-#
-# message = ERB.new(template, 0, "%<>")
-#
-# # Set up template data.
-# to = "Community Spokesman <spokesman at ruby_community.org>"
-# priorities = [ "Run Ruby Quiz",
-# "Document Modules",
-# "Answer Questions on Ruby Talk" ]
-#
-# # Produce result.
-# email = message.result
-# puts email
-#
-# <i>Generates:</i>
-#
-# From: James Edward Gray II <james at grayproductions.net>
-# To: Community Spokesman <spokesman at ruby_community.org>
-# Subject: Addressing Needs
-#
-# Community:
-#
-# Just wanted to send a quick note assuring that your needs are being addressed.
-#
-# I want you to know that my team will keep working on the issues, especially:
-#
-# * Run Ruby Quiz
-# * Document Modules
-# * Answer Questions on Ruby Talk
-#
-# Thanks for your patience.
-#
-# James Edward Gray II
-#
-# === Ruby in HTML
-#
-# ERB is often used in <tt>.rhtml</tt> files (HTML with embedded Ruby). Notice the need in
-# this example to provide a special binding when the template is run, so that the instance
-# variables in the Product object can be resolved.
-#
-# require "erb"
-#
-# # Build template data class.
-# class Product
-# def initialize( code, name, desc, cost )
-# @code = code
-# @name = name
-# @desc = desc
-# @cost = cost
-#
-# @features = [ ]
-# end
-#
-# def add_feature( feature )
-# @features << feature
-# end
-#
-# # Support templating of member data.
-# def get_binding
-# binding
-# end
-#
-# # ...
-# end
-#
-# # Create template.
-# template = %{
-# <html>
-# <head><title>Ruby Toys -- <%= @name %></title></head>
-# <body>
-#
-# <h1><%= @name %> (<%= @code %>)</h1>
-# <p><%= @desc %></p>
-#
-# <ul>
-# <% @features.each do |f| %>
-# <li><b><%= f %></b></li>
-# <% end %>
-# </ul>
-#
-# <p>
-# <% if @cost < 10 %>
-# <b>Only <%= @cost %>!!!</b>
-# <% else %>
-# Call for a price, today!
-# <% end %>
-# </p>
-#
-# </body>
-# </html>
-# }.gsub(/^ /, '')
-#
-# rhtml = ERB.new(template)
-#
-# # Set up template data.
-# toy = Product.new( "TZ-1002",
-# "Rubysapien",
-# "Geek's Best Friend! Responds to Ruby commands...",
-# 999.95 )
-# toy.add_feature("Listens for verbal commands in the Ruby language!")
-# toy.add_feature("Ignores Perl, Java, and all C variants.")
-# toy.add_feature("Karate-Chop Action!!!")
-# toy.add_feature("Matz signature on left leg.")
-# toy.add_feature("Gem studded eyes... Rubies, of course!")
-#
-# # Produce result.
-# rhtml.run(toy.get_binding)
-#
-# <i>Generates (some blank lines removed):</i>
-#
-# <html>
-# <head><title>Ruby Toys -- Rubysapien</title></head>
-# <body>
-#
-# <h1>Rubysapien (TZ-1002)</h1>
-# <p>Geek's Best Friend! Responds to Ruby commands...</p>
-#
-# <ul>
-# <li><b>Listens for verbal commands in the Ruby language!</b></li>
-# <li><b>Ignores Perl, Java, and all C variants.</b></li>
-# <li><b>Karate-Chop Action!!!</b></li>
-# <li><b>Matz signature on left leg.</b></li>
-# <li><b>Gem studded eyes... Rubies, of course!</b></li>
-# </ul>
-#
-# <p>
-# Call for a price, today!
-# </p>
-#
-# </body>
-# </html>
-#
-#
-# == Notes
-#
-# There are a variety of templating solutions available in various Ruby projects:
-# * ERB's big brother, eRuby, works the same but is written in C for speed;
-# * Amrita (smart at producing HTML/XML);
-# * cs/Template (written in C for speed);
-# * RDoc, distributed with Ruby, uses its own template engine, which can be reused elsewhere;
-# * and others; search the RAA.
-#
-# Rails, the web application framework, uses ERB to create views.
-#
+=begin rdoc
+= ERB -- Ruby Templating
+
+== Introduction
+
+ERB provides an easy to use but powerful templating system for Ruby. Using
+ERB, actual Ruby code can be added to any plain text document for the
+purposes of generating document information details and/or flow control.
+
+A very simple example is this:
+
+ require 'erb'
+
+ x = 42
+ template = ERB.new <<-EOF
+ The value of x is: <%= x %>
+ EOF
+ puts template.result(binding)
+
+<em>Prints:</em> The value of x is: 42
+
+More complex examples are given below.
+
+
+== Recognized Tags
+
+ERB recognizes certain tags in the provided template and converts them based
+on the rules below:
+
+ <% Ruby code -- inline with output %>
+ <%= Ruby expression -- replace with result %>
+ <%# comment -- ignored -- useful in testing %>
+ % a line of Ruby code -- treated as <% line %> (optional -- see ERB.new)
+ %% replaced with % if first thing on a line and % processing is used
+ <%% or %%> -- replace with <% or %> respectively
+
+All other text is passed through ERB filtering unchanged.
+
+
+== Options
+
+There are several settings you can change when you use ERB:
+* the nature of the tags that are recognized;
+* the value of <tt>$SAFE</tt> under which the template is run;
+* the binding used to resolve local variables in the template.
+
+See the ERB.new and ERB#result methods for more detail.
+
+== Character encodings
+
+ERB (or ruby code generated by ERB) returns a string in the same
+character encoding as the input string. When the input string has
+a magic comment, however, it returns a string in the encoding specified
+by the magic comment.
+
+ # -*- coding: UTF-8 -*-
+ require 'erb'
+
+ template = ERB.new <<EOF
+ <%#-*- coding: Big5 -*-%>
+ \_\_ENCODING\_\_ is <%= \_\_ENCODING\_\_ %>.
+ EOF
+ puts template.result
+
+<em>Prints:</em> \_\_ENCODING\_\_ is Big5.
+
+
+== Examples
+
+=== Plain Text
+
+ERB is useful for any generic templating situation. Note that in this example, we use the
+convenient "% at start of line" tag, and we quote the template literally with
+<tt>%q{...}</tt> to avoid trouble with the backslash.
+
+ require "erb"
+
+ # Create template.
+ template = %q{
+ From: James Edward Gray II <james at grayproductions.net>
+ To: <%= to %>
+ Subject: Addressing Needs
+
+ <%= to[/\w+/] %>:
+
+ Just wanted to send a quick note assuring that your needs are being
+ addressed.
+
+ I want you to know that my team will keep working on the issues,
+ especially:
+
+ <%# ignore numerous minor requests -- focus on priorities %>
+ % priorities.each do |priority|
+ * <%= priority %>
+ % end
+
+ Thanks for your patience.
+
+ James Edward Gray II
+ }.gsub(/^ /, '')
+
+ message = ERB.new(template, 0, "%<>")
+
+ # Set up template data.
+ to = "Community Spokesman <spokesman at ruby_community.org>"
+ priorities = [ "Run Ruby Quiz",
+ "Document Modules",
+ "Answer Questions on Ruby Talk" ]
+
+ # Produce result.
+ email = message.result
+ puts email
+
+<i>Generates:</i>
+
+ From: James Edward Gray II <james at grayproductions.net>
+ To: Community Spokesman <spokesman at ruby_community.org>
+ Subject: Addressing Needs
+
+ Community:
+
+ Just wanted to send a quick note assuring that your needs are being addressed.
+
+ I want you to know that my team will keep working on the issues, especially:
+
+ * Run Ruby Quiz
+ * Document Modules
+ * Answer Questions on Ruby Talk
+
+ Thanks for your patience.
+
+ James Edward Gray II
+
+=== Ruby in HTML
+
+ERB is often used in <tt>.rhtml</tt> files (HTML with embedded Ruby). Notice the need in
+this example to provide a special binding when the template is run, so that the instance
+variables in the Product object can be resolved.
+
+ require "erb"
+
+ # Build template data class.
+ class Product
+ def initialize( code, name, desc, cost )
+ @code = code
+ @name = name
+ @desc = desc
+ @cost = cost
+
+ @features = [ ]
+ end
+
+ def add_feature( feature )
+ @features << feature
+ end
+
+ # Support templating of member data.
+ def get_binding
+ binding
+ end
+
+ # ...
+ end
+
+ # Create template.
+ template = %{
+ <html>
+ <head><title>Ruby Toys -- <%= @name %></title></head>
+ <body>
+
+ <h1><%= @name %> (<%= @code %>)</h1>
+ <p><%= @desc %></p>
+
+ <ul>
+ <% @features.each do |f| %>
+ <li><b><%= f %></b></li>
+ <% end %>
+ </ul>
+
+ <p>
+ <% if @cost < 10 %>
+ <b>Only <%= @cost %>!!!</b>
+ <% else %>
+ Call for a price, today!
+ <% end %>
+ </p>
+
+ </body>
+ </html>
+ }.gsub(/^ /, '')
+
+ rhtml = ERB.new(template)
+
+ # Set up template data.
+ toy = Product.new( "TZ-1002",
+ "Rubysapien",
+ "Geek's Best Friend! Responds to Ruby commands...",
+ 999.95 )
+ toy.add_feature("Listens for verbal commands in the Ruby language!")
+ toy.add_feature("Ignores Perl, Java, and all C variants.")
+ toy.add_feature("Karate-Chop Action!!!")
+ toy.add_feature("Matz signature on left leg.")
+ toy.add_feature("Gem studded eyes... Rubies, of course!")
+
+ # Produce result.
+ rhtml.run(toy.get_binding)
+
+<i>Generates (some blank lines removed):</i>
+
+ <html>
+ <head><title>Ruby Toys -- Rubysapien</title></head>
+ <body>
+
+ <h1>Rubysapien (TZ-1002)</h1>
+ <p>Geek's Best Friend! Responds to Ruby commands...</p>
+
+ <ul>
+ <li><b>Listens for verbal commands in the Ruby language!</b></li>
+ <li><b>Ignores Perl, Java, and all C variants.</b></li>
+ <li><b>Karate-Chop Action!!!</b></li>
+ <li><b>Matz signature on left leg.</b></li>
+ <li><b>Gem studded eyes... Rubies, of course!</b></li>
+ </ul>
+
+ <p>
+ Call for a price, today!
+ </p>
+
+ </body>
+ </html>
+
+
+== Notes
+
+There are a variety of templating solutions available in various Ruby projects:
+* ERB's big brother, eRuby, works the same but is written in C for speed;
+* Amrita (smart at producing HTML/XML);
+* cs/Template (written in C for speed);
+* RDoc, distributed with Ruby, uses its own template engine, which can be reused elsewhere;
+* and others; search the RAA.
+
+Rails, the web application framework, uses ERB to create views.
+=end
class ERB
- Revision = '$Date:: 2008-06-02 00:15:12 -0700#$' #'
+ Revision = '$Date:: 2009-01-17 07:20:08 -0500#$' #'
# Returns revision information for the erb.rb module.
def self.version
- "erb.rb [2.0.4 #{ERB::Revision.split[1]}]"
+ "erb.rb [2.1.0 #{ERB::Revision.split[1]}]"
end
end
@@ -254,11 +272,13 @@
end
attr_reader :value
alias :to_s :value
+
+ def empty?
+ @value.empty?
+ end
end
class Scanner # :nodoc:
- SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
-
@scanner_map = {}
def self.regist_scanner(klass, trim_mode, percent)
@scanner_map[[trim_mode, percent]] = klass
@@ -283,8 +303,6 @@
end
class TrimScanner < Scanner # :nodoc:
- TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/
-
def initialize(src, trim_mode, percent)
super
@trim_mode = trim_mode
@@ -308,9 +326,7 @@
percent_line(line, &block)
end
else
- @src.each_line do |line|
- @scan_line.call(line, &block)
- end
+ @scan_line.call(@src, &block)
end
nil
end
@@ -329,57 +345,66 @@
end
def scan_line(line)
- line.split(SplitRegexp).each do |token|
- next if token.empty?
- yield(token)
+ line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ yield(token)
+ end
end
end
def trim_line1(line)
- line.split(TrimSplitRegexp).each do |token|
- next if token.empty?
- if token == "%>\n"
- yield('%>')
- yield(:cr)
- break
- end
- yield(token)
+ line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ if token == "%>\n"
+ yield('%>')
+ yield(:cr)
+ else
+ yield(token)
+ end
+ end
end
end
def trim_line2(line)
head = nil
- line.split(TrimSplitRegexp).each do |token|
- next if token.empty?
- head = token unless head
- if token == "%>\n"
- yield('%>')
- if is_erb_stag?(head)
- yield(:cr)
- else
- yield("\n")
- end
- break
- end
- yield(token)
+ line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ head = token unless head
+ if token == "%>\n"
+ yield('%>')
+ if is_erb_stag?(head)
+ yield(:cr)
+ else
+ yield("\n")
+ end
+ head = nil
+ else
+ yield(token)
+ head = nil if token == "\n"
+ end
+ end
end
end
- ExplicitTrimRegexp = /(^[ \t]*<%-)|(-%>\n?\z)|(<%-)|(-%>)|(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
def explicit_trim_line(line)
- line.split(ExplicitTrimRegexp).each do |token|
- next if token.empty?
- if @stag.nil? && /[ \t]*<%-/ =~ token
- yield('<%')
- elsif @stag && /-%>\n/ =~ token
- yield('%>')
- yield(:cr)
- elsif @stag && token == '-%>'
- yield('%>')
- else
- yield(token)
- end
- end
+ line.scan(/(.*?)(^[ \t]*<%\-|<%\-|<%%|%%>|<%=|<%#|<%|-%>\n|-%>|%>|\z)/m) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ if @stag.nil? && /[ \t]*<%-/ =~ token
+ yield('<%')
+ elsif @stag && token == "-%>\n"
+ yield('%>')
+ yield(:cr)
+ elsif @stag && token == '-%>'
+ yield('%>')
+ else
+ yield(token)
+ end
+ end
+ end
end
ERB_STAG = %w(<%= <%# <%)
@@ -392,11 +417,11 @@
class SimpleScanner < Scanner # :nodoc:
def scan
- @src.each_line do |line|
- line.split(SplitRegexp).each do |token|
- next if token.empty?
- yield(token)
- end
+ @src.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens|
+ tokens.each do |token|
+ next if token.empty?
+ yield(token)
+ end
end
end
end
@@ -407,75 +432,35 @@
require 'strscan'
class SimpleScanner2 < Scanner # :nodoc:
def scan
- stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
- etag_reg = /(.*?)(%%>|%>|\n|\z)/
+ stag_reg = /(.*?)(<%%|<%=|<%#|<%|\z)/m
+ etag_reg = /(.*?)(%%>|%>|\z)/m
scanner = StringScanner.new(@src)
while ! scanner.eos?
scanner.scan(@stag ? etag_reg : stag_reg)
- text = scanner[1]
- elem = scanner[2]
- yield(text) unless text.empty?
- yield(elem) unless elem.empty?
+ yield(scanner[1])
+ yield(scanner[2])
end
end
end
Scanner.regist_scanner(SimpleScanner2, nil, false)
- class PercentScanner < Scanner # :nodoc:
+ class ExplicitScanner < Scanner # :nodoc:
def scan
- new_line = true
- stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
- etag_reg = /(.*?)(%%>|%>|\n|\z)/
+ stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m
+ etag_reg = /(.*?)(%%>|-%>|%>|\z)/m
scanner = StringScanner.new(@src)
while ! scanner.eos?
- if new_line && @stag.nil?
- if scanner.scan(/%%/)
- yield('%')
- new_line = false
- next
- elsif scanner.scan(/%/)
- yield(PercentLine.new(scanner.scan(/.*?(\n|\z)/).chomp))
- next
- end
- end
scanner.scan(@stag ? etag_reg : stag_reg)
- text = scanner[1]
- elem = scanner[2]
- yield(text) unless text.empty?
- yield(elem) unless elem.empty?
- new_line = (elem == "\n")
- end
- end
- end
- Scanner.regist_scanner(PercentScanner, nil, true)
+ yield(scanner[1])
- class ExplicitScanner < Scanner # :nodoc:
- def scan
- new_line = true
- stag_reg = /(.*?)(<%%|<%=|<%#|<%-|<%|\n|\z)/
- etag_reg = /(.*?)(%%>|-%>|%>|\n|\z)/
- scanner = StringScanner.new(@src)
- while ! scanner.eos?
- if new_line && @stag.nil? && scanner.scan(/[ \t]*<%-/)
- yield('<%')
- new_line = false
- next
- end
- scanner.scan(@stag ? etag_reg : stag_reg)
- text = scanner[1]
elem = scanner[2]
- new_line = (elem == "\n")
- yield(text) unless text.empty?
- if elem == '-%>'
+ if /[ \t]*<%-/ =~ elem
+ yield('<%')
+ elsif elem == '-%>'
yield('%>')
- if scanner.scan(/(\n|\z)/)
- yield(:cr)
- new_line = true
- end
- elsif elem == '<%-'
- yield('<%')
+ yield(:cr) if scanner.scan(/(\n|\z)/)
else
- yield(elem) unless elem.empty?
+ yield(elem)
end
end
end
@@ -486,10 +471,10 @@
end
class Buffer # :nodoc:
- def initialize(compiler)
+ def initialize(compiler, enc=nil)
@compiler = compiler
@line = []
- @script = ""
+ @script = enc ? "#coding:#{enc.to_s}\n" : ""
@compiler.pre_cmd.each do |x|
push(x)
end
@@ -516,16 +501,31 @@
end
end
+ def content_dump(s)
+ n = s.count("\n")
+ if n > 0
+ s.dump + "\n" * n
+ else
+ s.dump
+ end
+ end
+
def compile(s)
- out = Buffer.new(self)
+ enc = s.encoding
+ raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
+ s = s.dup.force_encoding("ASCII-8BIT") # don't use constant Enoding::ASCII_8BIT for miniruby
+ enc = detect_magic_comment(s) || enc
+ out = Buffer.new(self, enc)
content = ''
scanner = make_scanner(s)
scanner.scan do |token|
+ next if token.nil?
+ next if token == ''
if scanner.stag.nil?
case token
when PercentLine
- out.push("#{@put_cmd} #{content.dump}") if content.size > 0
+ out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0
content = ''
out.push(token.to_s)
out.cr
@@ -533,12 +533,11 @@
out.cr
when '<%', '<%=', '<%#'
scanner.stag = token
- out.push("#{@put_cmd} #{content.dump}") if content.size > 0
+ out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0
content = ''
when "\n"
content << "\n"
- out.push("#{@put_cmd} #{content.dump}")
- out.cr
+ out.push("#{@put_cmd} #{content_dump(content)}")
content = ''
when '<%%'
content << '<%'
@@ -560,7 +559,7 @@
when '<%='
out.push("#{@insert_cmd}((#{content}).to_s)")
when '<%#'
- # out.push("# #{content.dump}")
+ # out.push("# #{content_dump(content)}")
end
scanner.stag = nil
content = ''
@@ -571,9 +570,9 @@
end
end
end
- out.push("#{@put_cmd} #{content.dump}") if content.size > 0
+ out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0
out.close
- out.script
+ return out.script, enc
end
def prepare_trim_mode(mode)
@@ -613,6 +612,18 @@
end
attr_reader :percent, :trim_mode
attr_accessor :put_cmd, :insert_cmd, :pre_cmd, :post_cmd
+
+ private
+ def detect_magic_comment(s)
+ if /\A<%#(.*)%>/ =~ s or (@percent and /\A%#(.*)/ =~ s)
+ comment = $1
+ comment = $1 if comment[/-\*-\s*(.*?)\s*-*-$/]
+ if %r"coding\s*[=:]\s*([[:alnum:]\-_]+)" =~ comment
+ enc = $1.sub(/-(?:mac|dos|unix)/i, '')
+ enc = Encoding.find(enc)
+ end
+ end
+ end
end
end
@@ -658,7 +669,7 @@
#
# def build
# b = binding
- # # create and run templates, filling member data variebles
+ # # create and run templates, filling member data variables
# ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "", "@product").result b
# <%= PRODUCT[:name] %>
# <%= PRODUCT[:desc] %>
@@ -688,7 +699,7 @@
@safe_level = safe_level
compiler = ERB::Compiler.new(trim_mode)
set_eoutvar(compiler, eoutvar)
- @src = compiler.compile(str)
+ @src, @enc = *compiler.compile(str)
@filename = nil
end
@@ -714,7 +725,7 @@
compiler.pre_cmd = cmd
cmd = []
- cmd.push(eoutvar)
+ cmd.push("#{eoutvar}.force_encoding(__ENCODING__)")
compiler.post_cmd = cmd
end
@@ -734,29 +745,62 @@
#
def result(b=TOPLEVEL_BINDING)
if @safe_level
- th = Thread.start {
+ proc {
$SAFE = @safe_level
- eval(@src, b, (@filename || '(erb)'), 1)
- }
- return th.value
+ eval(@src, b, (@filename || '(erb)'), 0)
+ }.call
else
- return eval(@src, b, (@filename || '(erb)'), 1)
+ eval(@src, b, (@filename || '(erb)'), 0)
end
end
- def def_method(mod, methodname, fname='(ERB)') # :nodoc:
- mod.module_eval("def #{methodname}\n" + self.src + "\nend\n", fname, 0)
+ # Define _methodname_ as instance method of _mod_ from compiled ruby source.
+ #
+ # example:
+ # filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml
+ # erb = ERB.new(File.read(filename))
+ # erb.def_method(MyClass, 'render(arg1, arg2)', filename)
+ # print MyClass.new.render('foo', 123)
+ def def_method(mod, methodname, fname='(ERB)')
+ src = self.src
+ magic_comment = "#coding:#{@enc}\n"
+ mod.module_eval do
+ eval(magic_comment + "def #{methodname}\n" + src + "\nend\n", binding, fname, -2)
+ end
end
- def def_module(methodname='erb') # :nodoc:
+ # Create unnamed module, define _methodname_ as instance method of it, and return it.
+ #
+ # example:
+ # filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml
+ # erb = ERB.new(File.read(filename))
+ # erb.filename = filename
+ # MyModule = erb.def_module('render(arg1, arg2)')
+ # class MyClass
+ # include MyModule
+ # end
+ def def_module(methodname='erb')
mod = Module.new
- def_method(mod, methodname)
+ def_method(mod, methodname, @filename || '(ERB)')
mod
end
- def def_class(superklass=Object, methodname='result') # :nodoc:
+ # Define unnamed class which has _methodname_ as instance method, and return it.
+ #
+ # example:
+ # class MyClass_
+ # def initialize(arg1, arg2)
+ # @arg1 = arg1; @arg2 = arg2
+ # end
+ # end
+ # filename = 'example.rhtml' # @arg1 and @arg2 are used in example.rhtml
+ # erb = ERB.new(File.read(filename))
+ # erb.filename = filename
+ # MyClass = erb.def_class(MyClass_, 'render()')
+ # print MyClass.new('foo', 123).render()
+ def def_class(superklass=Object, methodname='result')
cls = Class.new(superklass)
- def_method(cls, methodname)
+ def_method(cls, methodname, @filename || '(ERB)')
cls
end
end
@@ -812,15 +856,45 @@
#--
# ERB::DefMethod
class ERB
- module DefMethod # :nodoc:
+ # Utility module to define eRuby script as instance method.
+ #
+ # === Example
+ #
+ # example.rhtml:
+ # <% for item in @items %>
+ # <b><%= item %></b>
+ # <% end %>
+ #
+ # example.rb:
+ # require 'erb'
+ # class MyClass
+ # extend ERB::DefMethod
+ # def_erb_method('render()', 'example.rhtml')
+ # def initialize(items)
+ # @items = items
+ # end
+ # end
+ # print MyClass.new([10,20,30]).render()
+ #
+ # result:
+ #
+ # <b>10</b>
+ #
+ # <b>20</b>
+ #
+ # <b>30</b>
+ #
+ module DefMethod
public
- def def_erb_method(methodname, erb)
- if erb.kind_of? String
- fname = erb
- File.open(fname) {|f| erb = ERB.new(f.read) }
- erb.def_method(self, methodname, fname)
+ # define _methodname_ as instance method of current module, using ERB object or eRuby file
+ def def_erb_method(methodname, erb_or_fname)
+ if erb_or_fname.kind_of? String
+ fname = erb_or_fname
+ erb = ERB.new(File.read(fname))
+ erb.def_method(self, methodname, fname)
else
- erb.def_method(self, methodname)
+ erb = erb_or_fname
+ erb.def_method(self, methodname, erb.filename || '(ERB)')
end
end
module_function :def_erb_method
Modified: MacRuby/branches/experimental/lib/fileutils.rb
===================================================================
--- MacRuby/branches/experimental/lib/fileutils.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/fileutils.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -258,15 +258,24 @@
def rmdir(list, options = {})
fu_check_options options, OPT_TABLE['rmdir']
list = fu_list(list)
- fu_output_message "rmdir #{list.join ' '}" if options[:verbose]
+ parents = options[:parents]
+ fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if options[:verbose]
return if options[:noop]
list.each do |dir|
- Dir.rmdir dir.sub(%r</\z>, '')
+ begin
+ Dir.rmdir(dir = dir.sub(%r</\z>, ''))
+ if parents
+ until (parent = File.dirname(dir)) == '.' or parent == dir
+ Dir.rmdir(dir)
+ end
+ end
+ rescue Errno::ENOTEMPTY, Errno::ENOENT
+ end
end
end
module_function :rmdir
- OPT_TABLE['rmdir'] = [:noop, :verbose]
+ OPT_TABLE['rmdir'] = [:parents, :noop, :verbose]
#
# Options: force noop verbose
@@ -470,7 +479,7 @@
# +dest+ must respond to #write(str).
#
def copy_stream(src, dest)
- fu_copy_stream0 src, dest, fu_stream_blksize(src, dest)
+ IO.copy_stream(src, dest)
end
module_function :copy_stream
@@ -524,7 +533,7 @@
OPT_TABLE['move'] = [:force, :noop, :verbose, :secure]
def rename_cannot_overwrite_file? #:nodoc:
- /djgpp|cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+ /cygwin|mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
end
private_module_function :rename_cannot_overwrite_file?
@@ -1041,14 +1050,11 @@
private
def fu_windows?
- /mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+ /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
end
- def fu_copy_stream0(src, dest, blksize) #:nodoc:
- # FIXME: readpartial?
- while s = src.read(blksize)
- dest.write s
- end
+ def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
+ IO.copy_stream(src, dest)
end
def fu_stream_blksize(*streams)
@@ -1254,12 +1260,7 @@
end
def copy_file(dest)
- st = stat()
- File.open(path(), 'rb') {|r|
- File.open(dest, 'wb', st.mode) {|w|
- fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize())
- }
- }
+ IO.copy_stream(path(), dest)
end
def copy_metadata(path)
@@ -1507,8 +1508,7 @@
end
METHODS = singleton_methods() - [:private_module_function,
- :commands, :options, :have_option?, :options_of,
- :collect_method]
+ :commands, :options, :have_option?, :options_of, :collect_method]
#
# This module has all methods of FileUtils module, but it outputs messages
Modified: MacRuby/branches/experimental/lib/find.rb
===================================================================
--- MacRuby/branches/experimental/lib/find.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/find.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -33,6 +33,8 @@
# See the +Find+ module documentation for an example.
#
def find(*paths) # :yield: path
+ block_given? or return enum_for(__method__, *paths)
+
paths.collect!{|d| raise Errno::ENOENT unless File.exist?(d); d.dup}
while file = paths.shift
catch(:prune) do
Modified: MacRuby/branches/experimental/lib/forwardable.rb
===================================================================
--- MacRuby/branches/experimental/lib/forwardable.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/forwardable.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# forwardable.rb -
# $Release Version: 1.1$
-# $Revision: 14912 $
+# $Revision: 16810 $
# by Keiju ISHITSUKA(keiju at ishitsuka.com)
# original definition by delegator.rb
# Revised by Daniel J. Berger with suggestions from Florian Gross.
@@ -38,7 +38,7 @@
# @q = [ ] # prepare delegate object
# end
#
-# # setup prefered interface, enq() and deq()...
+# # setup preferred interface, enq() and deq()...
# def_delegator :@q, :push, :enq
# def_delegator :@q, :shift, :deq
#
Modified: MacRuby/branches/experimental/lib/gserver.rb
===================================================================
--- MacRuby/branches/experimental/lib/gserver.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/gserver.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -40,7 +40,7 @@
# super(port, *args)
# end
# def serve(io)
-# io.puts(Time.now.to_i)
+# io.puts(Time.now.to_s)
# end
# end
#
Modified: MacRuby/branches/experimental/lib/hotcocoa/delegate_builder.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/delegate_builder.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/delegate_builder.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -19,6 +19,12 @@
set_delegate if required_methods.empty?
end
+ def delegate_to(object, *method_names)
+ method_names.each do |method_name|
+ control.send(method_name, &object.method(method_name)) if object.respond_to?(method_name)
+ end
+ end
+
private
def increment_method_count
Modified: MacRuby/branches/experimental/lib/hotcocoa/graphics/canvas.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/graphics/canvas.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/graphics/canvas.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -166,7 +166,7 @@
# set the current fill (given a Color object, or RGBA values)
def fill(r=0, g=0, b=0, a=1)
case r
- when Graphics::Color
+ when Color
g = r.g
b = r.b
a = r.a
Modified: MacRuby/branches/experimental/lib/hotcocoa/kernel_ext.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/kernel_ext.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/kernel_ext.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -4,7 +4,7 @@
def framework(name)
if default_framework(name)
- HotCocoa::Mappings.framework_loaded(name)
+ HotCocoa::Mappings.framework_loaded
true
else
false
Modified: MacRuby/branches/experimental/lib/hotcocoa/kvo_accessors.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/kvo_accessors.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/kvo_accessors.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,48 +1,48 @@
class Object
- def self.kvo_array(key, &b)
- key = key.to_s
- capitalized_key = key[0].capitalize + key[1..-1]
- signatures = { :size => { selector: :"countOf#{capitalized_key}", type_signature: "i@:", flip: false },
- :[] => { selector: :"objectIn#{capitalized_key}AtIndex:", type_signature: "@@:i", flip: false },
- :insert => { selector: :"insertObject:in#{capitalized_key}AtIndex:", type_signature: "v@:@i", flip: true },
- :delete_at => { selector: :"removeObjectFrom#{capitalized_key}AtIndex:", type_signature: "v@:i", flip: false }
- }
- define_methods_with_signatures(signatures, &b)
- end
+ def self.kvo_array(key, &b)
+ key = key.to_s
+ capitalized_key = key[0].capitalize + key[1..-1]
+ signatures = { :size => { selector: :"countOf#{capitalized_key}", type_signature: "i@:", flip: false },
+ :[] => { selector: :"objectIn#{capitalized_key}AtIndex:", type_signature: "@@:i", flip: false },
+ :insert => { selector: :"insertObject:in#{capitalized_key}AtIndex:", type_signature: "v@:@i", flip: true },
+ :delete_at => { selector: :"removeObjectFrom#{capitalized_key}AtIndex:", type_signature: "v@:i", flip: false }
+ }
+ define_methods_with_signatures(signatures, &b)
+ end
- def self.kvo_set(key, &b)
- key = key.to_s
- capitalized_key = key[0].capitalize + key[1..-1]
- signatures = { :add => { selector: :"add#{capitalized_key}Object:", type_signature: "v@:@", flip: false },
- :delete => { selector: :"remove#{capitalized_key}Object:", type_signature: "v@:@", flip: false},
- :merge => { selector: :"add#{capitalized_key}:", type_signature: "v@:@", flip: false },
- :subtract => { selector: :"remove#{capitalized_key}:", type_signature: "v@:@", flip: false },
- :set => { selector: :"#{key}", type_signature: "@@:", flip: false }
- }
- define_methods_with_signatures(signatures, &b)
- end
+ def self.kvo_set(key, &b)
+ key = key.to_s
+ capitalized_key = key[0].capitalize + key[1..-1]
+ signatures = { :add => { selector: :"add#{capitalized_key}Object:", type_signature: "v@:@", flip: false },
+ :delete => { selector: :"remove#{capitalized_key}Object:", type_signature: "v@:@", flip: false},
+ :merge => { selector: :"add#{capitalized_key}:", type_signature: "v@:@", flip: false },
+ :subtract => { selector: :"remove#{capitalized_key}:", type_signature: "v@:@", flip: false },
+ :set => { selector: :"#{key}", type_signature: "@@:", flip: false }
+ }
+ define_methods_with_signatures(signatures, &b)
+ end
- private
- def self.define_methods_with_signatures(signatures, &b)
- c = Module.new
- c.module_eval &b
- c.instance_methods.each do |m|
- signature = signatures[m]
- if signature
- method = c.instance_method(m)
- if signature[:flip]
- method = Proc.new { |a, b| method.bind(self).call(b, a)}
- end
- c.send(:define_method, signature[:selector], method)
- c.send(:remove_method, m)
- c.send(:method_signature, signature[:selector], signature[:type_signature])
- elsif not Module.instance_methods.include?(m)
- raise ArgumentError, "Method `#{m}' isn't a KVO accessor"
- end
- end
-
- include c
+ private
+
+ def self.define_methods_with_signatures(signatures, &b)
+ c = Module.new
+ c.module_eval &b
+ c.instance_methods.each do |m|
+ signature = signatures[m]
+ if signature
+ method = c.instance_method(m)
+ if signature[:flip]
+ method = Proc.new { |a, b| method.bind(self).call(b, a)}
+ end
+ c.send(:define_method, signature[:selector], method)
+ c.send(:remove_method, m)
+ c.send(:method_signature, signature[:selector], signature[:type_signature])
+ elsif not Module.instance_methods.include?(m)
+ raise ArgumentError, "Method `#{m}' isn't a KVO accessor"
+ end
end
+ include c
+ end
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/layout_view.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/layout_view.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/layout_view.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -221,6 +221,8 @@
class LayoutView < NSView
+ attr_accessor :frame_color
+
def initWithFrame(frame)
super
@mode = :vertical
@@ -272,6 +274,10 @@
setFrame(frame)
end
+ def size=(size)
+ setFrameSize(size)
+ end
+
def margin
@margin
end
@@ -291,7 +297,7 @@
raise ArgumentError, "#{subview} is not a subview of #{self} and cannot be removed." unless subview.superview == self
options[:needs_display] == false ? subview.removeFromSuperviewWithoutNeedingDisplay : subview.removeFromSuperview
end
-
+
def addSubview(view)
super
if view.respond_to?(:layout)
@@ -318,12 +324,12 @@
relayout!
end
- if $DEBUG
- def drawRect(frame)
- NSColor.redColor.set
- NSFrameRect(frame)
+ def drawRect(frame)
+ if @frame_color
+ @frame_color.set
+ NSFrameRect(frame)
end
- end
+ end
def setFrame(frame)
super
Modified: MacRuby/branches/experimental/lib/hotcocoa/mapper.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mapper.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mapper.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -49,15 +49,20 @@
Views[guid] = control if guid
inst.customize(control)
map.each do |key, value|
- if control.respond_to?(key) && value == true
- if control.respond_to?("set#{key.to_s.capitalize}")
- eval "control.set#{key.to_s.capitalize}(true)"
+ if control.respond_to?("#{key}=")
+ eval "control.#{key} = value"
+ elsif control.respond_to?(key)
+ new_key = (key.start_with?('set') ? key : "set#{key[0].capitalize}#{key[1..(key.length - 1)]}")
+ if control.respond_to?(new_key)
+ eval "control.#{new_key}(value)"
else
control.send("#{key}")
end
+ elsif control.respond_to?("set#{Mapper.camel_case(key.to_s)}")
+ eval "control.set#{Mapper.camel_case(key.to_s)}(value)"
else
- eval "control.#{key}= value"
- end
+ NSLog "Unable to map #{key} as a method"
+ end
end
if default_empty_rect_used
control.sizeToFit if control.respondsToSelector(:sizeToFit) == true
@@ -71,6 +76,10 @@
end
control
end
+ # make the function callable using HotCocoa.xxxx
+ HotCocoa.send(:module_function, builder_method)
+ # module_function makes the instance method private, but we want it to stay public
+ HotCocoa.send(:public, builder_method)
self
end
@@ -122,16 +131,25 @@
unless Mapper.delegate_modules.has_key?(control_class)
delegate_module = Module.new
required_methods = []
- inherited_delegate_methods.each do |delegate_method, mapping|
- required_methods << delegate_method if mapping[:required]
- end
- inherited_delegate_methods.each do |delegate_method, mapping|
- parameters = mapping[:parameters] ? ", "+mapping[:parameters].map {|param| %{"#{param}"} }.join(",") : ""
+ delegate_methods = inherited_delegate_methods
+ if delegate_methods.size > 0
+ delegate_methods.each do |delegate_method, mapping|
+ required_methods << delegate_method if mapping[:required]
+ end
+ delegate_methods.each do |delegate_method, mapping|
+ parameters = mapping[:parameters] ? ", "+mapping[:parameters].map {|param| %{"#{param}"} }.join(",") : ""
+ delegate_module.module_eval %{
+ def #{mapping[:to]}(&block)
+ raise "Must pass in a block to use this delegate method" unless block_given?
+ @_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, #{required_methods.inspect})
+ @_delegate_builder.add_delegated_method(block, "#{delegate_method}" #{parameters})
+ end
+ }
+ end
delegate_module.module_eval %{
- def #{mapping[:to]}(&block)
- raise "Must pass in a block to use this delegate method" unless block_given?
+ def delegate_to(object)
@_delegate_builder ||= HotCocoa::DelegateBuilder.new(self, #{required_methods.inspect})
- @_delegate_builder.add_delegated_method(block, "#{delegate_method}" #{parameters})
+ @_delegate_builder.delegate_to(object, #{delegate_methods.values.map {|method| ":#{method[:to]}"}.join(', ')})
end
}
end
@@ -155,7 +173,7 @@
bindings_module = Module.new
instance.exposedBindings.each do |exposed_binding|
bindings_module.module_eval %{
- def #{underscore(exposed_binding)}=(value)
+ def #{Mapper.underscore(exposed_binding)}=(value)
if value.kind_of?(Hash)
options = value.delete(:options)
bind "#{exposed_binding}", toObject:value.keys.first, withKeyPath:value.values.first, options:options
@@ -187,14 +205,23 @@
result
end
- def underscore(camel_cased_word)
- camel_cased_word.to_s.gsub(/::/, '/').
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
+ def self.underscore(string)
+ string.gsub(/::/, '/').
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
tr("-", "_").
downcase
end
+ def self.camel_case(string)
+ if string !~ /_/ && string =~ /[A-Z]+.*/
+ string
+ else
+ string.split('_').map{ |e| e.capitalize }.join
+ end
+ end
+
+
end
end
end
\ No newline at end of file
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/alert.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/alert.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/alert.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -20,4 +20,6 @@
end
+ delegating "alertShowHelp:", :to => :show_help?
+
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/application.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/application.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/application.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -64,7 +64,7 @@
def on_quit(menu)
terminate(menu)
end
-
+
private
def find_menu(menu, path)
@@ -79,5 +79,34 @@
end
end
+
+ delegating "application:delegateHandlesKey:", :to => :delegate_handles_key?, :parameters => [:delegateHandlesKey]
+ delegating "application:openFile:", :to => :open_file, :parameters => [:openFile]
+ delegating "application:openFiles:", :to => :open_files, :parameters => [:openFiles]
+ delegating "application:openFileWithoutUI:", :to => :open_file_without_ui, :parameters => [:openFileWithoutUI]
+ delegating "application:openTempFile:", :to => :open_temp_file, :parameters => [:openTempFile]
+ delegating "application:printFile:", :to => :print_file
+ delegating "application:printFiles:withSettings:showPrintPanels:", :to => :print_files
+ delegating "application:willPresentError:", :to => :will_present_error
+ delegating "applicationDidBecomeActive:", :to => :did_become_active
+ delegating "applicationDidChangeScreenParameters:", :to => :did_change_screen_parameters
+ delegating "applicationDidFinishLaunching:", :to => :did_finish_launching
+ delegating "applicationDidHide:", :to => :did_hide
+ delegating "applicationDidResignActive:", :to => :resign_active
+ delegating "applicationDidUnhide:", :to => :did_unhide
+ delegating "applicationDidUpdate:", :to => :did_update
+ delegating "applicationDockMenu:", :to => :dock_menu
+ delegating "applicationOpenUntitledFile:", :to => :open_untitled_file
+ delegating "applicationShouldHandleReopen:hasVisibleWindows:", :to => :should_handle_reopen?, :parameters => [:hasVisibleWindows]
+ delegating "applicationShouldOpenUntitledFile:", :to => :should_open_untitled_file?
+ delegating "applicationShouldTerminate:", :to => :should_terminate?
+ delegating "applicationShouldTerminateAfterLastWindowClosed:", :to => :should_terminate_after_last_window_closed?
+ delegating "applicationWillBecomeActive:", :to => :will_become_active
+ delegating "applicationWillFinishLaunching:", :to => :will_finish_launching
+ delegating "applicationWillHide:", :to => :will_hide
+ delegating "applicationWillResignActive:", :to => :will_resign_active
+ delegating "applicationWillTerminate:", :to => :will_terminate
+ delegating "applicationWillUnhide:", :to => :will_unhide
+ delegating "applicationWillUpdate:", :to => :will_update
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/array_controller.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/array_controller.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/array_controller.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -10,18 +10,10 @@
custom_methods do
- def avoids_empty_selection=(value)
- setAvoidsEmptySelection(value)
- end
-
def avoids_empty_selection?
avoidsEmptySelection
end
- def preserves_selection=(value)
- setPreservesSelection(value)
- end
-
def preserves_selection?
preservesSelection
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/box.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/box.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/box.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -26,26 +26,14 @@
custom_methods do
- def title_position=(value)
- setTitlePosition(value)
- end
-
def type=(value)
setBoxType(value)
end
-
- def corner_radius=(value)
- setCornerRadius(value)
- end
def border=(value)
setBorderType(value)
end
- def title_font=(value)
- setTitleFont(value)
- end
-
end
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/button.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/button.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/button.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -30,8 +30,7 @@
:momentary_change => NSMomentaryChangeButton,
:on_off => NSOnOffButton,
:momentary_push_in => NSMomentaryPushInButton,
- :momentary_push => NSMomentaryPushButton,
- :momentary_light => NSMomentaryLight
+ :momentary_push => NSMomentaryPushButton
}
constant :state, {
@@ -39,6 +38,16 @@
:off => NSOffState,
:mixed => NSMixedState
}
+
+ constant :image_position, {
+ :text_only => NSNoImage,
+ :image_only => NSImageOnly,
+ :overlaps => NSImageOverlaps,
+ :left => NSImageLeft,
+ :right => NSImageRight,
+ :below => NSImageBelow,
+ :above => NSImageAbove
+ }
def init_with_options(button, options)
button.initWithFrame options.delete(:frame)
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/collection_view.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/collection_view.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/collection_view.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -8,10 +8,6 @@
custom_methods do
- def item_prototype=(item)
- setItemPrototype(item)
- end
-
def item_prototype
itemPrototype
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/column.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/column.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/column.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -15,7 +15,7 @@
def title=(newTitle)
headerCell.stringValue = newTitle
end
-
+
end
end
\ No newline at end of file
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/combo_box.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/combo_box.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/combo_box.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -16,4 +16,9 @@
end
+ delegating "comboBoxSelectionDidChange:", :to => :selection_did_change
+ delegating "comboBoxSelectionIsChanging:", :to => :selection_is_changing
+ delegating "comboBoxWillDismiss:", :to => :will_dismiss
+ delegating "comboBoxWillPopUp:", :to => :will_pop_up
+
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/label.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/label.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/label.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -20,10 +20,6 @@
setAlignment(value)
end
- def text_color=(value)
- setTextColor(value)
- end
-
end
end
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/movie_view.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/movie_view.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/movie_view.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -22,10 +22,6 @@
setZoomButtonsVisible(buttons.include?(:zoom))
end
- def fill_color=(color)
- setFillColor(color)
- end
-
end
end
\ No newline at end of file
Copied: MacRuby/branches/experimental/lib/hotcocoa/mappings/progress_indicator.rb (from rev 1886, MacRuby/trunk/lib/hotcocoa/mappings/progress_indicator.rb)
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/progress_indicator.rb (rev 0)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/progress_indicator.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,68 @@
+# Cocoa class: NSProgressIndicator
+# ================================
+#
+# Apple Documentation: http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSProgressIndicator_Class/Reference/Reference.html
+#
+# Usage Example:
+# --------------
+#
+
+
+HotCocoa::Mappings.map :progress_indicator => :NSProgressIndicator do
+
+ defaults :layout => {}, :frame => [0,0,250,20]
+
+ def init_with_options(progress_bar, options)
+ progress_bar.initWithFrame(options.delete(:frame))
+ end
+
+ custom_methods do
+
+ def to_f
+ doubleValue
+ end
+ alias :value :to_f
+
+ def value=(value)
+ setDoubleValue(value.to_f)
+ end
+
+ def start
+ startAnimation(nil)
+ end
+
+ def stop
+ stopAnimation(nil)
+ end
+
+ def show
+ setHidden(false)
+ end
+
+ def hide
+ setHidden(true)
+ end
+
+ def reset
+ setDoubleValue(0.0)
+ end
+
+ def style=(style_name)
+ if style_name == :spinning
+ setStyle(NSProgressIndicatorSpinningStyle)
+ else
+ setStyle(NSProgressIndicatorBarStyle)
+ end
+ end
+
+ def spinning_style
+ setStyle(NSProgressIndicatorSpinningStyle)
+ end
+
+ def bar_style
+ setStyle(NSProgressIndicatorBarStyle)
+ end
+
+ end
+
+end
\ No newline at end of file
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/scroll_view.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/scroll_view.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/scroll_view.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -11,11 +11,7 @@
def <<(view)
setDocumentView(view)
end
-
- def document_view=(view)
- setDocumentView(view)
- end
-
+
def background=(value)
setDrawsBackground(value)
end
@@ -28,10 +24,6 @@
setHasHorizontalScroller(value)
end
- def autoresizes_subviews=(value)
- setAutoresizesSubviews(value)
- end
-
end
end
Copied: MacRuby/branches/experimental/lib/hotcocoa/mappings/search_field.rb (from rev 1886, MacRuby/trunk/lib/hotcocoa/mappings/search_field.rb)
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/search_field.rb (rev 0)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/search_field.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,9 @@
+HotCocoa::Mappings.map :search_field => :NSSearchField do
+
+ defaults :layout => {}, :frame => DefaultEmptyRect
+
+ def init_with_options(search_field, options)
+ search_field.initWithFrame options.delete(:frame)
+ end
+
+end
\ No newline at end of file
Copied: MacRuby/branches/experimental/lib/hotcocoa/mappings/sort_descriptor.rb (from rev 1886, MacRuby/trunk/lib/hotcocoa/mappings/sort_descriptor.rb)
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/sort_descriptor.rb (rev 0)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/sort_descriptor.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,13 @@
+HotCocoa::Mappings.map :sort_descriptor => :NSSortDescriptor do
+
+ defaults :ascending => true
+
+ def init_with_options(sort_descriptor, opts)
+ if opts.has_key?(:selector)
+ sort_descriptor.initWithKey(opts.delete(:key), ascending:opts.delete(:ascending))
+ else
+ sort_descriptor.initWithKey(opts.delete(:key), ascending:opts.delete(:ascending), selector:opts.delete(:selector))
+ end
+ end
+
+end
\ No newline at end of file
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/table_view.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/table_view.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/table_view.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -10,7 +10,19 @@
:last_column_only => NSTableViewLastColumnOnlyAutoresizingStyle,
:first_column_only => NSTableViewFirstColumnOnlyAutoresizingStyle
}
+
+ constant :grid_style, {
+ :none => NSTableViewGridNone,
+ :vertical => NSTableViewSolidVerticalGridLineMask,
+ :horizontal => NSTableViewSolidHorizontalGridLineMask,
+ :both => NSTableViewSolidVerticalGridLineMask | NSTableViewSolidHorizontalGridLineMask
+ }
+ constant :selection_style, {
+ :regular => NSTableViewSelectionHighlightStyleRegular,
+ :source_list => NSTableViewSelectionHighlightStyleSourceList
+ }
+
def init_with_options(table_view, options)
table_view.initWithFrame(options.delete(:frame))
end
@@ -39,7 +51,60 @@
def column_resize=(style)
setColumnAutoresizingStyle(style)
end
-
+
+ def reload
+ reloadData
+ end
+
+ def grid_style=(value)
+ setGridStyleMask(value)
+ end
+
+ def on_double_action=(behavior)
+ if target && (
+ target.instance_variable_get("@action_behavior") ||
+ target.instance_variable_get("@double_action_behavior"))
+ object.instance_variable_set("@double_action_behavior", behavior)
+ object = target
+ else
+ object = Object.new
+ object.instance_variable_set("@double_action_behavior", behavior)
+ setTarget(object)
+ end
+ def object.perform_double_action(sender)
+ @double_action_behavior.call(sender)
+ end
+ setDoubleAction("perform_double_action:")
+ end
+
+ def on_double_action(&behavior)
+ self.on_double_action = behavior
+ self
+ end
+
end
+ delegating "tableView:willDisplayCell:forTableColumn:row:", :to => :will_display_cell, :parameters => [:willDisplayCell, :forTableColumn, :row]
+ delegating "tableView:dataCellForTableColumn:row:", :to => :data_cell, :parameters => [:dataCellForTableColumn, :row]
+ delegating "tableView:shouldShowCellExpansionForTableColumn:row:", :to => :expand_cell?, :parameters => [:shouldShowCellExpansionForTableColumn, :row]
+ delegating "tableView:isGroupRow:", :to => :is_group_row?, :parameters => [:isGroupRow]
+ delegating "tableView:shouldEditTableColumn:row:", :to => :edit_table_column?, :parameters => [:shouldEditTableColumn, :row]
+ delegating "tableView:heightOfRow:", :to => :height_of_row, :parameters => [:heightOfRow]
+ delegating "selectionShouldChangeInTableView:", :to => :change_selection?
+ delegating "tableView:shouldSelectRow:", :to => :select_row?, :parameters => [:shouldSelectRow]
+ delegating "tableView:selectionIndexesForProposedSelection:", :to => :indexes_for_selection, :parameters => [:selectIndexesForProposedSelection]
+ delegating "tableView:shouldSelectTableColumn:", :to => :select_column?, :parameters => [:shouldSelectTableColumn]
+ delegating "tableViewSelectionIsChanging:", :to => :selection_changing, :parameters => [:tableViewSelectionIsChanging]
+ delegating "tableViewSelectionDidChange:", :to => :selection_changed, :parameters => [:tableViewSelectionDidChange]
+ delegating "tableView:shouldTypeSelectForEvent:withCurrentSearchString:", :to => :type_select_for_event?, :parameters => [:shouldTypeSelectForEvent, :withCurrentSearchString]
+ delegating "tableView:typeSelectStringForTableColumn:row:", :to => :type_select_string, :parameters => [:typeSelectStringForTableColumn, :row]
+ delegating "tableView:nextTypeSelectMatchFromRow:toRow:forString:", :to => :find_in_range, :parameters => [:nextTypeSelectMatchFromRow, :toRow, :forString]
+ delegating "tableView:didDragTableColumn:", :to => :dragged_column, :parameters => [:didDragTableColumn]
+ delegating "tableViewColumnDidMove:", :to => :column_moved, :parameters => [:tableViewColumnDidMove]
+ delegating "tableViewColumnDidResize:", :to => :column_resized, :parameters => [:tableViewColumnDidResize]
+ delegating "tableView:didClickTableColumn:", :to => :clicked_column, :parameters => [:didClickTableColumn]
+ delegating "tableView:mouseDownInHeaderOfTableColumn:", :to => :header_clicked, :parameters => [:mouseDownInHeaderOfTableColumn]
+ delegating "tableView:shouldTrackCell:forTableColumn:row:", :to => :track_cell?, :parameters => [:shouldTrackCell, :forTableColumn, :row]
+ delegating "tableView:toolTipForCell:rect:tableColumn:row:mouseLocation:",:to => :tooltip_for_cell, :parameters => [:toolTipForCell, :rect, :tableColumn, :row, :mouseLocation]
+
end
\ No newline at end of file
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/text_field.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/text_field.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/text_field.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,3 +1,14 @@
+# NSTextField mappings
+#
+# Usage example:
+# @field = text_field(:text => "your text here", :layout => {:start => false}, :frame => [0, 0, 300, 300])
+# @field.text = "New text"
+
+# Cocoa Documentation:
+# http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTextField_Class/Reference/Reference.html
+#
+#
+
HotCocoa::Mappings.map :text_field => :NSTextField do
constant :text_align, {
@@ -18,10 +29,6 @@
setAlignment(value)
end
- def text_color=(value)
- setTextColor(value)
- end
-
end
delegating "control:textShouldBeginEditing:", :to => :should_begin_editing?, :parameters => [:textShouldBeginEditing]
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/web_view.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/web_view.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/web_view.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -5,7 +5,7 @@
def init_with_options(web_view, options)
web_view.initWithFrame(options.delete(:frame))
end
-
+
custom_methods do
def url=(url)
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings/window.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings/window.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings/window.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,3 +1,17 @@
+# Cocoa Reference: NSWindow
+#
+# Usage example:
+# ==============
+#
+# window :frame => [100, 100, 604, 500], :title => "My app", :style => [:titled, :closable, :miniaturizable, :resizable] do |win|
+# win.contentView.margin = 0
+# win.background_color = color(:name => 'white')
+# win.will_close { exit }
+# end
+#
+# Apple Developer Connection url: http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindow_Class/Reference/Reference.html
+#
+
HotCocoa::Mappings.map :window => :NSWindow do
defaults :style => [:titled, :closable, :miniaturizable, :resizable],
@@ -32,12 +46,12 @@
styleMask:options.delete(:style),
backing:options.delete(:backing),
defer:options.delete(:defer)
+ default_layout = options.delete(:default_layout)
if options[:view] == :layout
options.delete(:view)
window.setContentView(LayoutView.alloc.initWithFrame([0,0,window.contentView.frameSize.width, window.contentView.frameSize.height]))
- window.contentView.default_layout = options.delete(:default_layout)
+ window.contentView.default_layout = default_layout
elsif options[:view] == :nolayout
- options.delete(:default_layout)
options.delete(:view)
end
window
@@ -68,22 +82,10 @@
orderFrontRegardless
end
- def background_color=(color)
- setBackgroundColor(color)
- end
-
- def background_color
- backgroundColor
- end
-
def has_shadow?
hasShadow
end
- def has_shadow=(value)
- setHasShadow(value)
- end
-
end
delegating "window:shouldDragDocumentWithEvent:from:withPasteboard:", :to => :should_drag_document?, :parameters => [:shouldDragDocumentWithEvent, :from, :withPasteboard]
Modified: MacRuby/branches/experimental/lib/hotcocoa/mappings.rb
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mappings.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/hotcocoa/mappings.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,7 +3,7 @@
def self.reload
Dir.glob(File.join(File.dirname(__FILE__), "mappings", "*.rb")).each do |mapping|
- load mapping
+ require mapping
end
end
@@ -11,12 +11,19 @@
module TargetActionConvenience
def on_action=(behavior)
- object = Object.new
- object.instance_variable_set("@behavior", behavior)
+ if target && (
+ target.instance_variable_get("@action_behavior") ||
+ target.instance_variable_get("@double_action_behavior"))
+ object.instance_variable_set("@action_behavior", behavior)
+ object = target
+ else
+ object = Object.new
+ object.instance_variable_set("@action_behavior", behavior)
+ setTarget(object)
+ end
def object.perform_action(sender)
- @behavior.call(sender)
+ @action_behavior.call(sender)
end
- setTarget(object)
setAction("perform_action:")
end
@@ -67,7 +74,7 @@
# Registers a callback for after the specified framework has been loaded.
def self.on_framework(name, &block)
- (frameworks[name.to_s.downcase] ||= []) << block
+ (frameworks[name.to_s] ||= []) << block
end
# Returns the Hash of mapped frameworks.
@@ -81,19 +88,20 @@
end
# Registers a given framework as being loaded.
- def self.framework_loaded(name)
- name = name.to_s.downcase
- loaded_frameworks << name
- if frameworks[name]
- frameworks[name].each do |mapper|
- mapper.call
+ def self.framework_loaded
+ frameworks.keys.each do |key|
+ if loaded_framework?(key)
+ frameworks[key].each do |mapper|
+ mapper.call
+ end
+ frameworks.delete(key)
end
end
end
# Returns whether or not the framework has been loaded yet.
def self.loaded_framework?(name)
- loaded_frameworks.include?(name.to_s.downcase)
+ NSBundle.allFrameworks.map {|bundle| bundle.bundlePath.split("/").last}.select {|framework| framework.split(".")[1] == 'framework'}.map {|framework| framework.split(".")[0]}.include?(name.to_s)
end
end
Copied: MacRuby/branches/experimental/lib/hotcocoa/mvc.rb (from rev 1886, MacRuby/trunk/lib/hotcocoa/mvc.rb)
===================================================================
--- MacRuby/branches/experimental/lib/hotcocoa/mvc.rb (rev 0)
+++ MacRuby/branches/experimental/lib/hotcocoa/mvc.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,175 @@
+require 'hotcocoa'
+
+class HotCocoaApplication
+ attr_accessor :shared_application, :application_controller, :controllers
+
+ include HotCocoa
+
+ def self.instance=(instance)
+ @instance = instance
+ end
+
+ def self.instance
+ @instance
+ end
+
+ def initialize(application_file)
+ HotCocoaApplication.instance = self
+ @controllers = {}
+ load_controllers_and_views(directory_of(application_file))
+ @shared_application = application(ApplicationView.options[:application])
+ @shared_application.load_application_menu
+ @application_controller = controller(:application_controller)
+ shared_application.delegate_to(application_controller)
+ end
+
+ def start
+ @shared_application.run
+ end
+
+ def controller(controller_name)
+ controller_name_string = controller_name.to_s
+ controller_class = Object.const_get(controller_name_string !~ /_/ && controller_name_string =~ /[A-Z]+.*/ ? controller_name_string : controller_name_string.split('_').map{|e| e.capitalize}.join)
+ @controllers[controller_name] || create_controller_instance(controller_name, controller_class)
+ end
+
+ private
+
+ def create_controller_instance(controller_name, controller_class)
+ controller_instance = controller_class.new(self)
+ @controllers[controller_name] = controller_instance
+ controller_instance.application_window
+ controller_instance
+ end
+
+ def directory_of(application_file)
+ File.dirname(File.expand_path(application_file))
+ end
+
+ def load_controllers_and_views(directory)
+ Dir.glob(File.join(directory, 'controllers', '**', '*.rb')).each do |controller_file|
+ load(controller_file)
+ end
+ Dir.glob(File.join(directory, 'views', '**', '*.rb')).each do |view_file|
+ load(view_file)
+ end
+ end
+
+end
+
+class HotCocoaController
+
+ def self.view_instances
+ @view_instances ||= {}
+ end
+
+ attr_reader :application
+
+ def initialize(application)
+ @application = application
+ end
+
+ def application_window
+ @application.application_controller.application_window
+ end
+
+end
+
+class HotCocoaApplicationController < HotCocoaController
+
+ def initialize(application)
+ super(application)
+ end
+
+ def application_window
+ @application_window ||= ApplicationWindow.new(self).application_window
+ end
+
+end
+
+class HotCocoaWindow
+
+ attr_reader :application_controller, :application_window
+
+ def initialize(application_controller)
+ @application_controller = application_controller
+ render
+ end
+
+ def render
+ @application_window = HotCocoa.window(ApplicationView.options[:window])
+ @application_window.delegate_to(application_controller)
+ @application_window.view << application_controller.application_view
+ end
+
+end
+
+class HotCocoaView < HotCocoa::LayoutView
+
+ DefaultLayoutOptions = {:expand => [:width, :height]}
+
+ module ClassMethods
+ def controller(name=nil)
+ if name
+ @name = name
+ else
+ @name || :application_controller
+ end
+ end
+ def options(options=nil)
+ if options
+ @options = options
+ else
+ @options
+ end
+ end
+ end
+
+ def self.inherited(klass)
+ klass.extend(ClassMethods)
+ klass.send(:include, HotCocoa::Behaviors)
+ class_name = klass.name.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
+ HotCocoaController.class_eval %{
+ def #{class_name}
+ unless HotCocoaController.view_instances[:#{class_name}]
+ HotCocoaController.view_instances[:#{class_name}] = #{klass.name}.alloc.initWithFrame([0,0,0,0])
+ HotCocoaController.view_instances[:#{class_name}].setup_view
+ end
+ HotCocoaController.view_instances[:#{class_name}]
+ end
+ }, __FILE__, __LINE__
+ end
+
+ attr_reader :controller
+
+ def setup_view
+ unless @controller
+ @controller = class_controller
+ self.layout = layout_options
+ render
+ end
+ end
+
+ private
+
+ def class_controller
+ HotCocoaApplication.instance.controller(self.class.controller)
+ end
+
+ def layout_options
+ options = if self.class.options && self.class.options[:layout]
+ self.class.options[:layout]
+ else
+ DefaultLayoutOptions
+ end
+ end
+
+end
+
+class ApplicationWindow < HotCocoaWindow
+
+end
+
+class Application < HotCocoaApplication
+
+end
Modified: MacRuby/branches/experimental/lib/ipaddr.rb
===================================================================
--- MacRuby/branches/experimental/lib/ipaddr.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/ipaddr.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -7,7 +7,7 @@
#
# You can redistribute and/or modify it under the same terms as Ruby.
#
-# $Id: ipaddr.rb 15821 2008-03-21 12:15:06Z knu $
+# $Id: ipaddr.rb 19504 2008-09-23 21:39:21Z ryan $
#
# Contact:
# - Akinori MUSHA <knu at iDaemons.org> (current maintainer)
@@ -483,7 +483,7 @@
if prefixlen
mask!(prefixlen)
else
- @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
+ @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK
end
end
@@ -577,7 +577,6 @@
__END__
require 'test/unit'
-require 'test/unit/ui/console/testrunner'
class TC_IPAddr < Test::Unit::TestCase
def test_s_new
Modified: MacRuby/branches/experimental/lib/irb/completion.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/completion.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/completion.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# irb/completor.rb -
# $Release Version: 0.9$
-# $Revision: 14912 $
+# $Revision: 20880 $
# by Keiju ISHITSUKA(keiju at ishitsuka.com)
# From Original Idea of shugo at ruby-lang.org
#
@@ -11,7 +11,7 @@
module IRB
module InputCompletor
- @RCS_ID='-$Id: completion.rb 14912 2008-01-06 15:49:38Z akr $-'
+ @RCS_ID='-$Id: completion.rb 20880 2008-12-19 11:37:41Z yugui $-'
ReservedWords = [
"BEGIN", "END",
@@ -159,7 +159,7 @@
end
next if name != "IRB::Context" and
/^(IRB|SLex|RubyLex|RubyToken)/ =~ name
- candidates.concat m.instance_methods(false).collect{|m| m.to_s}
+ candidates.concat m.instance_methods(false).collect{|x| x.to_s}
}
candidates.sort!
candidates.uniq!
Modified: MacRuby/branches/experimental/lib/irb/context.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/context.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/context.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# irb/context.rb - irb context
# $Release Version: 0.9.5$
-# $Revision: 15442 $
+# $Revision: 19670 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -233,6 +233,7 @@
def inspect
array = []
for ivar in instance_variables.sort{|e1, e2| e1 <=> e2}
+ ivar = ivar.to_s
name = ivar.sub(/^@(.*)$/, '\1')
val = instance_eval(ivar)
case ivar
Modified: MacRuby/branches/experimental/lib/irb/ext/change-ws.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/ext/change-ws.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/ext/change-ws.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# irb/ext/cb.rb -
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 20880 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -56,6 +56,6 @@
# end
# end
# alias change_workspace change_binding
- end
+ end
end
Modified: MacRuby/branches/experimental/lib/irb/ext/multi-irb.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/ext/multi-irb.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/ext/multi-irb.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# irb/multi-irb.rb - multiple irb module
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 18837 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -14,7 +14,7 @@
module IRB
# job management class
class JobManager
- @RCS_ID='-$Id: multi-irb.rb 14912 2008-01-06 15:49:38Z akr $-'
+ @RCS_ID='-$Id: multi-irb.rb 18837 2008-08-25 13:41:11Z mame $-'
def initialize
# @jobs = [[thread, irb],...]
@@ -69,7 +69,7 @@
end
def search(key)
- case key
+ job = case key
when Integer
@jobs[key]
when Irb
@@ -77,10 +77,10 @@
when Thread
@jobs.assoc(key)
else
- assoc = @jobs.find{|k, v| v.context.main.equal?(key)}
- IRB.fail NoSuchJob, key if assoc.nil?
- assoc
+ @jobs.find{|k, v| v.context.main.equal?(key)}
end
+ IRB.fail NoSuchJob, key if job.nil?
+ job
end
def delete(key)
Modified: MacRuby/branches/experimental/lib/irb/ext/save-history.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/ext/save-history.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/ext/save-history.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,8 +2,8 @@
#
# save-history.rb -
# $Release Version: 0.9.5$
-# $Revision: 14912 $
-# by Keiju ISHITSUKAkeiju at ruby-lang.org)
+# $Revision: 19671 $
+# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
#
@@ -14,7 +14,7 @@
module IRB
module HistorySavingAbility
- @RCS_ID='-$Id: save-history.rb 14912 2008-01-06 15:49:38Z akr $-'
+ @RCS_ID='-$Id: save-history.rb 19671 2008-10-04 03:28:19Z keiju $-'
end
class Context
@@ -52,11 +52,11 @@
def HistorySavingAbility.create_finalizer
proc do
if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0
- if hf = IRB.conf[:HISTORY_FILE]
- file = File.expand_path(hf)
+ if history_file = IRB.conf[:HISTORY_FILE]
+ history_file = File.expand_path(history_file)
end
- file = IRB.rc_file("_history") unless file
- open(file, 'w' ) do |f|
+ history_file = IRB.rc_file("_history") unless history_file
+ open(history_file, 'w' ) do |f|
hist = HISTORY.to_a
f.puts(hist[-num..-1] || hist)
end
@@ -71,10 +71,12 @@
end
def load_history
- hist = IRB.conf[:HISTORY_FILE]
- hist = IRB.rc_file("_history") unless hist
- if File.exist?(hist)
- open(hist) do |f|
+ if history_file = IRB.conf[:HISTORY_FILE]
+ history_file = File.expand_path(history_file)
+ end
+ history_file = IRB.rc_file("_history") unless history_file
+ if File.exist?(history_file)
+ open(history_file) do |f|
f.each {|l| HISTORY << l.chomp}
end
end
Modified: MacRuby/branches/experimental/lib/irb/extend-command.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/extend-command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/extend-command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# irb/extend-command.rb - irb extend command
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 18837 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -111,7 +111,7 @@
end
end
- # aliases = [commans_alias, flag], ...
+ # aliases = [commands_alias, flag], ...
def self.def_extend_command(cmd_name, cmd_class, load_file = nil, *aliases)
case cmd_class
when Symbol
@@ -125,9 +125,14 @@
eval %[
def #{cmd_name}(*opts, &b)
require "#{load_file}"
+ arity = ExtendCommand::#{cmd_class}.instance_method(:execute).arity
+ args = (1..arity.abs).map {|i| "arg" + i.to_s }
+ args << "*opts" if arity < 0
+ args << "&block"
+ args = args.join(", ")
eval %[
- def #{cmd_name}(*opts, &b)
- ExtendCommand::#{cmd_class}.execute(irb_context, *opts, &b)
+ def #{cmd_name}(\#{args})
+ ExtendCommand::#{cmd_class}.execute(irb_context, \#{args})
end
]
send :#{cmd_name}, *opts, &b
Modified: MacRuby/branches/experimental/lib/irb/help.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/help.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/help.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
-# irb/help.rb - print usase module
+# irb/help.rb - print usage module
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 20882 $
# by Keiju ISHITSUKA(keiju at ishitsuka.com)
#
# --
@@ -9,24 +9,27 @@
#
#
+require 'irb/magic-file'
+
module IRB
def IRB.print_usage
lc = IRB.conf[:LC_MESSAGES]
path = lc.find("irb/help-message")
space_line = false
- File.foreach(path) do
- |l|
- if /^\s*$/ =~ l
- lc.puts l unless space_line
- space_line = true
- next
+ IRB::MagicFile.open(path){|f|
+ f.each_line do |l|
+ if /^\s*$/ =~ l
+ lc.puts l unless space_line
+ space_line = true
+ next
+ end
+ space_line = false
+
+ l.sub!(/#.*$/, "")
+ next if /^\s*$/ =~ l
+ lc.puts l
end
- space_line = false
-
- l.sub!(/#.*$/, "")
- next if /^\s*$/ =~ l
- lc.puts l
- end
+ }
end
end
Modified: MacRuby/branches/experimental/lib/irb/init.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/init.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/init.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# irb/init.rb - irb initialize module
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 20882 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -139,6 +139,11 @@
when /^-I(.+)?/
opt = $1 || ARGV.shift
load_path.concat(opt.split(File::PATH_SEPARATOR)) if opt
+ when '-U'
+ set_encoding("UTF-8", "UTF-8")
+ when /^-E(.+)?/, /^--encoding(?:=(.+))?/
+ opt = $1 || ARGV.shift
+ set_encoding(*opt.split(':', 2))
when "--inspect"
@CONF[:INSPECT_MODE] = true
when "--noinspect"
@@ -155,8 +160,9 @@
@CONF[:VERBOSE] = true
when "--noverbose"
@CONF[:VERBOSE] = false
- when "--prompt-mode", "--prompt"
- prompt_mode = ARGV.shift.upcase.tr("-", "_").intern
+ when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/
+ opt = $1 || ARGV.shift
+ prompt_mode = opt.upcase.tr("-", "_").intern
@CONF[:PROMPT_MODE] = prompt_mode
when "--noprompt"
@CONF[:PROMPT_MODE] = :NULL
@@ -166,14 +172,14 @@
@CONF[:PROMPT_MODE] = :SIMPLE
when "--tracer"
@CONF[:USE_TRACER] = true
- when "--back-trace-limit"
- @CONF[:BACK_TRACE_LIMIT] = ARGV.shift.to_i
- when "--context-mode"
- @CONF[:CONTEXT_MODE] = ARGV.shift.to_i
+ when /^--back-trace-limit(?:=(.+))?/
+ @CONF[:BACK_TRACE_LIMIT] = ($1 || ARGV.shift).to_i
+ when /^--context-mode(?:=(.+))?/
+ @CONF[:CONTEXT_MODE] = ($1 || ARGV.shift).to_i
when "--single-irb"
@CONF[:SINGLE_IRB] = true
- when "--irb_debug"
- @CONF[:DEBUG_LEVEL] = ARGV.shift.to_i
+ when /^--irb_debug=(?:=(.+))?/
+ @CONF[:DEBUG_LEVEL] = ($1 || ARGV.shift).to_i
when "-v", "--version"
print IRB.version, "\n"
exit 0
@@ -181,6 +187,12 @@
require "irb/help"
IRB.print_usage
exit 0
+ when "--"
+ if opt = ARGV.shfit
+ @CONF[:SCRIPT] = opt
+ $0 = opt
+ end
+ break
when /^-/
IRB.fail UnrecognizedSwitch, opt
else
@@ -195,6 +207,7 @@
end
end
$LOAD_PATH.unshift(*load_path)
+
end
# running config
@@ -249,10 +262,27 @@
for m in @CONF[:LOAD_MODULES]
begin
require m
- rescue # StandardError, ScriptError
- print $@[0], ":", $!.class, ": ", $!, "\n"
+ rescue LoadError => err
+ warn err.backtrace[0] << ":#{err.class}: #{err}"
end
end
end
+
+ DefaultEncodings = Struct.new(:external, :internal)
+ class << IRB
+ private
+ def set_encoding(extern, intern = nil)
+ verbose, $VERBOSE = $VERBOSE, nil
+ Encoding.default_external = extern unless extern.nil? || extern.empty?
+ Encoding.default_internal = intern unless intern.nil? || intern.empty?
+ @CONF[:ENCODINGS] = IRB::DefaultEncodings.new(extern, intern)
+ [$stdin, $stdout, $stderr].each do |io|
+ io.set_encoding(extern, intern)
+ end
+ @CONF[:LC_MESSAGES].instance_variable_set(:@encoding, extern)
+ ensure
+ $VERBOSE = verbose
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/irb/input-method.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/input-method.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/input-method.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,13 +1,16 @@
#
# irb/input-method.rb - input methods used irb
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 21546 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
#
#
#
+require 'irb/src_encoding'
+require 'irb/magic-file'
+
module IRB
#
# InputMethod
@@ -17,7 +20,7 @@
#
STDIN_FILE_NAME = "(line)"
class InputMethod
- @RCS_ID='-$Id: input-method.rb 14912 2008-01-06 15:49:38Z akr $-'
+ @RCS_ID='-$Id: input-method.rb 21546 2009-01-15 15:36:57Z yugui $-'
def initialize(file = STDIN_FILE_NAME)
@file_name = file
@@ -41,15 +44,18 @@
super
@line_no = 0
@line = []
+ @stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
+ @stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
end
def gets
print @prompt
- @line[@line_no += 1] = $stdin.gets
+ line = @stdin.gets
+ @line[@line_no += 1] = line
end
def eof?
- $stdin.eof?
+ @stdin.eof?
end
def readable_atfer_eof?
@@ -59,12 +65,16 @@
def line(line_no)
@line[line_no]
end
+
+ def encoding
+ @stdin.external_encoding
+ end
end
class FileInputMethod < InputMethod
def initialize(file)
super
- @io = open(file)
+ @io = IRB::MagicFile.open(file)
end
attr_reader :file_name
@@ -78,6 +88,10 @@
# print @prompt, l
l
end
+
+ def encoding
+ @io.external_encoding
+ end
end
begin
@@ -90,11 +104,14 @@
@line_no = 0
@line = []
@eof = false
+
+ @stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
+ @stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
end
def gets
- Readline.input = STDIN
- Readline.output = STDOUT
+ Readline.input = @stdin
+ Readline.output = @stdout
if l = readline(@prompt, false)
HISTORY.push(l) if !l.empty?
@line[@line_no += 1] = l + "\n"
@@ -115,6 +132,10 @@
def line(line_no)
@line[line_no]
end
+
+ def encoding
+ @stdin.external_encoding
+ end
end
rescue LoadError
end
Modified: MacRuby/branches/experimental/lib/irb/lc/help-message
===================================================================
--- MacRuby/branches/experimental/lib/irb/lc/help-message 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/lc/help-message 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,8 @@
+# -*- coding: US-ASCII -*-
#
# irb/lc/help-message.rb -
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 20882 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -14,6 +15,8 @@
-d Set $DEBUG to true (same as `ruby -d')
-r load-module Same as `ruby -r'
-I path Specify $LOAD_PATH directory
+ -U Same as `ruby -U`
+ -E enc Same as `ruby -E`
--inspect Use `inspect' for output (default except for bc mode)
--noinspect Don't use inspect for output
--readline Use Readline extension module
@@ -32,3 +35,4 @@
value is 16.
--irb_debug n Set internal debug level to n (not for popular use)
-v, --version Print the version of irb
+# vim:fileencoding=us-ascii
Copied: MacRuby/branches/experimental/lib/irb/lc/ja/encoding_aliases.rb (from rev 1886, MacRuby/trunk/lib/irb/lc/ja/encoding_aliases.rb)
===================================================================
--- MacRuby/branches/experimental/lib/irb/lc/ja/encoding_aliases.rb (rev 0)
+++ MacRuby/branches/experimental/lib/irb/lc/ja/encoding_aliases.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,8 @@
+module IRB
+ class Locale
+ @@legacy_encoding_alias_map = {
+ 'ujis' => Encoding::EUC_JP,
+ 'euc' => Encoding::EUC_JP
+ }.freeze
+ end
+end
Modified: MacRuby/branches/experimental/lib/irb/lc/ja/error.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/lc/ja/error.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/lc/ja/error.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
-#
+# -*- coding: utf-8 -*-
# irb/lc/ja/error.rb -
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 20882 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -13,14 +13,15 @@
module IRB
# exceptions
extend Exception2MessageMapper
- def_exception :UnrecognizedSwitch, '$B%9%$%C%A(B(%s)$B$,J,$j$^$;$s(B'
- def_exception :NotImplementedError, '`%s\'$B$NDj5A$,I,MW$G$9(B'
- def_exception :CantReturnToNormalMode, 'Normal$B%b!<%I$KLa$l$^$;$s(B.'
- def_exception :IllegalParameter, '$B%Q%i%a!<%?(B(%s)$B$,4V0c$C$F$$$^$9(B.'
- def_exception :IrbAlreadyDead, 'Irb$B$O4{$K;`$s$G$$$^$9(B.'
- def_exception :IrbSwitchedToCurrentThread, '$B%+%l%s%H%9%l%C%I$K at Z$jBX$o$j$^$7$?(B.'
- def_exception :NoSuchJob, '$B$=$N$h$&$J%8%g%V(B(%s)$B$O$"$j$^$;$s(B.'
- def_exception :CantShiftToMultiIrbMode, 'multi-irb mode$B$K0\$l$^$;$s(B.'
- def_exception :CantChangeBinding, '$B%P%$%s%G%#%s%0(B(%s)$B$KJQ99$G$-$^$;$s(B.'
- def_exception :UndefinedPromptMode, '$B%W%m%s%W%H%b!<%I(B(%s)$B$ODj5A$5$l$F$$$^$;$s(B.'
+ def_exception :UnrecognizedSwitch, 'スイッチ(%s)が分りません'
+ def_exception :NotImplementedError, '`%s\'の定義が必要です'
+ def_exception :CantReturnToNormalMode, 'Normalモードに戻れません.'
+ def_exception :IllegalParameter, 'パラメータ(%s)が間違っています.'
+ def_exception :IrbAlreadyDead, 'Irbは既に死んでいます.'
+ def_exception :IrbSwitchedToCurrentThread, 'カレントスレッドに切り替わりました.'
+ def_exception :NoSuchJob, 'そのようなジョブ(%s)はありません.'
+ def_exception :CantShiftToMultiIrbMode, 'multi-irb modeに移れません.'
+ def_exception :CantChangeBinding, 'バインディング(%s)に変更できません.'
+ def_exception :UndefinedPromptMode, 'プロンプトモード(%s)は定義されていません.'
end
+# vim:fileencoding=utf-8
Modified: MacRuby/branches/experimental/lib/irb/lc/ja/help-message
===================================================================
--- MacRuby/branches/experimental/lib/irb/lc/ja/help-message 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/lc/ja/help-message 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
-#
+# -*- coding: utf-8 -*-
# irb/lc/ja/help-message.rb -
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 20882 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -9,27 +9,31 @@
#
#
Usage: irb.rb [options] [programfile] [arguments]
- -f ~/.irbrc $B$rFI$_9~$^$J$$(B.
- -m bc$B%b!<%I(B($BJ,?t(B, $B9TNs$N7W;;$,$G$-$k(B)
- -d $DEBUG $B$r(Btrue$B$K$9$k(B(ruby -d $B$HF1$8(B)
- -r load-module ruby -r $B$HF1$8(B.
- -I path $LOAD_PATH $B$K(B path $B$rDI2C$9$k(B.
- --inspect $B7k2L=PNO$K(Binspect$B$rMQ$$$k(B(bc$B%b!<%I0J30$O%G%U%)%k%H(B).
- --noinspect $B7k2L=PNO$K(Binspect$B$rMQ$$$J$$(B.
- --readline readline$B%i%$%V%i%j$rMxMQ$9$k(B.
- --noreadline readline$B%i%$%V%i%j$rMxMQ$7$J$$(B.
+ -f ~/.irbrc を読み込まない.
+ -m bcモード(分数, 行列の計算ができる)
+ -d $DEBUG をtrueにする(ruby -d と同じ)
+ -r load-module ruby -r と同じ.
+ -I path $LOAD_PATH に path を追加する.
+ -U ruby -U と同じ.
+ -E enc ruby -E と同じ.
+ --inspect 結果出力にinspectを用いる(bcモード以外はデフォルト).
+ --noinspect 結果出力にinspectを用いない.
+ --readline readlineライブラリを利用する.
+ --noreadline readlineライブラリを利用しない.
--prompt prompt-mode/--prompt-mode prompt-mode
- $B%W%m%s%W%H%b!<%I$r at ZBX$($^$9(B. $B8=:_Dj5A$5$l$F$$$k%W(B
- $B%m%s%W%H%b!<%I$O(B, default, simple, xmp, inf-ruby$B$,(B
- $BMQ0U$5$l$F$$$^$9(B.
- --inf-ruby-mode emacs$B$N(Binf-ruby-mode$BMQ$N%W%m%s%W%HI=<($r9T$J$&(B. $BFC(B
- $B$K;XDj$,$J$$8B$j(B, readline$B%i%$%V%i%j$O;H$o$J$/$J$k(B.
- --simple-prompt $BHs>o$K%7%s%W%k$J%W%m%s%W%H$rMQ$$$k%b!<%I$G$9(B.
- --noprompt $B%W%m%s%W%HI=<($r9T$J$o$J$$(B.
- --tracer $B%3%^%s%I<B9T;~$K%H%l!<%9$r9T$J$&(B.
+ プロンプトモードを切替えます. 現在定義されているプ
+ ロンプトモードは, default, simple, xmp, inf-rubyが
+ 用意されています.
+ --inf-ruby-mode emacsのinf-ruby-mode用のプロンプト表示を行なう. 特
+ に指定がない限り, readlineライブラリは使わなくなる.
+ --simple-prompt 非常にシンプルなプロンプトを用いるモードです.
+ --noprompt プロンプト表示を行なわない.
+ --tracer コマンド実行時にトレースを行なう.
--back-trace-limit n
- $B%P%C%/%H%l!<%9I=<($r%P%C%/%H%l!<%9$NF,$+$i(B n, $B8e$m(B
- $B$+$i(Bn$B$@$19T$J$&(B. $B%G%U%)%k%H$O(B16
- --irb_debug n irb$B$N%G%P%C%0%G%P%C%0%l%Y%k$r(Bn$B$K at _Dj$9$k(B($BMxMQ$7$J(B
- $B$$J}$,L5Fq$G$7$g$&(B).
- -v, --version irb$B$N%P!<%8%g%s$rI=<($9$k(B
+ バックトレース表示をバックトレースの頭から n, 後ろ
+ からnだけ行なう. デフォルトは16
+ --irb_debug n irbのデバッグデバッグレベルをnに設定する(利用しな
+ い方が無難でしょう).
+ -v, --version irbのバージョンを表示する
+
+# vim:fileencoding=utf-8
Modified: MacRuby/branches/experimental/lib/irb/locale.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/locale.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/locale.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,50 +1,59 @@
#
# irb/locale.rb - internationalization module
# $Release Version: 0.9.5$
-# $Revision: 15536 $
+# $Revision: 20889 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
#
#
#
-
-autoload :Kconv, "kconv"
-
module IRB
class Locale
- @RCS_ID='-$Id: locale.rb 15536 2008-02-18 04:07:56Z akr $-'
+ @RCS_ID='-$Id: locale.rb 20889 2008-12-20 02:02:48Z yugui $-'
- JPDefaultLocale = "ja"
+ LOCALE_NAME_RE = %r[
+ (?<language>[[:alpha:]]{2})
+ (?:_
+ (?<territory>[[:alpha:]]{2,3})
+ (?:\.
+ (?<codeset>[^@]+)
+ )?
+ )?
+ (?:@
+ (?<modifier>.*)
+ )?
+ ]x
LOCALE_DIR = "/lc/"
+ @@legacy_encoding_alias_map = {}.freeze
+
def initialize(locale = nil)
- @lang = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C"
- end
+ @lang = @territory = @encoding_name = @modifier = nil
+ @locale = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C"
+ if m = LOCALE_NAME_RE.match(@locale)
+ @lang, @territory, @encoding_name, @modifier = m[:language], m[:territory], m[:codeset], m[:modifier]
- attr_reader :lang
-
- def lc2kconv(lang)
- case lang
- when "ja_JP.ujis", "ja_JP.euc", "ja_JP.eucJP", "ja_JP.EUC-JP"
- Kconv::EUC
- when "ja_JP.sjis", "ja_JP.SJIS"
- Kconv::SJIS
- when /ja_JP.utf-?8/i
- Kconv::UTF8
+ if @encoding_name
+ begin load 'irb/encoding_aliases.rb'; rescue LoadError; end
+ if @encoding = @@legacy_encoding_alias_map[@encoding_name]
+ warn "%s is obsolete. use %s" % ["#{@lang}_#{@territory}.#{@encoding_name}", "#{@lang}_#{@territory}.#{@encoding.name}"]
+ end
+ @encoding = Encoding.find(@encoding_name) rescue nil
+ end
end
+ @encoding ||= (Encoding.find('locale') rescue Encoding::ASCII_8BIT)
end
- private :lc2kconv
+ attr_reader :lang, :territory, :encoding, :modifieer
+
def String(mes)
mes = super(mes)
- case @lang
- when /^ja/
- mes = Kconv::kconv(mes, lc2kconv(@lang))
+ if @encoding
+ mes.encode(@encoding)
else
mes
end
- mes
end
def format(*opts)
@@ -106,27 +115,20 @@
dir = "" if dir == "."
base = File.basename(file)
- if /^ja(_JP)?$/ =~ @lang
- back, @lang = @lang, "C"
+ if dir[0] == ?/ #/
+ lc_path = search_file(dir, base)
+ return real_load(lc_path, priv) if lc_path
end
- begin
- if dir[0] == ?/ #/
- lc_path = search_file(dir, base)
- return real_load(lc_path, priv) if lc_path
- end
-
- for path in $:
- lc_path = search_file(path + "/" + dir, base)
- return real_load(lc_path, priv) if lc_path
- end
- ensure
- @lang = back if back
+
+ for path in $:
+ lc_path = search_file(path + "/" + dir, base)
+ return real_load(lc_path, priv) if lc_path
end
raise LoadError, "No such file to load -- #{file}"
end
def real_load(path, priv)
- src = self.String(File.read(path))
+ src = MagicFile.open(path){|f| f.read}
if priv
eval("self", TOPLEVEL_BINDING).extend(Module.new {eval(src, nil, path)})
else
@@ -152,29 +154,39 @@
end
def search_file(path, file)
- if File.exist?(p1 = path + lc_path(file, "C"))
- if File.exist?(p2 = path + lc_path(file))
- return p2
- else
- end
- return p1
- else
+ each_sublocale do |lc|
+ full_path = path + lc_path(file, lc)
+ return full_path if File.exist?(full_path)
end
nil
end
private :search_file
- def lc_path(file = "", lc = @lang)
- case lc
- when "C"
+ def lc_path(file = "", lc = @locale)
+ if lc.nil?
LOCALE_DIR + file
- when /^ja/
- LOCALE_DIR + "ja/" + file
else
LOCALE_DIR + @lang + "/" + file
end
end
private :lc_path
+
+ def each_sublocale
+ if @lang
+ if @territory
+ if @encoding_name
+ yield "#{@lang}_#{@territory}.#{@encoding_name}@#{@modifier}" if @modifier
+ yield "#{@lang}_#{@territory}.#{@encoding_name}"
+ end
+ yield "#{@lang}_#{@territory}@#{@modifier}" if @modifier
+ yield "#{@lang}_#{@territory}"
+ end
+ yield "#{@lang}@#{@modifier}" if @modifier
+ yield "#{@lang}"
+ end
+ yield nil
+ end
+ private :each_sublocale
end
end
Copied: MacRuby/branches/experimental/lib/irb/magic-file.rb (from rev 1886, MacRuby/trunk/lib/irb/magic-file.rb)
===================================================================
--- MacRuby/branches/experimental/lib/irb/magic-file.rb (rev 0)
+++ MacRuby/branches/experimental/lib/irb/magic-file.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,36 @@
+module IRB
+ class << (MagicFile = Object.new)
+ # see parser_magic_comment in parse.y
+ ENCODING_SPEC_RE = %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
+
+ def open(path)
+ io = File.open(path, 'rb')
+ line = io.gets
+ line = io.gets if line[0,2] == "#!"
+ encoding = detect_encoding(line)
+ encoding ||= default_src_encoding
+ io.rewind
+ io.set_encoding(encoding, nil)
+
+ if block_given?
+ begin
+ return (yield io)
+ ensure
+ io.close
+ end
+ else
+ return io
+ end
+ end
+
+ private
+ def detect_encoding(line)
+ return unless line[0] == ?#
+ line = line[1..-1]
+ line = $1 if line[/-\*-\s*(.*?)\s*-*-$/]
+ return nil unless ENCODING_SPEC_RE =~ line
+ encoding = $1
+ return encoding.sub(/-(?:mac|dos|unix)/i, '')
+ end
+ end
+end
Modified: MacRuby/branches/experimental/lib/irb/notifier.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/notifier.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/notifier.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
-# notifier.rb - optput methods used by irb
+# notifier.rb - output methods used by irb
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 16810 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
Modified: MacRuby/branches/experimental/lib/irb/ruby-lex.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/ruby-lex.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/ruby-lex.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
-# irb/ruby-lex.rb - ruby lexcal analizer
+# irb/ruby-lex.rb - ruby lexcal analyzer
# $Release Version: 0.9.5$
-# $Revision: 15267 $
+# $Revision: 20882 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -14,7 +14,7 @@
require "irb/ruby-token"
class RubyLex
- @RCS_ID='-$Id: ruby-lex.rb 15267 2008-01-27 07:43:31Z naruse $-'
+ @RCS_ID='-$Id: ruby-lex.rb 20882 2008-12-19 11:37:59Z yugui $-'
extend Exception2MessageMapper
def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
@@ -240,6 +240,7 @@
end
end
if @line != "\n"
+ @line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break unless l
Modified: MacRuby/branches/experimental/lib/irb/slex.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/slex.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/slex.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
-# irb/slex.rb - symple lex analizer
+# irb/slex.rb - simple lex analyzer
# $Release Version: 0.9.5$
-# $Revision: 14912 $
+# $Revision: 16810 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -14,7 +14,7 @@
module IRB
class SLex
- @RCS_ID='-$Id: slex.rb 14912 2008-01-06 15:49:38Z akr $-'
+ @RCS_ID='-$Id: slex.rb 16810 2008-06-04 09:37:38Z matz $-'
extend Exception2MessageMapper
def_exception :ErrNodeNothing, "node nothing"
Copied: MacRuby/branches/experimental/lib/irb/src_encoding.rb (from rev 1886, MacRuby/trunk/lib/irb/src_encoding.rb)
===================================================================
--- MacRuby/branches/experimental/lib/irb/src_encoding.rb (rev 0)
+++ MacRuby/branches/experimental/lib/irb/src_encoding.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,4 @@
+# DO NOT WRITE ANY MAGIC COMMENT HERE.
+def default_src_encoding
+ return __ENCODING__
+end
Modified: MacRuby/branches/experimental/lib/irb/xmp.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb/xmp.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb/xmp.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# xmp.rb - irb version of gotoken xmp
# $Release Version: 0.9$
-# $Revision: 14912 $
+# $Revision: 21633 $
# by Keiju ISHITSUKA(Nippon Rational Inc.)
#
# --
@@ -13,7 +13,7 @@
require "irb/frame"
class XMP
- @RCS_ID='-$Id: xmp.rb 14912 2008-01-06 15:49:38Z akr $-'
+ @RCS_ID='-$Id: xmp.rb 21633 2009-01-17 12:19:53Z yugui $-'
def initialize(bind = nil)
IRB.init_config(nil)
@@ -72,8 +72,20 @@
end
def puts(exps)
+ if @encoding and exps.encoding != @encoding
+ enc = Encoding.compatible?(@exps.join("\n"), exps)
+ if enc.nil?
+ raise Encoding::CompatibilityError, "Encoding in which the passed exression is encoded is not compatible to the preceding's one"
+ else
+ @encoding = enc
+ end
+ else
+ @encoding = exps.encoding
+ end
@exps.concat exps.split(/\n/)
end
+
+ attr_reader :encoding
end
end
Modified: MacRuby/branches/experimental/lib/irb.rb
===================================================================
--- MacRuby/branches/experimental/lib/irb.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/irb.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# irb.rb - irb main module
# $Release Version: 0.9.5 $
-# $Revision: 15689 $
+# $Revision: 20186 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -22,7 +22,7 @@
STDOUT.sync = true
module IRB
- @RCS_ID='-$Id: irb.rb 15689 2008-03-04 12:37:05Z matz $-'
+ @RCS_ID='-$Id: irb.rb 20186 2008-11-11 08:41:29Z yugui $-'
class Abort < Exception;end
@@ -107,7 +107,7 @@
f = @context.prompt_c
elsif indent > 0
f = @context.prompt_n
- else @context.prompt_i
+ else
f = @context.prompt_i
end
f = "" unless f
@@ -308,7 +308,7 @@
def inspect
ary = []
for iv in instance_variables
- case iv
+ case (iv = iv.to_s)
when "@signal_status"
ary.push format("%s=:%s", iv, @signal_status.id2name)
when "@context"
Modified: MacRuby/branches/experimental/lib/logger.rb
===================================================================
--- MacRuby/branches/experimental/lib/logger.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/logger.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,4 +1,4 @@
-# logger.rb - saimple logging utility
+# logger.rb - simple logging utility
# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nakahiro at sarion.co.jp>.
require 'monitor'
@@ -12,7 +12,7 @@
# License::
# You can redistribute it and/or modify it under the same terms of Ruby's
# license; either the dual license version in 2003, or any later version.
-# Revision:: $Id: logger.rb 12284 2007-05-16 12:52:52Z nahi $
+# Revision:: $Id: logger.rb 20321 2008-11-22 14:52:06Z yugui $
#
# See Logger for documentation.
#
@@ -181,8 +181,14 @@
class Logger
VERSION = "1.2.6"
- /: (\S+),v (\S+)/ =~ %q$Id: logger.rb 12284 2007-05-16 12:52:52Z nahi $
- ProgName = "#{$1}/#{$2}"
+ id, name, rev = %w$Id: logger.rb 20321 2008-11-22 14:52:06Z yugui $
+ if name
+ name = name.chomp(",v")
+ else
+ name = File.basename(__FILE__)
+ end
+ rev ||= "v#{VERSION}"
+ ProgName = "#{name}/#{rev}"
class Error < RuntimeError; end
class ShiftingError < Error; end
Modified: MacRuby/branches/experimental/lib/mathn.rb
===================================================================
--- MacRuby/branches/experimental/lib/mathn.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/mathn.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -9,125 +9,56 @@
#
#
-require "complex.rb"
-require "rational.rb"
+require "cmath.rb"
require "matrix.rb"
+require "prime.rb"
-class Integer
+require "mathn/rational"
+require "mathn/complex"
- def Integer.from_prime_division(pd)
- value = 1
- for prime, index in pd
- value *= prime**index
- end
- value
- end
-
- def prime_division
- raise ZeroDivisionError if self == 0
- ps = Prime.new
- value = self
- pv = []
- for prime in ps
- count = 0
- while (value1, mod = value.divmod(prime)
- mod) == 0
- value = value1
- count += 1
- end
- if count != 0
- pv.push [prime, count]
- end
- break if prime * prime >= value
- end
- if value > 1
- pv.push [value, 1]
- end
- return pv
- end
+unless defined?(Math.exp!)
+ Object.instance_eval{remove_const :Math}
+ Math = CMath
end
-
-class Prime
- include Enumerable
- # These are included as class variables to cache them for later uses. If memory
- # usage is a problem, they can be put in Prime#initialize as instance variables.
- # There must be no primes between @@primes[-1] and @@next_to_check.
- @@primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
- # @@next_to_check % 6 must be 1.
- @@next_to_check = 103 # @@primes[-1] - @@primes[-1] % 6 + 7
- @@ulticheck_index = 3 # @@primes.index(@@primes.reverse.find {|n|
- # n < Math.sqrt(@@next_to_check) })
- @@ulticheck_next_squared = 121 # @@primes[@@ulticheck_index + 1] ** 2
+class Fixnum
+ remove_method :/
+ alias / quo
- class << self
- # Return the prime cache.
- def cache
- return @@primes
- end
- alias primes cache
- alias primes_so_far cache
- end
-
- def initialize
- @index = -1
- end
-
- # Return primes given by this instance so far.
- def primes
- return @@primes[0, @index + 1]
- end
- alias primes_so_far primes
-
- def succ
- @index += 1
- while @index >= @@primes.length
- # Only check for prime factors up to the square root of the potential primes,
- # but without the performance hit of an actual square root calculation.
- if @@next_to_check + 4 > @@ulticheck_next_squared
- @@ulticheck_index += 1
- @@ulticheck_next_squared = @@primes.at(@@ulticheck_index + 1) ** 2
- end
- # Only check numbers congruent to one and five, modulo six. All others
- # are divisible by two or three. This also allows us to skip checking against
- # two and three.
- @@primes.push @@next_to_check if @@primes[2..@@ulticheck_index].find {|prime| @@next_to_check % prime == 0 }.nil?
- @@next_to_check += 4
- @@primes.push @@next_to_check if @@primes[2..@@ulticheck_index].find {|prime| @@next_to_check % prime == 0 }.nil?
- @@next_to_check += 2
- end
- return @@primes[@index]
- end
- alias next succ
+ alias power! ** unless defined?(0.power!)
- def each
- return to_enum(:each) unless block_given?
- loop do
- yield succ
+ def ** (other)
+ if self < 0 && other.round != other
+ Complex(self, 0.0) ** other
+ else
+ power!(other)
end
end
-end
-class Fixnum
- remove_method :/
- alias / quo
end
class Bignum
remove_method :/
alias / quo
-end
-class Rational
- Unify = true
+ alias power! ** unless defined?(0.power!)
- alias power! **
+ def ** (other)
+ if self < 0 && other.round != other
+ Complex(self, 0.0) ** other
+ else
+ power!(other)
+ end
+ end
+end
+
+class Rational
def ** (other)
if other.kind_of?(Rational)
other2 = other
if self < 0
- return Complex.__send__(:new!, self, 0) ** other
+ return Complex(self, 0.0) ** other
elsif other == 0
return Rational(1,1)
elsif self == 0
@@ -183,57 +114,13 @@
x ** y
end
end
-
- def power2(other)
- if other.kind_of?(Rational)
- if self < 0
- return Complex.__send__(:new!, self, 0) ** other
- elsif other == 0
- return Rational(1,1)
- elsif self == 0
- return Rational(0,1)
- elsif self == 1
- return Rational(1,1)
- end
-
- dem = nil
- x = self.denominator.to_f.to_i
- neard = self.denominator.to_f ** (1.0/other.denominator.to_f)
- loop do
- if (neard**other.denominator == self.denominator)
- dem = neaed
- break
- end
- end
- nearn = self.numerator.to_f ** (1.0/other.denominator.to_f)
- Rational(num,den)
-
- elsif other.kind_of?(Integer)
- if other > 0
- num = numerator ** other
- den = denominator ** other
- elsif other < 0
- num = denominator ** -other
- den = numerator ** -other
- elsif other == 0
- num = 1
- den = 1
- end
- Rational(num, den)
- elsif other.kind_of?(Float)
- Float(self) ** other
- else
- x , y = other.coerce(self)
- x ** y
- end
- end
end
module Math
remove_method(:sqrt)
def sqrt(a)
if a.kind_of?(Complex)
- abs = sqrt(a.real*a.real + a.image*a.image)
+ abs = sqrt(a.real*a.real + a.imag*a.imag)
# if not abs.kind_of?(Rational)
# return a**Rational(1,2)
# end
@@ -242,11 +129,13 @@
# if !(x.kind_of?(Rational) and y.kind_of?(Rational))
# return a**Rational(1,2)
# end
- if a.image >= 0
+ if a.imag >= 0
Complex(x, y)
else
Complex(x, -y)
end
+ elsif a.respond_to?(:nan?) and a.nan?
+ a
elsif a >= 0
rsqrt(a)
else
@@ -303,6 +192,15 @@
module_function :rsqrt
end
-class Complex
- Unify = true
+class Float
+ alias power! **
+
+ def ** (other)
+ if self < 0 && other.round != other
+ Complex(self, 0.0) ** other
+ else
+ power!(other)
+ end
+ end
+
end
Modified: MacRuby/branches/experimental/lib/matrix.rb
===================================================================
--- MacRuby/branches/experimental/lib/matrix.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/matrix.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -18,7 +18,6 @@
# See classes Matrix and Vector for documentation.
#
-
require "e2mmap.rb"
module ExceptionForMatrix # :nodoc:
@@ -141,10 +140,8 @@
#
#
def Matrix.columns(columns)
- rows = (0 .. columns[0].size - 1).collect {
- |i|
- (0 .. columns.size - 1).collect {
- |j|
+ rows = (0 .. columns[0].size - 1).collect {|i|
+ (0 .. columns.size - 1).collect {|j|
columns[j][i]
}
}
@@ -160,8 +157,7 @@
#
def Matrix.diagonal(*values)
size = values.size
- rows = (0 .. size - 1).collect {
- |j|
+ rows = (0 .. size - 1).collect {|j|
row = Array.new(size).fill(0, 0, size)
row[j] = values[j]
row
@@ -312,13 +308,11 @@
#
def column(j) # :yield: e
if block_given?
- 0.upto(row_size - 1) do
- |i|
+ 0.upto(row_size - 1) do |i|
yield @rows[i][j]
end
else
- col = (0 .. row_size - 1).collect {
- |i|
+ col = (0 .. row_size - 1).collect {|i|
@rows[i][j]
}
Vector.elements(col, false)
@@ -365,8 +359,7 @@
Matrix.Raise ArgumentError, param.inspect
end
- rows = @rows[from_row, size_row].collect{
- |row|
+ rows = @rows[from_row, size_row].collect{|row|
row[from_col, size_col]
}
Matrix.rows(rows, false)
@@ -410,17 +403,20 @@
other.compare_by_row_vectors(@rows)
end
- alias eql? ==
+ def eql?(other)
+ return false unless Matrix === other
+
+ other.compare_by_row_vectors(@rows, :eql?)
+ end
#
# Not really intended for general consumption.
#
- def compare_by_row_vectors(rows)
+ def compare_by_row_vectors(rows, comparison = :==)
return false unless @rows.size == rows.size
- 0.upto(@rows.size - 1) do
- |i|
- return false unless @rows[i] == rows[i]
+ 0.upto(@rows.size - 1) do |i|
+ return false unless @rows[i].send(comparison, rows[i])
end
true
end
@@ -459,10 +455,8 @@
def *(m) # m is matrix or vector or number
case(m)
when Numeric
- rows = @rows.collect {
- |row|
- row.collect {
- |e|
+ rows = @rows.collect {|row|
+ row.collect {|e|
e * m
}
}
@@ -474,13 +468,10 @@
when Matrix
Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
- rows = (0 .. row_size - 1).collect {
- |i|
- (0 .. m.column_size - 1).collect {
- |j|
+ rows = (0 .. row_size - 1).collect {|i|
+ (0 .. m.column_size - 1).collect {|j|
vij = 0
- 0.upto(column_size - 1) do
- |k|
+ 0.upto(column_size - 1) do |k|
vij += self[i, k] * m[k, j]
end
vij
@@ -513,10 +504,8 @@
Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
- rows = (0 .. row_size - 1).collect {
- |i|
- (0 .. column_size - 1).collect {
- |j|
+ rows = (0 .. row_size - 1).collect {|i|
+ (0 .. column_size - 1).collect {|j|
self[i, j] + m[i, j]
}
}
@@ -543,10 +532,8 @@
Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
- rows = (0 .. row_size - 1).collect {
- |i|
- (0 .. column_size - 1).collect {
- |j|
+ rows = (0 .. row_size - 1).collect {|i|
+ (0 .. column_size - 1).collect {|j|
self[i, j] - m[i, j]
}
}
@@ -562,10 +549,8 @@
def /(other)
case other
when Numeric
- rows = @rows.collect {
- |row|
- row.collect {
- |e|
+ rows = @rows.collect {|row|
+ row.collect {|e|
e / other
}
}
@@ -600,7 +585,7 @@
for k in 0..size
i = k
akk = a[k][k].abs
- for j in (k+1)..size
+ ((k+1)..size).each do |j|
v = a[j][k].abs
if v > akk
i = j
@@ -619,22 +604,18 @@
q = a[i][k].quo(akk)
a[i][k] = 0
- (k + 1).upto(size) do
- |j|
+ for j in (k + 1).. size
a[i][j] -= a[k][j] * q
end
- 0.upto(size) do
- |j|
+ for j in 0..size
@rows[i][j] -= @rows[k][j] * q
end
end
- (k + 1).upto(size) do
- |j|
+ for j in (k + 1).. size
a[k][j] = a[k][j].quo(akk)
end
- 0.upto(size) do
- |j|
+ for j in 0..size
@rows[k][j] = @rows[k][j].quo(akk)
end
end
@@ -698,26 +679,27 @@
det = 1
k = 0
- begin
+ loop do
if (akk = a[k][k]) == 0
i = k
- begin
- return 0 if (i += 1) > size
- end while a[i][k] == 0
+ loop do
+ return 0 if (ii += 1) > size
+ break unless a[i][k] == 0
+ end
a[i], a[k] = a[k], a[i]
akk = a[k][k]
det *= -1
end
- (k + 1).upto(size) do
- |i|
+
+ for i in k + 1 .. size
q = a[i][k].quo(akk)
- (k + 1).upto(size) do
- |j|
+ (k + 1).upto(size) do |j|
a[i][j] -= a[k][j] * q
end
end
det *= akk
- end while (k += 1) <= size
+ break unless (k += 1) <= size
+ end
det
end
alias det determinant
@@ -740,16 +722,18 @@
det = 1
k = 0
- begin
+ loop do
if a[k][k].zero?
i = k
- begin
+ loop do
return 0 if (i += 1) > size
- end while a[i][k].zero?
+ break unless a[i][k].zero?
+ end
a[i], a[k] = a[k], a[i]
det *= -1
end
- (k + 1).upto(size) do |i|
+
+ for i in (k + 1)..size
q = a[i][k].quo(a[k][k])
k.upto(size) do |j|
a[i][j] -= a[k][j] * q
@@ -761,7 +745,8 @@
end
end
det *= a[k][k]
- end while (k += 1) <= size
+ break unless (k += 1) <= size
+ end
det
end
alias det_e determinant_e
@@ -786,31 +771,32 @@
end
rank = 0
k = 0
- begin
+ loop do
if (akk = a[k][k]) == 0
i = k
exists = true
- begin
+ loop do
if (i += 1) > a_column_size - 1
exists = false
break
end
- end while a[i][k] == 0
+ break unless a[i][k] == 0
+ end
if exists
a[i], a[k] = a[k], a[i]
akk = a[k][k]
else
i = k
exists = true
- begin
+ loop do
if (i += 1) > a_row_size - 1
exists = false
break
end
- end while a[k][i] == 0
+ break unless a[k][i] == 0
+ end
if exists
- k.upto(a_column_size - 1) do
- |j|
+ k.upto(a_column_size - 1) do |j|
a[j][k], a[j][i] = a[j][i], a[j][k]
end
akk = a[k][k]
@@ -819,16 +805,16 @@
end
end
end
- (k + 1).upto(a_row_size - 1) do
- |i|
+
+ for i in (k + 1)..(a_row_size - 1)
q = a[i][k].quo(akk)
- (k + 1).upto(a_column_size - 1) do
- |j|
+ for j in (k + 1)..(a_column_size - 1)
a[i][j] -= a[k][j] * q
end
end
rank += 1
- end while (k += 1) <= a_column_size - 1
+ break unless (k += 1) <= a_column_size - 1
+ end
return rank
end
@@ -874,8 +860,7 @@
#
def trace
tr = 0
- 0.upto(column_size - 1) do
- |i|
+ 0.upto(column_size - 1) do |i|
tr += @rows[i][i]
end
tr
@@ -917,8 +902,7 @@
# Returns an array of the row vectors of the matrix. See Vector.
#
def row_vectors
- rows = (0 .. row_size - 1).collect {
- |i|
+ rows = (0 .. row_size - 1).collect {|i|
row(i)
}
rows
@@ -928,8 +912,7 @@
# Returns an array of the column vectors of the matrix. See Vector.
#
def column_vectors
- columns = (0 .. column_size - 1).collect {
- |i|
+ columns = (0 .. column_size - 1).collect {|i|
column(i)
}
columns
@@ -962,8 +945,7 @@
# Overrides Object#to_s
#
def to_s
- "Matrix[" + @rows.collect{
- |row|
+ "Matrix[" + @rows.collect{|row|
"[" + row.collect{|e| e.to_s}.join(", ") + "]"
}.join(", ")+"]"
end
@@ -1170,8 +1152,7 @@
#
def each2(v) # :yield: e1, e2
Vector.Raise ErrDimensionMismatch if size != v.size
- 0.upto(size - 1) do
- |i|
+ 0.upto(size - 1) do |i|
yield @elements[i], v[i]
end
end
@@ -1182,8 +1163,7 @@
#
def collect2(v) # :yield: e1, e2
Vector.Raise ErrDimensionMismatch if size != v.size
- (0 .. size - 1).collect do
- |i|
+ (0 .. size - 1).collect do |i|
yield @elements[i], v[i]
end
end
@@ -1200,13 +1180,17 @@
other.compare_by(@elements)
end
- alias eqn? ==
+ def eql?(other)
+ return false unless Vector === other
+
+ other.compare_by(@elements, :eql?)
+ end
#
# For internal use.
#
- def compare_by(elements)
- @elements == elements
+ def compare_by(elements, comparison = :==)
+ @elements.send(comparison, elements)
end
#
@@ -1250,8 +1234,7 @@
case v
when Vector
Vector.Raise ErrDimensionMismatch if size != v.size
- els = collect2(v) {
- |v1, v2|
+ els = collect2(v) {|v1, v2|
v1 + v2
}
Vector.elements(els, false)
@@ -1270,8 +1253,7 @@
case v
when Vector
Vector.Raise ErrDimensionMismatch if size != v.size
- els = collect2(v) {
- |v1, v2|
+ els = collect2(v) {|v1, v2|
v1 - v2
}
Vector.elements(els, false)
@@ -1295,8 +1277,7 @@
Vector.Raise ErrDimensionMismatch if size != v.size
p = 0
- each2(v) {
- |v1, v2|
+ each2(v) {|v1, v2|
p += v1 * v2
}
p
@@ -1306,8 +1287,7 @@
# Like Array#collect.
#
def collect # :yield: e
- els = @elements.collect {
- |v|
+ els = @elements.collect {|v|
yield v
}
Vector.elements(els, false)
@@ -1318,8 +1298,7 @@
# Like Vector#collect2, but returns a Vector instead of an Array.
#
def map2(v) # :yield: e1, e2
- els = collect2(v) {
- |v1, v2|
+ els = collect2(v) {|v1, v2|
yield v1, v2
}
Vector.elements(els, false)
Deleted: MacRuby/branches/experimental/lib/minitest/autorun.rb
===================================================================
--- MacRuby/trunk/lib/minitest/autorun.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/minitest/autorun.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,9 +0,0 @@
-############################################################
-# This file is imported from a different project.
-# DO NOT make modifications in this repo.
-# File a patch instead and assign it to Ryan Davis
-############################################################
-
-require 'minitest/unit'
-
-MiniTest::Unit.autorun
Copied: MacRuby/branches/experimental/lib/minitest/autorun.rb (from rev 1886, MacRuby/trunk/lib/minitest/autorun.rb)
===================================================================
--- MacRuby/branches/experimental/lib/minitest/autorun.rb (rev 0)
+++ MacRuby/branches/experimental/lib/minitest/autorun.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,9 @@
+############################################################
+# This file is imported from a different project.
+# DO NOT make modifications in this repo.
+# File a patch instead and assign it to Ryan Davis
+############################################################
+
+require 'minitest/unit'
+
+MiniTest::Unit.autorun
Deleted: MacRuby/branches/experimental/lib/minitest/mock.rb
===================================================================
--- MacRuby/trunk/lib/minitest/mock.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/minitest/mock.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,37 +0,0 @@
-############################################################
-# This file is imported from a different project.
-# DO NOT make modifications in this repo.
-# File a patch instead and assign it to Ryan Davis
-############################################################
-
-class MockExpectationError < StandardError; end
-
-module MiniTest
- class Mock
- def initialize
- @expected_calls = {}
- @actual_calls = Hash.new {|h,k| h[k] = [] }
- end
-
- def expect(name, retval, args=[])
- n, r, a = name, retval, args # for the closure below
- @expected_calls[name] = { :retval => retval, :args => args }
- self.class.__send__(:define_method, name) { |*x|
- raise ArgumentError unless @expected_calls[n][:args].size == x.size
- @actual_calls[n] << { :retval => r, :args => x }
- retval
- }
- self
- end
-
- def verify
- @expected_calls.each_key do |name|
- expected = @expected_calls[name]
- msg = "expected #{name}, #{expected.inspect}"
- raise MockExpectationError, msg unless
- @actual_calls.has_key? name and @actual_calls[name].include?(expected)
- end
- true
- end
- end
-end
Copied: MacRuby/branches/experimental/lib/minitest/mock.rb (from rev 1886, MacRuby/trunk/lib/minitest/mock.rb)
===================================================================
--- MacRuby/branches/experimental/lib/minitest/mock.rb (rev 0)
+++ MacRuby/branches/experimental/lib/minitest/mock.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,37 @@
+############################################################
+# This file is imported from a different project.
+# DO NOT make modifications in this repo.
+# File a patch instead and assign it to Ryan Davis
+############################################################
+
+class MockExpectationError < StandardError; end
+
+module MiniTest
+ class Mock
+ def initialize
+ @expected_calls = {}
+ @actual_calls = Hash.new {|h,k| h[k] = [] }
+ end
+
+ def expect(name, retval, args=[])
+ n, r, a = name, retval, args # for the closure below
+ @expected_calls[name] = { :retval => retval, :args => args }
+ self.class.__send__(:define_method, name) { |*x|
+ raise ArgumentError unless @expected_calls[n][:args].size == x.size
+ @actual_calls[n] << { :retval => r, :args => x }
+ retval
+ }
+ self
+ end
+
+ def verify
+ @expected_calls.each_key do |name|
+ expected = @expected_calls[name]
+ msg = "expected #{name}, #{expected.inspect}"
+ raise MockExpectationError, msg unless
+ @actual_calls.has_key? name and @actual_calls[name].include?(expected)
+ end
+ true
+ end
+ end
+end
Deleted: MacRuby/branches/experimental/lib/minitest/spec.rb
===================================================================
--- MacRuby/trunk/lib/minitest/spec.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/minitest/spec.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,89 +0,0 @@
-############################################################
-# This file is imported from a different project.
-# DO NOT make modifications in this repo.
-# File a patch instead and assign it to Ryan Davis
-############################################################
-
-#!/usr/bin/ruby -w
-
-require 'minitest/unit'
-
-class Module
- def infect_with_assertions pos_prefix, neg_prefix, skip_re, map = {}
- MiniTest::Assertions.public_instance_methods(false).each do |meth|
- meth = meth.to_s
-
- new_name = case meth
- when /^assert/ then
- meth.sub(/^assert/, pos_prefix.to_s)
- when /^refute/ then
- meth.sub(/^refute/, neg_prefix.to_s)
- end
- next unless new_name
- next if new_name =~ skip_re
-
- regexp, replacement = map.find { |re, _| new_name =~ re }
- new_name.sub! regexp, replacement if replacement
-
- # warn "%-22p -> %p %p" % [meth, new_name, regexp]
- self.class_eval <<-EOM
- def #{new_name} *args, &block
- return MiniTest::Spec.current.#{meth}(*args, &self) if Proc === self
- return MiniTest::Spec.current.#{meth}(args.first, self) if args.size == 1
- return MiniTest::Spec.current.#{meth}(self, *args)
- end
- EOM
- end
- end
-end
-
-Object.infect_with_assertions(:must, :wont,
- /^(must|wont)$|wont_(throw)|
- must_(block|not?_|nothing|raise$)/x,
- /(must_throw)s/ => '\1',
- /(?!not)_same/ => '_be_same_as',
- /_in_/ => '_be_within_',
- /_operator/ => '_be',
- /_includes/ => '_include',
- /(must|wont)_(.*_of|nil|empty)/ => '\1_be_\2',
- /must_raises/ => 'must_raise')
-
-class Object
- alias :must_be_close_to :must_be_within_delta
- alias :wont_be_close_to :wont_be_within_delta
-end
-
-module Kernel
- def describe desc, &block
- cls = Class.new(MiniTest::Spec)
- Object.const_set desc.to_s.split(/\W+/).map { |s| s.capitalize }.join, cls
-
- cls.class_eval(&block)
- end
- private :describe
-end
-
-class MiniTest::Spec < MiniTest::Unit::TestCase
- def self.current
- @@current_spec
- end
-
- def initialize name
- super
- @@current_spec = self
- end
-
- def self.before(type = :each, &block)
- raise "unsupported before type: #{type}" unless type == :each
- define_method :setup, &block
- end
-
- def self.after(type = :each, &block)
- raise "unsupported after type: #{type}" unless type == :each
- define_method :teardown, &block
- end
-
- def self.it desc, &block
- define_method "test_#{desc.gsub(/\W+/, '_').downcase}", &block
- end
-end
Copied: MacRuby/branches/experimental/lib/minitest/spec.rb (from rev 1886, MacRuby/trunk/lib/minitest/spec.rb)
===================================================================
--- MacRuby/branches/experimental/lib/minitest/spec.rb (rev 0)
+++ MacRuby/branches/experimental/lib/minitest/spec.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,89 @@
+############################################################
+# This file is imported from a different project.
+# DO NOT make modifications in this repo.
+# File a patch instead and assign it to Ryan Davis
+############################################################
+
+#!/usr/bin/ruby -w
+
+require 'minitest/unit'
+
+class Module
+ def infect_with_assertions pos_prefix, neg_prefix, skip_re, map = {}
+ MiniTest::Assertions.public_instance_methods(false).each do |meth|
+ meth = meth.to_s
+
+ new_name = case meth
+ when /^assert/ then
+ meth.sub(/^assert/, pos_prefix.to_s)
+ when /^refute/ then
+ meth.sub(/^refute/, neg_prefix.to_s)
+ end
+ next unless new_name
+ next if new_name =~ skip_re
+
+ regexp, replacement = map.find { |re, _| new_name =~ re }
+ new_name.sub! regexp, replacement if replacement
+
+ # warn "%-22p -> %p %p" % [meth, new_name, regexp]
+ self.class_eval <<-EOM
+ def #{new_name} *args, &block
+ return MiniTest::Spec.current.#{meth}(*args, &self) if Proc === self
+ return MiniTest::Spec.current.#{meth}(args.first, self) if args.size == 1
+ return MiniTest::Spec.current.#{meth}(self, *args)
+ end
+ EOM
+ end
+ end
+end
+
+Object.infect_with_assertions(:must, :wont,
+ /^(must|wont)$|wont_(throw)|
+ must_(block|not?_|nothing|raise$)/x,
+ /(must_throw)s/ => '\1',
+ /(?!not)_same/ => '_be_same_as',
+ /_in_/ => '_be_within_',
+ /_operator/ => '_be',
+ /_includes/ => '_include',
+ /(must|wont)_(.*_of|nil|empty)/ => '\1_be_\2',
+ /must_raises/ => 'must_raise')
+
+class Object
+ alias :must_be_close_to :must_be_within_delta
+ alias :wont_be_close_to :wont_be_within_delta
+end
+
+module Kernel
+ def describe desc, &block
+ cls = Class.new(MiniTest::Spec)
+ Object.const_set desc.to_s.split(/\W+/).map { |s| s.capitalize }.join, cls
+
+ cls.class_eval(&block)
+ end
+ private :describe
+end
+
+class MiniTest::Spec < MiniTest::Unit::TestCase
+ def self.current
+ @@current_spec
+ end
+
+ def initialize name
+ super
+ @@current_spec = self
+ end
+
+ def self.before(type = :each, &block)
+ raise "unsupported before type: #{type}" unless type == :each
+ define_method :setup, &block
+ end
+
+ def self.after(type = :each, &block)
+ raise "unsupported after type: #{type}" unless type == :each
+ define_method :teardown, &block
+ end
+
+ def self.it desc, &block
+ define_method "test_#{desc.gsub(/\W+/, '_').downcase}", &block
+ end
+end
Deleted: MacRuby/branches/experimental/lib/minitest/unit.rb
===================================================================
--- MacRuby/trunk/lib/minitest/unit.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/minitest/unit.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,497 +0,0 @@
-############################################################
-# This file is imported from a different project.
-# DO NOT make modifications in this repo.
-# File a patch instead and assign it to Ryan Davis
-############################################################
-
-##
-#
-# Totally minimal drop-in replacement for test-unit
-#
-# TODO: refute -> debunk, prove/rebut, show/deny... lots of possibilities
-
-module MiniTest
- class Assertion < Exception; end
- class Skip < Assertion; end
-
- file = if RUBY_VERSION =~ /^1\.9/ then # bt's expanded, but __FILE__ isn't :(
- File.expand_path __FILE__
- elsif __FILE__ =~ /^[^\.]/ then # assume both relative
- require 'pathname'
- pwd = Pathname.new Dir.pwd
- pn = Pathname.new File.expand_path(__FILE__)
- pn = File.join(".", pn.relative_path_from(pwd)) unless pn.relative?
- pn.to_s
- else # assume both are expanded
- __FILE__
- end
-
- # './lib' in project dir, or '/usr/local/blahblah' if installed
- MINI_DIR = File.dirname(File.dirname(file))
-
- def self.filter_backtrace bt
- return ["No backtrace"] unless bt
-
- new_bt = []
- bt.each do |line|
- break if line.rindex(MINI_DIR, 0)
- new_bt << line
- end
-
- new_bt = bt.reject { |line| line.rindex(MINI_DIR, 0) } if new_bt.empty?
- new_bt = bt.dup if new_bt.empty?
- new_bt
- end
-
- module Assertions
- def mu_pp(obj)
- s = obj.inspect
- s = s.force_encoding(Encoding.default_external) if defined? Encoding
- s
- end
-
- def _assertions= n
- @_assertions = n
- end
-
- def _assertions
- @_assertions ||= 0
- end
-
- def assert test, msg = nil
- msg ||= "Failed assertion, no message given."
- self._assertions += 1
- unless test then
- msg = msg.call if Proc === msg
- raise MiniTest::Assertion, msg
- end
- true
- end
-
- def assert_block msg = nil
- msg = message(msg) { "Expected block to return true value" }
- assert yield, msg
- end
-
- def assert_empty obj, msg = nil
- msg = message(msg) { "Expected #{obj.inspect} to be empty" }
- assert_respond_to obj, :empty?
- assert obj.empty?, msg
- end
-
- def assert_equal exp, act, msg = nil
- msg = message(msg) { "Expected #{mu_pp(exp)}, not #{mu_pp(act)}" }
- assert(exp == act, msg)
- end
-
- def assert_in_delta exp, act, delta = 0.001, msg = nil
- n = (exp - act).abs
- msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to be < #{delta}" }
- assert delta >= n, msg
- end
-
- def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
- assert_in_delta a, b, [a, b].min * epsilon, msg
- end
-
- def assert_includes collection, obj, msg = nil
- msg = message(msg) { "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}" }
- assert_respond_to collection, :include?
- assert collection.include?(obj), msg
- end
-
- def assert_instance_of cls, obj, msg = nil
- msg = message(msg) { "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}" }
- flip = (Module === obj) && ! (Module === cls) # HACK for specs
- obj, cls = cls, obj if flip
- assert obj.instance_of?(cls), msg
- end
-
- def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
- msg = message(msg) {
- "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
- flip = (Module === obj) && ! (Module === cls) # HACK for specs
- obj, cls = cls, obj if flip
- assert obj.kind_of?(cls), msg
- end
-
- def assert_match exp, act, msg = nil
- msg = message(msg) { "Expected #{mu_pp(exp)} to match #{mu_pp(act)}" }
- assert_respond_to act, :"=~"
- exp = /#{Regexp.escape(exp)}/ if String === exp && String === act
- assert exp =~ act, msg
- end
-
- def assert_nil obj, msg = nil
- msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
- assert obj.nil?, msg
- end
-
- def assert_operator o1, op, o2, msg = nil
- msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
- assert o1.__send__(op, o2), msg
- end
-
- def assert_raises *exp
- msg = String === exp.last ? exp.pop : nil
- should_raise = false
- begin
- yield
- should_raise = true
- rescue Exception => e
- assert(exp.any? { |ex|
- ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class
- }, exception_details(e, "#{mu_pp(exp)} exception expected, not"))
-
- return e
- end
-
- exp = exp.first if exp.size == 1
- flunk "#{mu_pp(exp)} expected but nothing was raised." if should_raise
- end
-
- def assert_respond_to obj, meth, msg = nil
- msg = message(msg) {
- "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
- }
- flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
- obj, meth = meth, obj if flip
- assert obj.respond_to?(meth), msg
- end
-
- def assert_same exp, act, msg = nil
- msg = message(msg) {
- data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
- "Expected %s (0x%x) to be the same as %s (0x%x)" % data
- }
- assert exp.equal?(act), msg
- end
-
- def assert_send send_ary, m = nil
- recv, msg, *args = send_ary
- m = message(m) {
- "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
- assert recv.__send__(msg, *args), m
- end
-
- def assert_throws sym, msg = nil
- default = "Expected #{mu_pp(sym)} to have been thrown"
- caught = true
- catch(sym) do
- begin
- yield
- rescue ArgumentError => e # 1.9 exception
- default += ", not #{e.message.split(/ /).last}"
- rescue NameError => e # 1.8 exception
- default += ", not #{e.name.inspect}"
- end
- caught = false
- end
-
- assert caught, message(msg) { default }
- end
-
- def capture_io
- require 'stringio'
-
- orig_stdout, orig_stderr = $stdout, $stderr
- captured_stdout, captured_stderr = StringIO.new, StringIO.new
- $stdout, $stderr = captured_stdout, captured_stderr
-
- yield
-
- return captured_stdout.string, captured_stderr.string
- ensure
- $stdout = orig_stdout
- $stderr = orig_stderr
- end
-
- def exception_details e, msg
- "#{msg}\nClass: <#{e.class}>\nMessage: <#{e.message.inspect}>\n---Backtrace---\n#{MiniTest::filter_backtrace(e.backtrace).join("\n")}\n---------------"
- end
-
- def flunk msg = nil
- msg ||= "Epic Fail!"
- assert false, msg
- end
-
- def message msg = nil, &default
- proc {
- if msg then
- msg = msg.to_s unless String === msg
- msg += '.' unless msg.empty?
- msg += "\n#{default.call}."
- msg.strip
- else
- "#{default.call}."
- end
- }
- end
-
- # used for counting assertions
- def pass msg = nil
- assert true
- end
-
- def refute test, msg = nil
- msg ||= "Failed refutation, no message given"
- not assert(! test, msg)
- end
-
- def refute_empty obj, msg = nil
- msg = message(msg) { "Expected #{obj.inspect} to not be empty" }
- assert_respond_to obj, :empty?
- refute obj.empty?, msg
- end
-
- def refute_equal exp, act, msg = nil
- msg = message(msg) { "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}" }
- refute exp == act, msg
- end
-
- def refute_in_delta exp, act, delta = 0.001, msg = nil
- n = (exp - act).abs
- msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to not be < #{delta}" }
- refute delta > n, msg
- end
-
- def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
- refute_in_delta a, b, a * epsilon, msg
- end
-
- def refute_includes collection, obj, msg = nil
- msg = message(msg) { "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}" }
- assert_respond_to collection, :include?
- refute collection.include?(obj), msg
- end
-
- def refute_instance_of cls, obj, msg = nil
- msg = message(msg) { "Expected #{mu_pp(obj)} to not be an instance of #{cls}" }
- flip = (Module === obj) && ! (Module === cls) # HACK for specs
- obj, cls = cls, obj if flip
- refute obj.instance_of?(cls), msg
- end
-
- def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
- msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
- flip = (Module === obj) && ! (Module === cls) # HACK for specs
- obj, cls = cls, obj if flip
- refute obj.kind_of?(cls), msg
- end
-
- def refute_match exp, act, msg = nil
- msg = message(msg) { "Expected #{mu_pp(exp)} to not match #{mu_pp(act)}" }
- assert_respond_to act, :"=~"
- exp = /#{Regexp.escape(exp)}/ if String === exp && String === act
- refute exp =~ act, msg
- end
-
- def refute_nil obj, msg = nil
- msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
- refute obj.nil?, msg
- end
-
- def refute_operator o1, op, o2, msg = nil
- msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}" }
- refute o1.__send__(op, o2), msg
- end
-
- def refute_respond_to obj, meth, msg = nil
- msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
- flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
- obj, meth = meth, obj if flip
- refute obj.respond_to?(meth), msg
- end
-
- def refute_same exp, act, msg = nil
- msg = message(msg) { "Expected #{mu_pp(act)} to not be the same as #{mu_pp(exp)}" }
- refute exp.equal?(act), msg
- end
-
- def skip msg = nil, bt = caller
- msg ||= "Skipped, no message given"
- raise MiniTest::Skip, msg, bt
- end
- end
-
- class Unit
- VERSION = "1.3.1"
-
- attr_accessor :report, :failures, :errors, :skips
- attr_accessor :test_count, :assertion_count
-
- @@installed_at_exit ||= false
- @@out = $stdout
-
- def self.autorun
- at_exit {
- next if $! # don't run if there was an exception
- exit_code = MiniTest::Unit.new.run(ARGV)
- exit false if exit_code && exit_code != 0
- } unless @@installed_at_exit
- @@installed_at_exit = true
- end
-
- def self.output= stream
- @@out = stream
- end
-
- def location e
- last_before_assertion = ""
- e.backtrace.reverse_each do |s|
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise)/
- last_before_assertion = s
- end
- last_before_assertion.sub(/:in .*$/, '')
- end
-
- def puke klass, meth, e
- e = case e
- when MiniTest::Skip then
- @skips += 1
- "Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
- when MiniTest::Assertion then
- @failures += 1
- "Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
- else
- @errors += 1
- bt = MiniTest::filter_backtrace(e.backtrace).join("\n ")
- "Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
- end
- @report << e
- e[0, 1]
- end
-
- def initialize
- @report = []
- @errors = @failures = @skips = 0
- @verbose = false
- end
-
- ##
- # Top level driver, controls all output and filtering.
-
- def run args = []
- @verbose = args.delete('-v')
-
- filter = if args.first =~ /^(-n|--name)$/ then
- args.shift
- arg = args.shift
- arg =~ /\/(.*)\// ? Regexp.new($1) : arg
- else
- /./ # anything - ^test_ already filtered by #tests
- end
-
- @@out.puts "Loaded suite #{$0.sub(/\.rb$/, '')}\nStarted"
-
- start = Time.now
- run_test_suites filter
-
- @@out.puts
- @@out.puts "Finished in #{'%.6f' % (Time.now - start)} seconds."
-
- @report.each_with_index do |msg, i|
- @@out.puts "\n%3d) %s" % [i + 1, msg]
- end
-
- @@out.puts
-
- format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
- @@out.puts format % [test_count, assertion_count, failures, errors, skips]
-
- return failures + errors if @test_count > 0 # or return nil...
- end
-
- def run_test_suites filter = /./
- @test_count, @assertion_count = 0, 0
- old_sync, @@out.sync = @@out.sync, true if @@out.respond_to? :sync=
- TestCase.test_suites.each do |suite|
- suite.test_methods.grep(filter).each do |test|
- inst = suite.new test
- inst._assertions = 0
- @@out.print "#{suite}##{test}: " if @verbose
-
- t = Time.now if @verbose
- result = inst.run(self)
-
- @@out.print "%.2f s: " % (Time.now - t) if @verbose
- @@out.print result
- @@out.puts if @verbose
- @test_count += 1
- @assertion_count += inst._assertions
- end
- end
- @@out.sync = old_sync if @@out.respond_to? :sync=
- [@test_count, @assertion_count]
- end
-
- class TestCase
- attr_reader :name
-
- def run runner
- result = '.'
- begin
- @passed = nil
- self.setup
- self.__send__ self.name
- @passed = true
- rescue Exception => e
- @passed = false
- result = runner.puke(self.class, self.name, e)
- ensure
- begin
- self.teardown
- rescue Exception => e
- result = runner.puke(self.class, self.name, e)
- end
- end
- result
- end
-
- def initialize name
- @name = name
- @passed = nil
- end
-
- def self.reset
- @@test_suites = {}
- end
-
- reset
-
- def self.inherited klass
- @@test_suites[klass] = true
- end
-
- def self.test_order
- :random
- end
-
- def self.test_suites
- @@test_suites.keys.sort_by { |ts| ts.name }
- end
-
- def self.test_methods
- methods = public_instance_methods(true).grep(/^test/).map { |m|
- m.to_s
- }.sort
-
- if self.test_order == :random then
- max = methods.size
- methods = methods.sort_by { rand(max) }
- end
-
- methods
- end
-
- def setup; end
- def teardown; end
-
- def passed?
- @passed
- end
-
- include MiniTest::Assertions
- end # class TestCase
- end # class Test
-end # module Mini
Copied: MacRuby/branches/experimental/lib/minitest/unit.rb (from rev 1886, MacRuby/trunk/lib/minitest/unit.rb)
===================================================================
--- MacRuby/branches/experimental/lib/minitest/unit.rb (rev 0)
+++ MacRuby/branches/experimental/lib/minitest/unit.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,497 @@
+############################################################
+# This file is imported from a different project.
+# DO NOT make modifications in this repo.
+# File a patch instead and assign it to Ryan Davis
+############################################################
+
+##
+#
+# Totally minimal drop-in replacement for test-unit
+#
+# TODO: refute -> debunk, prove/rebut, show/deny... lots of possibilities
+
+module MiniTest
+ class Assertion < Exception; end
+ class Skip < Assertion; end
+
+ file = if RUBY_VERSION =~ /^1\.9/ then # bt's expanded, but __FILE__ isn't :(
+ File.expand_path __FILE__
+ elsif __FILE__ =~ /^[^\.]/ then # assume both relative
+ require 'pathname'
+ pwd = Pathname.new Dir.pwd
+ pn = Pathname.new File.expand_path(__FILE__)
+ pn = File.join(".", pn.relative_path_from(pwd)) unless pn.relative?
+ pn.to_s
+ else # assume both are expanded
+ __FILE__
+ end
+
+ # './lib' in project dir, or '/usr/local/blahblah' if installed
+ MINI_DIR = File.dirname(File.dirname(file))
+
+ def self.filter_backtrace bt
+ return ["No backtrace"] unless bt
+
+ new_bt = []
+ bt.each do |line|
+ break if line.rindex(MINI_DIR, 0)
+ new_bt << line
+ end
+
+ new_bt = bt.reject { |line| line.rindex(MINI_DIR, 0) } if new_bt.empty?
+ new_bt = bt.dup if new_bt.empty?
+ new_bt
+ end
+
+ module Assertions
+ def mu_pp(obj)
+ s = obj.inspect
+ s = s.force_encoding(Encoding.default_external) if defined? Encoding
+ s
+ end
+
+ def _assertions= n
+ @_assertions = n
+ end
+
+ def _assertions
+ @_assertions ||= 0
+ end
+
+ def assert test, msg = nil
+ msg ||= "Failed assertion, no message given."
+ self._assertions += 1
+ unless test then
+ msg = msg.call if Proc === msg
+ raise MiniTest::Assertion, msg
+ end
+ true
+ end
+
+ def assert_block msg = nil
+ msg = message(msg) { "Expected block to return true value" }
+ assert yield, msg
+ end
+
+ def assert_empty obj, msg = nil
+ msg = message(msg) { "Expected #{obj.inspect} to be empty" }
+ assert_respond_to obj, :empty?
+ assert obj.empty?, msg
+ end
+
+ def assert_equal exp, act, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(exp)}, not #{mu_pp(act)}" }
+ assert(exp == act, msg)
+ end
+
+ def assert_in_delta exp, act, delta = 0.001, msg = nil
+ n = (exp - act).abs
+ msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to be < #{delta}" }
+ assert delta >= n, msg
+ end
+
+ def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
+ assert_in_delta a, b, [a, b].min * epsilon, msg
+ end
+
+ def assert_includes collection, obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}" }
+ assert_respond_to collection, :include?
+ assert collection.include?(obj), msg
+ end
+
+ def assert_instance_of cls, obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}" }
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
+ obj, cls = cls, obj if flip
+ assert obj.instance_of?(cls), msg
+ end
+
+ def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
+ obj, cls = cls, obj if flip
+ assert obj.kind_of?(cls), msg
+ end
+
+ def assert_match exp, act, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(exp)} to match #{mu_pp(act)}" }
+ assert_respond_to act, :"=~"
+ exp = /#{Regexp.escape(exp)}/ if String === exp && String === act
+ assert exp =~ act, msg
+ end
+
+ def assert_nil obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
+ assert obj.nil?, msg
+ end
+
+ def assert_operator o1, op, o2, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
+ assert o1.__send__(op, o2), msg
+ end
+
+ def assert_raises *exp
+ msg = String === exp.last ? exp.pop : nil
+ should_raise = false
+ begin
+ yield
+ should_raise = true
+ rescue Exception => e
+ assert(exp.any? { |ex|
+ ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class
+ }, exception_details(e, "#{mu_pp(exp)} exception expected, not"))
+
+ return e
+ end
+
+ exp = exp.first if exp.size == 1
+ flunk "#{mu_pp(exp)} expected but nothing was raised." if should_raise
+ end
+
+ def assert_respond_to obj, meth, msg = nil
+ msg = message(msg) {
+ "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
+ }
+ flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
+ obj, meth = meth, obj if flip
+ assert obj.respond_to?(meth), msg
+ end
+
+ def assert_same exp, act, msg = nil
+ msg = message(msg) {
+ data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
+ "Expected %s (0x%x) to be the same as %s (0x%x)" % data
+ }
+ assert exp.equal?(act), msg
+ end
+
+ def assert_send send_ary, m = nil
+ recv, msg, *args = send_ary
+ m = message(m) {
+ "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
+ assert recv.__send__(msg, *args), m
+ end
+
+ def assert_throws sym, msg = nil
+ default = "Expected #{mu_pp(sym)} to have been thrown"
+ caught = true
+ catch(sym) do
+ begin
+ yield
+ rescue ArgumentError => e # 1.9 exception
+ default += ", not #{e.message.split(/ /).last}"
+ rescue NameError => e # 1.8 exception
+ default += ", not #{e.name.inspect}"
+ end
+ caught = false
+ end
+
+ assert caught, message(msg) { default }
+ end
+
+ def capture_io
+ require 'stringio'
+
+ orig_stdout, orig_stderr = $stdout, $stderr
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
+ $stdout, $stderr = captured_stdout, captured_stderr
+
+ yield
+
+ return captured_stdout.string, captured_stderr.string
+ ensure
+ $stdout = orig_stdout
+ $stderr = orig_stderr
+ end
+
+ def exception_details e, msg
+ "#{msg}\nClass: <#{e.class}>\nMessage: <#{e.message.inspect}>\n---Backtrace---\n#{MiniTest::filter_backtrace(e.backtrace).join("\n")}\n---------------"
+ end
+
+ def flunk msg = nil
+ msg ||= "Epic Fail!"
+ assert false, msg
+ end
+
+ def message msg = nil, &default
+ proc {
+ if msg then
+ msg = msg.to_s unless String === msg
+ msg += '.' unless msg.empty?
+ msg += "\n#{default.call}."
+ msg.strip
+ else
+ "#{default.call}."
+ end
+ }
+ end
+
+ # used for counting assertions
+ def pass msg = nil
+ assert true
+ end
+
+ def refute test, msg = nil
+ msg ||= "Failed refutation, no message given"
+ not assert(! test, msg)
+ end
+
+ def refute_empty obj, msg = nil
+ msg = message(msg) { "Expected #{obj.inspect} to not be empty" }
+ assert_respond_to obj, :empty?
+ refute obj.empty?, msg
+ end
+
+ def refute_equal exp, act, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}" }
+ refute exp == act, msg
+ end
+
+ def refute_in_delta exp, act, delta = 0.001, msg = nil
+ n = (exp - act).abs
+ msg = message(msg) { "Expected #{exp} - #{act} (#{n}) to not be < #{delta}" }
+ refute delta > n, msg
+ end
+
+ def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
+ refute_in_delta a, b, a * epsilon, msg
+ end
+
+ def refute_includes collection, obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}" }
+ assert_respond_to collection, :include?
+ refute collection.include?(obj), msg
+ end
+
+ def refute_instance_of cls, obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be an instance of #{cls}" }
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
+ obj, cls = cls, obj if flip
+ refute obj.instance_of?(cls), msg
+ end
+
+ def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
+ flip = (Module === obj) && ! (Module === cls) # HACK for specs
+ obj, cls = cls, obj if flip
+ refute obj.kind_of?(cls), msg
+ end
+
+ def refute_match exp, act, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(exp)} to not match #{mu_pp(act)}" }
+ assert_respond_to act, :"=~"
+ exp = /#{Regexp.escape(exp)}/ if String === exp && String === act
+ refute exp =~ act, msg
+ end
+
+ def refute_nil obj, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
+ refute obj.nil?, msg
+ end
+
+ def refute_operator o1, op, o2, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}" }
+ refute o1.__send__(op, o2), msg
+ end
+
+ def refute_respond_to obj, meth, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
+ flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs
+ obj, meth = meth, obj if flip
+ refute obj.respond_to?(meth), msg
+ end
+
+ def refute_same exp, act, msg = nil
+ msg = message(msg) { "Expected #{mu_pp(act)} to not be the same as #{mu_pp(exp)}" }
+ refute exp.equal?(act), msg
+ end
+
+ def skip msg = nil, bt = caller
+ msg ||= "Skipped, no message given"
+ raise MiniTest::Skip, msg, bt
+ end
+ end
+
+ class Unit
+ VERSION = "1.3.1"
+
+ attr_accessor :report, :failures, :errors, :skips
+ attr_accessor :test_count, :assertion_count
+
+ @@installed_at_exit ||= false
+ @@out = $stdout
+
+ def self.autorun
+ at_exit {
+ next if $! # don't run if there was an exception
+ exit_code = MiniTest::Unit.new.run(ARGV)
+ exit false if exit_code && exit_code != 0
+ } unless @@installed_at_exit
+ @@installed_at_exit = true
+ end
+
+ def self.output= stream
+ @@out = stream
+ end
+
+ def location e
+ last_before_assertion = ""
+ e.backtrace.reverse_each do |s|
+ break if s =~ /in .(assert|refute|flunk|pass|fail|raise)/
+ last_before_assertion = s
+ end
+ last_before_assertion.sub(/:in .*$/, '')
+ end
+
+ def puke klass, meth, e
+ e = case e
+ when MiniTest::Skip then
+ @skips += 1
+ "Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
+ when MiniTest::Assertion then
+ @failures += 1
+ "Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
+ else
+ @errors += 1
+ bt = MiniTest::filter_backtrace(e.backtrace).join("\n ")
+ "Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
+ end
+ @report << e
+ e[0, 1]
+ end
+
+ def initialize
+ @report = []
+ @errors = @failures = @skips = 0
+ @verbose = false
+ end
+
+ ##
+ # Top level driver, controls all output and filtering.
+
+ def run args = []
+ @verbose = args.delete('-v')
+
+ filter = if args.first =~ /^(-n|--name)$/ then
+ args.shift
+ arg = args.shift
+ arg =~ /\/(.*)\// ? Regexp.new($1) : arg
+ else
+ /./ # anything - ^test_ already filtered by #tests
+ end
+
+ @@out.puts "Loaded suite #{$0.sub(/\.rb$/, '')}\nStarted"
+
+ start = Time.now
+ run_test_suites filter
+
+ @@out.puts
+ @@out.puts "Finished in #{'%.6f' % (Time.now - start)} seconds."
+
+ @report.each_with_index do |msg, i|
+ @@out.puts "\n%3d) %s" % [i + 1, msg]
+ end
+
+ @@out.puts
+
+ format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
+ @@out.puts format % [test_count, assertion_count, failures, errors, skips]
+
+ return failures + errors if @test_count > 0 # or return nil...
+ end
+
+ def run_test_suites filter = /./
+ @test_count, @assertion_count = 0, 0
+ old_sync, @@out.sync = @@out.sync, true if @@out.respond_to? :sync=
+ TestCase.test_suites.each do |suite|
+ suite.test_methods.grep(filter).each do |test|
+ inst = suite.new test
+ inst._assertions = 0
+ @@out.print "#{suite}##{test}: " if @verbose
+
+ t = Time.now if @verbose
+ result = inst.run(self)
+
+ @@out.print "%.2f s: " % (Time.now - t) if @verbose
+ @@out.print result
+ @@out.puts if @verbose
+ @test_count += 1
+ @assertion_count += inst._assertions
+ end
+ end
+ @@out.sync = old_sync if @@out.respond_to? :sync=
+ [@test_count, @assertion_count]
+ end
+
+ class TestCase
+ attr_reader :name
+
+ def run runner
+ result = '.'
+ begin
+ @passed = nil
+ self.setup
+ self.__send__ self.name
+ @passed = true
+ rescue Exception => e
+ @passed = false
+ result = runner.puke(self.class, self.name, e)
+ ensure
+ begin
+ self.teardown
+ rescue Exception => e
+ result = runner.puke(self.class, self.name, e)
+ end
+ end
+ result
+ end
+
+ def initialize name
+ @name = name
+ @passed = nil
+ end
+
+ def self.reset
+ @@test_suites = {}
+ end
+
+ reset
+
+ def self.inherited klass
+ @@test_suites[klass] = true
+ end
+
+ def self.test_order
+ :random
+ end
+
+ def self.test_suites
+ @@test_suites.keys.sort_by { |ts| ts.name }
+ end
+
+ def self.test_methods
+ methods = public_instance_methods(true).grep(/^test/).map { |m|
+ m.to_s
+ }.sort
+
+ if self.test_order == :random then
+ max = methods.size
+ methods = methods.sort_by { rand(max) }
+ end
+
+ methods
+ end
+
+ def setup; end
+ def teardown; end
+
+ def passed?
+ @passed
+ end
+
+ include MiniTest::Assertions
+ end # class TestCase
+ end # class Test
+end # module Mini
Modified: MacRuby/branches/experimental/lib/mkmf.rb
===================================================================
--- MacRuby/branches/experimental/lib/mkmf.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/mkmf.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -9,11 +9,12 @@
ORIG_LIBPATH = ENV['LIB']
CXX_EXT = %w[cc cxx cpp]
-if /mswin|bccwin|mingw|msdosdjgpp|human|os2/ !~ CONFIG['build_os']
+if /mswin|bccwin|mingw|os2/ !~ CONFIG['build_os']
CXX_EXT.concat(%w[C])
end
SRC_EXT = %w[c m] << CXX_EXT
-$static = $config_h = nil
+$static = nil
+$config_h = '$(arch_hdrdir)/ruby/config.h'
$default_static = $static
unless defined? $configure_args
@@ -59,10 +60,10 @@
$bccwin = /bccwin/ =~ RUBY_PLATFORM
$mingw = /mingw/ =~ RUBY_PLATFORM
$cygwin = /cygwin/ =~ RUBY_PLATFORM
-$human = /human/ =~ RUBY_PLATFORM
$netbsd = /netbsd/ =~ RUBY_PLATFORM
$os2 = /os2/ =~ RUBY_PLATFORM
$beos = /beos/ =~ RUBY_PLATFORM
+$haiku = /haiku/ =~ RUBY_PLATFORM
$solaris = /solaris/ =~ RUBY_PLATFORM
$dest_prefix_pattern = (File::PATH_SEPARATOR == ';' ? /\A([[:alpha:]]:)?/ : /\A/)
@@ -158,8 +159,7 @@
$topdir = $hdrdir
$top_srcdir = $hdrdir
$arch_hdrdir = $hdrdir + "/$(arch)"
-elsif File.exist?(($hdrdir = ($top_srcdir ||= topdir) + "/include") + "/ruby.h") and
- File.exist?("#{CONFIG["EXTOUT"]}/include/#{CONFIG["arch"]}/ruby/config.h")
+elsif File.exist?(($hdrdir = ($top_srcdir ||= topdir) + "/include") + "/ruby.h")
$topdir ||= RbConfig::CONFIG["topdir"]
$arch_hdrdir = "$(extout)/include/$(arch)"
else
@@ -194,6 +194,10 @@
FileUtils.rm_f(Dir[*files])
end
+def rm_rf(*files)
+ FileUtils.rm_rf(Dir[*files])
+end
+
# Returns time stamp of the +target+ file if it exists and is newer
# than or equal to all of +times+.
def modified?(target, times)
@@ -232,9 +236,13 @@
@postpone = 0
@quiet = $extmk
+ def self::log_open
+ @log ||= File::open(@logfile, 'wb')
+ @log.sync = true
+ end
+
def self::open
- @log ||= File::open(@logfile, 'w')
- @log.sync = true
+ log_open
$stderr.reopen(@log)
$stdout.reopen(@log)
yield
@@ -244,8 +252,7 @@
end
def self::message(*s)
- @log ||= File::open(@logfile, 'w')
- @log.sync = true
+ log_open
@log.printf(*s)
end
@@ -320,8 +327,11 @@
end
def create_tmpsrc(src)
+ src = "#{COMMON_HEADERS}\n#{src}"
src = yield(src) if block_given?
- src = src.gsub(/[ \t]+$/, '').gsub(/\A\n+|^\n+$/, '').sub(/[^\n]\z/, "\\&\n")
+ src.gsub!(/[ \t]+$/, '')
+ src.gsub!(/\A\n+|^\n+$/, '')
+ src.sub!(/[^\n]\z/, "\\&\n")
count = 0
begin
open(CONFTEST_C, "wb") do |cfile|
@@ -336,11 +346,26 @@
src
end
+def have_devel?
+ unless defined? $have_devel
+ $have_devel = true
+ $have_devel = try_link(MAIN_DOES_NOTHING)
+ end
+ $have_devel
+end
+
def try_do(src, command, &b)
+ unless have_devel?
+ raise <<MSG
+The complier failed to generate an executable file.
+You have to install development tools first.
+MSG
+ end
src = create_tmpsrc(src, &b)
xsystem(command)
ensure
log_src(src)
+ rm_rf 'conftest.dSYM'
end
def link_command(ldflags, opt="", libpath=$DEFLIBPATH|$LIBPATH)
@@ -372,7 +397,7 @@
'arch_hdrdir' => "#$arch_hdrdir",
'top_srcdir' => $top_srcdir.quote)
RbConfig::expand("$(CPP) #$INCFLAGS #$CPPFLAGS #$CFLAGS #{opt} #{CONFTEST_C} #{outfile}",
- conf).gsub(/-arch\s+\w+/, '')
+ conf).gsub(/-arch\s+\w+/,'')
end
def libpathflag(libpath=$DEFLIBPATH|$LIBPATH)
@@ -444,7 +469,6 @@
def try_static_assert(expr, headers = nil, opt = "", &b)
headers = cpp_include(headers)
try_compile(<<SRC, opt, &b)
-#{COMMON_HEADERS}
#{headers}
/*top*/
int conftest_const[(#{expr}) ? 1 : -1];
@@ -483,8 +507,7 @@
upper = -upper if neg
return upper
else
- src = %{#{COMMON_HEADERS}
-#{includes}
+ src = %{#{includes}
#include <stdio.h>
/*top*/
int conftest_const = (int)(#{const});
@@ -501,16 +524,16 @@
def try_func(func, libs, headers = nil, &b)
headers = cpp_include(headers)
- try_link(<<"SRC", libs, &b) or try_link(<<"SRC", libs, &b)
-#{COMMON_HEADERS}
+ try_link(<<"SRC", libs, &b) or
#{headers}
/*top*/
-int main() { return 0; }
+#{MAIN_DOES_NOTHING}
int t() { void ((*volatile p)()); p = (void ((*)()))#{func}; return 0; }
SRC
+ try_link(<<"SRC", libs, &b)
#{headers}
/*top*/
-int main() { return 0; }
+#{MAIN_DOES_NOTHING}
int t() { #{func}(); return 0; }
SRC
end
@@ -518,10 +541,9 @@
def try_var(var, headers = nil, &b)
headers = cpp_include(headers)
try_compile(<<"SRC", &b)
-#{COMMON_HEADERS}
#{headers}
/*top*/
-int main() { return 0; }
+#{MAIN_DOES_NOTHING}
int t() { const volatile void *volatile p; p = &(&#{var})[0]; return 0; }
SRC
end
@@ -785,7 +807,7 @@
def have_header(header, &b)
checking_for header do
if try_cpp(cpp_include(header), &b)
- # FIXME: cannot use this in MacRuby yet
+ #FIXME: cannot use this in MacRuby yet
#$defs.push(format("-DHAVE_%s", header.tr("a-z./\055", "A-Z___")))
str = header.tr("a-z", "A-Z").tr("./\055", "_")
$defs.push(format("-DHAVE_%s", str))
@@ -839,10 +861,9 @@
def have_struct_member(type, member, headers = nil, &b)
checking_for checking_message("#{type}.#{member}", headers) do
if try_compile(<<"SRC", &b)
-#{COMMON_HEADERS}
#{cpp_include(headers)}
/*top*/
-int main() { return 0; }
+#{MAIN_DOES_NOTHING}
int s = (char *)&((#{type}*)0)->#{member} - (char *)0;
SRC
$defs.push(format("-DHAVE_%s_%s", type.tr_cpp, member.tr_cpp))
@@ -856,7 +877,6 @@
def try_type(type, headers = nil, opt = "", &b)
if try_compile(<<"SRC", opt, &b)
-#{COMMON_HEADERS}
#{cpp_include(headers)}
/*top*/
typedef #{type} conftest_type;
@@ -911,7 +931,6 @@
def try_const(const, headers = nil, opt = "", &b)
const, type = *const
if try_compile(<<"SRC", opt, &b)
-#{COMMON_HEADERS}
#{cpp_include(headers)}
/*top*/
typedef #{type || 'int'} conftest_type;
@@ -976,11 +995,10 @@
# pointer.
def scalar_ptr_type?(type, member = nil, headers = nil, &b)
try_compile(<<"SRC", &b) # pointer
-#{COMMON_HEADERS}
#{cpp_include(headers)}
/*top*/
volatile #{type} conftestval;
-int main() { return 0; }
+#{MAIN_DOES_NOTHING}
int t() {return (int)(1-*(conftestval#{member ? ".#{member}" : ""}));}
SRC
end
@@ -989,11 +1007,10 @@
# pointer.
def scalar_type?(type, member = nil, headers = nil, &b)
try_compile(<<"SRC", &b) # pointer
-#{COMMON_HEADERS}
#{cpp_include(headers)}
/*top*/
volatile #{type} conftestval;
-int main() { return 0; }
+#{MAIN_DOES_NOTHING}
int t() {return (int)(1-(conftestval#{member ? ".#{member}" : ""}));}
SRC
end
@@ -1171,7 +1188,7 @@
#
def create_header(header = "extconf.h")
message "creating %s\n", header
- # FIXME: cannot use this in MacRuby yet
+ #FIXME: cannot use this in MacRuby yet
#sym = header.tr("a-z./\055", "A-Z___")
sym = header.tr("a-z", "A-Z").tr("./\055", "_")
hdr = ["#ifndef #{sym}\n#define #{sym}\n"]
@@ -1287,6 +1304,29 @@
s.tr('/', '\\')
end
+# Converts native path to format acceptable in Makefile
+#
+# Internal use only.
+#
+if !CROSS_COMPILING
+ case CONFIG['build_os']
+ when 'mingw32'
+ def mkintpath(path)
+ # mingw uses make from msys and it needs special care
+ # converts from C:\some\path to /C/some/path
+ path = path.dup
+ path.tr!('\\', '/')
+ path.sub!(/\A([A-Za-z]):(?=\/)/, '/\1')
+ path
+ end
+ end
+end
+unless defined?(mkintpath)
+ def mkintpath(path)
+ path
+ end
+end
+
def configuration(srcdir)
mk = []
vpath = $VPATH.dup
@@ -1296,8 +1336,6 @@
if CONFIG['target_os'] != 'cygwin'
vpath = vpath.map {|p| p.sub(/.*/, '$(shell cygpath -u \&)')}
end
- when 'msdosdjgpp', 'mingw32'
- CONFIG['PATH_SEPARATOR'] = ';'
end
end
CONFIG["hdrdir"] ||= $hdrdir
@@ -1305,14 +1343,10 @@
SHELL = /bin/sh
#### Start of system configuration section. ####
-#{
-if $extmk
- "top_srcdir = " + $top_srcdir.sub(%r"\A#{Regexp.quote($topdir)}/", "$(topdir)/")
-end
-}
-srcdir = #{srcdir.gsub(/\$\((srcdir)\)|\$\{(srcdir)\}/) {CONFIG[$1||$2]}.quote}
-topdir = #{($extmk ? CONFIG["topdir"] : $topdir).quote}
-hdrdir = #{CONFIG["hdrdir"].quote}
+#{"top_srcdir = " + $top_srcdir.sub(%r"\A#{Regexp.quote($topdir)}/", "$(topdir)/") if $extmk}
+srcdir = #{srcdir.gsub(/\$\((srcdir)\)|\$\{(srcdir)\}/) {mkintpath(CONFIG[$1||$2])}.quote}
+topdir = #{mkintpath($extmk ? CONFIG["topdir"] : $topdir).quote}
+hdrdir = #{mkintpath(CONFIG["hdrdir"]).quote}
arch_hdrdir = #{$arch_hdrdir}
VPATH = #{vpath.join(CONFIG['PATH_SEPARATOR'])}
}
@@ -1338,7 +1372,8 @@
else
sep = ""
end
- extconf_h = $extconf_h ? "-DRUBY_EXTCONF_H=\\\"$(RUBY_EXTCONF_H)\\\" " : $defs.join(" ")<<" "
+ possible_command = (proc {|s| s if /top_srcdir/ !~ s} unless $extmk)
+ extconf_h = $extconf_h ? "-DRUBY_EXTCONF_H=\\\"$(RUBY_EXTCONF_H)\\\" " : $defs.join(" ") << " "
mk << %{
CC = #{CONFIG['CC']}
CXX = #{CONFIG['CXX']}
@@ -1350,6 +1385,10 @@
COUTFLAG = #{COUTFLAG}
RUBY_EXTCONF_H = #{$extconf_h}
+cflags = #{CONFIG['cflags']}
+optflags = #{CONFIG['optflags']}
+debugflags = #{CONFIG['debugflags']}
+warnflags = #{CONFIG['warnflags']}
CFLAGS = #{$static ? '' : CONFIG['CCDLFLAGS']} #$CFLAGS #$ARCH_FLAG
INCFLAGS = -I. #$INCFLAGS
DEFS = #{CONFIG['DEFS']}
@@ -1371,12 +1410,14 @@
ruby_version = #{RbConfig::CONFIG['ruby_version']}
ruby = #{$ruby}
RUBY = $(ruby#{sep})
-RM = #{config_string('RM') || '$(RUBY) -run -e rm -- -f'}
-MAKEDIRS = #{config_string('MAKEDIRS') || '@$(RUBY) -run -e mkdir -- -p'}
-INSTALL = #{config_string('INSTALL') || '@$(RUBY) -run -e install -- -vp'}
+RM = #{config_string('RM', &possible_command) || '$(RUBY) -run -e rm -- -f'}
+RM_RF = #{'$(RUBY) -run -e rm -- -rf'}
+RMDIRS = #{config_string('RMDIRS', &possible_command) || '$(RUBY) -run -e rmdir -- -p'}
+MAKEDIRS = #{config_string('MAKEDIRS', &possible_command) || '@$(RUBY) -run -e mkdir -- -p'}
+INSTALL = #{config_string('INSTALL', &possible_command) || '@$(RUBY) -run -e install -- -vp'}
INSTALL_PROG = #{config_string('INSTALL_PROG') || '$(INSTALL) -m 0755'}
INSTALL_DATA = #{config_string('INSTALL_DATA') || '$(INSTALL) -m 0644'}
-COPY = #{config_string('CP') || '@$(RUBY) -run -e cp -- -v'}
+COPY = #{config_string('CP', &possible_command) || '@$(RUBY) -run -e cp -- -v'}
#### End of system configuration section. ####
@@ -1433,7 +1474,7 @@
end
depend.each_line do |line|
line.gsub!(/\.o\b/, ".#{$OBJEXT}")
- line.gsub!(/\$\((?:hdr|top)dir\)\/config.h/, $config_h) if $config_h
+ line.gsub!(/\$\((?:hdr|top)dir\)\/config.h/, $config_h)
line.gsub!(%r"\$\(hdrdir\)/(?!ruby(?![^:;/\s]))(?=[-\w]+\.h)", '\&ruby/')
if $nmake && /\A\s*\$\(RM|COPY\)/ =~ line
line.gsub!(%r"[-\w\./]{2,}"){$&.tr("/", "\\")}
@@ -1570,6 +1611,11 @@
end
origdef ||= ''
+ if $extout and $INSTALLFILES
+ $cleanfiles.concat($INSTALLFILES.collect {|files, dir|File.join(dir, files.sub(/\A\.\//, ''))})
+ $distcleandirs.concat($INSTALLFILES.collect {|files, dir| dir})
+ end
+
if $extmk and not $extconf_h
create_header
end
@@ -1587,6 +1633,7 @@
CLEANFILES = #{$cleanfiles.join(' ')}
DISTCLEANFILES = #{$distcleanfiles.join(' ')}
+DISTCLEANDIRS = #{$distcleandirs.join(' ')}
extout = #{$extout}
extout_prefix = #{$extout_prefix}
@@ -1603,16 +1650,28 @@
" #"
# TODO: fixme
install_dirs.each {|d| mfile.print("%-14s= %s\n" % d) if /^[[:upper:]]/ =~ d[0]}
- n = ($extout ? '$(RUBYARCHDIR)/' : '') + '$(TARGET).'
+ n = ($extout ? '$(RUBYARCHDIR)/' : '') + '$(TARGET)'
mfile.print "
TARGET_SO = #{($extout ? '$(RUBYARCHDIR)/' : '')}$(DLLIB)
-CLEANLIBS = #{n}#{CONFIG['DLEXT']} #{n}il? #{n}tds #{n}map
-CLEANOBJS = *.#{$OBJEXT} *.#{$LIBEXT} *.s[ol] *.pdb *.exp *.bak
+CLEANLIBS = #{n}.#{CONFIG['DLEXT']} #{config_string('cleanlibs') {|t| t.gsub(/\$\*/) {n}}}
+CLEANOBJS = *.#{$OBJEXT} #{config_string('cleanobjs') {|t| t.gsub(/\$\*/, '$(TARGET)')}} *.bak
-all: #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"}
-static: $(STATIC_LIB)#{$extout ? " install-rb" : ""}
+all: #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"}
+static: $(STATIC_LIB)#{$extout ? " install-rb" : ""}
"
mfile.print CLEANINGS
+ fsep = config_string('BUILD_FILE_SEPARATOR') {|s| s unless s == "/"}
+ if fsep
+ sep = ":/=#{fsep}"
+ fseprepl = proc {|s|
+ s = s.gsub("/", fsep)
+ s = s.gsub(/(\$\(\w+)(\))/) {$1+sep+$2}
+ s = s.gsub(/(\$\{\w+)(\})/) {$1+sep+$2}
+ }
+ else
+ fseprepl = proc {|s| s}
+ sep = ""
+ end
dirs = []
mfile.print "install: install-so install-rb\n\n"
sodir = (dir = "$(RUBYARCHDIR)").dup
@@ -1621,18 +1680,13 @@
f = "$(DLLIB)"
dest = "#{dir}/#{f}"
mfile.puts dir, "install-so: #{dest}"
- unless $extout
+ if $extout
+ mfile.print "clean-so::\n"
+ mfile.print "\t at -$(RM) #{fseprepl[dest]}\n"
+ mfile.print "\t at -$(RMDIRS) #{fseprepl[dir]}\n"
+ else
mfile.print "#{dest}: #{f}\n"
- if (sep = config_string('BUILD_FILE_SEPARATOR'))
- f.gsub!("/", sep)
- dir.gsub!("/", sep)
- sep = ":/="+sep
- f.gsub!(/(\$\(\w+)(\))/) {$1+sep+$2}
- f.gsub!(/(\$\{\w+)(\})/) {$1+sep+$2}
- dir.gsub!(/(\$\(\w+)(\))/) {$1+sep+$2}
- dir.gsub!(/(\$\{\w+)(\})/) {$1+sep+$2}
- end
- mfile.print "\t$(INSTALL_PROG) #{f} #{dir}\n"
+ mfile.print "\t$(INSTALL_PROG) #{fseprepl[f]} #{fseprepl[dir]}\n"
if defined?($installed_list)
mfile.print "\t at echo #{dir}/#{File.basename(f)}>>$(INSTALLED_LIST)\n"
end
@@ -1650,31 +1704,35 @@
unless dirs.include?(dir)
dirs << dir
mfile.print "pre-install-rb#{sfx}: #{dir}\n"
- end if $nmake
+ end
for f in files
dest = "#{dir}/#{File.basename(f)}"
mfile.print("install-rb#{sfx}: #{dest}\n")
mfile.print("#{dest}: #{f}\n")
- mfile.print("\t$(MAKEDIRS) $(@D)\n") unless $nmake
mfile.print("\t$(#{$extout ? 'COPY' : 'INSTALL_DATA'}) ")
- sep = config_string('BUILD_FILE_SEPARATOR')
- if sep
- f = f.gsub("/", sep)
- sep = ":/="+sep
- f = f.gsub(/(\$\(\w+)(\))/) {$1+sep+$2}
- f = f.gsub(/(\$\{\w+)(\})/) {$1+sep+$2}
- else
- sep = ""
- end
- mfile.print("#{f} $(@D#{sep})\n")
+ mfile.print("#{fseprepl[f]} $(@D#{sep})\n")
if defined?($installed_list) and !$extout
mfile.print("\t at echo #{dest}>>$(INSTALLED_LIST)\n")
end
+ if $extout
+ mfile.print("clean-rb#{sfx}::\n")
+ mfile.print("\t at -$(RM) #{fseprepl[dest]}\n")
+ end
end
end
+ if $extout
+ dirs.uniq!
+ dirs.reverse!
+ unless dirs.empty?
+ mfile.print("clean-rb#{sfx}::\n")
+ for dir in dirs
+ mfile.print("\t at -$(RMDIRS) #{fseprepl[dir]}\n")
+ end
+ end
+ end
end
dirs.unshift(sodir) if target and !dirs.include?(sodir)
- dirs.each {|d| mfile.print "#{d}:\n\t$(MAKEDIRS) $@\n" if $nmake || d == sodir}
+ dirs.each {|d| mfile.print "#{d}:\n\t$(MAKEDIRS) $@\n"}
mfile.print <<-SITEINSTALL
@@ -1703,9 +1761,10 @@
end
end
- sep = config_string('BUILD_FILE_SEPARATOR') {|s| ":/=#{s}" if s != "/"} || ""
mfile.print "$(RUBYARCHDIR)/" if $extout
- mfile.print "$(DLLIB): ", (makedef ? "$(DEFFILE) " : ""), "$(OBJS)\n"
+ mfile.print "$(DLLIB): "
+ mfile.print "$(DEFFILE) " if makedef
+ mfile.print "$(OBJS) Makefile\n"
mfile.print "\t at -$(RM) $(@#{sep})\n"
mfile.print "\t at -$(MAKEDIRS) $(@D)\n" if $extout
link_so = LINK_SO.gsub(/^/, "\t")
@@ -1730,11 +1789,11 @@
if File.exist?(depend)
mfile.print("###\n", *depend_rules(File.read(depend)))
else
- headers = %w[ruby.h defines.h]
+ headers = %w[$(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h]
if RULE_SUBST
headers.each {|h| h.sub!(/.*/, &RULE_SUBST.method(:%))}
end
- headers << $config_h if $config_h
+ headers << $config_h
headers << '$(RUBY_EXTCONF_H)' if $extconf_h
mfile.print "$(OBJS): ", headers.join(' '), "\n"
end
@@ -1756,7 +1815,9 @@
$ARCH_FLAG = with_config("arch_flag", arg_config("ARCH_FLAG", config["ARCH_FLAG"])).dup
$CPPFLAGS = with_config("cppflags", arg_config("CPPFLAGS", config["CPPFLAGS"])).dup
$LDFLAGS = with_config("ldflags", arg_config("LDFLAGS", config["LDFLAGS"])).dup
- $INCFLAGS = "-I$(arch_hdrdir) -I$(hdrdir) -I$(srcdir)"
+ $INCFLAGS = "-I$(arch_hdrdir)"
+ $INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk
+ $INCFLAGS << " -I$(hdrdir) -I$(srcdir)"
$DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup
$LIBEXT = config['LIBEXT'].dup
$OBJEXT = config["OBJEXT"].dup
@@ -1783,6 +1844,7 @@
$cleanfiles = config_string('CLEANFILES') {|s| Shellwords.shellwords(s)} || []
$cleanfiles << "mkmf.log"
$distcleanfiles = config_string('DISTCLEANFILES') {|s| Shellwords.shellwords(s)} || []
+ $distcleandirs = config_string('DISTCLEANDIRS') {|s| Shellwords.shellwords(s)} || []
$extout ||= nil
$extout_prefix ||= nil
@@ -1844,7 +1906,7 @@
EXPORT_PREFIX = config_string('EXPORT_PREFIX') {|s| s.strip}
-hdr = []
+hdr = ['#include "ruby.h"' "\n"]
config_string('COMMON_MACROS') do |s|
Shellwords.shellwords(s).each do |w|
hdr << "#define " + w.split(/=/, 2).join(" ")
@@ -1873,17 +1935,25 @@
LIBPATHFLAG = config_string('LIBPATHFLAG') || ' -L"%s"'
RPATHFLAG = config_string('RPATHFLAG') || ''
LIBARG = config_string('LIBARG') || '-l%s'
+MAIN_DOES_NOTHING = config_string('MAIN_DOES_NOTHING') || 'int main() {return 0;}'
sep = config_string('BUILD_FILE_SEPARATOR') {|s| ":/=#{s}" if sep != "/"} || ""
CLEANINGS = "
-clean:
- @-$(RM) $(CLEANLIBS#{sep}) $(CLEANOBJS#{sep}) $(CLEANFILES#{sep})
+clean-rb-default::
+clean-rb::
+clean-so::
+clean: clean-so clean-rb-default clean-rb
+\t\t at -$(RM) $(CLEANLIBS#{sep}) $(CLEANOBJS#{sep}) $(CLEANFILES#{sep})
-distclean: clean
- @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
- @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES#{sep})
+distclean-rb-default::
+distclean-rb::
+distclean-so::
+distclean: clean distclean-so distclean-rb-default distclean-rb
+\t\t at -$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+\t\t at -$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES#{sep})
+\t\t at -$(RMDIRS) $(DISTCLEANDIRS#{sep})
-realclean: distclean
+realclean: distclean
"
if not $extmk and /\A(extconf|makefile).rb\z/ =~ File.basename($0)
Modified: MacRuby/branches/experimental/lib/net/ftp.rb
===================================================================
--- MacRuby/branches/experimental/lib/net/ftp.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/net/ftp.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -170,9 +170,9 @@
end
def open_socket(host, port)
- if defined? SOCKSsocket and ENV["SOCKS_SERVER"]
+ if defined? SOCKSSocket and ENV["SOCKS_SERVER"]
@passive = true
- return SOCKSsocket.open(host, port)
+ return SOCKSSocket.open(host, port)
else
return TCPSocket.open(host, port)
end
@@ -296,12 +296,9 @@
def sendport(host, port)
af = (@sock.peeraddr)[0]
if af == "AF_INET"
- hbytes = host.split(".")
- pbytes = [port / 256, port % 256]
- bytes = hbytes + pbytes
- cmd = "PORT " + bytes.join(",")
+ cmd = "PORT " + (host.split(".") + port.divmod(256)).join(",")
elsif af == "AF_INET6"
- cmd = "EPRT |2|" + host + "|" + sprintf("%d", port) + "|"
+ cmd = sprintf("EPRT |2|%s|%d|", host, port)
else
raise FTPProtoError, host
end
@@ -399,9 +396,11 @@
synchronize do
resp = sendcmd('USER ' + user)
if resp[0] == ?3
+ raise FTPReplyError, resp if passwd.nil?
resp = sendcmd('PASS ' + passwd)
end
if resp[0] == ?3
+ raise FTPReplyError, resp if acct.nil?
resp = sendcmd('ACCT ' + acct)
end
end
@@ -725,9 +724,9 @@
begin
voidcmd("CDUP")
return
- rescue FTPPermError
- if $![0, 3] != "500"
- raise FTPPermError, $!
+ rescue FTPPermError => e
+ if e.message[0, 3] != "500"
+ raise e
end
end
end
Modified: MacRuby/branches/experimental/lib/net/http.rb
===================================================================
--- MacRuby/branches/experimental/lib/net/http.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/net/http.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -22,7 +22,7 @@
# http://www.ruby-lang.org/ja/man/html/net_http.html
#
#--
-# $Id: http.rb 16033 2008-04-15 08:12:30Z kazu $
+# $Id: http.rb 18805 2008-08-24 03:21:36Z naruse $
#++
require 'net/protocol'
@@ -284,7 +284,7 @@
class HTTP < Protocol
# :stopdoc:
- Revision = %q$Revision: 16033 $.split[1]
+ Revision = %q$Revision: 18805 $.split[1]
HTTPVersion = '1.1'
@newimpl = true
begin
@@ -486,6 +486,11 @@
@enable_post_connection_check = true
@compression = nil
@sspi_enabled = false
+ if defined?(SSL_ATTRIBUTES)
+ SSL_ATTRIBUTES.each do |name|
+ instance_variable_set "@#{name}", nil
+ end
+ end
end
def inspect
@@ -1521,7 +1526,7 @@
private :encode_kvpair
def urlencode(str)
- str.gsub(/[^a-zA-Z0-9_\.\-]/n) { sprintf('%%%02x', $&[0]) }
+ str.dup.force_encoding('ASCII-8BIT').gsub(/[^a-zA-Z0-9_\.\-]/){'%%%02x' % $&.ord}
end
private :urlencode
@@ -1643,7 +1648,7 @@
private
def send_request_with_body(sock, ver, path, body)
- self.content_length = body.length
+ self.content_length = body.bytesize
delete 'Transfer-Encoding'
supply_default_content_type
write_header sock, ver, path
Modified: MacRuby/branches/experimental/lib/net/https.rb
===================================================================
--- MacRuby/branches/experimental/lib/net/https.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/net/https.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -16,7 +16,7 @@
You can get it from RAA or Ruby's CVS repository.
== Version
- $Id: https.rb 14371 2007-12-20 16:21:22Z gotoyuzo $
+ $Id: https.rb 18512 2008-08-12 05:20:09Z aamine $
2001-11-06: Contiributed to Ruby/OpenSSL project.
2004-03-06: Some code is merged in to net/http.
@@ -65,11 +65,11 @@
: key, key=((|key|))
Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
- (This method is appeared in Michal Rokos's OpenSSL extention.)
+ (This method is appeared in Michal Rokos's OpenSSL extension.)
: cert, cert=((|cert|))
Sets an OpenSSL::X509::Certificate object as client certificate
- (This method is appeared in Michal Rokos's OpenSSL extention).
+ (This method is appeared in Michal Rokos's OpenSSL extension).
: ca_file, ca_file=((|path|))
Sets path of a CA certification file in PEM format.
@@ -124,7 +124,7 @@
ssl_version key cert ca_file ca_path cert_store ciphers
verify_mode verify_callback verify_depth ssl_timeout
)
- attr_accessor *SSL_ATTRIBUTES
+ attr_accessor(*SSL_ATTRIBUTES)
def peer_cert
if not use_ssl? or not @socket
Modified: MacRuby/branches/experimental/lib/net/imap.rb
===================================================================
--- MacRuby/branches/experimental/lib/net/imap.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/net/imap.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -287,10 +287,16 @@
# Disconnects from the server.
def disconnect
- if SSL::SSLSocket === @sock
- @sock.io.shutdown
- else
- @sock.shutdown
+ begin
+ begin
+ # try to call SSL::SSLSocket#io.
+ @sock.io.shutdown
+ rescue NoMethodError
+ # @sock is not an SSL::SSLSocket.
+ @sock.shutdown
+ end
+ rescue Errno::ENOTCONN
+ # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
end
@receiver_thread.join
@sock.close
@@ -3210,7 +3216,7 @@
].join(':')
)
- return response.keys.map { |k| qdval(k.to_s, response[k]) }.join(',')
+ return response.keys.map {|key| qdval(key.to_s, response[key]) }.join(',')
when STAGE_TWO
@stage = nil
# if at the second stage, return an empty string
@@ -3243,7 +3249,7 @@
return @nc[nonce]
end
- # some reponses needs quoting
+ # some responses need quoting
def qdval(k, v)
return if k.nil? or v.nil?
if %w"username authzid realm nonce cnonce digest-uri qop".include? k
Modified: MacRuby/branches/experimental/lib/net/pop.rb
===================================================================
--- MacRuby/branches/experimental/lib/net/pop.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/net/pop.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -15,7 +15,7 @@
# NOTE: You can find Japanese version of this document at:
# http://www.ruby-lang.org/ja/man/html/net_pop.html
#
-# $Id: pop.rb 16033 2008-04-15 08:12:30Z kazu $
+# $Id: pop.rb 19776 2008-10-14 02:22:46Z kazu $
#
# See Net::POP3 for documentation.
#
@@ -196,7 +196,7 @@
#
class POP3 < Protocol
- Revision = %q$Revision: 16033 $.split[1]
+ Revision = %q$Revision: 19776 $.split[1]
#
# Class Parameters
@@ -683,9 +683,8 @@
end
def set_all_uids #:nodoc: internal use only (called from POPMail#uidl)
- command().uidl.each do |num, uid|
- @mails.find {|m| m.number == num }.uid = uid
- end
+ uidl = command().uidl
+ @mails.each {|m| m.uid = uidl[m.number] }
end
def logging(msg)
@@ -871,9 +870,11 @@
@socket = sock
@error_occured = false
res = check_response(critical { recv_response() })
- @apop_stamp = res.slice(/<.+>/)
+ @apop_stamp = res.slice(/<[!-~]+@[!-~]+>/)
end
+ attr_reader :socket
+
def inspect
"#<#{self.class} socket=#{@socket}>"
end
Modified: MacRuby/branches/experimental/lib/net/smtp.rb
===================================================================
--- MacRuby/branches/experimental/lib/net/smtp.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/net/smtp.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -14,7 +14,7 @@
# NOTE: You can find Japanese version of this document at:
# http://www.ruby-lang.org/ja/man/html/net_smtp.html
#
-# $Id: smtp.rb 16033 2008-04-15 08:12:30Z kazu $
+# $Id: smtp.rb 18351 2008-08-04 05:46:53Z shyouhei $
#
# See Net::SMTP for documentation.
#
@@ -172,7 +172,7 @@
#
class SMTP
- Revision = %q$Revision: 16033 $.split[1]
+ Revision = %q$Revision: 18351 $.split[1]
# The default SMTP port number, 25.
def SMTP.default_port
@@ -437,7 +437,7 @@
# +port+ is the port to connect to; it defaults to port 25.
#
# +helo+ is the _HELO_ _domain_ provided by the client to the
- # server (see overview comments); it defaults to 'localhost.localdomain'.
+ # server (see overview comments); it defaults to 'localhost'.
#
# The remaining arguments are used for SMTP authentication, if required
# or desired. +user+ is the account name; +secret+ is your password
@@ -457,7 +457,7 @@
# * IOError
# * TimeoutError
#
- def SMTP.start(address, port = nil, helo = 'localhost.localdomain',
+ def SMTP.start(address, port = nil, helo = 'localhost',
user = nil, secret = nil, authtype = nil,
&block) # :yield: smtp
new(address, port).start(helo, user, secret, authtype, &block)
@@ -518,7 +518,7 @@
# * IOError
# * TimeoutError
#
- def start(helo = 'localhost.localdomain',
+ def start(helo = 'localhost',
user = nil, secret = nil, authtype = nil) # :yield: smtp
if block_given?
begin
@@ -837,7 +837,7 @@
def rcptto(to_addr)
if $SAFE > 0
- raise SecurityError, 'tainted to_addr' if to.tainted?
+ raise SecurityError, 'tainted to_addr' if to_addr.tainted?
end
getok("RCPT TO:<#{to_addr}>")
end
Modified: MacRuby/branches/experimental/lib/net/telnet.rb
===================================================================
--- MacRuby/branches/experimental/lib/net/telnet.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/net/telnet.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -164,7 +164,7 @@
CR = "\015"
LF = "\012"
EOL = CR + LF
- REVISION = '$Id: telnet.rb 16257 2008-05-01 14:57:40Z jeg2 $'
+ REVISION = '$Id: telnet.rb 17387 2008-06-17 14:04:48Z jeg2 $'
# :startdoc:
#
@@ -564,8 +564,8 @@
@dumplog.log_dump('<', c) if @options.has_key?("Dump_log")
if @options["Telnetmode"]
c = rest + c
- if Integer(c.rindex(/#{IAC}#{SE}/no)) <
- Integer(c.rindex(/#{IAC}#{SB}/no))
+ if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) <
+ Integer(c.rindex(/#{IAC}#{SB}/no) || 0)
buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)])
rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1]
elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) ||
Modified: MacRuby/branches/experimental/lib/open-uri.rb
===================================================================
--- MacRuby/branches/experimental/lib/open-uri.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/open-uri.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -132,6 +132,11 @@
options ||= {}
OpenURI.check_options(options)
+ if /\Arb?(?:\Z|:([^:]+))/ =~ mode
+ encoding, = $1,Encoding.find($1) if $1
+ mode = nil
+ end
+
unless mode == nil ||
mode == 'r' || mode == 'rb' ||
mode == File::RDONLY
@@ -139,6 +144,7 @@
end
io = open_loop(uri, options)
+ io.set_encoding(encoding) if encoding
if block_given?
begin
yield io
@@ -542,7 +548,7 @@
# :proxy => true
# :proxy => false
# :proxy => nil
- #
+ #
# If :proxy option is specified, the value should be String, URI,
# boolean or nil.
# When String or URI is given, it is treated as proxy URI.
@@ -556,7 +562,7 @@
# Synopsis:
# :proxy_http_basic_authentication => ["http://proxy.foo.com:8000/", "proxy-user", "proxy-password"]
# :proxy_http_basic_authentication => [URI.parse("http://proxy.foo.com:8000/"), "proxy-user", "proxy-password"]
- #
+ #
# If :proxy option is specified, the value should be an Array with 3 elements.
# It should contain a proxy URI, a proxy user name and a proxy password.
# The proxy URI should be a String, an URI or nil.
@@ -564,7 +570,7 @@
#
# If nil is given for the proxy URI, this option is just ignored.
#
- # If :proxy and :proxy_http_basic_authentication is specified,
+ # If :proxy and :proxy_http_basic_authentication is specified,
# ArgumentError is raised.
#
# [:http_basic_authentication]
@@ -579,14 +585,14 @@
# [:content_length_proc]
# Synopsis:
# :content_length_proc => lambda {|content_length| ... }
- #
+ #
# If :content_length_proc option is specified, the option value procedure
# is called before actual transfer is started.
# It takes one argument which is expected content length in bytes.
- #
+ #
# If two or more transfer is done by HTTP redirection, the procedure
# is called only one for a last transfer.
- #
+ #
# When expected content length is unknown, the procedure is called with
# nil.
# It is happen when HTTP response has no Content-Length header.
@@ -657,7 +663,7 @@
# :redirect=>false is used to disable HTTP redirects at all.
# OpenURI::HTTPRedirect exception raised on redirection.
# It is true by default.
- # The true means redirectoins between http and ftp is permitted.
+ # The true means redirections between http and ftp is permitted.
#
def open(*rest, &block)
OpenURI.open_uri(self, *rest, &block)
Modified: MacRuby/branches/experimental/lib/open3.rb
===================================================================
--- MacRuby/branches/experimental/lib/open3.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/open3.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -66,7 +66,6 @@
pid = spawn(*cmd, STDIN=>pw[0], STDOUT=>pr[1], STDERR=>pe[1])
wait_thr = Process.detach(pid)
- wait_thr[:pid] = pid
pw[0].close
pr[1].close
pe[1].close
Modified: MacRuby/branches/experimental/lib/optparse/version.rb
===================================================================
--- MacRuby/branches/experimental/lib/optparse/version.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/optparse/version.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
# OptionParser internal utility
class << OptionParser
- def show_version(*pkg)
+ def show_version(*pkgs)
progname = ARGV.options.program_name
result = false
show = proc do |klass, cname, version|
@@ -19,14 +19,14 @@
puts str
result = true
end
- if pkg.size == 1 and pkg[0] == "all"
+ if pkgs.size == 1 and pkgs[0] == "all"
self.search_const(::Object, /\AV(?:ERSION|ersion)\z/) do |klass, cname, version|
unless cname[1] == ?e and klass.const_defined?(:Version)
show.call(klass, cname.intern, version)
end
end
else
- pkg.each do |pkg|
+ pkgs.each do |pkg|
begin
pkg = pkg.split(/::|\//).inject(::Object) {|m, c| m.const_get(c)}
v = case
@@ -46,8 +46,8 @@
result
end
- def each_const(path, klass = ::Object)
- path.split(/::|\//).inject(klass) do |klass, name|
+ def each_const(path, base = ::Object)
+ path.split(/::|\//).inject(base) do |klass, name|
raise NameError, path unless Module === klass
klass.constants.grep(/#{name}/i) do |c|
klass.const_defined?(c) or next
Modified: MacRuby/branches/experimental/lib/optparse.rb
===================================================================
--- MacRuby/branches/experimental/lib/optparse.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/optparse.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -203,7 +203,7 @@
#
class OptionParser
# :stopdoc:
- RCSID = %w$Id: optparse.rb 13823 2007-11-04 20:17:06Z nobu $[1..-1].each {|s| s.freeze}.freeze
+ RCSID = %w$Id: optparse.rb 21070 2008-12-26 11:16:16Z yugui $[1..-1].each {|s| s.freeze}.freeze
Version = (RCSID[1].split('.').collect {|s| s.to_i}.extend(Comparable).freeze if RCSID[1])
LastModified = (Time.gm(*RCSID[2, 2].join('-').scan(/\d+/).collect {|s| s.to_i}) if RCSID[2])
Release = RCSID[2]
@@ -301,7 +301,8 @@
end
def self.incompatible_argument_styles(arg, t)
- raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}"
+ raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}",
+ ParseError.filter_backtrace(caller(2)))
end
def self.pattern
@@ -529,7 +530,8 @@
#
def accept(t, pat = /.*/nm, &block)
if pat
- pat.respond_to?(:match) or raise TypeError, "has no `match'"
+ pat.respond_to?(:match) or
+ raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2))
else
pat = t if t.respond_to?(:match)
end
@@ -629,17 +631,21 @@
# method which is called on every option.
#
def summarize(*args, &block)
- list.each do |opt|
+ sum = []
+ list.reverse_each do |opt|
if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
- opt.summarize(*args, &block)
- elsif !opt
- yield("")
+ s = []
+ opt.summarize(*args) {|l| s << l}
+ sum.concat(s.reverse)
+ elsif !opt or opt.empty?
+ sum << ""
elsif opt.respond_to?(:each_line)
- opt.each_line(&block)
+ sum.concat([*opt.each_line].reverse)
else
- opt.each(&block)
+ sum.concat([*opt.each].reverse)
end
end
+ sum.reverse_each(&block)
end
def add_banner(to) # :nodoc:
@@ -826,7 +832,7 @@
#
# Directs to reject specified class argument.
#
- # +t+:: Argument class speficier, any object including Class.
+ # +t+:: Argument class specifier, any object including Class.
#
# reject(t)
#
@@ -962,7 +968,8 @@
# +indent+:: Indentation, defaults to @summary_indent.
#
def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
- visit(:summarize, {}, {}, width, max, indent, &(blk || proc {|l| to << l + $/}))
+ blk ||= proc {|l| to << (l.index($/, -1) ? l : l + $/)}
+ visit(:summarize, {}, {}, width, max, indent, &blk)
to
end
@@ -987,17 +994,14 @@
#
def notwice(obj, prv, msg)
unless !prv or prv == obj
- begin
- raise ArgumentError, "argument #{msg} given twice: #{obj}"
- rescue
- $@[0, 2] = nil
- raise
- end
+ raise(ArgumentError, "argument #{msg} given twice: #{obj}",
+ ParseError.filter_backtrace(caller(2)))
end
obj
end
private :notwice
+ SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a}
#
# Creates an OptionParser::Switch from the parameters. The parsed argument
# value is passed to the given block, where it can be processed.
@@ -1037,13 +1041,13 @@
# "-x[OPTIONAL]"
# "-x"
# There is also a special form which matches character range (not full
- # set of regural expression):
+ # set of regular expression):
# "-[a-z]MANDATORY"
# "-[a-z][OPTIONAL]"
# "-[a-z]"
#
# [Argument style and description:]
- # Instead of specifying mandatory or optional orguments directly in the
+ # Instead of specifying mandatory or optional arguments directly in the
# switch parameter, this separate parameter can be used.
# "=MANDATORY"
# "=[OPTIONAL]"
@@ -1076,9 +1080,13 @@
end
# directly specified pattern(any object possible to match)
- if !(String === o) and o.respond_to?(:match)
+ if (!(String === o || Symbol === o)) and o.respond_to?(:match)
pattern = notwice(o, pattern, 'pattern')
- conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
+ if pattern.respond_to?(:convert)
+ conv = pattern.method(:convert).to_proc
+ else
+ conv = SPLAT_PROC
+ end
next
end
@@ -1097,7 +1105,7 @@
end
o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
when Module
- raise ArgumentError, "unsupported argument type: #{o}"
+ raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
when *ArgumentStyle.keys
style = notwice(ArgumentStyle[o], style, 'style')
when /^--no-([^\[\]=\s]*)(.+)?/
@@ -1162,7 +1170,9 @@
s = (style || default_style).new(pattern || default_pattern,
conv, sdesc, ldesc, arg, desc, block)
elsif !block
- raise ArgumentError, "no switch given" if style or pattern
+ if style or pattern
+ raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
+ end
s = desc
else
short << pattern
@@ -1474,6 +1484,7 @@
#
def environment(env = File.basename($0, '.*'))
env = ENV[env] || ENV[env.upcase] or return
+ require 'shellwords'
parse(*Shellwords.shellwords(env))
end
@@ -1604,6 +1615,17 @@
argv
end
+ def self.filter_backtrace(array)
+ unless $DEBUG
+ array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~))
+ end
+ array
+ end
+
+ def set_backtrace(array)
+ super(self.class.filter_backtrace(array))
+ end
+
def set_option(opt, eq)
if eq
@args[0] = opt
Modified: MacRuby/branches/experimental/lib/ostruct.rb
===================================================================
--- MacRuby/branches/experimental/lib/ostruct.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/ostruct.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -112,25 +112,23 @@
def inspect
str = "#<#{self.class}"
- Thread.current[InspectKey] ||= []
- if Thread.current[InspectKey].include?(self) then
- str << " ..."
- else
+ ids = (Thread.current[InspectKey] ||= [])
+ if ids.include?(object_id)
+ return str << ' ...>'
+ end
+
+ ids << object_id
+ begin
first = true
for k,v in @table
str << "," unless first
first = false
-
- Thread.current[InspectKey] << v
- begin
- str << " #{k}=#{v.inspect}"
- ensure
- Thread.current[InspectKey].pop
- end
+ str << " #{k}=#{v.inspect}"
end
+ return str << '>'
+ ensure
+ ids.pop
end
-
- str << ">"
end
alias :to_s :inspect
Modified: MacRuby/branches/experimental/lib/pathname.rb
===================================================================
--- MacRuby/branches/experimental/lib/pathname.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/pathname.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -43,7 +43,7 @@
# base = p.basename # Pathname:ruby
# dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby]
# data = p.read
-# p.open { |f| _ }
+# p.open { |f| _ }
# p.each_line { |line| _ }
#
# === Example 2: Using standard Ruby
@@ -55,7 +55,7 @@
# base = File.basename(p) # "ruby"
# dir, base = File.split(p) # ["/usr/bin", "ruby"]
# data = File.read(p)
-# File.open(p) { |f| _ }
+# File.open(p) { |f| _ }
# File.foreach(p) { |line| _ }
#
# === Example 3: Special features
@@ -71,7 +71,7 @@
# p5.cleanpath # Pathname:articles
# p5.realpath # Pathname:/home/gavin/articles
# p5.children # [Pathname:/home/gavin/articles/linux, ...]
-#
+#
# == Breakdown of functionality
#
# === Core methods
@@ -262,7 +262,7 @@
SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
end
- # Return a pathname which the extention of the basename is substituted by
+ # Return a pathname which the extension of the basename is substituted by
# <i>repl</i>.
#
# If self has no extension part, <i>repl</i> is appended.
@@ -452,7 +452,7 @@
# Returns a real (absolute) pathname of +self+ in the actual filesystem.
# The real pathname doesn't contain symlinks or useless dots.
#
- # No arguments should be given; the old behaviour is *obsoleted*.
+ # No arguments should be given; the old behaviour is *obsoleted*.
#
def realpath
path = @path
@@ -517,6 +517,7 @@
# # yields "usr", "bin", and "ruby".
#
def each_filename # :yield: filename
+ return to_enum(__method__) unless block_given?
prefix, names = split_names(@path)
names.each {|filename| yield filename }
nil
@@ -587,7 +588,7 @@
# p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
# p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
#
- # This method doesn't access the file system; it is pure string manipulation.
+ # This method doesn't access the file system; it is pure string manipulation.
#
def +(other)
other = Pathname.new(other) unless Pathname === other
@@ -1062,6 +1063,10 @@
end
end
+class Pathname
+ undef =~
+end
+
module Kernel
# create a pathname object.
#
Modified: MacRuby/branches/experimental/lib/pp.rb
===================================================================
--- MacRuby/branches/experimental/lib/pp.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/pp.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,10 +1,10 @@
# == Pretty-printer for Ruby objects.
-#
+#
# = Which seems better?
-#
+#
# non-pretty-printed output by #p is:
# #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>
-#
+#
# pretty-printed output by #pp is:
# #<PP:0x81fedf0
# @buffer=[],
@@ -22,17 +22,17 @@
# @newline="\n",
# @output=#<IO:0x8114ee4>,
# @output_width=2>
-#
+#
# I like the latter. If you do too, this library is for you.
-#
+#
# = Usage
-#
+#
# pp(obj)
#
# output +obj+ to +$>+ in pretty printed format.
-#
+#
# It returns +nil+.
-#
+#
# = Output Customization
# To define your customized pretty printing function for your classes,
# redefine a method #pretty_print(+pp+) in the class.
@@ -67,10 +67,10 @@
class PP < PrettyPrint
# Outputs +obj+ to +out+ in pretty printed format of
# +width+ columns in width.
- #
+ #
# If +out+ is omitted, +$>+ is assumed.
# If +width+ is omitted, 79 is assumed.
- #
+ #
# PP.pp returns +out+.
def PP.pp(obj, out=$>, width=79)
q = PP.new(out, width)
@@ -82,7 +82,7 @@
# Outputs +obj+ to +out+ like PP.pp but with no indent and
# newline.
- #
+ #
# PP.singleline_pp returns +out+.
def PP.singleline_pp(obj, out=$>)
q = SingleLine.new(out)
@@ -138,12 +138,12 @@
# Adds +obj+ to the pretty printing buffer
# using Object#pretty_print or Object#pretty_print_cycle.
- #
+ #
# Object#pretty_print_cycle is used when +obj+ is already
# printed, a.k.a the object reference chain has a cycle.
def pp(obj)
id = obj.object_id
-
+
if check_inspect_key(id)
group {obj.pretty_print_cycle self}
return
@@ -158,7 +158,7 @@
end
# A convenience method which is same as follows:
- #
+ #
# group(1, '#<' + obj.class.name, '>') { ... }
def object_group(obj, &block) # :yield:
group(1, '#<' + obj.class.name, '>', &block)
@@ -185,7 +185,7 @@
end
# A convenience method which is same as follows:
- #
+ #
# text ','
# breakable
def comma_breakable
@@ -195,23 +195,23 @@
# Adds a separated list.
# The list is separated by comma with breakable space, by default.
- #
+ #
# #seplist iterates the +list+ using +iter_method+.
# It yields each object to the block given for #seplist.
# The procedure +separator_proc+ is called between each yields.
- #
+ #
# If the iteration is zero times, +separator_proc+ is not called at all.
- #
+ #
# If +separator_proc+ is nil or not given,
# +lambda { comma_breakable }+ is used.
# If +iter_method+ is not given, :each is used.
- #
+ #
# For example, following 3 code fragments has similar effect.
- #
+ #
# q.seplist([1,2,3]) {|v| xxx v }
- #
+ #
# q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v }
- #
+ #
# xxx 1
# q.comma_breakable
# xxx 2
@@ -275,11 +275,11 @@
# A default pretty printing method for general objects.
# It calls #pretty_print_instance_variables to list instance variables.
- #
+ #
# If +self+ has a customized (redefined) #inspect method,
# the result of self.inspect is used but it obviously has no
# line break hints.
- #
+ #
# This module provides predefined #pretty_print methods for some of
# the most commonly used built-in classes for convenience.
def pretty_print(q)
@@ -302,7 +302,7 @@
end
# Returns a sorted array of instance variable names.
- #
+ #
# This method should return an array of names of instance variables as symbols or strings as:
# +[:@a, :@b]+.
def pretty_print_instance_variables
@@ -311,7 +311,7 @@
# Is #inspect implementation using #pretty_print.
# If you implement #pretty_print, it can be used as follows.
- #
+ #
# alias inspect pretty_print_inspect
#
# However, doing this requires that every class that #inspect is called on
@@ -629,7 +629,7 @@
result = PP.pp(a, '')
assert_equal("#{a.inspect}\n", result)
end
-
+
def test_to_s_without_iv
a = Object.new
def a.to_s() "aaa" end
Modified: MacRuby/branches/experimental/lib/prettyprint.rb
===================================================================
--- MacRuby/branches/experimental/lib/prettyprint.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/prettyprint.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,7 +2,7 @@
# This class implements a pretty printing algorithm. It finds line breaks and
# nice indentations for grouped structure.
-#
+#
# By default, the class assumes that primitive elements are strings and each
# byte in the strings have single column in width. But it can be used for
# other situations by giving suitable arguments for some methods:
@@ -18,28 +18,28 @@
# == Bugs
# * Box based formatting?
# * Other (better) model/algorithm?
-#
+#
# == References
# Christian Lindig, Strictly Pretty, March 2000,
# http://www.st.cs.uni-sb.de/~lindig/papers/#pretty
-#
+#
# Philip Wadler, A prettier printer, March 1998,
# http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
-#
+#
# == Author
# Tanaka Akira <akr at m17n.org>
-#
+#
class PrettyPrint
# This is a convenience method which is same as follows:
- #
+ #
# begin
# q = PrettyPrint.new(output, maxwidth, newline, &genspace)
# ...
# q.flush
# output
# end
- #
+ #
def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n})
q = PrettyPrint.new(output, maxwidth, newline, &genspace)
yield q
Copied: MacRuby/branches/experimental/lib/prime.rb (from rev 1886, MacRuby/trunk/lib/prime.rb)
===================================================================
--- MacRuby/branches/experimental/lib/prime.rb (rev 0)
+++ MacRuby/branches/experimental/lib/prime.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,461 @@
+#
+# = prime.rb
+#
+# Prime numbers and factorization library.
+#
+# Copyright::
+# Copyright (c) 1998-2008 Keiju ISHITSUKA(SHL Japan Inc.)
+# Copyright (c) 2008 Yuki Sonoda (Yugui) <yugui at yugui.jp>
+#
+# Documentation::
+# Yuki Sonoda
+#
+
+require "singleton"
+require "forwardable"
+
+class Integer
+ # Re-composes a prime factorization and returns the product.
+ #
+ # See Prime#int_from_prime_division for more details.
+ def Integer.from_prime_division(pd)
+ Prime.int_from_prime_division(pd)
+ end
+
+ # Returns the factorization of +self+.
+ #
+ # See Prime#prime_division for more details.
+ def prime_division(generator = Prime::Generator23.new)
+ Prime.prime_division(self, generator)
+ end
+
+ # Returns true if +self+ is a prime number, false for a composite.
+ def prime?
+ Prime.prime?(self)
+ end
+
+ # Iterates the given block over all prime numbers.
+ #
+ # See +Prime+#each for more details.
+ def Integer.each_prime(ubound, &block) # :yields: prime
+ Prime.each(ubound, &block)
+ end
+end
+
+#
+# The set of all prime numbers.
+#
+# == Example
+# Prime.each(100) do |prime|
+# p prime #=> 2, 3, 5, 7, 11, ...., 97
+# end
+#
+# == Retrieving the instance
+# +Prime+.new is obsolete. Now +Prime+ has the default instance and you can
+# access it as +Prime+.instance.
+#
+# For convenience, each instance method of +Prime+.instance can be accessed
+# as a class method of +Prime+.
+#
+# e.g.
+# Prime.instance.prime?(2) #=> true
+# Prime.prime?(2) #=> true
+#
+# == Generators
+# A "generator" provides an implementation of enumerating pseudo-prime
+# numbers and it remembers the position of enumeration and upper bound.
+# Futhermore, it is a external iterator of prime enumeration which is
+# compatible to an Enumerator.
+#
+# +Prime+::+PseudoPrimeGenerator+ is the base class for generators.
+# There are few implementations of generator.
+#
+# [+Prime+::+EratosthenesGenerator+]
+# Uses eratosthenes's sieve.
+# [+Prime+::+TrialDivisionGenerator+]
+# Uses the trial division method.
+# [+Prime+::+Generator23+]
+# Generates all positive integers which is not divided by 2 nor 3.
+# This sequence is very bad as a pseudo-prime sequence. But this
+# is faster and uses much less memory than other generators. So,
+# it is suitable for factorizing an integer which is not large but
+# has many prime factors. e.g. for Prime#prime? .
+class Prime
+ include Enumerable
+ @the_instance = Prime.new
+
+ # obsolete. Use +Prime+::+instance+ or class methods of +Prime+.
+ def initialize
+ @generator = EratosthenesGenerator.new
+ extend OldCompatibility
+ warn "Prime::new is obsolete. use Prime::instance or class methods of Prime."
+ end
+
+ class<<self
+ extend Forwardable
+ include Enumerable
+ # Returns the default instance of Prime.
+ def instance; @the_instance end
+
+ def method_added(method) # :nodoc:
+ (class<<self;self;end).def_delegator :instance, method
+ end
+ end
+
+ # Iterates the given block over all prime numbers.
+ #
+ # == Parameters
+ # +ubound+::
+ # Optional. An arbitrary positive number.
+ # The upper bound of enumeration. The method enumerates
+ # prime numbers infinitely if +ubound+ is nil.
+ # +generator+::
+ # Optional. An implementation of pseudo-prime generator.
+ #
+ # == Return value
+ # An evaluated value of the given block at the last time.
+ # Or an enumerator which is compatible to an +Enumerator+
+ # if no block given.
+ #
+ # == Description
+ # Calls +block+ once for each prime numer, passing the prime as
+ # a parameter.
+ #
+ # +ubound+::
+ # Upper bound of prime numbers. The iterator stops after
+ # yields all prime numbers p <= +ubound+.
+ #
+ # == Note
+ # +Prime+.+new+ returns a object extended by +Prime+::+OldCompatibility+
+ # in order to compatibility to Ruby 1.9, and +Prime+#each is overwritten
+ # by +Prime+::+OldCompatibility+#+each+.
+ #
+ # +Prime+.+new+ is now obsolete. Use +Prime+.+instance+.+each+ or simply
+ # +Prime+.+each+.
+ def each(ubound = nil, generator = EratosthenesGenerator.new, &block)
+ generator.upper_bound = ubound
+ generator.each(&block)
+ end
+
+
+ # Returns true if +value+ is prime, false for a composite.
+ #
+ # == Parameters
+ # +value+:: an arbitrary integer to be checked.
+ # +generator+:: optional. A pseudo-prime generator.
+ def prime?(value, generator = Prime::Generator23.new)
+ for num in generator
+ q,r = value.divmod num
+ return true if q < num
+ return false if r == 0
+ end
+ end
+
+ # Re-composes a prime factorization and returns the product.
+ #
+ # == Parameters
+ # +pd+:: Array of pairs of integers. The each internal
+ # pair consists of a prime number -- a prime factor --
+ # and a natural number -- an exponent.
+ #
+ # == Example
+ # For [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]], it returns
+ # p_1**e_1 * p_2**e_2 * .... * p_n**e_n.
+ #
+ # Prime.int_from_prime_division([[2,2], [3,1]]) #=> 12
+ def int_from_prime_division(pd)
+ pd.inject(1){|value, (prime, index)|
+ value *= prime**index
+ }
+ end
+
+ # Returns the factorization of +value+.
+ #
+ # == Parameters
+ # +value+:: An arbitrary integer.
+ # +generator+:: Optional. A pseudo-prime generator.
+ # +generator+.succ must return the next
+ # pseudo-prime number in the ascendent
+ # order. It must generate all prime numbers,
+ # but may generate non prime numbers.
+ #
+ # === Exceptions
+ # +ZeroDivisionError+:: when +value+ is zero.
+ #
+ # == Example
+ # For an arbitrary integer
+ # n = p_1**e_1 * p_2**e_2 * .... * p_n**e_n,
+ # prime_division(n) returns
+ # [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]].
+ #
+ # Prime.prime_division(12) #=> [[2,2], [3,1]]
+ #
+ def prime_division(value, generator= Prime::Generator23.new)
+ raise ZeroDivisionError if value == 0
+ pv = []
+ for prime in generator
+ count = 0
+ while (value1, mod = value.divmod(prime)
+ mod) == 0
+ value = value1
+ count += 1
+ end
+ if count != 0
+ pv.push [prime, count]
+ end
+ break if value1 <= prime
+ end
+ if value > 1
+ pv.push [value, 1]
+ end
+ return pv
+ end
+
+ # An abstract class for enumerating pseudo-prime numbers.
+ #
+ # Concrete subclasses should override succ, next, rewind.
+ class PseudoPrimeGenerator
+ include Enumerable
+
+ def initialize(ubound = nil)
+ @ubound = ubound
+ end
+
+ def upper_bound=(ubound)
+ @ubound = ubound
+ end
+ def upper_bound
+ @ubound
+ end
+
+ # returns the next pseudo-prime number, and move the internal
+ # position forward.
+ #
+ # +PseudoPrimeGenerator+#succ raises +NotImplementedError+.
+ def succ
+ raise NotImplementedError, "need to define `succ'"
+ end
+
+ # alias of +succ+.
+ def next
+ raise NotImplementedError, "need to define `next'"
+ end
+
+ # Rewinds the internal position for enumeration.
+ #
+ # See +Enumerator+#rewind.
+ def rewind
+ raise NotImplementedError, "need to define `rewind'"
+ end
+
+ # Iterates the given block for each prime numbers.
+ def each(&block)
+ return self.dup unless block
+ if @ubound
+ last_value = nil
+ loop do
+ prime = succ
+ break last_value if prime > @ubound
+ last_value = block.call(prime)
+ end
+ else
+ loop do
+ block.call(succ)
+ end
+ end
+ end
+
+ # see +Enumerator+#with_index.
+ alias with_index each_with_index
+
+ # see +Enumerator+#with_object.
+ def with_object(obj)
+ return enum_for(:with_object) unless block_given?
+ each do |prime|
+ yield prime, obj
+ end
+ end
+ end
+
+ # An implementation of +PseudoPrimeGenerator+.
+ #
+ # Uses +EratosthenesSieve+.
+ class EratosthenesGenerator < PseudoPrimeGenerator
+ def initialize
+ @last_prime = nil
+ end
+
+ def succ
+ @last_prime = @last_prime ? EratosthenesSieve.instance.next_to(@last_prime) : 2
+ end
+ def rewind
+ initialize
+ end
+ alias next succ
+ end
+
+ # An implementation of +PseudoPrimeGenerator+ which uses
+ # a prime table generated by trial division.
+ class TrialDivisionGenerator<PseudoPrimeGenerator
+ def initialize
+ @index = -1
+ end
+
+ def succ
+ TrialDivision.instance[@index += 1]
+ end
+ def rewind
+ initialize
+ end
+ alias next succ
+ end
+
+ # Generates all integer which are greater than 2 and
+ # are not divided by 2 nor 3.
+ #
+ # This is a pseudo-prime generator, suitable on
+ # checking primality of a integer by brute force
+ # method.
+ class Generator23<PseudoPrimeGenerator
+ def initialize
+ @prime = 1
+ @step = nil
+ end
+
+ def succ
+ loop do
+ if (@step)
+ @prime += @step
+ @step = 6 - @step
+ else
+ case @prime
+ when 1; @prime = 2
+ when 2; @prime = 3
+ when 3; @prime = 5; @step = 2
+ end
+ end
+ return @prime
+ end
+ end
+ alias next succ
+ def rewind
+ initialize
+ end
+ end
+
+
+
+
+ # Internal use. An implementation of prime table by trial division method.
+ class TrialDivision
+ include Singleton
+
+ def initialize # :nodoc:
+ # These are included as class variables to cache them for later uses. If memory
+ # usage is a problem, they can be put in Prime#initialize as instance variables.
+
+ # There must be no primes between @primes[-1] and @next_to_check.
+ @primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101]
+ # @next_to_check % 6 must be 1.
+ @next_to_check = 103 # @primes[-1] - @primes[-1] % 6 + 7
+ @ulticheck_index = 3 # @primes.index(@primes.reverse.find {|n|
+ # n < Math.sqrt(@@next_to_check) })
+ @ulticheck_next_squared = 121 # @primes[@ulticheck_index + 1] ** 2
+ end
+
+ # Returns the cached prime numbers.
+ def cache
+ return @primes
+ end
+ alias primes cache
+ alias primes_so_far cache
+
+ # Returns the +index+th prime number.
+ #
+ # +index+ is a 0-based index.
+ def [](index)
+ while index >= @primes.length
+ # Only check for prime factors up to the square root of the potential primes,
+ # but without the performance hit of an actual square root calculation.
+ if @next_to_check + 4 > @ulticheck_next_squared
+ @ulticheck_index += 1
+ @ulticheck_next_squared = @primes.at(@ulticheck_index + 1) ** 2
+ end
+ # Only check numbers congruent to one and five, modulo six. All others
+
+ # are divisible by two or three. This also allows us to skip checking against
+ # two and three.
+ @primes.push @next_to_check if @primes[2.. at ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil?
+ @next_to_check += 4
+ @primes.push @next_to_check if @primes[2.. at ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil?
+ @next_to_check += 2
+ end
+ return @primes[index]
+ end
+ end
+
+ # Internal use. An implementation of eratosthenes's sieve
+ class EratosthenesSieve
+ include Singleton
+
+ def initialize # :nodoc:
+ # bitmap for odd prime numbers less than 256.
+ # For an arbitrary odd number n, @table[i][j] is 1 when n is prime where i,j = n.divmod(32) .
+ @table = [0xcb6e, 0x64b4, 0x129a, 0x816d, 0x4c32, 0x864a, 0x820d, 0x2196]
+ end
+
+ # returns the least odd prime number which is greater than +n+.
+ def next_to(n)
+ n = (n-1).div(2)*2+3 # the next odd number of given n
+ i,j = n.divmod(32)
+ loop do
+ extend_table until @table.length > i
+ if !@table[i].zero?
+ (j...32).step(2) do |k|
+ return 32*i+k if !@table[i][k.div(2)].zero?
+ end
+ end
+ i += 1; j = 1
+ end
+ end
+
+ private
+ def extend_table
+ orig_len = @table.length
+ new_len = [orig_len**2, orig_len+256].min
+ lbound = orig_len*32
+ ubound = new_len*32
+ @table.fill(0xFFFF, orig_len...new_len)
+ (3..Integer(Math.sqrt(ubound))).step(2) do |p|
+ i, j = p.divmod(32)
+ next if @table[i][j.div(2)].zero?
+
+ start = (lbound.div(2*p)*2+1)*p # odd multiple of p which is greater than or equal to lbound
+ (start...ubound).step(2*p) do |n|
+ i, j = n.divmod(32)
+ @table[i] &= 0xFFFF ^ (1<<(j.div(2)))
+ end
+ end
+ end
+ end
+
+ # Provides a +Prime+ object with compatibility to Ruby 1.8 when instanciated via +Prime+.+new+.
+ module OldCompatibility
+ # Returns the next prime number and forwards internal pointer.
+ def succ
+ @generator.succ
+ end
+ alias next succ
+
+ # Overwrites Prime#each.
+ #
+ # Iterates the given block over all prime numbers. Note that enumeration starts from
+ # the current position of internal pointer, not rewound.
+ def each(&block)
+ return @generator.dup unless block_given?
+ loop do
+ yield succ
+ end
+ end
+ end
+end
Modified: MacRuby/branches/experimental/lib/profile.rb
===================================================================
--- MacRuby/branches/experimental/lib/profile.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/profile.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,6 +1,6 @@
require 'profiler'
-VM::InstructionSequence.compile_option = {
+RubyVM::InstructionSequence.compile_option = {
:trace_instruction => true,
:specialized_instruction => false
}
Modified: MacRuby/branches/experimental/lib/profiler.rb
===================================================================
--- MacRuby/branches/experimental/lib/profiler.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/profiler.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -34,7 +34,7 @@
total = Process.times[0] - @@start
if total == 0 then total = 0.01 end
data = @@map.values
- data = data.sort_by{|x| x[2]}
+ data = data.sort_by{|x| -x[2]}
sum = 0
f.printf " %% cumulative self self total\n"
f.printf " time seconds seconds calls ms/call ms/call name\n"
Modified: MacRuby/branches/experimental/lib/pstore.rb
===================================================================
--- MacRuby/branches/experimental/lib/pstore.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/pstore.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -13,14 +13,14 @@
require "thread"
#
-# PStore implements a file based persistance mechanism based on a Hash. User
+# PStore implements a file based persistence mechanism based on a Hash. User
# code can store hierarchies of Ruby objects (values) into the data store file
# by name (keys). An object hierarchy may be just a single object. User code
# may later read values back from the data store or even update data, as needed.
#
# The transactional behavior ensures that any changes succeed or fail together.
# This can be used to ensure that the data store is not left in a transitory
-# state, where some values were upated but others were not.
+# state, where some values were updated but others were not.
#
# Behind the scenes, Ruby objects are stored to the data store file with
# Marshal. That carries the usual limitations. Proc objects cannot be
@@ -345,9 +345,9 @@
end
end
end
+ value
ensure
@transaction = false
- value
end
private
Modified: MacRuby/branches/experimental/lib/racc/parser.rb
===================================================================
--- MacRuby/branches/experimental/lib/racc/parser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/racc/parser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -159,7 +159,6 @@
reduce_n, use_result, * = arg
_racc_init_sysvars
- tok = nil
act = nil
i = nil
nerr = 0
@@ -189,7 +188,7 @@
;
end
- while not (i = action_pointer[@racc_state[-1]]) or
+ while not(i = action_pointer[@racc_state[-1]]) or
not @racc_read_next or
@racc_t == 0 # $
unless i and i += @racc_t and
Modified: MacRuby/branches/experimental/lib/rake/gempackagetask.rb
===================================================================
--- MacRuby/branches/experimental/lib/rake/gempackagetask.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rake/gempackagetask.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -9,12 +9,6 @@
require 'rubygems/user_interaction'
require 'rubygems/builder'
-begin
- Gem.manage_gems
-rescue NoMethodError => ex
- # Using rubygems prior to 0.6.1
-end
-
module Rake
# Create a package based upon a Gem spec. Gem packages, as well as
Modified: MacRuby/branches/experimental/lib/rake/loaders/makefile.rb
===================================================================
--- MacRuby/branches/experimental/lib/rake/loaders/makefile.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rake/loaders/makefile.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -7,31 +7,26 @@
# Load the makefile dependencies in +fn+.
def load(fn)
- buffer = ''
open(fn) do |mf|
- mf.each do |line|
- next if line =~ /^\s*#/
- buffer << line
- if buffer =~ /\\$/
- buffer.sub!(/\\\n/, ' ')
- state = :append
- else
- process_line(buffer)
- buffer = ''
- end
+ lines = mf.read
+ lines.gsub!(/#[^\n]*\n/m, "")
+ lines.gsub!(/\\\n/, ' ')
+ lines.split("\n").each do |line|
+ process_line(line)
end
end
- process_line(buffer) if buffer != ''
end
private
# Process one logical line of makefile data.
def process_line(line)
- file_task, args = line.split(':')
+ file_tasks, args = line.split(':')
return if args.nil?
dependents = args.split
- file file_task => dependents
+ file_tasks.strip.split.each do |file_task|
+ file file_task => dependents
+ end
end
end
Modified: MacRuby/branches/experimental/lib/rake/packagetask.rb
===================================================================
--- MacRuby/branches/experimental/lib/rake/packagetask.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rake/packagetask.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -44,7 +44,7 @@
# end
#
class PackageTask < TaskLib
- # Name of the package (from the GEM Spec).
+ # Name of the package.
attr_accessor :name
# Version of the package (e.g. '1.3.2').
@@ -122,6 +122,7 @@
task :package => ["#{package_dir}/#{file}"]
file "#{package_dir}/#{file}" => [package_dir_path] + package_files do
chdir(package_dir) do
+ sh %{env}
sh %{#{@tar_command} #{flag}cvf #{file} #{package_name}}
end
end
Modified: MacRuby/branches/experimental/lib/rake/rdoctask.rb
===================================================================
--- MacRuby/branches/experimental/lib/rake/rdoctask.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rake/rdoctask.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -55,7 +55,7 @@
# RDoc. (default is none)
attr_accessor :main
- # Name of template to be used by rdoc. (default is 'html')
+ # Name of template to be used by rdoc. (defaults to rdoc's default)
attr_accessor :template
# List of files to be included in the rdoc generation. (default is [])
@@ -74,7 +74,7 @@
@rdoc_dir = 'html'
@main = nil
@title = nil
- @template = 'html'
+ @template = nil
@external = false
@options = []
yield self if block_given?
@@ -91,18 +91,18 @@
task name
desc "Force a rebuild of the RDOC files"
- task paste("re", name) => [paste("clobber_", name), name]
+ task "re#{name}" => ["clobber_#{name}", name]
desc "Remove rdoc products"
- task paste("clobber_", name) do
+ task "clobber_#{name}" do
rm_r rdoc_dir rescue nil
end
-
- task :clobber => [paste("clobber_", name)]
+ task :clobber => ["clobber_#{name}"]
+
directory @rdoc_dir
task name => [rdoc_target]
- file rdoc_target => @rdoc_files + [$rakefile] do
+ file rdoc_target => @rdoc_files + [Rake.application.rakefile] do
rm_r @rdoc_dir rescue nil
args = option_list + @rdoc_files
if @external
Modified: MacRuby/branches/experimental/lib/rake/tasklib.rb
===================================================================
--- MacRuby/branches/experimental/lib/rake/tasklib.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rake/tasklib.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,11 +6,16 @@
# Base class for Task Libraries.
class TaskLib
-
include Cloneable
- # Make a symbol by pasting two strings together.
- def paste(a,b)
+ # Make a symbol by pasting two strings together.
+ #
+ # NOTE: DEPRECATED! This method is kinda stupid. I don't know why
+ # I didn't just use string interpolation. But now other task
+ # libraries depend on this so I can't remove it without breaking
+ # other people's code. So for now it stays for backwards
+ # compatibility. BUT DON'T USE IT.
+ def paste(a,b) # :nodoc:
(a.to_s + b.to_s).intern
end
end
Modified: MacRuby/branches/experimental/lib/rake/testtask.rb
===================================================================
--- MacRuby/branches/experimental/lib/rake/testtask.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rake/testtask.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -136,7 +136,12 @@
end
def fix # :nodoc:
- ''
+ case RUBY_VERSION
+ when '1.8.2'
+ find_file 'rake/ruby182_test_unit_fix'
+ else
+ nil
+ end || ''
end
def rake_loader # :nodoc:
Copied: MacRuby/branches/experimental/lib/rake/win32.rb (from rev 1886, MacRuby/trunk/lib/rake/win32.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rake/win32.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rake/win32.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,34 @@
+module Rake
+
+ # Win 32 interface methods for Rake. Windows specific functionality
+ # will be placed here to collect that knowledge in one spot.
+ module Win32
+ class << self
+ # True if running on a windows system.
+ def windows?
+ # assume other DOSish systems are extinct.
+ File::ALT_SEPARATOR == '\\'
+ end
+ end
+
+ class << self
+ # The standard directory containing system wide rake files on
+ # Win 32 systems. Try the following environment variables (in
+ # order):
+ #
+ # * APPDATA
+ # * HOME
+ # * HOMEDRIVE + HOMEPATH
+ # * USERPROFILE
+ #
+ # If the above are not defined, retruns the personal folder.
+ def win32_system_dir #:nodoc:
+ win32_shared_path = ENV['APPDATA']
+ if !win32_shared_path or win32_shared_path.empty?
+ win32_shared_path = '~'
+ end
+ File.expand_path('Rake', win32_shared_path)
+ end
+ end if windows?
+ end
+end
Modified: MacRuby/branches/experimental/lib/rake.rb
===================================================================
--- MacRuby/branches/experimental/lib/rake.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rake.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -29,15 +29,17 @@
# as a library via a require statement, but it can be distributed
# independently as an application.
-RAKEVERSION = '0.8.0'
+RAKEVERSION = '0.8.3'
require 'rbconfig'
-require 'getoptlong'
require 'fileutils'
require 'singleton'
-require 'thread'
+require 'monitor'
+require 'optparse'
require 'ostruct'
+require 'rake/win32'
+
######################################################################
# Rake extensions to Module.
#
@@ -58,7 +60,7 @@
# end
#
def rake_extension(method)
- if instance_methods.include?(method.to_s) || instance_methods.include?(method.to_sym)
+ if method_defined?(method)
$stderr.puts "WARNING: Possible conflict with Rake extension: #{self}##{method} already exists"
else
yield
@@ -82,7 +84,7 @@
if newext != ''
newext = (newext =~ /^\./) ? newext : ("." + newext)
end
- dup.sub!(%r(([^/\\])\.[^./\\]*$)) { $1 + newext } || self + newext
+ self.chomp(File.extname(self)) << newext
end
end
@@ -159,7 +161,7 @@
# 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b'
# 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d'
#
- # Also the %d, %p, $f, $n, %x, and %X operators can take a
+ # Also the %d, %p, %f, %n, %x, and %X operators can take a
# pattern/replacement argument to perform simple string substititions on a
# particular part of the path. The pattern and replacement are speparated
# by a comma and are enclosed by curly braces. The replacement spec comes
@@ -201,17 +203,13 @@
when '%f'
result << File.basename(self)
when '%n'
- result << File.basename(self).ext
+ result << File.basename(self, '.*')
when '%d'
result << File.dirname(self)
when '%x'
- result << $1 if self =~ /[^\/](\.[^.]+)$/
+ result << File.extname(self)
when '%X'
- if self =~ /^(.*[^\/])(\.[^.]+)$/
- result << $1
- else
- result << self
- end
+ result << self.ext
when '%p'
result << self
when '%s'
@@ -239,6 +237,28 @@
##############################################################################
module Rake
+ # Errors -----------------------------------------------------------
+
+ # Error indicating an ill-formed task declaration.
+ class TaskArgumentError < ArgumentError
+ end
+
+ # Error indicating a recursion overflow error in task selection.
+ class RuleRecursionOverflowError < StandardError
+ def initialize(*args)
+ super
+ @targets = []
+ end
+
+ def add_target(target)
+ @targets << target
+ end
+
+ def message
+ super + ": [" + @targets.reverse.join(' => ') + "]"
+ end
+ end
+
# --------------------------------------------------------------------------
# Rake module singleton methods.
#
@@ -266,16 +286,22 @@
module Cloneable
# Clone an object by making a new object and setting all the instance
# variables to the same values.
- def clone
+ def dup
sibling = self.class.new
instance_variables.each do |ivar|
value = self.instance_variable_get(ivar)
new_value = value.clone rescue value
sibling.instance_variable_set(ivar, new_value)
end
+ sibling.taint if tainted?
sibling
end
- alias dup clone
+
+ def clone
+ sibling = dup
+ sibling.freeze if frozen?
+ sibling
+ end
end
####################################################################
@@ -286,12 +312,15 @@
attr_reader :names
+ # Create a TaskArgument object with a list of named arguments
+ # (given by :names) and a set of associated values (given by
+ # :values). :parent is the parent argument object.
def initialize(names, values, parent=nil)
@names = names
@parent = parent
@hash = {}
names.each_with_index { |name, i|
- @hash[name.to_sym] = values[i]
+ @hash[name.to_sym] = values[i] unless values[i].nil?
}
end
@@ -307,6 +336,13 @@
lookup(index.to_sym)
end
+ # Specify a hash of default values for task arguments. Use the
+ # defaults only if there is no specific value for the given
+ # argument.
+ def with_defaults(defaults)
+ @hash = defaults.merge(@hash)
+ end
+
def each(&block)
@hash.each(&block)
end
@@ -342,6 +378,8 @@
end
end
+ EMPTY_TASK_ARGS = TaskArguments.new([], [])
+
####################################################################
# InvocationChain tracks the chain of task invocations to detect
# circular dependencies.
@@ -409,6 +447,9 @@
# List of prerequisites for a task.
attr_reader :prerequisites
+ # List of actions attached to a task.
+ attr_reader :actions
+
# Application owning this task.
attr_accessor :application
@@ -446,12 +487,12 @@
# +enhance+ to add actions and prerequisites.
def initialize(task_name, app)
@name = task_name.to_s
- @prerequisites = FileList[]
+ @prerequisites = []
@actions = []
@already_invoked = false
@full_comment = nil
@comment = nil
- @lock = Mutex.new
+ @lock = Monitor.new
@application = app
@scope = app.current_scope
@arg_names = nil
@@ -488,6 +529,31 @@
@arg_names || []
end
+ # Reenable the task, allowing its tasks to be executed if the task
+ # is invoked again.
+ def reenable
+ @already_invoked = false
+ end
+
+ # Clear the existing prerequisites and actions of a rake task.
+ def clear
+ clear_prerequisites
+ clear_actions
+ self
+ end
+
+ # Clear the existing prerequisites of a rake task.
+ def clear_prerequisites
+ prerequisites.clear
+ self
+ end
+
+ # Clear the existing actions on a rake task.
+ def clear_actions
+ actions.clear
+ self
+ end
+
# Invoke the task if it is needed. Prerequites are invoked first.
def invoke(*args)
task_args = TaskArguments.new(arg_names, args)
@@ -496,7 +562,7 @@
# Same as invoke, but explicitly pass a call chain to detect
# circular dependencies.
- def invoke_with_call_chain(task_args, invocation_chain)
+ def invoke_with_call_chain(task_args, invocation_chain) # :nodoc:
new_chain = InvocationChain.append(self, invocation_chain)
@lock.synchronize do
if application.options.trace
@@ -511,7 +577,7 @@
protected :invoke_with_call_chain
# Invoke all the prerequisites of a task.
- def invoke_prerequisites(task_args, invocation_chain)
+ def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
@prerequisites.each { |n|
prereq = application[n, @scope]
prereq_args = task_args.new_scope(prereq.arg_names)
@@ -529,7 +595,8 @@
private :format_trace_flags
# Execute the actions associated with this task.
- def execute(args)
+ def execute(args=nil)
+ args ||= EMPTY_TASK_ARGS
if application.options.dryrun
puts "** Execute (dry run) #{name}"
return
@@ -735,6 +802,7 @@
# parallel using Ruby threads.
#
class MultiTask < Task
+ private
def invoke_prerequisites(args, invocation_chain)
threads = @prerequisites.collect { |p|
Thread.new(p) { |r| application[r].invoke_with_call_chain(args, invocation_chain) }
@@ -772,8 +840,8 @@
# end
# end
#
-def file(args, &block)
- Rake::FileTask.define_task(args, &block)
+def file(*args, &block)
+ Rake::FileTask.define_task(*args, &block)
end
# Declare a file creation task.
@@ -868,7 +936,8 @@
# added to the FileUtils utility functions.
#
module FileUtils
- RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
+ RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']).
+ sub(/.*\s.*/m, '"\&"')
OPT_TABLE['sh'] = %w(noop verbose)
OPT_TABLE['ruby'] = %w(noop verbose)
@@ -899,14 +968,25 @@
ok or fail "Command failed with status (#{status.exitstatus}): [#{show_command}]"
}
end
+ if RakeFileUtils.verbose_flag == :default
+ options[:verbose] = false
+ else
+ options[:verbose] ||= RakeFileUtils.verbose_flag
+ end
+ options[:noop] ||= RakeFileUtils.nowrite_flag
rake_check_options options, :noop, :verbose
rake_output_message cmd.join(" ") if options[:verbose]
unless options[:noop]
- res = system(*cmd)
+ res = rake_system(*cmd)
block.call(res, $?)
end
end
+ def rake_system(*cmd)
+ system(*cmd)
+ end
+ private :rake_system
+
# Run a Ruby interpreter with the given arguments.
#
# Example:
@@ -961,7 +1041,7 @@
class << self
attr_accessor :verbose_flag, :nowrite_flag
end
- RakeFileUtils.verbose_flag = true
+ RakeFileUtils.verbose_flag = :default
RakeFileUtils.nowrite_flag = false
$fileutils_verbose = true
@@ -969,10 +1049,10 @@
FileUtils::OPT_TABLE.each do |name, opts|
default_options = []
- if opts.include?('verbose')
+ if opts.include?(:verbose) || opts.include?("verbose")
default_options << ':verbose => RakeFileUtils.verbose_flag'
end
- if opts.include?('noop')
+ if opts.include?(:noop) || opts.include?("noop")
default_options << ':noop => RakeFileUtils.nowrite_flag'
end
@@ -1088,28 +1168,13 @@
# other objects.
include RakeFileUtils
-# FIXME: this doesn't work in MacRuby yet
+#FIXME: cannot use this in MacRuby yet
#private(*FileUtils.instance_methods(false))
#private(*RakeFileUtils.instance_methods(false))
######################################################################
module Rake
- class RuleRecursionOverflowError < StandardError
- def initialize(*args)
- super
- @targets = []
- end
-
- def add_target(target)
- @targets << target
- end
-
- def message
- super + ": [" + @targets.reverse.join(' => ') + "]"
- end
- end
-
# #########################################################################
# A FileList is essentially an array with a few helper methods defined to
# make file manipulation a bit easier.
@@ -1164,12 +1229,12 @@
]
DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).collect{ |s| s.to_s }.sort.uniq
-
+
# Now do the delegation.
DELEGATING_METHODS.each_with_index do |sym, i|
if SPECIAL_RETURN.include?(sym)
class_eval do
- define_method(sym) do |*args, &block|
+ define_method(sym) do |*args, &block|
resolve
result = @items.send(sym, *args, &block)
FileList.new.import(result)
@@ -1177,7 +1242,7 @@
end
else
class_eval do
- define_method(sym) do |*args, &block|
+ define_method(sym) do |*args, &block|
resolve
result = @items.send(sym, *args, &block)
result.object_id == @items.object_id ? self : result
@@ -1185,6 +1250,7 @@
end
end
end
+
# Create a file list from the globbable patterns given. If you wish to
# perform multiple includes or excludes at object build time, use the
@@ -1408,9 +1474,9 @@
# name, line number, and the matching line of text. If no block is given,
# a standard emac style file:linenumber:line message will be printed to
# standard out.
- def egrep(pattern)
+ def egrep(pattern, *opt)
each do |fn|
- open(fn) do |inf|
+ open(fn, "rb", *opt) do |inf|
count = 0
inf.each do |line|
count += 1
@@ -1502,7 +1568,7 @@
class << self
# Yield each file or directory component.
- def each_dir_parent(dir)
+ def each_dir_parent(dir) # :nodoc:
old_length = nil
while dir != '.' && dir.length != old_length
yield(dir)
@@ -1641,23 +1707,65 @@
# Resolve the arguments for a task/rule. Returns a triplet of
# [task_name, arg_name_list, prerequisites].
def resolve_args(args)
+ if args.last.is_a?(Hash)
+ deps = args.pop
+ resolve_args_with_dependencies(args, deps)
+ else
+ resolve_args_without_dependencies(args)
+ end
+ end
+
+ # Resolve task arguments for a task or rule when there are no
+ # dependencies declared.
+ #
+ # The patterns recognized by this argument resolving function are:
+ #
+ # task :t
+ # task :t, [:a]
+ # task :t, :a (deprecated)
+ #
+ def resolve_args_without_dependencies(args)
task_name = args.shift
- arg_names = args #.map { |a| a.to_sym }
- needs = []
- if task_name.is_a?(Hash)
- hash = task_name
- task_name = hash.keys[0]
- needs = hash[task_name]
+ if args.size == 1 && args.first.respond_to?(:to_ary)
+ arg_names = args.first.to_ary
+ else
+ arg_names = args
end
- if arg_names.last.is_a?(Hash)
- hash = arg_names.pop
- needs = hash[:needs]
- fail "Unrecognized keys in task hash: #{hash.keys.inspect}" if hash.size > 1
+ [task_name, arg_names, []]
+ end
+ private :resolve_args_without_dependencies
+
+ # Resolve task arguments for a task or rule when there are
+ # dependencies declared.
+ #
+ # The patterns recognized by this argument resolving function are:
+ #
+ # task :t => [:d]
+ # task :t, [a] => [:d]
+ # task :t, :needs => [:d] (deprecated)
+ # task :t, :a, :needs => [:d] (deprecated)
+ #
+ def resolve_args_with_dependencies(args, hash) # :nodoc:
+ fail "Task Argument Error" if hash.size != 1
+ key, value = hash.map { |k, v| [k,v] }.first
+ if args.empty?
+ task_name = key
+ arg_names = []
+ deps = value
+ elsif key == :needs
+ task_name = args.shift
+ arg_names = args
+ deps = value
+ else
+ task_name = args.shift
+ arg_names = key
+ deps = value
end
- needs = [needs] unless needs.respond_to?(:to_ary)
- [task_name, arg_names, needs]
+ deps = [deps] unless deps.respond_to?(:to_ary)
+ [task_name, arg_names, deps]
end
-
+ private :resolve_args_with_dependencies
+
# If a rule can be found that matches the task name, enhance the
# task with the prerequisites and actions from the rule. Set the
# source attribute of the task appropriately for the rule. Return
@@ -1748,15 +1856,23 @@
"_anon_#{@seed}"
end
+ def trace_rule(level, message)
+ puts "#{" "*level}#{message}" if Rake.application.options.trace_rules
+ end
+
# Attempt to create a rule given the list of prerequisites.
def attempt_rule(task_name, extensions, block, level)
sources = make_sources(task_name, extensions)
prereqs = sources.collect { |source|
+ trace_rule level, "Attempting Rule #{task_name} => #{source}"
if File.exist?(source) || Rake::Task.task_defined?(source)
+ trace_rule level, "(#{task_name} => #{source} ... EXIST)"
source
- elsif parent = enhance_with_matching_rule(sources.first, level+1)
+ elsif parent = enhance_with_matching_rule(source, level+1)
+ trace_rule level, "(#{task_name} => #{source} ... ENHANCE)"
parent.name
else
+ trace_rule level, "(#{task_name} => #{source} ... FAIL)"
return nil
end
}
@@ -1813,41 +1929,6 @@
DEFAULT_RAKEFILES = ['rakefile', 'Rakefile', 'rakefile.rb', 'Rakefile.rb'].freeze
- OPTIONS = [ # :nodoc:
- ['--classic-namespace', '-C', GetoptLong::NO_ARGUMENT,
- "Put Task and FileTask in the top level namespace"],
- ['--describe', '-D', GetoptLong::OPTIONAL_ARGUMENT,
- "Describe the tasks (matching optional PATTERN), then exit."],
- ['--rakefile', '-f', GetoptLong::OPTIONAL_ARGUMENT,
- "Use FILE as the rakefile."],
- ['--help', '-h', '-H', GetoptLong::NO_ARGUMENT,
- "Display this help message."],
- ['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT,
- "Include LIBDIR in the search path for required modules."],
- ['--dry-run', '-n', GetoptLong::NO_ARGUMENT,
- "Do a dry run without executing actions."],
- ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
- "Do not search parent directories for the Rakefile."],
- ['--prereqs', '-P', GetoptLong::NO_ARGUMENT,
- "Display the tasks and dependencies, then exit."],
- ['--quiet', '-q', GetoptLong::NO_ARGUMENT,
- "Do not log messages to standard output."],
- ['--require', '-r', GetoptLong::REQUIRED_ARGUMENT,
- "Require MODULE before executing rakefile."],
- ['--rakelibdir', '-R', GetoptLong::REQUIRED_ARGUMENT,
- "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')"],
- ['--silent', '-s', GetoptLong::NO_ARGUMENT,
- "Like --quiet, but also suppresses the 'in directory' announcement."],
- ['--tasks', '-T', GetoptLong::OPTIONAL_ARGUMENT,
- "Display the tasks (matching optional PATTERN) with descriptions, then exit."],
- ['--trace', '-t', GetoptLong::NO_ARGUMENT,
- "Turn on invoke/execute tracing, enable full backtrace."],
- ['--verbose', '-v', GetoptLong::NO_ARGUMENT,
- "Log message to standard output (default)."],
- ['--version', '-V', GetoptLong::NO_ARGUMENT,
- "Display the program version."],
- ]
-
# Initialize a Rake::Application object.
def initialize
super
@@ -1860,8 +1941,10 @@
@default_loader = Rake::DefaultLoader.new
@original_dir = Dir.pwd
@top_level_tasks = []
+ add_loader('rb', DefaultLoader.new)
add_loader('rf', DefaultLoader.new)
add_loader('rake', DefaultLoader.new)
+ @tty_output = STDOUT.tty?
end
# Run the Rake application. The run method performs the following three steps:
@@ -1885,8 +1968,7 @@
def init(app_name='rake')
standard_exception_handling do
@name = app_name
- handle_options
- collect_tasks
+ collect_tasks handle_options
end
end
@@ -1947,10 +2029,10 @@
yield
rescue SystemExit => ex
# Exit silently with current status
- exit(ex.status)
- rescue SystemExit, GetoptLong::InvalidOption => ex
+ raise
+ rescue OptionParser::InvalidOption => ex
# Exit silently
- exit(1)
+ exit(false)
rescue Exception => ex
# Exit with error message
$stderr.puts "rake aborted!"
@@ -1961,7 +2043,7 @@
$stderr.puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
$stderr.puts "(See full trace by running task with --trace)"
end
- exit(1)
+ exit(false)
end
end
@@ -1970,30 +2052,28 @@
def have_rakefile
@rakefiles.each do |fn|
if File.exist?(fn) || fn == ''
- @rakefile = fn
- return true
+ return fn
end
end
- return false
+ return nil
end
- # Display the rake command line help.
- def help
- puts "rake [-f rakefile] {options} targets..."
- puts
- puts "Options are ..."
- puts
- OPTIONS.sort.each do |long, short, mode, desc|
- if mode == GetoptLong::REQUIRED_ARGUMENT
- if desc =~ /\b([A-Z]{2,})\b/
- long = long + "=#{$1}"
- end
- end
- printf " %-20s (%s)\n", long, short
- printf " %s\n", desc
- end
+ # True if we are outputting to TTY, false otherwise
+ def tty_output?
+ @tty_output
end
+ # Override the detected TTY output state (mostly for testing)
+ def tty_output=( tty_output_state )
+ @tty_output = tty_output_state
+ end
+
+ # We will truncate output if we are outputting to a TTY or if we've been
+ # given an explicit column width to honor
+ def truncate_output?
+ tty_output? || ENV['RAKE_COLUMNS']
+ end
+
# Display the tasks and dependencies.
def display_tasks_and_comments
displayable_tasks = tasks.select { |t|
@@ -2009,19 +2089,51 @@
end
else
width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
- max_column = 80 - name.size - width - 7
+ max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
displayable_tasks.each do |t|
printf "#{name} %-#{width}s # %s\n",
- t.name_with_args, truncate(t.comment, max_column)
+ t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
end
end
end
+ def terminal_width
+ if ENV['RAKE_COLUMNS']
+ result = ENV['RAKE_COLUMNS'].to_i
+ else
+ result = unix? ? dynamic_width : 80
+ end
+ (result < 10) ? 80 : result
+ rescue
+ 80
+ end
+
+ # Calculate the dynamic width of the
+ def dynamic_width
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
+ end
+
+ def dynamic_width_stty
+ %x{stty size 2>/dev/null}.split[1].to_i
+ end
+
+ def dynamic_width_tput
+ %x{tput cols 2>/dev/null}.to_i
+ end
+
+ def unix?
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+ end
+
+ def windows?
+ Win32.windows?
+ end
+
def truncate(string, width)
if string.length <= width
string
else
- string[0, width-3] + "..."
+ ( string[0, width-3] || "" ) + "..."
end
end
@@ -2033,77 +2145,141 @@
end
end
- # Return a list of the command line options supported by the
- # program.
- def command_line_options
- OPTIONS.collect { |lst| lst[0..-2] }
+ # A list of all the standard options used in rake, suitable for
+ # passing to OptionParser.
+ def standard_rake_options
+ [
+ ['--classic-namespace', '-C', "Put Task and FileTask in the top level namespace",
+ lambda { |value|
+ require 'rake/classic_namespace'
+ options.classic_namespace = true
+ }
+ ],
+ ['--describe', '-D [PATTERN]', "Describe the tasks (matching optional PATTERN), then exit.",
+ lambda { |value|
+ options.show_tasks = true
+ options.full_description = true
+ options.show_task_pattern = Regexp.new(value || '')
+ }
+ ],
+ ['--dry-run', '-n', "Do a dry run without executing actions.",
+ lambda { |value|
+ verbose(true)
+ nowrite(true)
+ options.dryrun = true
+ options.trace = true
+ }
+ ],
+ ['--execute', '-e CODE', "Execute some Ruby code and exit.",
+ lambda { |value|
+ eval(value)
+ exit
+ }
+ ],
+ ['--execute-print', '-p CODE', "Execute some Ruby code, print the result, then exit.",
+ lambda { |value|
+ puts eval(value)
+ exit
+ }
+ ],
+ ['--execute-continue', '-E CODE',
+ "Execute some Ruby code, then continue with normal task processing.",
+ lambda { |value| eval(value) }
+ ],
+ ['--libdir', '-I LIBDIR', "Include LIBDIR in the search path for required modules.",
+ lambda { |value| $:.push(value) }
+ ],
+ ['--prereqs', '-P', "Display the tasks and dependencies, then exit.",
+ lambda { |value| options.show_prereqs = true }
+ ],
+ ['--quiet', '-q', "Do not log messages to standard output.",
+ lambda { |value| verbose(false) }
+ ],
+ ['--rakefile', '-f [FILE]', "Use FILE as the rakefile.",
+ lambda { |value|
+ value ||= ''
+ @rakefiles.clear
+ @rakefiles << value
+ }
+ ],
+ ['--rakelibdir', '--rakelib', '-R RAKELIBDIR',
+ "Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')",
+ lambda { |value| options.rakelib = value.split(':') }
+ ],
+ ['--require', '-r MODULE', "Require MODULE before executing rakefile.",
+ lambda { |value|
+ begin
+ require value
+ rescue LoadError => ex
+ begin
+ rake_require value
+ rescue LoadError => ex2
+ raise ex
+ end
+ end
+ }
+ ],
+ ['--rules', "Trace the rules resolution.",
+ lambda { |value| options.trace_rules = true }
+ ],
+ ['--no-search', '--nosearch', '-N', "Do not search parent directories for the Rakefile.",
+ lambda { |value| options.nosearch = true }
+ ],
+ ['--silent', '-s', "Like --quiet, but also suppresses the 'in directory' announcement.",
+ lambda { |value|
+ verbose(false)
+ options.silent = true
+ }
+ ],
+ ['--system', '-g',
+ "Using system wide (global) rakefiles (usually '~/.rake/*.rake').",
+ lambda { |value| options.load_system = true }
+ ],
+ ['--no-system', '--nosystem', '-G',
+ "Use standard project Rakefile search paths, ignore system wide rakefiles.",
+ lambda { |value| options.ignore_system = true }
+ ],
+ ['--tasks', '-T [PATTERN]', "Display the tasks (matching optional PATTERN) with descriptions, then exit.",
+ lambda { |value|
+ options.show_tasks = true
+ options.show_task_pattern = Regexp.new(value || '')
+ options.full_description = false
+ }
+ ],
+ ['--trace', '-t', "Turn on invoke/execute tracing, enable full backtrace.",
+ lambda { |value|
+ options.trace = true
+ verbose(true)
+ }
+ ],
+ ['--verbose', '-v', "Log message to standard output (default).",
+ lambda { |value| verbose(true) }
+ ],
+ ['--version', '-V', "Display the program version.",
+ lambda { |value|
+ puts "rake, version #{RAKEVERSION}"
+ exit
+ }
+ ]
+ ]
end
- # Do the option defined by +opt+ and +value+.
- def do_option(opt, value)
- case opt
- when '--describe'
- options.show_tasks = true
- options.show_task_pattern = Regexp.new(value || '.')
- options.full_description = true
- when '--dry-run'
- verbose(true)
- nowrite(true)
- options.dryrun = true
- options.trace = true
- when '--help'
- help
- exit
- when '--libdir'
- $:.push(value)
- when '--nosearch'
- options.nosearch = true
- when '--prereqs'
- options.show_prereqs = true
- when '--quiet'
- verbose(false)
- when '--rakefile'
- @rakefiles.clear
- @rakefiles << value
- when '--rakelibdir'
- options.rakelib = value.split(':')
- when '--require'
- begin
- require value
- rescue LoadError => ex
- begin
- rake_require value
- rescue LoadError => ex2
- raise ex
- end
- end
- when '--silent'
- verbose(false)
- options.silent = true
- when '--tasks'
- options.show_tasks = true
- options.show_task_pattern = Regexp.new(value || '.')
- options.full_description = false
- when '--trace'
- options.trace = true
- verbose(true)
- when '--verbose'
- verbose(true)
- when '--version'
- puts "rake, version #{RAKEVERSION}"
- exit
- when '--classic-namespace'
- require 'rake/classic_namespace'
- options.classic_namespace = true
- end
- end
-
# Read and handle the command line options.
def handle_options
options.rakelib = ['rakelib']
- opts = GetoptLong.new(*command_line_options)
- opts.each { |opt, value| do_option(opt, value) }
+ opts = OptionParser.new
+ opts.banner = "rake [-f rakefile] {options} targets..."
+ opts.separator ""
+ opts.separator "Options are ..."
+
+ opts.on_tail("-h", "--help", "-H", "Display this help message.") do
+ puts opts
+ exit
+ end
+
+ standard_rake_options.each { |args| opts.on(*args) }
+ parsed_argv = opts.parse(ARGV)
# If class namespaces are requested, set the global options
# according to the values in the options structure.
@@ -2114,12 +2290,11 @@
$dryrun = options.dryrun
$silent = options.silent
end
- rescue NoMethodError => ex
- raise GetoptLong::InvalidOption, "While parsing options, error = #{ex.class}:#{ex.message}"
+ parsed_argv
end
# Similar to the regular Ruby +require+ command, but will check
- # for .rake files in addition to .rb files.
+ # for *.rake files in addition to *.rb files.
def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
return false if loaded.include?(file_name)
paths.each do |path|
@@ -2134,34 +2309,85 @@
fail LoadError, "Can't find #{file_name}"
end
- def raw_load_rakefile # :nodoc:
+ def find_rakefile_location
here = Dir.pwd
- while ! have_rakefile
+ while ! (fn = have_rakefile)
Dir.chdir("..")
if Dir.pwd == here || options.nosearch
- fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})"
+ return nil
end
here = Dir.pwd
end
- puts "(in #{Dir.pwd})" unless options.silent
- $rakefile = @rakefile
- load File.expand_path(@rakefile) if @rakefile != ''
- options.rakelib.each do |rlib|
- Dir["#{rlib}/*.rake"].each do |name| add_import name end
+ [fn, here]
+ ensure
+ Dir.chdir(Rake.original_dir)
+ end
+
+ def raw_load_rakefile # :nodoc:
+ rakefile, location = find_rakefile_location
+ if (! options.ignore_system) &&
+ (options.load_system || rakefile.nil?) &&
+ system_dir && File.directory?(system_dir)
+ puts "(in #{Dir.pwd})" unless options.silent
+ glob("#{system_dir}/*.rake") do |name|
+ add_import name
+ end
+ else
+ fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if
+ rakefile.nil?
+ @rakefile = rakefile
+ Dir.chdir(location)
+ puts "(in #{Dir.pwd})" unless options.silent
+ $rakefile = @rakefile if options.classic_namespace
+ load File.expand_path(@rakefile) if @rakefile && @rakefile != ''
+ options.rakelib.each do |rlib|
+ glob("#{rlib}/*.rake") do |name|
+ add_import name
+ end
+ end
end
load_imports
end
+ def glob(path, &block)
+ Dir[path.gsub("\\", '/')].each(&block)
+ end
+ private :glob
+
+ # The directory path containing the system wide rakefiles.
+ def system_dir
+ @system_dir ||=
+ begin
+ if ENV['RAKE_SYSTEM']
+ ENV['RAKE_SYSTEM']
+ else
+ standard_system_dir
+ end
+ end
+ end
+
+ # The standard directory containing system wide rake files.
+ if Win32.windows?
+ def standard_system_dir #:nodoc:
+ Win32.win32_system_dir
+ end
+ else
+ def standard_system_dir #:nodoc:
+ File.expand_path('.rake', '~')
+ end
+ end
+ private :standard_system_dir
+
# Collect the list of tasks on the command line. If no tasks are
# given, return a list containing only the default task.
# Environmental assignments are processed at this time as well.
- def collect_tasks
+ def collect_tasks(argv)
@top_level_tasks = []
- ARGV.each do |arg|
+ argv.each do |arg|
if arg =~ /^(\w+)=(.*)$/
ENV[$1] = $2
else
- @top_level_tasks << arg
+ @top_level_tasks << arg unless arg =~ /^-/
end
end
@top_level_tasks.push("default") if @top_level_tasks.size == 0
Modified: MacRuby/branches/experimental/lib/rational.rb
===================================================================
--- MacRuby/branches/experimental/lib/rational.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rational.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,7 +3,7 @@
alias quof fdiv
alias rdiv quo
- alias power! **
+ alias power! ** unless defined?(0.power!)
alias rpower **
end
@@ -13,7 +13,7 @@
alias quof fdiv
alias rdiv quo
- alias power! **
+ alias power! ** unless defined?(0.power!)
alias rpower **
end
Modified: MacRuby/branches/experimental/lib/rdoc/README
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/README 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/README 2009-06-19 22:42:24 UTC (rev 1889)
@@ -17,7 +17,7 @@
* If you want to include extensions written in C, see RDoc::C_Parser
* For information on the various markups available in comment blocks, see
RDoc::Markup.
-* If you want to drive RDoc programatically, see RDoc::RDoc.
+* If you want to drive RDoc programmatically, see RDoc::RDoc.
* If you want to use the library to format text blocks into HTML, have a look
at RDoc::Markup.
* If you want to try writing your own HTML output template, see
@@ -196,7 +196,7 @@
Stop and start adding new documentation elements to the current container.
For example, if a class has a number of constants that you don't want to
document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the
- last. If you don't specifiy a +:startdoc:+ by the end of the container,
+ last. If you don't specify a +:startdoc:+ by the end of the container,
disables documentation for the entire class or module.
= Other stuff
Modified: MacRuby/branches/experimental/lib/rdoc/code_objects.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/code_objects.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/code_objects.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,8 +6,8 @@
module RDoc
##
- # We contain the common stuff for contexts (which are containers)
- # and other elements (methods, attributes and so on)
+ # We contain the common stuff for contexts (which are containers) and other
+ # elements (methods, attributes and so on)
class CodeObject
@@ -31,6 +31,13 @@
attr_reader :document_self
+ def initialize
+ @document_self = true
+ @document_children = true
+ @force_documentation = false
+ @done_documenting = false
+ end
+
def document_self=(val)
@document_self = val
if !val
@@ -64,6 +71,14 @@
# Do we _force_ documentation, even is we wouldn't normally show the entity
attr_accessor :force_documentation
+ def parent_file_name
+ @parent ? @parent.file_base_name : '(unknown)'
+ end
+
+ def parent_name
+ @parent ? @parent.name : '(unknown)'
+ end
+
# Default callbacks to nothing, but this is overridden for classes
# and modules
def remove_classes_and_modules
@@ -72,13 +87,6 @@
def remove_methods_etc
end
- def initialize
- @document_self = true
- @document_children = true
- @force_documentation = false
- @done_documenting = false
- end
-
# Access the code object's comment
attr_reader :comment
@@ -106,15 +114,24 @@
end
- # A Context is something that can hold modules, classes, methods,
- # attributes, aliases, requires, and includes. Classes, modules, and
- # files are all Contexts.
+ ##
+ # A Context is something that can hold modules, classes, methods,
+ # attributes, aliases, requires, and includes. Classes, modules, and files
+ # are all Contexts.
class Context < CodeObject
- attr_reader :name, :method_list, :attributes, :aliases, :constants
- attr_reader :requires, :includes, :in_files, :visibility
- attr_reader :sections
+ attr_reader :aliases
+ attr_reader :attributes
+ attr_reader :constants
+ attr_reader :current_section
+ attr_reader :in_files
+ attr_reader :includes
+ attr_reader :method_list
+ attr_reader :name
+ attr_reader :requires
+ attr_reader :sections
+ attr_reader :visibility
class Section
attr_reader :title, :comment, :sequence
@@ -129,12 +146,22 @@
set_comment(comment)
end
- private
+ def ==(other)
+ self.class === other and @sequence == other.sequence
+ end
- # Set the comment for this section from the original comment block
- # If the first line contains :section:, strip it and use the rest. Otherwise
- # remove lines up to the line containing :section:, and look for
- # those lines again at the end and remove them. This lets us write
+ def inspect
+ "#<%s:0x%x %s %p>" % [
+ self.class, object_id,
+ @sequence, title
+ ]
+ end
+
+ ##
+ # Set the comment for this section from the original comment block If
+ # the first line contains :section:, strip it and use the rest.
+ # Otherwise remove lines up to the line containing :section:, and look
+ # for those lines again at the end and remove them. This lets us write
#
# # ---------------------
# # :SECTION: The title
@@ -144,9 +171,10 @@
def set_comment(comment)
return unless comment
- if comment =~ /^.*?:section:.*$/
+ if comment =~ /^#[ \t]*:section:.*\n/
start = $`
rest = $'
+
if start.empty?
@comment = rest
else
@@ -157,13 +185,13 @@
end
@comment = nil if @comment.empty?
end
+
end
-
def initialize
- super()
+ super
- @in_files = []
+ @in_files = []
@name ||= "unknown"
@comment ||= ""
@@ -177,29 +205,53 @@
initialize_classes_and_modules
end
+ ##
# map the class hash to an array externally
+
def classes
@classes.values
end
+ ##
# map the module hash to an array externally
+
def modules
@modules.values
end
+ ##
+ # return the classes Hash (only to be used internally)
+
+ def classes_hash
+ @classes
+ end
+ protected :classes_hash
+
+ ##
+ # return the modules Hash (only to be used internally)
+
+ def modules_hash
+ @modules
+ end
+ protected :modules_hash
+
+ ##
# Change the default visibility for new methods
+
def ongoing_visibility=(vis)
@visibility = vis
end
- # Given an array +methods+ of method names, set the
- # visibility of the corresponding AnyMethod object
+ ##
+ # Yields Method and Attr entries matching the list of names in +methods+.
+ # Attributes are only returned when +singleton+ is false.
- def set_visibility_for(methods, vis, singleton=false)
+ def methods_matching(methods, singleton = false)
count = 0
+
@method_list.each do |m|
- if methods.include?(m.name) && m.singleton == singleton
- m.visibility = vis
+ if methods.include? m.name and m.singleton == singleton then
+ yield m
count += 1
end
end
@@ -209,14 +261,23 @@
# perhaps we need to look at attributes
@attributes.each do |a|
- if methods.include?(a.name)
- a.visibility = vis
- count += 1
- end
+ yield a if methods.include? a.name
end
end
+ ##
+ # Given an array +methods+ of method names, set the visibility of the
+ # corresponding AnyMethod object
+
+ def set_visibility_for(methods, vis, singleton = false)
+ methods_matching methods, singleton do |m|
+ m.visibility = vis
+ end
+ end
+
+ ##
# Record the file that we happen to find it in
+
def record_location(toplevel)
@in_files << toplevel unless @in_files.include?(toplevel)
end
@@ -227,7 +288,24 @@
end
def add_class(class_type, name, superclass)
- add_class_or_module(@classes, class_type, name, superclass)
+ klass = add_class_or_module @classes, class_type, name, superclass
+
+ #
+ # If the parser encounters Container::Item before encountering
+ # Container, then it assumes that Container is a module. This
+ # may not be the case, so remove Container from the module list
+ # if present and transfer any contained classes and modules to
+ # the new class.
+ #
+ mod = @modules.delete(name)
+
+ if mod then
+ klass.classes_hash.update(mod.classes_hash)
+ klass.modules_hash.update(mod.modules_hash)
+ klass.method_list.concat(mod.method_list)
+ end
+
+ return klass
end
def add_module(class_type, name)
@@ -235,28 +313,46 @@
end
def add_method(a_method)
- puts "Adding #@visibility method #{a_method.name} to #@name" if $DEBUG_RDOC
a_method.visibility = @visibility
add_to(@method_list, a_method)
+
+ unmatched_alias_list = @unmatched_alias_lists[a_method.name]
+ if unmatched_alias_list then
+ unmatched_alias_list.each do |unmatched_alias|
+ add_alias_impl unmatched_alias, a_method
+ @aliases.delete unmatched_alias
+ end
+
+ @unmatched_alias_lists.delete a_method.name
+ end
end
def add_attribute(an_attribute)
add_to(@attributes, an_attribute)
end
+ def add_alias_impl(an_alias, meth)
+ new_meth = AnyMethod.new(an_alias.text, an_alias.new_name)
+ new_meth.is_alias_for = meth
+ new_meth.singleton = meth.singleton
+ new_meth.params = meth.params
+ new_meth.comment = "Alias for \##{meth.name}"
+ meth.add_alias(new_meth)
+ add_method(new_meth)
+ end
+
def add_alias(an_alias)
meth = find_instance_method_named(an_alias.old_name)
- if meth
- new_meth = AnyMethod.new(an_alias.text, an_alias.new_name)
- new_meth.is_alias_for = meth
- new_meth.singleton = meth.singleton
- new_meth.params = meth.params
- new_meth.comment = "Alias for \##{meth.name}"
- meth.add_alias(new_meth)
- add_method(new_meth)
+
+ if meth then
+ add_alias_impl(an_alias, meth)
else
add_to(@aliases, an_alias)
+ unmatched_alias_list = @unmatched_alias_lists[an_alias.old_name] ||= []
+ unmatched_alias_list.push(an_alias)
end
+
+ an_alias
end
def add_include(an_include)
@@ -269,20 +365,21 @@
# Requires always get added to the top-level (file) context
def add_require(a_require)
- if self.kind_of? TopLevel
- add_to(@requires, a_require)
+ if TopLevel === self then
+ add_to @requires, a_require
else
- parent.add_require(a_require)
+ parent.add_require a_require
end
end
def add_class_or_module(collection, class_type, name, superclass=nil)
cls = collection[name]
- if cls
+
+ if cls then
+ cls.superclass = superclass unless cls.module?
puts "Reusing class/module #{name}" if $DEBUG_RDOC
else
cls = class_type.new(name, superclass)
- puts "Adding class/module #{name} to #@name" if $DEBUG_RDOC
# collection[name] = cls if @document_self && !@done_documenting
collection[name] = cls if !@done_documenting
cls.parent = self
@@ -292,7 +389,7 @@
end
def add_to(array, thing)
- array << thing if @document_self && !@done_documenting
+ array << thing if @document_self and not @done_documenting
thing.parent = self
thing.section = @current_section
end
@@ -312,6 +409,10 @@
@requires = []
@includes = []
@constants = []
+
+ # This Hash maps a method name to a list of unmatched
+ # aliases (aliases of a method not yet encountered).
+ @unmatched_alias_lists = {}
end
# and remove classes and modules when we see a :nodoc: all
@@ -326,9 +427,12 @@
# Find a named module
def find_module_named(name)
- return self if self.name == name
+ # First check the enclosed modules, then check the module itself,
+ # then check the enclosing modules (this mirrors the check done by
+ # the Ruby parser)
res = @modules[name] || @classes[name]
return res if res
+ return self if self.name == name
find_enclosing_module_named(name)
end
@@ -371,26 +475,31 @@
name <=> other.name
end
- # Look up the given symbol. If method is non-nil, then
- # we assume the symbol references a module that
- # contains that method
- def find_symbol(symbol, method=nil)
+ ##
+ # Look up +symbol+. If +method+ is non-nil, then we assume the symbol
+ # references a module that contains that method.
+
+ def find_symbol(symbol, method = nil)
result = nil
+
case symbol
- when /^::(.*)/
+ when /^::(.*)/ then
result = toplevel.find_symbol($1)
- when /::/
+ when /::/ then
modules = symbol.split(/::/)
- unless modules.empty?
+
+ unless modules.empty? then
module_name = modules.shift
result = find_module_named(module_name)
- if result
+
+ if result then
modules.each do |name|
result = result.find_module_named(name)
break unless result
end
end
end
+
else
# if a method is specified, then we're definitely looking for
# a module, otherwise it could be any symbol
@@ -408,22 +517,21 @@
end
end
end
- if result && method
- if !result.respond_to?(:find_local_symbol)
- #p result.name
- #p method
- fail
- end
+
+ if result and method then
+ fail unless result.respond_to? :find_local_symbol
result = result.find_local_symbol(method)
end
+
result
end
-
+
def find_local_symbol(symbol)
res = find_method_named(symbol) ||
find_constant_named(symbol) ||
find_attribute_named(symbol) ||
- find_module_named(symbol)
+ find_module_named(symbol) ||
+ find_file_named(symbol)
end
# Handle sections
@@ -454,7 +562,14 @@
def find_attribute_named(name)
@attributes.find {|m| m.name == name}
end
-
+
+ ##
+ # Find a named file, or return nil
+
+ def find_file_named(name)
+ toplevel.class.find_file_named(name)
+ end
+
end
##
@@ -465,24 +580,31 @@
attr_accessor :file_relative_name
attr_accessor :file_absolute_name
attr_accessor :diagram
-
+
@@all_classes = {}
@@all_modules = {}
+ @@all_files = {}
def self.reset
@@all_classes = {}
@@all_modules = {}
+ @@all_files = {}
end
def initialize(file_name)
super()
@name = "TopLevel"
- @file_relative_name = file_name
- @file_absolute_name = file_name
- @file_stat = File.stat(file_name)
- @diagram = nil
+ @file_relative_name = file_name
+ @file_absolute_name = file_name
+ @file_stat = File.stat(file_name)
+ @diagram = nil
+ @@all_files[file_name] = self
end
+ def file_base_name
+ File.basename @file_absolute_name
+ end
+
def full_name
nil
end
@@ -496,10 +618,11 @@
def add_class_or_module(collection, class_type, name, superclass)
cls = collection[name]
- if cls
- puts "Reusing class/module #{name}" if $DEBUG_RDOC
+ if cls then
+ cls.superclass = superclass unless cls.module?
+ puts "Reusing class/module #{cls.full_name}" if $DEBUG_RDOC
else
- if class_type == NormalModule
+ if class_type == NormalModule then
all = @@all_modules
else
all = @@all_classes
@@ -507,13 +630,20 @@
cls = all[name]
- if !cls
- cls = class_type.new(name, superclass)
+ if !cls then
+ cls = class_type.new name, superclass
all[name] = cls unless @done_documenting
+ else
+ # If the class has been encountered already, check that its
+ # superclass has been set (it may not have been, depending on
+ # the context in which it was encountered).
+ if class_type == NormalClass
+ if !cls.superclass then
+ cls.superclass = superclass
+ end
+ end
end
- puts "Adding class/module #{name} to #{@name}" if $DEBUG_RDOC
-
collection[name] = cls unless @done_documenting
cls.parent = self
@@ -534,6 +664,10 @@
nil
end
+ def self.find_file_named(name)
+ @@all_files[name]
+ end
+
def find_local_symbol(symbol)
find_class_or_module_named(symbol) || super
end
@@ -551,14 +685,23 @@
find_class_or_module_named(name) || find_enclosing_module_named(name)
end
+ def inspect
+ "#<%s:0x%x %p modules: %p classes: %p>" % [
+ self.class, object_id,
+ file_base_name,
+ @modules.map { |n,m| m },
+ @classes.map { |n,c| c }
+ ]
+ end
+
end
- # ClassModule is the base class for objects representing either a
- # class or a module.
+ ##
+ # ClassModule is the base class for objects representing either a class or a
+ # module.
class ClassModule < Context
- attr_reader :superclass
attr_accessor :diagram
def initialize(name, superclass = nil)
@@ -569,7 +712,15 @@
super()
end
+ def find_class_named(name)
+ return self if full_name == name
+ @classes.each_value {|c| return c if c.find_class_named(name) }
+ nil
+ end
+
+ ##
# Return the fully qualified name of this class or module
+
def full_name
if @parent && @parent.full_name
@parent.full_name + "::" + @name
@@ -583,49 +734,106 @@
File.join(prefix, *path) + ".html"
end
- # Return +true+ if this object represents a module
- def is_module?
+ ##
+ # Does this object represent a module?
+
+ def module?
false
end
- # to_s is simply for debugging
+ ##
+ # Get the superclass of this class. Attempts to retrieve the superclass'
+ # real name by following module nesting.
+
+ def superclass
+ raise NoMethodError, "#{full_name} is a module" if module?
+
+ scope = self
+
+ begin
+ superclass = scope.classes.find { |c| c.name == @superclass }
+
+ return superclass.full_name if superclass
+ scope = scope.parent
+ end until scope.nil? or TopLevel === scope
+
+ @superclass
+ end
+
+ ##
+ # Set the superclass of this class
+
+ def superclass=(superclass)
+ raise NoMethodError, "#{full_name} is a module" if module?
+
+ if @superclass.nil? or @superclass == 'Object' then
+ @superclass = superclass
+ end
+ end
+
def to_s
- res = self.class.name + ": " + @name
- res << @comment.to_s
- res << super
- res
+ "#{self.class}: #{@name} #{@comment} #{super}"
end
- def find_class_named(name)
- return self if full_name == name
- @classes.each_value {|c| return c if c.find_class_named(name) }
- nil
- end
end
+ ##
# Anonymous classes
+
class AnonClass < ClassModule
end
+ ##
# Normal classes
+
class NormalClass < ClassModule
+
+ def inspect
+ superclass = @superclass ? " < #{@superclass}" : nil
+ "<%s:0x%x class %s%s includes: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ @name, superclass, @includes, @attributes, @method_list, @aliases
+ ]
+ end
+
end
+ ##
# Singleton classes
+
class SingleClass < ClassModule
end
+ ##
# Module
+
class NormalModule < ClassModule
- def is_module?
+
+ def comment=(comment)
+ return if comment.empty?
+ comment = @comment << "# ---\n" << comment unless @comment.empty?
+
+ super
+ end
+
+ def inspect
+ "#<%s:0x%x module %s includes: %p attributes: %p methods: %p aliases: %p>" % [
+ self.class, object_id,
+ @name, @includes, @attributes, @method_list, @aliases
+ ]
+ end
+
+ def module?
true
end
+
end
##
# AnyMethod is the base class for objects representing methods
class AnyMethod < CodeObject
+
attr_accessor :name
attr_accessor :visibility
attr_accessor :block_params
@@ -663,44 +871,71 @@
@name <=> other.name
end
- def to_s
- res = self.class.name + ": " + @name + " (" + @text + ")\n"
- res << @comment.to_s
- res
+ def add_alias(method)
+ @aliases << method
end
+ def inspect
+ alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil
+ "#<%s:0x%x %s%s%s (%s)%s>" % [
+ self.class, object_id,
+ parent_name,
+ singleton ? '::' : '#',
+ name,
+ visibility,
+ alias_for,
+ ]
+ end
+
def param_seq
- p = params.gsub(/\s*\#.*/, '')
- p = p.tr("\n", " ").squeeze(" ")
- p = "(" + p + ")" unless p[0] == ?(
+ params = params.gsub(/\s*\#.*/, '')
+ params = params.tr("\n", " ").squeeze(" ")
+ params = "(#{params})" unless p[0] == ?(
- if (block = block_params)
- # If this method has explicit block parameters, remove any
- # explicit &block
-$stderr.puts p
- p.sub!(/,?\s*&\w+/)
-$stderr.puts p
+ if block = block_params then # yes, =
+ # If this method has explicit block parameters, remove any explicit
+ # &block
+ params.sub!(/,?\s*&\w+/)
block.gsub!(/\s*\#.*/, '')
block = block.tr("\n", " ").squeeze(" ")
if block[0] == ?(
block.sub!(/^\(/, '').sub!(/\)/, '')
end
- p << " {|#{block}| ...}"
+ params << " { |#{block}| ... }"
end
- p
+
+ params
end
- def add_alias(method)
- @aliases << method
+ def to_s
+ res = self.class.name + ": " + @name + " (" + @text + ")\n"
+ res << @comment.to_s
+ res
end
+
end
- # Represent an alias, which is an old_name/ new_name pair associated
- # with a particular context
+ ##
+ # GhostMethod represents a method referenced only by a comment
+
+ class GhostMethod < AnyMethod
+ end
+
+ ##
+ # MetaMethod represents a meta-programmed method
+
+ class MetaMethod < AnyMethod
+ end
+
+ ##
+ # Represent an alias, which is an old_name/ new_name pair associated with a
+ # particular context
+
class Alias < CodeObject
+
attr_accessor :text, :old_name, :new_name, :comment
-
+
def initialize(text, old_name, new_name, comment)
super()
@text = text
@@ -709,12 +944,22 @@
self.comment = comment
end
+ def inspect
+ "#<%s:0x%x %s.alias_method %s, %s>" % [
+ self.class, object_id,
+ parent.name, @old_name, @new_name,
+ ]
+ end
+
def to_s
"alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}"
end
+
end
+ ##
# Represent a constant
+
class Constant < CodeObject
attr_accessor :name, :value
@@ -726,7 +971,9 @@
end
end
+ ##
# Represent attributes
+
class Attr < CodeObject
attr_accessor :text, :name, :rw, :visibility
@@ -739,16 +986,33 @@
self.comment = comment
end
+ def <=>(other)
+ self.name <=> other.name
+ end
+
+ def inspect
+ attr = case rw
+ when 'RW' then :attr_accessor
+ when 'R' then :attr_reader
+ when 'W' then :attr_writer
+ else
+ " (#{rw})"
+ end
+
+ "#<%s:0x%x %s.%s :%s>" % [
+ self.class, object_id,
+ parent_name, attr, @name,
+ ]
+ end
+
def to_s
"attr: #{self.name} #{self.rw}\n#{self.comment}"
end
- def <=>(other)
- self.name <=> other.name
- end
end
- # a required file
+ ##
+ # A required file
class Require < CodeObject
attr_accessor :name
@@ -759,18 +1023,39 @@
self.comment = comment
end
+ def inspect
+ "#<%s:0x%x require '%s' in %s>" % [
+ self.class,
+ object_id,
+ @name,
+ parent_file_name,
+ ]
+ end
+
end
- # an included module
+ ##
+ # An included module
+
class Include < CodeObject
+
attr_accessor :name
def initialize(name, comment)
super()
@name = name
self.comment = comment
+
end
+ def inspect
+ "#<%s:0x%x %s.include %s>" % [
+ self.class,
+ object_id,
+ parent_name, @name,
+ ]
+ end
+
end
end
Modified: MacRuby/branches/experimental/lib/rdoc/diagram.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/diagram.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/diagram.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -311,28 +311,30 @@
# inclusion on the page
def wrap_in_image_map(src, dot)
- res = %{<map id="map" name="map">\n}
+ res = ""
dot_map = `dot -Tismap #{src}`
- dot_map.split($/).each do |area|
- unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
- $stderr.puts "Unexpected output from dot:\n#{area}"
- return nil
- end
+
+ if(!dot_map.empty?)
+ res << %{<map id="map" name="map">\n}
+ dot_map.split($/).each do |area|
+ unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
+ $stderr.puts "Unexpected output from dot:\n#{area}"
+ return nil
+ end
- xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
- url, area_name = $5, $6
+ xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
+ url, area_name = $5, $6
- res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
- res << %{ href="#{url}" alt="#{area_name}" />\n}
+ res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
+ res << %{ href="#{url}" alt="#{area_name}" />\n}
+ end
+ res << "</map>\n"
end
- res << "</map>\n"
-# map_file = src.sub(/.dot/, '.map')
-# system("dot -Timap #{src} -o #{map_file}")
- res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">}
+
+ res << %{<img src="#{dot}" usemap="#map" alt="#{dot}" />}
return res
end
end
end
-
Modified: MacRuby/branches/experimental/lib/rdoc/generator/chm/chm.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/chm/chm.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/chm/chm.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,6 +6,8 @@
HTML = RDoc::Generator::HTML::HTML
INDEX = HTML::INDEX
+
+ STYLE = HTML::STYLE
CLASS_INDEX = HTML::CLASS_INDEX
CLASS_PAGE = HTML::CLASS_PAGE
Copied: MacRuby/branches/experimental/lib/rdoc/generator/html/common.rb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/html/common.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html/common.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html/common.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,24 @@
+#
+# The templates require further refactoring. In particular,
+# * Some kind of HTML generation library should be used.
+#
+# Also, all of the templates require some TLC from a designer.
+#
+# Right now, this file contains some constants that are used by all
+# of the templates.
+#
+module RDoc::Generator::HTML::Common
+ XHTML_STRICT_PREAMBLE = <<-EOF
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+EOF
+
+ XHTML_FRAME_PREAMBLE = <<-EOF
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+EOF
+
+ HTML_ELEMENT = <<-EOF
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+EOF
+end
Copied: MacRuby/branches/experimental/lib/rdoc/generator/html/frameless.rb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/html/frameless.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html/frameless.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html/frameless.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,92 @@
+require 'rdoc/generator/html/html'
+
+##
+# = CSS2 RDoc HTML template
+#
+# This is a template for RDoc that uses XHTML 1.0 Strict and dictates a
+# bit more of the appearance of the output to cascading stylesheets than the
+# default. It was designed for clean inline code display, and uses DHTMl to
+# toggle the visbility of each method's source with each click on the '[source]'
+# link.
+#
+# Frameless basically is the html template without frames.
+#
+# == Authors
+#
+# * Michael Granger <ged at FaerieMUD.org>
+#
+# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
+#
+# This work is licensed under the Creative Commons Attribution License. To view
+# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or
+# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California
+# 94305, USA.
+
+module RDoc::Generator::HTML::FRAMELESS
+
+ FRAMELESS = true
+
+ FONTS = RDoc::Generator::HTML::HTML::FONTS
+
+ STYLE = RDoc::Generator::HTML::HTML::STYLE
+
+ HEADER = RDoc::Generator::HTML::HTML::HEADER
+
+ FOOTER = <<-EOF
+ <div id="popupmenu" class="index">
+ <br />
+ <h1 class="index-entries section-bar">Files</h1>
+ <ul>
+<% values["file_list"].each do |file| %>
+ <li><a href="<%= file["href"] %>"><%= file["name"] %></a></li>
+<% end %>
+ </ul>
+
+ <br />
+ <h1 class="index-entries section-bar">Classes</h1>
+ <ul>
+<% values["class_list"].each do |klass| %>
+ <li><a href="<%= klass["href"] %>"><%= klass["name"] %></a></li>
+<% end %>
+ </ul>
+
+ <br />
+ <h1 class="index-entries section-bar">Methods</h1>
+ <ul>
+<% values["method_list"].each do |method| %>
+ <li><a href="<%= method["href"] %>"><%= method["name"] %></a></li>
+<% end %>
+ </ul>
+ </div>
+</body>
+</html>
+ EOF
+
+ FILE_PAGE = RDoc::Generator::HTML::HTML::FILE_PAGE
+
+ CLASS_PAGE = RDoc::Generator::HTML::HTML::CLASS_PAGE
+
+ METHOD_LIST = RDoc::Generator::HTML::HTML::METHOD_LIST
+
+ BODY = HEADER + %{
+
+<%= template_include %> <!-- banner header -->
+
+ <div id="bodyContent">
+
+} + METHOD_LIST + %{
+
+ </div>
+
+} + FOOTER
+
+ SRC_PAGE = RDoc::Generator::HTML::HTML::SRC_PAGE
+
+ FR_INDEX_BODY = RDoc::Generator::HTML::HTML::FR_INDEX_BODY
+
+ FILE_INDEX = RDoc::Generator::HTML::HTML::FILE_INDEX
+
+ CLASS_INDEX = RDoc::Generator::HTML::HTML::CLASS_INDEX
+
+ METHOD_INDEX = RDoc::Generator::HTML::HTML::METHOD_INDEX
+end
Modified: MacRuby/branches/experimental/lib/rdoc/generator/html/hefss.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html/hefss.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html/hefss.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,16 +1,16 @@
require 'rdoc/generator/html'
-require 'rdoc/generator/html/html'
+require 'rdoc/generator/html/kilmerfactory'
module RDoc::Generator::HTML::HEFSS
FONTS = "Verdana, Arial, Helvetica, sans-serif"
-STYLE = <<-EOF
-body,p { font-family: Verdana, Arial, Helvetica, sans-serif;
+ CENTRAL_STYLE = <<-EOF
+body,p { font-family: <%= values["fonts"] %>;
color: #000040; background: #BBBBBB;
}
-td { font-family: Verdana, Arial, Helvetica, sans-serif;
+td { font-family: <%= values["fonts"] %>;
color: #000040;
}
@@ -21,16 +21,20 @@
}
.big-title-font { color: white;
- font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-family: <%= values["fonts"] %>;
font-size: large;
height: 50px}
.small-title-font { color: purple;
- font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-family: <%= values["fonts"] %>;
font-size: small; }
.aqua { color: purple }
+#diagram img {
+ border: 0;
+}
+
.method-name, attr-name {
font-family: monospace; font-weight: bold;
}
@@ -75,241 +79,6 @@
color: #0000AA;
}
-.column-title {
- font-size: medium;
- font-weight: bold;
- text_decoration: none;
- padding: 3px 3px 3px 20px;
- color: #3333CC;
- }
-
-.variable-name {
- font-family: monospace;
- font-size: medium;
- text_decoration: none;
- padding: 3px 3px 3px 20px;
- color: #0000AA;
-}
-
-.row-name {
- font-size: medium;
- font-weight: medium;
- font-family: monospace;
- text_decoration: none;
- padding: 3px 3px 3px 20px;
-}
-
-.paramsig {
- font-size: small;
-}
-
-.srcbut { float: right }
-
- EOF
-
- BODY = <<-EOF
-<html><head>
- <title><%= values["title"] %></title>
- <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
- <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
- <script type="text/javascript" language="JavaScript">
- <!--
- function popCode(url) {
- parent.frames.source.location = url
- }
- //-->
- </script>
-</head>
-<body bgcolor="#BBBBBB">
-
-<%= template_include %> <!-- banner header -->
-
-<% if values["diagram"] then %>
-<table width="100%"><tr><td align="center">
-<%= values["diagram"] %>
-</td></tr></table>
-<% end %>
-
-<% if values["description"] then %>
-<div class="description"><%= values["description"] %></div>
-<% end %>
-
-<% if values["requires"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Required files</td></tr>
-</table><br />
-<div class="name-list">
-<% values["requires"].each do |requires| %>
-<%= href requires["aref"], requires["name"] %>
-<% end # values["requires"] %>
-<% end %>
-</div>
-
-<% if values["sections"] then %>
-<% values["sections"].each do |sections| %>
-<% if sections["method_list"] then %>
-<% sections["method_list"].each do |method_list| %>
-<% if method_list["methods"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Subroutines and Functions</td></tr>
-</table><br />
-<div class="name-list">
-<% method_list["methods"].each do |methods| %>
-<a href="<%= methods["codeurl"] %>" target="source"><%= methods["name"] %></a>
-<% end # values["methods"] %>
-</div>
-<% end %>
-<% end # values["method_list"] %>
-<% end %>
-
-<% if sections["attributes"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Arguments</td></tr>
-</table><br />
-<table cellspacing="5">
-<% sections["attributes"].each do |attributes| %>
- <tr valign="top">
-<% if attributes["rw"] then %>
- <td align="center" class="attr-rw"> [<%= attributes["rw"] %>] </td>
-<% end %>
-<% unless attributes["rw"] then %>
- <td></td>
-<% end %>
- <td class="attr-name"><%= attributes["name"] %></td>
- <td><%= attributes["a_desc"] %></td>
- </tr>
-<% end # values["attributes"] %>
-</table>
-<% end %>
-<% end # values["sections"] %>
-<% end %>
-
-<% if values["classlist"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Modules</td></tr>
-</table><br />
-<%= values["classlist"] %><br />
-<% end %>
-
- <%= template_include %> <!-- method descriptions -->
-
-</body>
-</html>
- EOF
-
- FILE_PAGE = <<-EOF
-<table width="100%">
- <tr class="title-row">
- <td><table width="100%"><tr>
- <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font><%= values["short_name"] %></td>
- <td align="right"><table cellspacing="0" cellpadding="2">
- <tr>
- <td class="small-title-font">Path:</td>
- <td class="small-title-font"><%= values["full_path"] %>
-<% if values["cvsurl"] then %>
- (<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-<% end %>
- </td>
- </tr>
- <tr>
- <td class="small-title-font">Modified:</td>
- <td class="small-title-font"><%= values["dtm_modified"] %></td>
- </tr>
- </table>
- </td></tr></table></td>
- </tr>
-</table><br />
- EOF
-
- CLASS_PAGE = <<-EOF
-<table width="100%" border="0" cellspacing="0">
- <tr class="title-row">
- <td class="big-title-font">
- <font size="-3"><b><%= values["classmod"] %></b><br /></font><%= values["full_name"] %>
- </td>
- <td align="right">
- <table cellspacing="0" cellpadding="2">
- <tr valign="top">
- <td class="small-title-font">In:</td>
- <td class="small-title-font">
-<% values["infiles"].each do |infiles| %>
-<%= href infiles["full_path_url"], infiles["full_path"] %>
-<% if infiles["cvsurl"] then %>
- (<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-<% end %>
-<% end # values["infiles"] %>
- </td>
- </tr>
-<% if values["parent"] then %>
- <tr>
- <td class="small-title-font">Parent:</td>
- <td class="small-title-font">
-<% if values["par_url"] then %>
- <a href="<%= values["par_url"] %>" class="cyan">
-<% end %>
-<%= values["parent"] %>
-<% if values["par_url"] then %>
- </a>
-<% end %>
- </td>
- </tr>
-<% end %>
- </table>
- </td>
- </tr>
-</table><br />
- EOF
-
- METHOD_LIST = <<-EOF
-<% if values["includes"] then %>
-<div class="tablesubsubtitle">Uses</div><br />
-<div class="name-list">
-<% values["includes"].each do |includes| %>
- <span class="method-name"><%= href includes["aref"], includes["name"] %></span>
-<% end # values["includes"] %>
-</div>
-<% end %>
-
-<% if values["sections"] then %>
-<% values["sections"].each do |sections| %>
-<% if sections["method_list"] then %>
-<% sections["method_list"].each do |method_list| %>
-<% if method_list["methods"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle"><%= method_list["type"] %> <%= method_list["category"] %> methods</td></tr>
-</table>
-<% method_list["methods"].each do |methods| %>
-<table width="100%" cellspacing="0" cellpadding="5" border="0">
-<tr><td class="methodtitle">
-<a name="<%= methods["aref"] %>">
-<b><%= methods["name"] %></b><%= methods["params"] %>
-<% if methods["codeurl"] then %>
-<a href="<%= methods["codeurl"] %>" target="source" class="srclink">src</a>
-<% end %>
-</a></td></tr>
-</table>
-<% if method_list["m_desc"] then %>
-<div class="description">
-<%= method_list["m_desc"] %>
-</div>
-<% end %>
-<% end # method_list["methods"] %>
-<% end %>
-<% end # sections["method_list"] %>
-<% end %>
-<% end # values["sections"] %>
-<% end %>
- EOF
-
- SRC_PAGE = <<-EOF
-<html>
-<head><title><%= values["title"] %></title>
-<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
-<style type="text/css">
- .kw { color: #3333FF; font-weight: bold }
- .cmt { color: green; font-style: italic }
- .str { color: #662222; font-style: italic }
- .re { color: #662222; }
.ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; }
.ruby-identifier { color: #222222; }
@@ -319,35 +88,23 @@
.ruby-operator { color: #111111; }
.ruby-regexp { color: #662222; }
.ruby-value { color: #662222; font-style: italic }
-</style>
-</head>
-<body bgcolor="#BBBBBB">
-<pre><%= values["code"] %></pre>
-</body>
-</html>
+
+.srcbut { float: right }
EOF
- FR_INDEX_BODY = %{
-<%= template_include %>
+ INDEX_STYLE = <<-EOF
+body {
+ background-color: #bbbbbb;
+ font-family: #{FONTS};
+ font-size: 11px;
+ font-style: normal;
+ line-height: 14px;
+ color: #000040;
}
- FILE_INDEX = <<-EOF
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
-<style type="text/css">
-<!--
- body {
-background-color: #bbbbbb;
- font-family: #{FONTS};
- font-size: 11px;
- font-style: normal;
- line-height: 14px;
- color: #000040;
- }
div.banner {
background: #bbbbcc;
- color: white;
+ color: white;
padding: 1;
margin: 0;
font-size: 90%;
@@ -356,59 +113,38 @@
text-align: center;
width: 100%;
}
+EOF
--->
-</style>
-<base target="docwin">
-</head>
-<body>
-<div class="banner"><%= values["list_title"] %></div>
-<% values["entries"].each do |entries| %>
-<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
-<% end # values["entries"] %>
-</body></html>
- EOF
+ FACTORY = RDoc::Generator::HTML::
+ KilmerFactory.new(:central_css => CENTRAL_STYLE,
+ :index_css => INDEX_STYLE,
+ :method_list_heading => "Subroutines and Functions",
+ :class_and_module_list_heading => "Classes and Modules",
+ :attribute_list_heading => "Arguments")
- CLASS_INDEX = FILE_INDEX
- METHOD_INDEX = FILE_INDEX
+ STYLE = FACTORY.get_STYLE()
- INDEX = <<-EOF
-<html>
-<head>
- <title><%= values["title"] %></title>
- <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
-</head>
+ METHOD_LIST = FACTORY.get_METHOD_LIST()
+
+ BODY = FACTORY.get_BODY()
+
+ FILE_PAGE = FACTORY.get_FILE_PAGE()
-<frameset cols="20%,*">
- <frameset rows="15%,35%,50%">
- <frame src="fr_file_index.html" title="Files" name="Files">
- <frame src="fr_class_index.html" name="Modules">
- <frame src="fr_method_index.html" name="Subroutines and Functions">
- </frameset>
- <frameset rows="80%,20%">
- <frame src="<%= values["initial_page"] %>" name="docwin">
- <frame src="blank.html" name="source">
- </frameset>
- <noframes>
- <body bgcolor="#BBBBBB">
- Click <a href="html/index.html">here</a> for a non-frames
- version of this page.
- </body>
- </noframes>
-</frameset>
+ CLASS_PAGE = FACTORY.get_CLASS_PAGE()
-</html>
- EOF
+ SRC_PAGE = FACTORY.get_SRC_PAGE()
- # Blank page to use as a target
- BLANK = %{
-<html><body bgcolor="#BBBBBB"></body></html>
-}
+ FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY()
- def write_extra_pages
- template = TemplatePage.new(BLANK)
- File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
- end
+ FILE_INDEX = FACTORY.get_FILE_INDEX()
-end
+ CLASS_INDEX = FACTORY.get_CLASS_INDEX()
+ METHOD_INDEX = FACTORY.get_METHOD_INDEX()
+
+ INDEX = FACTORY.get_INDEX()
+
+ def self.write_extra_pages(values)
+ FACTORY.write_extra_pages(values)
+ end
+end
Modified: MacRuby/branches/experimental/lib/rdoc/generator/html/html.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html/html.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html/html.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,56 +1,78 @@
require 'rdoc/generator/html'
-require 'rdoc/generator/html/one_page_html'
+require 'rdoc/generator/html/common'
##
# = CSS2 RDoc HTML template
#
-# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a
+# This is a template for RDoc that uses XHTML 1.0 Strict and dictates a
# bit more of the appearance of the output to cascading stylesheets than the
# default. It was designed for clean inline code display, and uses DHTMl to
-# toggle the visbility of each method's source with each click on the '[source]'
-# link.
+# toggle the visibility of each method's source with each click on the
+# '[source]' link.
#
+# This template *also* forms the basis of the frameless template.
+#
# == Authors
#
# * Michael Granger <ged at FaerieMUD.org>
#
# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
#
-# This work is licensed under the Creative Commons Attribution License. To view
-# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or
-# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California
-# 94305, USA.
+# This work is licensed under the Creative Commons Attribution License. To
+# view a copy of this license, visit
+# http://creativecommons.org/licenses/by/1.0/ or send a letter to Creative
+# Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
module RDoc::Generator::HTML::HTML
+ include RDoc::Generator::HTML::Common
+
FONTS = "Verdana,Arial,Helvetica,sans-serif"
STYLE = <<-EOF
body {
- font-family: Verdana,Arial,Helvetica,sans-serif;
- font-size: 90%;
- margin: 0;
- margin-left: 40px;
- padding: 0;
- background: white;
+ font-family: #{FONTS};
+ font-size: 90%;
+ margin: 0;
+ margin-left: 40px;
+ padding: 0;
+ background: white;
+ color: black;
}
-h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
-h1 { font-size: 150%; }
-h2,h3,h4 { margin-top: 1em; }
+h1, h2, h3, h4 {
+ margin: 0;
+ background: transparent;
+}
-a { background: #eef; color: #039; text-decoration: none; }
-a:hover { background: #039; color: #eef; }
+h1 {
+ font-size: 150%;
+}
+h2,h3,h4 {
+ margin-top: 1em;
+}
+
+:link, :visited {
+ background: #eef;
+ color: #039;
+ text-decoration: none;
+}
+
+:link:hover, :visited:hover {
+ background: #039;
+ color: #eef;
+}
+
/* Override the base stylesheet's Anchor inside a table cell */
-td > a {
+td > :link, td > :visited {
background: transparent;
color: #039;
text-decoration: none;
}
/* and inside a section title */
-.section-title > a {
+.section-title > :link, .section-title > :visited {
background: transparent;
color: #eee;
text-decoration: none;
@@ -58,181 +80,255 @@
/* === Structural elements =================================== */
-div#index {
- margin: 0;
- margin-left: -40px;
- padding: 0;
- font-size: 90%;
+.index {
+ margin: 0;
+ margin-left: -40px;
+ padding: 0;
+ font-size: 90%;
}
-
-div#index a {
- margin-left: 0.7em;
+.index :link, .index :visited {
+ margin-left: 0.7em;
}
-div#index .section-bar {
- margin-left: 0px;
- padding-left: 0.7em;
- background: #ccc;
- font-size: small;
+.index .section-bar {
+ margin-left: 0px;
+ padding-left: 0.7em;
+ background: #ccc;
+ font-size: small;
}
-
-div#classHeader, div#fileHeader {
- width: auto;
- color: white;
- padding: 0.5em 1.5em 0.5em 1.5em;
- margin: 0;
- margin-left: -40px;
- border-bottom: 3px solid #006;
+#classHeader, #fileHeader {
+ width: auto;
+ color: white;
+ padding: 0.5em 1.5em 0.5em 1.5em;
+ margin: 0;
+ margin-left: -40px;
+ border-bottom: 3px solid #006;
}
-div#classHeader a, div#fileHeader a {
- background: inherit;
- color: white;
+#classHeader :link, #fileHeader :link,
+#classHeader :visited, #fileHeader :visited {
+ background: inherit;
+ color: white;
}
-div#classHeader td, div#fileHeader td {
- background: inherit;
- color: white;
+#classHeader td, #fileHeader td {
+ background: inherit;
+ color: white;
}
-
-div#fileHeader {
- background: #057;
+#fileHeader {
+ background: #057;
}
-div#classHeader {
- background: #048;
+#classHeader {
+ background: #048;
}
-
.class-name-in-header {
font-size: 180%;
font-weight: bold;
}
+#bodyContent {
+ padding: 0 1.5em 0 1.5em;
+}
-div#bodyContent {
- padding: 0 1.5em 0 1.5em;
+#description {
+ padding: 0.5em 1.5em;
+ background: #efefef;
+ border: 1px dotted #999;
}
-div#description {
- padding: 0.5em 1.5em;
- background: #efefef;
- border: 1px dotted #999;
+#description h1, #description h2, #description h3,
+#description h4, #description h5, #description h6 {
+ color: #125;
+ background: transparent;
}
-div#description h1,h2,h3,h4,h5,h6 {
- color: #125;;
- background: transparent;
+#validator-badges {
+ text-align: center;
}
-div#validator-badges {
- text-align: center;
+#validator-badges img {
+ border: 0;
}
-div#validator-badges img { border: 0; }
-div#copyright {
- color: #333;
- background: #efefef;
- font: 0.75em sans-serif;
- margin-top: 5em;
- margin-bottom: 0;
- padding: 0.5em 2em;
+#copyright {
+ color: #333;
+ background: #efefef;
+ font: 0.75em sans-serif;
+ margin-top: 5em;
+ margin-bottom: 0;
+ padding: 0.5em 2em;
}
-
/* === Classes =================================== */
table.header-table {
- color: white;
- font-size: small;
+ color: white;
+ font-size: small;
}
.type-note {
- font-size: small;
- color: #DEDEDE;
+ font-size: small;
+ color: #dedede;
}
-.xxsection-bar {
- background: #eee;
- color: #333;
- padding: 3px;
+.section-bar {
+ color: #333;
+ border-bottom: 1px solid #999;
+ margin-left: -20px;
}
-.section-bar {
- color: #333;
- border-bottom: 1px solid #999;
- margin-left: -20px;
+.section-title {
+ background: #79a;
+ color: #eee;
+ padding: 3px;
+ margin-top: 2em;
+ margin-left: -30px;
+ border: 1px solid #999;
}
+.top-aligned-row {
+ vertical-align: top
+}
-.section-title {
- background: #79a;
- color: #eee;
- padding: 3px;
- margin-top: 2em;
- margin-left: -30px;
- border: 1px solid #999;
+.bottom-aligned-row {
+ vertical-align: bottom
}
-.top-aligned-row { vertical-align: top }
-.bottom-aligned-row { vertical-align: bottom }
+#diagram img {
+ border: 0;
+}
/* --- Context section classes ----------------------- */
.context-row { }
-.context-item-name { font-family: monospace; font-weight: bold; color: black; }
-.context-item-value { font-size: small; color: #448; }
-.context-item-desc { color: #333; padding-left: 2em; }
+.context-item-name {
+ font-family: monospace;
+ font-weight: bold;
+ color: black;
+}
+
+.context-item-value {
+ font-size: small;
+ color: #448;
+}
+
+.context-item-desc {
+ color: #333;
+ padding-left: 2em;
+}
+
/* --- Method classes -------------------------- */
+
.method-detail {
- background: #efefef;
- padding: 0;
- margin-top: 0.5em;
- margin-bottom: 1em;
- border: 1px dotted #ccc;
+ background: #efefef;
+ padding: 0;
+ margin-top: 0.5em;
+ margin-bottom: 1em;
+ border: 1px dotted #ccc;
}
+
.method-heading {
color: black;
background: #ccc;
border-bottom: 1px solid #666;
padding: 0.2em 0.5em 0 0.5em;
}
-.method-signature { color: black; background: inherit; }
-.method-name { font-weight: bold; }
-.method-args { font-style: italic; }
-.method-description { padding: 0 0.5em 0 0.5em; }
+.method-signature {
+ color: black;
+ background: inherit;
+}
+
+.method-name {
+ font-weight: bold;
+}
+
+.method-args {
+ font-style: italic;
+}
+
+.method-description {
+ padding: 0 0.5em 0 0.5em;
+}
+
/* --- Source code sections -------------------- */
-a.source-toggle { font-size: 90%; }
+:link.source-toggle, :visited.source-toggle {
+ font-size: 90%;
+}
+
div.method-source-code {
- background: #262626;
- color: #ffdead;
- margin: 1em;
- padding: 0.5em;
- border: 1px dashed #999;
- overflow: hidden;
+ background: #262626;
+ color: #ffdead;
+ margin: 1em;
+ padding: 0.5em;
+ border: 1px dashed #999;
+ overflow: auto;
}
-div.method-source-code pre { color: #ffdead; overflow: hidden; }
+div.method-source-code pre {
+ color: #ffdead;
+}
/* --- Ruby keyword styles --------------------- */
-.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
+.standalone-code {
+ background: #221111;
+ color: #ffdead;
+ overflow: auto;
+}
-.ruby-constant { color: #7fffd4; background: transparent; }
-.ruby-keyword { color: #00ffff; background: transparent; }
-.ruby-ivar { color: #eedd82; background: transparent; }
-.ruby-operator { color: #00ffee; background: transparent; }
-.ruby-identifier { color: #ffdead; background: transparent; }
-.ruby-node { color: #ffa07a; background: transparent; }
-.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
-.ruby-regexp { color: #ffa07a; background: transparent; }
-.ruby-value { color: #7fffd4; background: transparent; }
+.ruby-constant {
+ color: #7fffd4;
+ background: transparent;
+}
+
+.ruby-keyword {
+ color: #00ffff;
+ background: transparent;
+}
+
+.ruby-ivar {
+ color: #eedd82;
+ background: transparent;
+}
+
+.ruby-operator {
+ color: #00ffee;
+ background: transparent;
+}
+
+.ruby-identifier {
+ color: #ffdead;
+ background: transparent;
+}
+
+.ruby-node {
+ color: #ffa07a;
+ background: transparent;
+}
+
+.ruby-comment {
+ color: #b22222;
+ font-weight: bold;
+ background: transparent;
+}
+
+.ruby-regexp {
+ color: #ffa07a;
+ background: transparent;
+}
+
+.ruby-value {
+ color: #7fffd4;
+ background: transparent;
+}
EOF
@@ -240,15 +336,7 @@
### H E A D E R T E M P L A T E
#####################################################################
- XHTML_PREAMBLE = <<-EOF
-<?xml version="1.0" encoding="<%= values["charset"] %>"?>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- EOF
-
- HEADER = XHTML_PREAMBLE + <<-EOF
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ HEADER = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
@@ -281,7 +369,7 @@
}
// Make codeblocks hidden by default
- document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" )
+ document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }<\\/style>" )
// ]]>
</script>
@@ -291,13 +379,6 @@
EOF
#####################################################################
-### C O N T E X T C O N T E N T T E M P L A T E
-#####################################################################
-
- CONTEXT_CONTENT = %{
-}
-
-#####################################################################
### F O O T E R T E M P L A T E
#####################################################################
@@ -361,7 +442,7 @@
(<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
<% end %>
<br />
-<% end # values["infiles"] %>
+<% end %><%# values["infiles"] %>
</td>
</tr>
@@ -388,39 +469,38 @@
#####################################################################
METHOD_LIST = <<-EOF
-
<div id="contextContent">
<% if values["diagram"] then %>
<div id="diagram">
<%= values["diagram"] %>
</div>
-<% end %>
+<% end
-<% if values["description"] then %>
+ if values["description"] then %>
<div id="description">
<%= values["description"] %>
</div>
-<% end %>
+<% end
-<% if values["requires"] then %>
+ if values["requires"] then %>
<div id="requires-list">
<h3 class="section-bar">Required files</h3>
<div class="name-list">
<% values["requires"].each do |requires| %>
<%= href requires["aref"], requires["name"] %>
-<% end # values["requires"] %>
+<% end %><%# values["requires"] %>
</div>
</div>
-<% end %>
+<% end
-<% if values["toc"] then %>
+ if values["toc"] then %>
<div id="contents-list">
<h3 class="section-bar">Contents</h3>
<ul>
<% values["toc"].each do |toc| %>
- <li><a href="#<%= values["href"] %>"><%= values["secname"] %></a></li>
-<% end # values["toc"] %>
+ <li><a href="#<%= toc["href"] %>"><%= toc["secname"] %></a></li>
+<% end %><%# values["toc"] %>
</ul>
<% end %>
</div>
@@ -430,16 +510,14 @@
<h3 class="section-bar">Methods</h3>
<div class="name-list">
-<% values["methods"].each do |methods| %>
+<% values["methods"].each do |methods| %>
<%= href methods["aref"], methods["name"] %>
-<% end # values["methods"] %>
+<% end %><%# values["methods"] %>
</div>
</div>
<% end %>
-
</div>
-
<!-- if includes -->
<% if values["includes"] then %>
<div id="includes">
@@ -448,140 +526,137 @@
<div id="includes-list">
<% values["includes"].each do |includes| %>
<span class="include-name"><%= href includes["aref"], includes["name"] %></span>
-<% end # values["includes"] %>
+<% end %><%# values["includes"] %>
</div>
</div>
-<% end %>
+<% end
-<% values["sections"].each do |sections| %>
+ values["sections"].each do |sections| %>
<div id="section">
-<% if sections["sectitle"] then %>
+<% if sections["sectitle"] then %>
<h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
-<% if sections["seccomment"] then %>
+<% if sections["seccomment"] then %>
<div class="section-comment">
<%= sections["seccomment"] %>
</div>
-<% end %>
-<% end %>
+<% end
+ end
-<% if values["classlist"] then %>
+ if sections["classlist"] then %>
<div id="class-list">
<h3 class="section-bar">Classes and Modules</h3>
- <%= values["classlist"] %>
+ <%= sections["classlist"] %>
</div>
-<% end %>
+<% end
-<% if values["constants"] then %>
+ if sections["constants"] then %>
<div id="constants-list">
<h3 class="section-bar">Constants</h3>
<div class="name-list">
<table summary="Constants">
-<% values["constants"].each do |constants| %>
+<% sections["constants"].each do |constants| %>
<tr class="top-aligned-row context-row">
<td class="context-item-name"><%= constants["name"] %></td>
<td>=</td>
<td class="context-item-value"><%= constants["value"] %></td>
-<% if values["desc"] then %>
- <td width="3em"> </td>
+<% if constants["desc"] then %>
+ <td> </td>
<td class="context-item-desc"><%= constants["desc"] %></td>
-<% end %>
+<% end %>
</tr>
-<% end # values["constants"] %>
+<% end %><%# sections["constants"] %>
</table>
</div>
</div>
-<% end %>
+<% end
-<% if values["aliases"] then %>
+ if sections["aliases"] then %>
<div id="aliases-list">
<h3 class="section-bar">External Aliases</h3>
<div class="name-list">
- <table summary="aliases">
-<% values["aliases"].each do |aliases| $stderr.puts({ :aliases => aliases }.inspect) %>
+ <table summary="aliases">
+<% sections["aliases"].each do |aliases| %>
<tr class="top-aligned-row context-row">
- <td class="context-item-name"><%= values["old_name"] %></td>
+ <td class="context-item-name"><%= aliases["old_name"] %></td>
<td>-></td>
- <td class="context-item-value"><%= values["new_name"] %></td>
+ <td class="context-item-value"><%= aliases["new_name"] %></td>
</tr>
-<% if values["desc"] then %>
+<% if aliases["desc"] then %>
<tr class="top-aligned-row context-row">
<td> </td>
- <td colspan="2" class="context-item-desc"><%= values["desc"] %></td>
+ <td colspan="2" class="context-item-desc"><%= aliases["desc"] %></td>
</tr>
-<% end %>
-<% end # values["aliases"] %>
+<% end
+ end %><%# sections["aliases"] %>
</table>
</div>
</div>
-<% end %>
+<% end %>
-
-<% if values["attributes"] then %>
+<% if sections["attributes"] then %>
<div id="attribute-list">
<h3 class="section-bar">Attributes</h3>
<div class="name-list">
<table>
-<% values["attributes"].each do |attributes| $stderr.puts({ :attributes => attributes }.inspect) %>
+<% sections["attributes"].each do |attribute| %>
<tr class="top-aligned-row context-row">
- <td class="context-item-name"><%= values["name"] %></td>
-<% if values["rw"] then %>
- <td class="context-item-value"> [<%= values["rw"] %>] </td>
-<% end %>
-<% unless values["rw"] then %>
+ <td class="context-item-name"><%= attribute["name"] %></td>
+<% if attribute["rw"] then %>
+ <td class="context-item-value"> [<%= attribute["rw"] %>] </td>
+<% end
+ unless attribute["rw"] then %>
<td class="context-item-value"> </td>
-<% end %>
- <td class="context-item-desc"><%= values["a_desc"] %></td>
+<% end %>
+ <td class="context-item-desc"><%= attribute["a_desc"] %></td>
</tr>
-<% end # values["attributes"] %>
+<% end %><%# sections["attributes"] %>
</table>
</div>
</div>
-<% end %>
-
+<% end %>
-
<!-- if method_list -->
-<% if sections["method_list"] then %>
+<% if sections["method_list"] then %>
<div id="methods">
-<% sections["method_list"].each do |method_list| %>
-<% if method_list["methods"] then %>
+<% sections["method_list"].each do |method_list|
+ if method_list["methods"] then %>
<h3 class="section-bar"><%= method_list["type"] %> <%= method_list["category"] %> methods</h3>
-<% method_list["methods"].each do |methods| %>
+<% method_list["methods"].each do |methods| %>
<div id="method-<%= methods["aref"] %>" class="method-detail">
<a name="<%= methods["aref"] %>"></a>
<div class="method-heading">
-<% if methods["codeurl"] then %>
+<% if methods["codeurl"] then %>
<a href="<%= methods["codeurl"] %>" target="Code" class="method-signature"
onclick="popupCode('<%= methods["codeurl"] %>');return false;">
-<% end %>
-<% if methods["sourcecode"] then %>
+<% end
+ if methods["sourcecode"] then %>
<a href="#<%= methods["aref"] %>" class="method-signature">
-<% end %>
-<% if methods["callseq"] then %>
+<% end
+ if methods["callseq"] then %>
<span class="method-name"><%= methods["callseq"] %></span>
-<% end %>
-<% unless methods["callseq"] then %>
+<% end
+ unless methods["callseq"] then %>
<span class="method-name"><%= methods["name"] %></span><span class="method-args"><%= methods["params"] %></span>
-<% end %>
-<% if methods["codeurl"] then %>
+<% end
+ if methods["codeurl"] then %>
</a>
-<% end %>
-<% if methods["sourcecode"] then %>
+<% end
+ if methods["sourcecode"] then %>
</a>
-<% end %>
+<% end %>
</div>
<div class="method-description">
-<% if methods["m_desc"] then %>
+<% if methods["m_desc"] then %>
<%= methods["m_desc"] %>
-<% end %>
-<% if methods["sourcecode"] then %>
+<% end
+ if methods["sourcecode"] then %>
<p><a class="source-toggle" href="#"
onclick="toggleCode('<%= methods["aref"] %>-source');return false;">[Source]</a></p>
<div class="method-source-code" id="<%= methods["aref"] %>-source">
@@ -589,17 +664,17 @@
<%= methods["sourcecode"] %>
</pre>
</div>
-<% end %>
+<% end %>
</div>
</div>
-<% end # method_list["methods"] %>
-<% end %>
-<% end # sections["method_list"] %>
+<% end %><%# method_list["methods"] %><%
+ end
+ end %><%# sections["method_list"] %>
</div>
-<% end %>
-<% end # values["sections"] %>
+<% end %>
+<% end %><%# values["sections"] %>
EOF
#####################################################################
@@ -622,8 +697,7 @@
### S O U R C E C O D E T E M P L A T E
#####################################################################
- SRC_PAGE = XHTML_PREAMBLE + <<-EOF
-<html>
+ SRC_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
@@ -640,30 +714,27 @@
### I N D E X F I L E T E M P L A T E S
#####################################################################
- FR_INDEX_BODY = %{
-<%= template_include %>
-}
+ FR_INDEX_BODY = %{<%= template_include %>}
- FILE_INDEX = XHTML_PREAMBLE + <<-EOF
+ FILE_INDEX = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + <<-EOF
<!--
- <%= values["list_title"] %>
+ <%= values["title"] %>
-->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
- <title><%= values["list_title"] %></title>
+ <title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" />
<base target="docwin" />
</head>
<body>
-<div id="index">
+<div class="index">
<h1 class="section-bar"><%= values["list_title"] %></h1>
<div id="index-entries">
<% values["entries"].each do |entries| %>
<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
-<% end # values["entries"] %>
+<% end %><%# values["entries"] %>
</div>
</div>
</body>
@@ -673,18 +744,12 @@
CLASS_INDEX = FILE_INDEX
METHOD_INDEX = FILE_INDEX
- INDEX = <<-EOF
-<?xml version="1.0" encoding="<%= values["charset"] %>"?>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
-
+ INDEX = XHTML_FRAME_PREAMBLE + HTML_ELEMENT + <<-EOF
<!--
<%= values["title"] %>
-->
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
Modified: MacRuby/branches/experimental/lib/rdoc/generator/html/kilmer.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html/kilmer.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html/kilmer.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,10 +1,11 @@
require 'rdoc/generator/html'
+require 'rdoc/generator/html/kilmerfactory'
module RDoc::Generator::HTML::KILMER
FONTS = "Verdana, Arial, Helvetica, sans-serif"
- STYLE = <<-EOF
+ CENTRAL_STYLE = <<-EOF
body,td,p { font-family: <%= values["fonts"] %>;
color: #000040;
}
@@ -30,6 +31,10 @@
.aqua { color: black }
+#diagram img {
+ border: 0;
+}
+
.method-name, .attr-name {
font-family: font-family: <%= values["fonts"] %>;
font-weight: bold;
@@ -67,7 +72,7 @@
font-weight: bold;
text-decoration: none;
color: #000033;
- background-color: white;
+ background: #ccc;
}
.srclink {
@@ -78,232 +83,8 @@
background-color: white;
}
-.paramsig {
- font-size: small;
-}
-
.srcbut { float: right }
- EOF
- BODY = <<-EOF
-<html><head>
- <title><%= values["title"] %></title>
- <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
- <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
- <script type="text/javascript" language="JavaScript">
- <!--
- function popCode(url) {
- parent.frames.source.location = url
- }
- //-->
- </script>
-</head>
-<body bgcolor="white">
-
-<%= template_include %> <!-- banner header -->
-
-<% if values["diagram"] then %>
-<table width="100%"><tr><td align="center">
-<%= values["diagram"] %>
-</td></tr></table>
-<% end %>
-
-<% if values["description"] then %>
-<div class="description"><%= values["description"] %></div>
-<% end %>
-
-<% if values["requires"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Required files</td></tr>
-</table><br />
-<div class="name-list">
-<% values["requires"].each do |requires| %>
-<%= href requires["aref"], requires["name"] %>
-<% end # values["requires"] %>
-<% end %>
-</div>
-
-<% if values["methods"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Methods</td></tr>
-</table><br />
-<div class="name-list">
-<% values["methods"].each do |methods| %>
-<%= href methods["aref"], methods["name"] %>,
-<% end # values["methods"] %>
-</div>
-<% end %>
-
-
-<% values["sections"].each do |sections| %>
- <div id="section">
-<% if sections["sectitle"] then %>
- <h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
-<% if sections["seccomment"] then %>
- <div class="section-comment">
- <%= sections["seccomment"] %>
- </div>
-<% end %>
-<% end %>
-
-<% if sections["attributes"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Attributes</td></tr>
-</table><br />
-<table cellspacing="5">
-<% sections["attributes"].each do |attributes| %>
- <tr valign="top">
-<% if attributes["rw"] then %>
- <td align="center" class="attr-rw"> [<%= attributes["rw"] %>] </td>
-<% end %>
-<% unless attributes["rw"] then %>
- <td></td>
-<% end %>
- <td class="attr-name"><%= attributes["name"] %></td>
- <td><%= attributes["a_desc"] %></td>
- </tr>
-<% end # sections["attributes"] %>
-</table>
-<% end %>
-
-<% if sections["classlist"] then %>
-<table cellpadding="5" width="100%">
-<tr><td class="tablesubtitle">Classes and Modules</td></tr>
-</table><br />
-<%= sections["classlist"] %><br />
-<% end %>
-
- <%= template_include %> <!-- method descriptions -->
-
-<% end # values["sections"] %>
-
-</body>
-</html>
- EOF
-
- FILE_PAGE = <<-EOF
-<table width="100%">
- <tr class="title-row">
- <td><table width="100%"><tr>
- <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font><%= values["short_name"] %></td>
- <td align="right"><table cellspacing="0" cellpadding="2">
- <tr>
- <td class="small-title-font">Path:</td>
- <td class="small-title-font"><%= values["full_path"] %>
-<% if values["cvsurl"] then %>
- (<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-<% end %>
- </td>
- </tr>
- <tr>
- <td class="small-title-font">Modified:</td>
- <td class="small-title-font"><%= values["dtm_modified"] %></td>
- </tr>
- </table>
- </td></tr></table></td>
- </tr>
-</table><br />
- EOF
-
- CLASS_PAGE = <<-EOF
-<table width="100%" border="0" cellspacing="0">
- <tr class="title-row">
- <td class="big-title-font">
- <font size="-3"><b><%= values["classmod"] %></b><br /></font><%= values["full_name"] %>
- </td>
- <td align="right">
- <table cellspacing="0" cellpadding="2">
- <tr valign="top">
- <td class="small-title-font">In:</td>
- <td class="small-title-font">
-<% values["infiles"].each do |infiles| %>
-<%= href infiles["full_path_url"], infiles["full_path"] %>
-<% if infiles["cvsurl"] then %>
- (<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
-<% end %>
-<% end # values["infiles"] %>
- </td>
- </tr>
-<% if values["parent"] then %>
- <tr>
- <td class="small-title-font">Parent:</td>
- <td class="small-title-font">
-<% if values["par_url"] then %>
- <a href="<%= values["par_url"] %>" class="cyan">
-<% end %>
-<%= values["parent"] %>
-<% if values["par_url"] then %>
- </a>
-<% end %>
- </td>
- </tr>
-<% end %>
- </table>
- </td>
- </tr>
-</table><br />
- EOF
-
- METHOD_LIST = <<-EOF
-<% if values["includes"] then %>
-<div class="tablesubsubtitle">Included modules</div><br />
-<div class="name-list">
-<% values["includes"].each do |includes| %>
- <span class="method-name"><%= href includes["aref"], includes["name"] %></span>
-<% end # values["includes"] %>
-</div>
-<% end %>
-
-<% if values["method_list"] then %>
-<% values["method_list"].each do |method_list| $stderr.puts({ :method_list => method_list }.inspect) %>
-<% if values["methods"] then %>
-<table cellpadding=5 width="100%">
-<tr><td class="tablesubtitle"><%= values["type"] %> <%= values["category"] %> methods</td></tr>
-</table>
-<% values["methods"].each do |methods| $stderr.puts({ :methods => methods }.inspect) %>
-<table width="100%" cellspacing="0" cellpadding="5" border="0">
-<tr><td class="methodtitle">
-<a name="<%= values["aref"] %>">
-<% if values["callseq"] then %>
-<b><%= values["callseq"] %></b>
-<% end %>
-<% unless values["callseq"] then %>
- <b><%= values["name"] %></b><%= values["params"] %>
-<% end %>
-<% if values["codeurl"] then %>
-<a href="<%= values["codeurl"] %>" target="source" class="srclink">src</a>
-<% end %>
-</a></td></tr>
-</table>
-<% if values["m_desc"] then %>
-<div class="description">
-<%= values["m_desc"] %>
-</div>
-<% end %>
-<% if values["aka"] then %>
-<div class="aka">
-This method is also aliased as
-<% values["aka"].each do |aka| $stderr.puts({ :aka => aka }.inspect) %>
-<a href="<%= values["aref"] %>"><%= values["name"] %></a>
-<% end # values["aka"] %>
-</div>
-<% end %>
-<% if values["sourcecode"] then %>
-<pre class="source">
-<%= values["sourcecode"] %>
-</pre>
-<% end %>
-<% end # values["methods"] %>
-<% end %>
-<% end # values["method_list"] %>
-<% end %>
- EOF
-
- SRC_PAGE = <<-EOF
-<html>
-<head><title><%= values["title"] %></title>
-<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
-<style type="text/css">
.ruby-comment { color: green; font-style: italic }
.ruby-constant { color: #4433aa; font-weight: bold; }
.ruby-identifier { color: #222222; }
@@ -313,28 +94,9 @@
.ruby-operator { color: #111111; }
.ruby-regexp { color: #662222; }
.ruby-value { color: #662222; font-style: italic }
- .kw { color: #3333FF; font-weight: bold }
- .cmt { color: green; font-style: italic }
- .str { color: #662222; font-style: italic }
- .re { color: #662222; }
-</style>
-</head>
-<body bgcolor="white">
-<pre><%= values["code"] %></pre>
-</body>
-</html>
EOF
- FR_INDEX_BODY = %{
-<%= template_include %>
-}
-
- FILE_INDEX = <<-EOF
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
-<style>
-<!--
+ INDEX_STYLE = <<-EOF
body {
background-color: #ddddff;
font-family: #{FONTS};
@@ -355,64 +117,35 @@
text-align: center;
width: 100%;
}
+EOF
--->
-</style>
-<base target="docwin">
-</head>
-<body>
-<div class="banner"><%= values["list_title"] %></div>
-<% values["entries"].each do |entries| %>
-<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
-<% end # values["entries"] %>
-</body></html>
- EOF
+ FACTORY = RDoc::Generator::HTML::
+ KilmerFactory.new(:central_css => CENTRAL_STYLE,
+ :index_css => INDEX_STYLE)
- CLASS_INDEX = FILE_INDEX
- METHOD_INDEX = FILE_INDEX
+ STYLE = FACTORY.get_STYLE()
- INDEX = <<-EOF
-<html>
-<head>
- <title><%= values["title"] %></title>
- <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>">
-</head>
+ METHOD_LIST = FACTORY.get_METHOD_LIST()
+
+ BODY = FACTORY.get_BODY()
+
+ FILE_PAGE = FACTORY.get_FILE_PAGE()
-<frameset cols="20%,*">
- <frameset rows="15%,35%,50%">
- <frame src="fr_file_index.html" title="Files" name="Files">
- <frame src="fr_class_index.html" name="Classes">
- <frame src="fr_method_index.html" name="Methods">
- </frameset>
-<% if values["inline_source"] then %>
- <frame src="<%= values["initial_page"] %>" name="docwin">
-<% end %>
-<% unless values["inline_source"] then %>
- <frameset rows="80%,20%">
- <frame src="<%= values["initial_page"] %>" name="docwin">
- <frame src="blank.html" name="source">
- </frameset>
-<% end %>
- <noframes>
- <body bgcolor="white">
- Click <a href="html/index.html">here</a> for a non-frames
- version of this page.
- </body>
- </noframes>
-</frameset>
+ CLASS_PAGE = FACTORY.get_CLASS_PAGE()
-</html>
- EOF
+ SRC_PAGE = FACTORY.get_SRC_PAGE()
- # A blank page to use as a target
- BLANK = %{
-<html><body bgcolor="white"></body></html>
-}
+ FR_INDEX_BODY = FACTORY.get_FR_INDEX_BODY()
- def write_extra_pages
- template = TemplatePage.new(BLANK)
- File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
- end
+ FILE_INDEX = FACTORY.get_FILE_INDEX()
-end
+ CLASS_INDEX = FACTORY.get_CLASS_INDEX()
+ METHOD_INDEX = FACTORY.get_METHOD_INDEX()
+
+ INDEX = FACTORY.get_INDEX()
+
+ def self.write_extra_pages(values)
+ FACTORY.write_extra_pages(values)
+ end
+end
Copied: MacRuby/branches/experimental/lib/rdoc/generator/html/kilmerfactory.rb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/html/kilmerfactory.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html/kilmerfactory.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html/kilmerfactory.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,427 @@
+require 'rdoc/generator/html'
+require 'rdoc/generator/html/common'
+
+#
+# This class generates Kilmer-style templates. Right now,
+# rdoc is shipped with two such templates:
+# * kilmer
+# * hefss
+#
+# Kilmer-style templates use frames. The left side of the page has
+# three frames stacked on top of each other: one lists
+# files, one lists classes, and one lists methods. If source code
+# is not inlined, an additional frame runs across the bottom of
+# the page and will be used to display method source code.
+# The central (and largest frame) display class and file
+# pages.
+#
+# The constructor of this class accepts a Hash containing stylistic
+# attributes. Then, a get_BLAH instance method of this class returns a
+# value for the template's BLAH constant. get_BODY, for instance, returns
+# the value of the template's BODY constant.
+#
+class RDoc::Generator::HTML::KilmerFactory
+
+ include RDoc::Generator::HTML::Common
+
+ #
+ # The contents of the stylesheet that should be used for the
+ # central frame (for the class and file pages).
+ #
+ # This must be specified in the Hash passed to the constructor.
+ #
+ attr_reader :central_css
+
+ #
+ # The contents of the stylesheet that should be used for the
+ # index pages.
+ #
+ # This must be specified in the Hash passed to the constructor.
+ #
+ attr_reader :index_css
+
+ #
+ # The heading that should be displayed before listing methods.
+ #
+ # If not supplied, this defaults to "Methods".
+ #
+ attr_reader :method_list_heading
+
+ #
+ # The heading that should be displayed before listing classes and
+ # modules.
+ #
+ # If not supplied, this defaults to "Classes and Modules".
+ #
+ attr_reader :class_and_module_list_heading
+
+ #
+ # The heading that should be displayed before listing attributes.
+ #
+ # If not supplied, this defaults to "Attributes".
+ #
+ attr_reader :attribute_list_heading
+
+ #
+ # ====Description:
+ # This method constructs a KilmerFactory instance, which
+ # can be used to build Kilmer-style template classes.
+ # The +style_attributes+ argument is a Hash that contains the
+ # values of the classes attributes (Symbols mapped to Strings).
+ #
+ # ====Parameters:
+ # [style_attributes]
+ # A Hash describing the appearance of the Kilmer-style.
+ #
+ def initialize(style_attributes)
+ @central_css = style_attributes[:central_css]
+ if(!@central_css)
+ raise ArgumentError, "did not specify a value for :central_css"
+ end
+
+ @index_css = style_attributes[:index_css]
+ if(!@index_css)
+ raise ArgumentError, "did not specify a value for :index_css"
+ end
+
+ @method_list_heading = style_attributes[:method_list_heading]
+ if(!@method_list_heading)
+ @method_list_heading = "Methods"
+ end
+
+ @class_and_module_list_heading = style_attributes[:class_and_module_list_heading]
+ if(!@class_and_module_list_heading)
+ @class_and_module_list_heading = "Classes and Modules"
+ end
+
+ @attribute_list_heading = style_attributes[:attribute_list_heading]
+ if(!@attribute_list_heading)
+ @attribute_list_heading = "Attributes"
+ end
+ end
+
+ def get_STYLE
+ return @central_css
+ end
+
+ def get_METHOD_LIST
+ return %{
+<% if values["diagram"] then %>
+<div id="diagram">
+<table width="100%"><tr><td align="center">
+<%= values["diagram"] %>
+</td></tr></table>
+</div>
+<% end %>
+
+<% if values["description"] then %>
+<div class="description"><%= values["description"] %></div>
+<% end %>
+
+<% if values["requires"] then %>
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Required files</td></tr>
+</table><br />
+<div class="name-list">
+<% values["requires"].each do |requires| %>
+<%= href requires["aref"], requires["name"] %>
+<% end %><%# values["requires"] %>
+</div>
+<% end %>
+
+<% if values["methods"] then %>
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">#{@method_list_heading}</td></tr>
+</table><br />
+<div class="name-list">
+<% values["methods"].each do |methods| %>
+<%= href methods["aref"], methods["name"] %>,
+<% end %><%# values["methods"] %>
+</div>
+<% end %>
+
+<% if values["includes"] then %>
+<div class="tablesubsubtitle">Included modules</div><br />
+<div class="name-list">
+<% values["includes"].each do |includes| %>
+ <span class="method-name"><%= href includes["aref"], includes["name"] %></span>
+<% end %><%# values["includes"] %>
+</div>
+<% end %>
+
+<% values["sections"].each do |sections| %>
+ <div id="section">
+<% if sections["sectitle"] then %>
+ <h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2>
+<% if sections["seccomment"] then %>
+ <div class="section-comment">
+ <%= sections["seccomment"] %>
+ </div>
+<% end %>
+<% end %>
+<% if sections["attributes"] then %>
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">#{@attribute_list_heading}</td></tr>
+</table><br />
+<table cellspacing="5">
+<% sections["attributes"].each do |attributes| %>
+ <tr valign="top">
+<% if attributes["rw"] then %>
+ <td align="center" class="attr-rw"> [<%= attributes["rw"] %>] </td>
+<% end %>
+<% unless attributes["rw"] then %>
+ <td></td>
+<% end %>
+ <td class="attr-name"><%= attributes["name"] %></td>
+ <td><%= attributes["a_desc"] %></td>
+ </tr>
+<% end %><%# sections["attributes"] %>
+</table>
+<% end %>
+
+<% if sections["classlist"] then %>
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">#{@class_and_module_list_heading}</td></tr>
+</table><br />
+<%= sections["classlist"] %><br />
+<% end %>
+
+<% if sections["method_list"] then %>
+<% sections["method_list"].each do |method_list| %>
+<% if method_list["methods"] then %>
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle"><%= method_list["type"] %> <%= method_list["category"] %> methods</td></tr>
+</table>
+<% method_list["methods"].each do |methods| %>
+<table width="100%" cellspacing="0" cellpadding="5" border="0">
+<tr><td class="methodtitle">
+<a name="<%= methods["aref"] %>">
+<% if methods["callseq"] then %>
+<b><%= methods["callseq"] %></b>
+<% end %>
+<% unless methods["callseq"] then %>
+ <b><%= methods["name"] %></b><%= methods["params"] %>
+<% end %>
+</a>
+<% if methods["codeurl"] then %>
+<a href="<%= methods["codeurl"] %>" target="source" class="srclink">src</a>
+<% end %>
+</td></tr>
+</table>
+<% if methods["m_desc"] then %>
+<div class="description">
+<%= methods["m_desc"] %>
+</div>
+<% end %>
+<% if methods["aka"] then %>
+<div class="aka">
+This method is also aliased as
+<% methods["aka"].each do |aka| %>
+<a href="<%= methods["aref"] %>"><%= methods["name"] %></a>
+<% end %><%# methods["aka"] %>
+</div>
+<% end %>
+<% if methods["sourcecode"] then %>
+<pre class="source">
+<%= methods["sourcecode"] %>
+</pre>
+<% end %>
+<% end %><%# method_list["methods"] %>
+<% end %>
+<% end %><%# sections["method_list"] %>
+<% end %>
+
+<% end %><%# values["sections"] %>
+</div>
+}
+ end
+
+ def get_BODY
+ return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
+<head>
+ <title><%= values["title"] %></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+ <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
+ <script type="text/javascript">
+ <!--
+ function popCode(url) {
+ parent.frames.source.location = url
+ }
+ //-->
+ </script>
+</head>
+<body>
+<div class="bodyContent">
+<%= template_include %> <!-- banner header -->
+
+#{get_METHOD_LIST()}
+</div>
+</body>
+</html>
+}
+ end
+
+def get_FILE_PAGE
+ return %{
+<table width="100%">
+ <tr class="title-row">
+ <td><table width="100%"><tr>
+ <td class="big-title-font" colspan="2">File<br /><%= values["short_name"] %></td>
+ <td align="right"><table cellspacing="0" cellpadding="2">
+ <tr>
+ <td class="small-title-font">Path:</td>
+ <td class="small-title-font"><%= values["full_path"] %>
+<% if values["cvsurl"] then %>
+ (<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+<% end %>
+ </td>
+ </tr>
+ <tr>
+ <td class="small-title-font">Modified:</td>
+ <td class="small-title-font"><%= values["dtm_modified"] %></td>
+ </tr>
+ </table>
+ </td></tr></table></td>
+ </tr>
+</table><br />
+}
+end
+
+def get_CLASS_PAGE
+ return %{
+<table width="100%" border="0" cellspacing="0">
+ <tr class="title-row">
+ <td class="big-title-font">
+ <%= values["classmod"] %><br /><%= values["full_name"] %>
+ </td>
+ <td align="right">
+ <table cellspacing="0" cellpadding="2">
+ <tr valign="top">
+ <td class="small-title-font">In:</td>
+ <td class="small-title-font">
+<% values["infiles"].each do |infiles| %>
+<%= href infiles["full_path_url"], infiles["full_path"] %>
+<% if infiles["cvsurl"] then %>
+ (<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+<% end %>
+<% end %><%# values["infiles"] %>
+ </td>
+ </tr>
+<% if values["parent"] then %>
+ <tr>
+ <td class="small-title-font">Parent:</td>
+ <td class="small-title-font">
+<% if values["par_url"] then %>
+ <a href="<%= values["par_url"] %>" class="cyan">
+<% end %>
+<%= values["parent"] %>
+<% if values["par_url"] then %>
+ </a>
+<% end %>
+ </td>
+ </tr>
+<% end %>
+ </table>
+ </td>
+ </tr>
+</table><br />
+}
+end
+
+def get_SRC_PAGE
+ return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
+<head><title><%= values["title"] %></title>
+<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+<link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
+</head>
+<body>
+<pre><%= values["code"] %></pre>
+</body>
+</html>
+}
+end
+
+def get_FR_INDEX_BODY
+ return %{<%= template_include %>}
+end
+
+def get_FILE_INDEX
+ return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
+<head>
+<title><%= values["title"] %></title>
+<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+<style type="text/css">
+<!--
+#{@index_css}
+-->
+</style>
+<base target="docwin" />
+</head>
+<body>
+<div class="index">
+<div class="banner"><%= values["list_title"] %></div>
+<% values["entries"].each do |entries| %>
+<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br />
+<% end %><%# values["entries"] %>
+</div>
+</body></html>
+}
+end
+
+def get_CLASS_INDEX
+ return get_FILE_INDEX
+end
+
+def get_METHOD_INDEX
+ return get_FILE_INDEX
+end
+
+def get_INDEX
+ return XHTML_FRAME_PREAMBLE + HTML_ELEMENT + %{
+<head>
+ <title><%= values["title"] %></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+</head>
+
+<frameset cols="20%,*">
+ <frameset rows="15%,35%,50%">
+ <frame src="fr_file_index.html" title="Files" name="Files" />
+ <frame src="fr_class_index.html" name="Classes" />
+ <frame src="fr_method_index.html" name="Methods" />
+ </frameset>
+<% if values["inline_source"] then %>
+ <frame src="<%= values["initial_page"] %>" name="docwin" />
+<% end %>
+<% unless values["inline_source"] then %>
+ <frameset rows="80%,20%">
+ <frame src="<%= values["initial_page"] %>" name="docwin" />
+ <frame src="blank.html" name="source" />
+ </frameset>
+<% end %>
+</frameset>
+
+</html>
+}
+end
+
+def get_BLANK
+ # This will be displayed in the source code frame before
+ # any source code has been selected.
+ return XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
+<head>
+ <title>Source Code Frame <%= values["title_suffix"] %></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
+ <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" />
+</head>
+<body>
+</body>
+</html>
+}
+end
+
+def write_extra_pages(values)
+ template = RDoc::TemplatePage.new(get_BLANK())
+ File.open("blank.html", "w") { |f| template.write_html_on(f, values) }
+end
+
+end
Modified: MacRuby/branches/experimental/lib/rdoc/generator/html/one_page_html.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html/one_page_html.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html/one_page_html.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,10 @@
require 'rdoc/generator/html'
+require 'rdoc/generator/html/common'
module RDoc::Generator::HTML::ONE_PAGE_HTML
+ include RDoc::Generator::HTML::Common
+
CONTENTS_XML = <<-EOF
<% if defined? classes and classes["description"] then %>
<%= classes["description"] %>
@@ -17,7 +20,7 @@
<% unless requires["aref"] then %>
<li><%= requires["name"] %></li>
<% end %>
-<% end # files["requires"] %>
+<% end %><%# files["requires"] %>
</ul>
<% end %>
@@ -31,7 +34,7 @@
<% unless includes["aref"] then %>
<li><%= includes["name"] %></li>
<% end %>
-<% end # classes["includes"] %>
+<% end %><%# classes["includes"] %>
</ul>
<% end %>
@@ -42,7 +45,7 @@
<table>
<% sections["attributes"].each do |attributes| %>
<tr><td><%= attributes["name"] %></td><td><%= attributes["rw"] %></td><td><%= attributes["a_desc"] %></td></tr>
-<% end # sections["attributes"] %>
+<% end %><%# sections["attributes"] %>
</table>
<% end %>
@@ -68,36 +71,34 @@
<%= methods["sourcecode"] %>
</pre></blockquote>
<% end %>
-<% end # method_list["methods"] %>
+<% end %><%# method_list["methods"] %>
<% end %>
-<% end # sections["method_list"] %>
+<% end %><%# sections["method_list"] %>
<% end %>
-<% end # classes["sections"] %>
+<% end %><%# classes["sections"] %>
<% end %>
EOF
- ONE_PAGE = %{
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
+ ONE_PAGE = XHTML_STRICT_PREAMBLE + HTML_ELEMENT + %{
<head>
<title><%= values["title"] %></title>
<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" />
</head>
<body>
<% values["files"].each do |files| %>
-<h2>File: <%= files["short_name"] %></h2>
+<h2>File: <a name="<%= files["href"] %>"><%= files["short_name"] %></a></h2>
<table>
<tr><td>Path:</td><td><%= files["full_path"] %></td></tr>
<tr><td>Modified:</td><td><%= files["dtm_modified"] %></td></tr>
</table>
} + CONTENTS_XML + %{
-<% end # values["files"] %>
+<% end %><%# values["files"] %>
<% if values["classes"] then %>
<h2>Classes</h2>
<% values["classes"].each do |classes| %>
<% if classes["parent"] then %>
-<h3><%= classes["classmod"] %> <%= classes["full_name"] %> < <%= href classes["par_url"], classes["parent"] %></h3>
+<h3><%= classes["classmod"] %> <a name="<%= classes["href"] %>"><%= classes["full_name"] %></a> < <%= href classes["par_url"], classes["parent"] %></h3>
<% end %>
<% unless classes["parent"] then %>
<h3><%= classes["classmod"] %> <%= classes["full_name"] %></h3>
@@ -107,11 +108,11 @@
(in files
<% classes["infiles"].each do |infiles| %>
<%= href infiles["full_path_url"], infiles["full_path"] %>
-<% end # classes["infiles"] %>
+<% end %><%# classes["infiles"] %>
)
<% end %>
} + CONTENTS_XML + %{
-<% end # values["classes"] %>
+<% end %><%# values["classes"] %>
<% end %>
</body>
</html>
Modified: MacRuby/branches/experimental/lib/rdoc/generator/html.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/html.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/html.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -68,7 +68,6 @@
def initialize(options) #:not-new:
@options = options
load_html_template
- @main_page_path = nil
end
##
@@ -82,7 +81,7 @@
@classes = []
write_style_sheet
- gen_sub_directories()
+ gen_sub_directories
build_indices
generate_html
end
@@ -94,6 +93,15 @@
# If the template name contains a slash, use it literally
def load_html_template
+ #
+ # If the template is not a path, first look for it
+ # in rdoc's HTML template directory. Perhaps this behavior should
+ # be reversed (first try to include the template and, only if that
+ # fails, try to include it in the default template directory).
+ # One danger with reversing the behavior, however, is that
+ # if something like require 'html' could load up an
+ # unrelated file in the standard library or in a gem.
+ #
template = @options.template
unless template =~ %r{/|\\} then
@@ -101,14 +109,25 @@
template)
end
- require template
+ begin
+ require template
- @template = self.class.const_get @options.template.upcase
- @options.template_class = @template
+ @template = self.class.const_get @options.template.upcase
+ @options.template_class = @template
+ rescue LoadError => e
+ #
+ # The template did not exist in the default template directory, so
+ # see if require can find the template elsewhere (in a gem, for
+ # instance).
+ #
+ if(e.message[template] && template != @options.template)
+ template = @options.template
+ retry
+ end
- rescue LoadError
- $stderr.puts "Could not find HTML template '#{template}'"
- exit 99
+ $stderr.puts "Could not find HTML template '#{template}': #{e.message}"
+ exit 99
+ end
end
##
@@ -146,17 +165,20 @@
end
def build_indices
- @files, @classes = RDoc::Generator::Context.build_indicies(@toplevels,
- @options)
+ @files, @classes = RDoc::Generator::Context.build_indices(@toplevels,
+ @options)
end
##
# Generate all the HTML
def generate_html
+ @main_url = main_url
+
# the individual descriptions for files and classes
gen_into(@files)
gen_into(@classes)
+
# and the index files
gen_file_index
gen_class_index
@@ -164,18 +186,52 @@
gen_main_index
# this method is defined in the template file
- write_extra_pages if defined? write_extra_pages
+ values = {
+ 'title_suffix' => CGI.escapeHTML("[#{@options.title}]"),
+ 'charset' => @options.charset,
+ 'style_url' => style_url('', @options.css),
+ }
+
+ @template.write_extra_pages(values) if @template.respond_to?(:write_extra_pages)
end
def gen_into(list)
+ #
+ # The file, class, and method lists technically should be regenerated
+ # for every output file, in order that the relative links be correct
+ # (we are worried here about frameless templates, which need this
+ # information for every generated page). Doing this is a bit slow,
+ # however. For a medium-sized gem, this increased rdoc's runtime by
+ # about 5% (using the 'time' command-line utility). While this is not
+ # necessarily a problem, I do not want to pessimize rdoc for large
+ # projects, however, and so we only regenerate the lists when the
+ # directory of the output file changes, which seems like a reasonable
+ # optimization.
+ #
+ file_list = {}
+ class_list = {}
+ method_list = {}
+ prev_op_dir = nil
+
list.each do |item|
- if item.document_self
- op_file = item.path
- FileUtils.mkdir_p(File.dirname(op_file))
- open(op_file, "w") { |file| item.write_on(file) }
+ next unless item.document_self
+
+ op_file = item.path
+ op_dir = File.dirname(op_file)
+
+ if(op_dir != prev_op_dir)
+ file_list = index_to_links op_file, @files
+ class_list = index_to_links op_file, @classes
+ method_list = index_to_links op_file, RDoc::Generator::Method.all_methods
end
+ prev_op_dir = op_dir
+
+ FileUtils.mkdir_p op_dir
+
+ open op_file, 'w' do |io|
+ item.write_on io, file_list, class_list, method_list
+ end
end
-
end
def gen_file_index
@@ -203,8 +259,9 @@
values = {
"entries" => res,
+ 'title' => CGI.escapeHTML("#{title} [#{@options.title}]"),
'list_title' => CGI.escapeHTML(title),
- 'index_url' => main_url,
+ 'index_url' => @main_url,
'charset' => @options.charset,
'style_url' => style_url('', @options.css),
}
@@ -221,54 +278,105 @@
# line.
def gen_main_index
- template = RDoc::TemplatePage.new @template::INDEX
+ if @template.const_defined? :FRAMELESS then
+ #
+ # If we're using a template without frames, then just redirect
+ # to it from index.html.
+ #
+ # One alternative to this, expanding the main page's template into
+ # index.html, is tricky because the relative URLs will be different
+ # (since index.html is located in at the site's root,
+ # rather than within a files or a classes subdirectory).
+ #
+ open 'index.html', 'w' do |f|
+ f.puts(%{<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">})
+ f.puts(%{<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+ lang="en">})
+ f.puts(%{<head>})
+ f.puts(%{<title>#{CGI.escapeHTML(@options.title)}</title>})
+ f.puts(%{<meta http-equiv="refresh" content="0; url=#{@main_url}" />})
+ f.puts(%{</head>})
+ f.puts(%{<body></body>})
+ f.puts(%{</html>})
+ end
+ else
+ main = RDoc::TemplatePage.new @template::INDEX
- open 'index.html', 'w' do |f|
- classes = @classes.sort.map { |klass| klass.value_hash }
+ open 'index.html', 'w' do |f|
+ style_url = style_url '', @options.css
+
+ classes = @classes.sort.map { |klass| klass.value_hash }
+
+ values = {
+ 'initial_page' => @main_url,
+ 'style_url' => style_url('', @options.css),
+ 'title' => CGI.escapeHTML(@options.title),
+ 'charset' => @options.charset,
+ 'classes' => classes,
+ }
+
+ values['inline_source'] = @options.inline_source
- values = {
- 'main_page' => @main_page,
- 'initial_page' => main_url,
- 'style_url' => style_url('', @options.css),
- 'title' => CGI.escapeHTML(@options.title),
- 'charset' => @options.charset,
- 'classes' => classes,
- }
-
- values['inline_source'] = @options.inline_source
-
- template.write_html_on f, values
+ main.write_html_on f, values
+ end
end
end
+ def index_to_links(output_path, collection)
+ collection.sort.map do |f|
+ next unless f.document_self
+ { "href" => RDoc::Markup::ToHtml.gen_relative_url(output_path, f.path),
+ "name" => f.index_name }
+ end.compact
+ end
+
##
# Returns the url of the main page
def main_url
- @main_page = @options.main_page
- @main_page_ref = nil
- if @main_page
- @main_page_ref = RDoc::Generator::AllReferences[@main_page]
- if @main_page_ref then
- @main_page_path = @main_page_ref.path
+ main_page = @options.main_page
+
+ #
+ # If a main page has been specified (--main), then search for it
+ # in the AllReferences array. This allows either files or classes
+ # to be used for the main page.
+ #
+ if main_page then
+ main_page_ref = RDoc::Generator::AllReferences[main_page]
+
+ if main_page_ref then
+ return main_page_ref.path
else
- $stderr.puts "Could not find main page #{@main_page}"
+ $stderr.puts "Could not find main page #{main_page}"
end
end
- unless @main_page_path then
- file = @files.find { |context| context.document_self }
- @main_page_path = file.path if file
+ #
+ # No main page has been specified, so just use the README.
+ #
+ @files.each do |file|
+ if file.name =~ /^README/ then
+ return file.path
+ end
end
- unless @main_page_path then
- $stderr.puts "Couldn't find anything to document"
- $stderr.puts "Perhaps you've used :stopdoc: in all classes"
- exit 1
+ #
+ # There's no README (shame! shame!). Just use the first file
+ # that will be documented.
+ #
+ @files.each do |file|
+ if file.document_self then
+ return file.path
+ end
end
- @main_page_path
+ #
+ # There are no files to be documented... Something seems very wrong.
+ #
+ raise RDoc::Error, "Couldn't find anything to document (perhaps :stopdoc: has been used in all classes)!"
end
+ private :main_url
end
@@ -314,12 +422,9 @@
'charset' => @options.charset,
'files' => gen_into(@files),
'classes' => gen_into(@classes),
- 'title' => CGI.escapeHTML(@options.title),
+ 'title' => CGI.escapeHTML(@options.title),
}
- # this method is defined in the template file
- write_extra_pages if defined? write_extra_pages
-
template = RDoc::TemplatePage.new @template::ONE_PAGE
if @options.op_name
@@ -337,34 +442,4 @@
end
res
end
-
- def gen_file_index
- gen_an_index(@files, 'Files')
- end
-
- def gen_class_index
- gen_an_index(@classes, 'Classes')
- end
-
- def gen_method_index
- gen_an_index(RDoc::Generator::Method.all_methods, 'Methods')
- end
-
- def gen_an_index(collection, title)
- res = []
- collection.sort.each do |f|
- if f.document_self
- res << { "href" => f.path, "name" => f.index_name }
- end
- end
-
- return {
- "entries" => res,
- 'list_title' => title,
- 'index_url' => main_url,
- }
- end
-
end
-
-
Modified: MacRuby/branches/experimental/lib/rdoc/generator/ri.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/ri.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/ri.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -16,12 +16,8 @@
new(options)
end
- class << self
- protected :new
- end
-
##
- # Set up a new RDoc::Generator::RI.
+ # Set up a new ri generator
def initialize(options) #:not-new:
@options = options
@@ -45,18 +41,19 @@
def process_class(from_class)
generate_class_info(from_class)
- # now recure into this classes constituent classess
+ # now recurse into this class' constituent classes
from_class.each_classmodule do |mod|
process_class(mod)
end
end
def generate_class_info(cls)
- if cls === RDoc::NormalModule
+ case cls
+ when RDoc::NormalModule then
cls_desc = RDoc::RI::ModuleDescription.new
else
cls_desc = RDoc::RI::ClassDescription.new
- cls_desc.superclass = cls.superclass
+ cls_desc.superclass = cls.superclass
end
cls_desc.name = cls.name
Deleted: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/class.texinfo.erb
===================================================================
--- MacRuby/trunk/lib/rdoc/generator/texinfo/class.texinfo.erb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/class.texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,44 +0,0 @@
- at node <%= @v['class']['full_name'].gsub(/::/, '-') %>
- at chapter <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %>
-
-<% if @v['class']["parent"] and @v['class']['par_url'] %>
-Inherits <%= href @v['class']["par_url"], @v['class']["parent"] %><% end %>
-
-<%= @v['class']["description"] %>
-
-<% if @v['class']["includes"] %>
-Includes
-<% @v['class']["includes"].each do |include| %>
-* <%= href include["aref"], include["name"] %>
-<% end # @v['class']["includes"] %>
-<% end %>
-
-<% if @v['class']["sections"] %>
-<% @v['class']["sections"].each do |section| %>
-<% if section["attributes"] %>
-Attributes
-<% section["attributes"].each do |attributes| %>
-* <%= attributes["name"] %> <%= attributes["rw"] %> <%= attributes["a_desc"] %>
-<% end # section["attributes"] %>
-<% end %>
-<% end %>
-
-<% @v['class']["sections"].each do |section| %>
-<% if section["method_list"] %>
-Methods
- at menu
-<% section["method_list"].each_with_index do |method_list, i| %>
-<%= i %>
-<% (method_list["methods"] || []).each do |method| %>
-* <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix method_list %><%= method['name'] %>::<% end %>
-<% end %>
- at end menu
-
-<% section["method_list"].each do |method_list| %>
-<% (method_list["methods"] || []).uniq.each do |method| %>
-<%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}),
- 'method.texinfo.erb').render %><% end %>
-<% end %>
-<% end # if section["method_list"] %>
-<% end # @v['class']["sections"] %>
-<% end %>
Copied: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/class.texinfo.erb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/texinfo/class.texinfo.erb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/texinfo/class.texinfo.erb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/class.texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,44 @@
+ at node <%= @v['class']['full_name'].gsub(/::/, '-') %>
+ at chapter <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %>
+
+<% if @v['class']["parent"] and @v['class']['par_url'] %>
+Inherits <%= href @v['class']["par_url"], @v['class']["parent"] %><% end %>
+
+<%= @v['class']["description"] %>
+
+<% if @v['class']["includes"] %>
+Includes
+<% @v['class']["includes"].each do |include| %>
+* <%= href include["aref"], include["name"] %>
+<% end # @v['class']["includes"] %>
+<% end %>
+
+<% if @v['class']["sections"] %>
+<% @v['class']["sections"].each do |section| %>
+<% if section["attributes"] %>
+Attributes
+<% section["attributes"].each do |attributes| %>
+* <%= attributes["name"] %> <%= attributes["rw"] %> <%= attributes["a_desc"] %>
+<% end # section["attributes"] %>
+<% end %>
+<% end %>
+
+<% @v['class']["sections"].each do |section| %>
+<% if section["method_list"] %>
+Methods
+ at menu
+<% section["method_list"].each_with_index do |method_list, i| %>
+<%= i %>
+<% (method_list["methods"] || []).each do |method| %>
+* <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix method_list %><%= method['name'] %>::<% end %>
+<% end %>
+ at end menu
+
+<% section["method_list"].each do |method_list| %>
+<% (method_list["methods"] || []).uniq.each do |method| %>
+<%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}),
+ 'method.texinfo.erb').render %><% end %>
+<% end %>
+<% end # if section["method_list"] %>
+<% end # @v['class']["sections"] %>
+<% end %>
Deleted: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/file.texinfo.erb
===================================================================
--- MacRuby/trunk/lib/rdoc/generator/texinfo/file.texinfo.erb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/file.texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,6 +0,0 @@
-<% if false %>
-<h2>File: <%= @v['file']["short_name"] %></h2>
-Path: <%= @v['file']["full_path"] %>
-
-<%= TexinfoTemplate.new(@v, 'content.texinfo.erb').render %>
-<% end %>
Copied: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/file.texinfo.erb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/texinfo/file.texinfo.erb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/texinfo/file.texinfo.erb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/file.texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,6 @@
+<% if false %>
+<h2>File: <%= @v['file']["short_name"] %></h2>
+Path: <%= @v['file']["full_path"] %>
+
+<%= TexinfoTemplate.new(@v, 'content.texinfo.erb').render %>
+<% end %>
Deleted: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/method.texinfo.erb
===================================================================
--- MacRuby/trunk/lib/rdoc/generator/texinfo/method.texinfo.erb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/method.texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,6 +0,0 @@
- at node <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
- at section <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
-<%= @v['method']["type"] %> <%= @v['method']["category"] %> method:
-<%= target @v['method']["aref"], @v['method']['callseq'] ||
- @v['method']["name"] + @v['method']["params"] %>
-<%= @v['method']["m_desc"] %>
Copied: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/method.texinfo.erb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/texinfo/method.texinfo.erb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/texinfo/method.texinfo.erb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/method.texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,6 @@
+ at node <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
+ at section <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %><%= method_prefix @v['list'] %><%= @v['method']['name'] %>
+<%= @v['method']["type"] %> <%= @v['method']["category"] %> method:
+<%= target @v['method']["aref"], @v['method']['callseq'] ||
+ @v['method']["name"] + @v['method']["params"] %>
+<%= @v['method']["m_desc"] %>
Deleted: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/texinfo.erb
===================================================================
--- MacRuby/trunk/lib/rdoc/generator/texinfo/texinfo.erb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,28 +0,0 @@
-\input texinfo @c -*-texinfo-*-
- at c %**start of header
- at setfilename <%= @v['filename'] %>
- at settitle <%= @v['title'] %>
- at c %**end of header
-
- at contents @c TODO: whitespace is a mess... =\
-
- at ifnottex
- at node Top
-
- at top <%= @v['title'] %>
- at end ifnottex
-
-<% if @f = @v['files'].detect { |f| f.name =~ /Readme/i } %>
-<%= @f.values['description'] %><% end %>
-
- at menu
-<% @v['classes'].each do |klass| %>
-* <%= klass.name.gsub(/::/, '-') %>::<% end %>
- at c TODO: add files
- at end menu
-
-<% (@v['classes'] || []).each_with_index do |klass, i| %>
-<%= TexinfoTemplate.new(@v.merge('class' => klass.values),
- 'class.texinfo.erb').render %><% end %>
-
- at bye
Copied: MacRuby/branches/experimental/lib/rdoc/generator/texinfo/texinfo.erb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/texinfo/texinfo.erb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/texinfo/texinfo.erb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo/texinfo.erb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,28 @@
+\input texinfo @c -*-texinfo-*-
+ at c %**start of header
+ at setfilename <%= @v['filename'] %>
+ at settitle <%= @v['title'] %>
+ at c %**end of header
+
+ at contents @c TODO: whitespace is a mess... =\
+
+ at ifnottex
+ at node Top
+
+ at top <%= @v['title'] %>
+ at end ifnottex
+
+<% if @f = @v['files'].detect { |f| f.name =~ /Readme/i } %>
+<%= @f.values['description'] %><% end %>
+
+ at menu
+<% @v['classes'].each do |klass| %>
+* <%= klass.name.gsub(/::/, '-') %>::<% end %>
+ at c TODO: add files
+ at end menu
+
+<% (@v['classes'] || []).each_with_index do |klass, i| %>
+<%= TexinfoTemplate.new(@v.merge('class' => klass.values),
+ 'class.texinfo.erb').render %><% end %>
+
+ at bye
Copied: MacRuby/branches/experimental/lib/rdoc/generator/texinfo.rb (from rev 1886, MacRuby/trunk/lib/rdoc/generator/texinfo.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/texinfo.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/generator/texinfo.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,81 @@
+require 'rdoc/rdoc'
+require 'rdoc/generator'
+require 'rdoc/markup/to_texinfo'
+
+module RDoc
+ module Generator
+ # This generates Texinfo files for viewing with GNU Info or Emacs
+ # from RDoc extracted from Ruby source files.
+ class TEXINFO
+ # What should the .info file be named by default?
+ DEFAULT_INFO_FILENAME = 'rdoc.info'
+
+ include Generator::MarkUp
+
+ # Accept some options
+ def initialize(options)
+ @options = options
+ @options.inline_source = true
+ @options.op_name ||= 'rdoc.texinfo'
+ @options.formatter = ::RDoc::Markup::ToTexInfo.new
+ end
+
+ # Generate the +texinfo+ files
+ def generate(toplevels)
+ @toplevels = toplevels
+ @files, @classes = ::RDoc::Generator::Context.build_indices(@toplevels,
+ @options)
+
+ (@files + @classes).each { |x| x.value_hash }
+
+ open(@options.op_name, 'w') do |f|
+ f.puts TexinfoTemplate.new('files' => @files,
+ 'classes' => @classes,
+ 'filename' => @options.op_name.gsub(/texinfo/, 'info'),
+ 'title' => @options.title).render
+ end
+ # TODO: create info files and install?
+ end
+
+ class << self
+ # Factory? We don't need no stinkin' factory!
+ alias_method :for, :new
+ end
+ end
+
+ # Basically just a wrapper around ERB.
+ # Should probably use RDoc::TemplatePage instead
+ class TexinfoTemplate
+ BASE_DIR = ::File.expand_path(::File.dirname(__FILE__)) # have to calculate this when the file's loaded.
+
+ def initialize(values, file = 'texinfo.erb')
+ @v, @file = [values, file]
+ end
+
+ def template
+ ::File.read(::File.join(BASE_DIR, 'texinfo', @file))
+ end
+
+ # Go!
+ def render
+ ERB.new(template).result binding
+ end
+
+ def href(location, text)
+ text # TODO: how does texinfo do hyperlinks?
+ end
+
+ def target(name, text)
+ text # TODO: how do hyperlink targets work?
+ end
+
+ # TODO: this is probably implemented elsewhere?
+ def method_prefix(section)
+ { 'Class' => '.',
+ 'Module' => '::',
+ 'Instance' => '#',
+ }[section['category']]
+ end
+ end
+ end
+end
Modified: MacRuby/branches/experimental/lib/rdoc/generator/xml/xml.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/xml/xml.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/xml/xml.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -17,11 +17,23 @@
href="<%= requires["aref"] %>"
<% end %>
/>
-<% end # files["requires"] %>
+<% end %><%# files["requires"] %>
</required-file-list>
<% end %>
<% if defined? classes and classes["sections"] then %>
<% classes["sections"].each do |sections| %>
+<% if sections["constants"] then %>
+ <constant-list>
+<% sections["constants"].each do |constant| %>
+ <constant name="<%= constant["name"] %>">
+<% if constant["value"] then %>
+ <value><%= constant["value"] %></value>
+<% end %>
+ <description><%= constant["a_desc"] %></description>
+ </constant>
+<% end %><%# sections["constants"] %>
+ </constant-list>
+<% end %>
<% if sections["attributes"] then %>
<attribute-list>
<% sections["attributes"].each do |attributes| %>
@@ -31,7 +43,7 @@
<% end %>
<description><%= attributes["a_desc"] %></description>
</attribute>
-<% end # sections["attributes"] %>
+<% end %><%# sections["attributes"] %>
</attribute-list>
<% end %>
<% if sections["method_list"] then %>
@@ -52,12 +64,12 @@
</source-code-listing>
<% end %>
</method>
-<% end # method_list["methods"] %>
+<% end %><%# method_list["methods"] %>
<% end %>
-<% end # sections["method_list"] %>
+<% end %><%# sections["method_list"] %>
</method-list>
<% end %>
-<% end # classes["sections"] %>
+<% end %><%# classes["sections"] %>
<% end %>
<% if defined? classes and classes["includes"] then %>
<included-module-list>
@@ -67,7 +79,7 @@
href="<%= includes["aref"] %>"
<% end %>
/>
-<% end # classes["includes"] %>
+<% end %><%# classes["includes"] %>
</included-module-list>
<% end %>
</contents>
@@ -84,7 +96,7 @@
</file-info>
} + CONTENTS_XML + %{
</file>
-<% end # values["files"] %>
+<% end %><%# values["files"] %>
</file-list>
<class-module-list>
<% values["classes"].each do |classes| %>
@@ -94,7 +106,7 @@
<infiles>
<% classes["infiles"].each do |infiles| %>
<infile><%= href infiles["full_path_url"], infiles["full_path"] %></infile>
-<% end # classes["infiles"] %>
+<% end %><%# classes["infiles"] %>
</infiles>
<% end %>
<% if classes["parent"] then %>
@@ -103,7 +115,7 @@
</classmod-info>
} + CONTENTS_XML + %{
</<%= classes["classmod"] %>>
-<% end # values["classes"] %>
+<% end %><%# values["classes"] %>
</class-module-list>
</rdoc>
}
Modified: MacRuby/branches/experimental/lib/rdoc/generator/xml.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator/xml.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator/xml.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -34,15 +34,15 @@
##
# Generate:
#
- # * a list of HtmlFile objects for each TopLevel object.
- # * a list of HtmlClass objects for each first level
+ # * a list of File objects for each TopLevel object.
+ # * a list of Class objects for each first level
# class or module in the TopLevel objects
# * a complete list of all hyperlinkable terms (file,
# class, module, and method names)
def build_indices
@info.each do |toplevel|
- @files << RDoc::Generator::HtmlFile.new(toplevel, @options, RDoc::Generator::FILE_DIR)
+ @files << RDoc::Generator::File.new(toplevel, @options, RDoc::Generator::FILE_DIR)
end
RDoc::TopLevel.all_classes_and_modules.each do |cls|
@@ -51,7 +51,7 @@
end
def build_class_list(from, html_file, class_dir)
- @classes << RDoc::Generator::HtmlClass.new(from, html_file, class_dir, @options)
+ @classes << RDoc::Generator::Class.new(from, html_file, class_dir, @options)
from.each_classmodule do |mod|
build_class_list(mod, html_file, class_dir)
end
@@ -68,9 +68,6 @@
'classes' => gen_into(@classes)
}
- # this method is defined in the template file
- write_extra_pages if defined? write_extra_pages
-
template = RDoc::TemplatePage.new @template::ONE_PAGE
if @options.op_name
Modified: MacRuby/branches/experimental/lib/rdoc/generator.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/generator.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/generator.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -7,12 +7,12 @@
module RDoc::Generator
##
- # Name of sub-direcory that holds file descriptions
+ # Name of sub-directory that holds file descriptions
FILE_DIR = "files"
##
- # Name of sub-direcory that holds class descriptions
+ # Name of sub-directory that holds class descriptions
CLASS_DIR = "classes"
@@ -22,27 +22,6 @@
CSS_NAME = "rdoc-style.css"
##
- # Converts a target url to one that is relative to a given path
-
- def self.gen_url(path, target)
- from = ::File.dirname path
- to, to_file = ::File.split target
-
- from = from.split "/"
- to = to.split "/"
-
- while from.size > 0 and to.size > 0 and from[0] == to[0] do
- from.shift
- to.shift
- end
-
- from.fill ".."
- from.concat to
- from << to_file
- ::File.join(*from)
- end
-
- ##
# Build a hash of all items that can be cross-referenced. This is used when
# we output required and included names: if the names appear in this hash,
# we can generate an html cross reference to the appropriate description.
@@ -80,11 +59,6 @@
def markup(str, remove_para = false)
return '' unless str
- unless defined? @formatter then
- @formatter = RDoc::Markup::ToHtmlCrossref.new(path, self,
- @options.show_hash)
- end
-
# Convert leading comment markers to spaces, but only if all non-blank
# lines have them
if str =~ /^(?>\s*)[^\#]/ then
@@ -93,7 +67,7 @@
content = str.gsub(/^\s*(#+)/) { $1.tr '#', ' ' }
end
- res = @formatter.convert content
+ res = formatter.convert content
if remove_para then
res.sub!(/^<p>/, '')
@@ -114,7 +88,7 @@
if %r{^(https?:/)?/} =~ css_name
css_name
else
- RDoc::Generator.gen_url path, css_name
+ RDoc::Markup::ToHtml.gen_relative_url path, css_name
end
end
@@ -153,7 +127,7 @@
# * a complete list of all hyperlinkable terms (file, class, module, and
# method names)
- def self.build_indicies(toplevels, options)
+ def self.build_indices(toplevels, options)
files = []
classes = []
@@ -186,6 +160,11 @@
@template = options.template_class
end
+ def formatter
+ @formatter ||= @options.formatter ||
+ RDoc::Markup::ToHtmlCrossref.new(path, self, @options.show_hash)
+ end
+
##
# convenience method to build a hyperlink
@@ -201,7 +180,7 @@
if @options.all_one_file
"#" + path
else
- RDoc::Generator.gen_url from_path, path
+ RDoc::Markup::ToHtml.gen_relative_url from_path, path
end
end
@@ -215,7 +194,7 @@
list = @context.method_list
unless @options.show_all then
- list = list.find_all do |m|
+ list = list.select do |m|
m.visibility == :public or
m.visibility == :protected or
m.force_documentation
@@ -230,17 +209,15 @@
##
# Build a summary list of all the methods in this context
- def build_method_summary_list(path_prefix="")
+ def build_method_summary_list(path_prefix = "")
collect_methods unless @methods
- meths = @methods.sort
- res = []
- meths.each do |meth|
- res << {
+
+ @methods.sort.map do |meth|
+ {
"name" => CGI.escapeHTML(meth.name),
- "aref" => "#{path_prefix}\##{meth.aref}"
+ "aref" => "##{meth.aref}"
}
end
- res
end
##
@@ -248,36 +225,40 @@
# corresponding method
def build_alias_summary_list(section)
- values = []
- @context.aliases.each do |al|
+ @context.aliases.map do |al|
next unless al.section == section
+
res = {
'old_name' => al.old_name,
'new_name' => al.new_name,
}
- if al.comment && !al.comment.empty?
- res['desc'] = markup(al.comment, true)
+
+ if al.comment and not al.comment.empty? then
+ res['desc'] = markup al.comment, true
end
- values << res
- end
- values
+
+ res
+ end.compact
end
##
# Build a list of constants
def build_constants_summary_list(section)
- values = []
- @context.constants.each do |co|
+ @context.constants.map do |co|
next unless co.section == section
+
res = {
'name' => co.name,
'value' => CGI.escapeHTML(co.value)
}
- res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
- values << res
- end
- values
+
+ if co.comment and not co.comment.empty? then
+ res['desc'] = markup co.comment, true
+ end
+
+ res
+ end.compact
end
def build_requires_list(context)
@@ -339,54 +320,58 @@
def build_method_detail_list(section)
outer = []
- methods = @methods.sort
+ methods = @methods.sort.select do |m|
+ m.document_self and m.section == section
+ end
+
for singleton in [true, false]
for vis in [ :public, :protected, :private ]
res = []
methods.each do |m|
- if m.section == section and
- m.document_self and
- m.visibility == vis and
- m.singleton == singleton
- row = {}
- if m.call_seq
- row["callseq"] = m.call_seq.gsub(/->/, '→')
- else
- row["name"] = CGI.escapeHTML(m.name)
- row["params"] = m.params
- end
- desc = m.description.strip
- row["m_desc"] = desc unless desc.empty?
- row["aref"] = m.aref
- row["visibility"] = m.visibility.to_s
+ next unless m.visibility == vis and m.singleton == singleton
- alias_names = []
- m.aliases.each do |other|
- if other.viewer # won't be if the alias is private
- alias_names << {
- 'name' => other.name,
- 'aref' => other.viewer.as_href(path)
- }
- end
+ row = {}
+
+ if m.call_seq then
+ row["callseq"] = m.call_seq.gsub(/->/, '→')
+ else
+ row["name"] = CGI.escapeHTML(m.name)
+ row["params"] = m.params
+ end
+
+ desc = m.description.strip
+ row["m_desc"] = desc unless desc.empty?
+ row["aref"] = m.aref
+ row["visibility"] = m.visibility.to_s
+
+ alias_names = []
+
+ m.aliases.each do |other|
+ if other.viewer then # won't be if the alias is private
+ alias_names << {
+ 'name' => other.name,
+ 'aref' => other.viewer.as_href(path)
+ }
end
- unless alias_names.empty?
- row["aka"] = alias_names
- end
+ end
- if @options.inline_source
- code = m.source_code
- row["sourcecode"] = code if code
- else
- code = m.src_url
- if code
- row["codeurl"] = code
- row["imgurl"] = m.img_url
- end
+ row["aka"] = alias_names unless alias_names.empty?
+
+ if @options.inline_source then
+ code = m.source_code
+ row["sourcecode"] = code if code
+ else
+ code = m.src_url
+ if code then
+ row["codeurl"] = code
+ row["imgurl"] = m.img_url
end
- res << row
end
+
+ res << row
end
- if res.size > 0
+
+ if res.size > 0 then
outer << {
"type" => vis.to_s.capitalize,
"category" => singleton ? "Class" : "Instance",
@@ -395,6 +380,7 @@
end
end
end
+
outer
end
@@ -403,8 +389,8 @@
# in this context.
def build_class_list(level, from, section, infile=nil)
- res = ""
- prefix = " ::" * level;
+ prefix = ' ::' * level;
+ res = ''
from.modules.sort.each do |mod|
next unless mod.section == section
@@ -412,8 +398,8 @@
if mod.document_self
res <<
prefix <<
- "Module " <<
- href(url(mod.viewer.path), "link", mod.full_name) <<
+ 'Module ' <<
+ href(url(mod.viewer.path), 'link', mod.full_name) <<
"<br />\n" <<
build_class_list(level + 1, mod, section, infile)
end
@@ -421,12 +407,13 @@
from.classes.sort.each do |cls|
next unless cls.section == section
- next if infile && !cls.defined_in?(infile)
+ next if infile and not cls.defined_in?(infile)
+
if cls.document_self
- res <<
+ res <<
prefix <<
- "Class " <<
- href(url(cls.viewer.path), "link", cls.full_name) <<
+ 'Class ' <<
+ href(url(cls.viewer.path), 'link', cls.full_name) <<
"<br />\n" <<
build_class_list(level + 1, cls, section, infile)
end
@@ -436,7 +423,7 @@
end
def url(target)
- RDoc::Generator.gen_url path, target
+ RDoc::Markup::ToHtml.gen_relative_url path, target
end
def aref_to(target)
@@ -475,7 +462,7 @@
def add_table_of_sections
toc = []
@context.sections.each do |section|
- if section.title
+ if section.title then
toc << {
'secname' => section.title,
'href' => section.sequence
@@ -495,12 +482,14 @@
attr_reader :methods
attr_reader :path
+ attr_reader :values
def initialize(context, html_file, prefix, options)
- super(context, options)
+ super context, options
@html_file = html_file
- @is_module = context.is_module?
+ @html_class = self
+ @is_module = context.module?
@values = {}
context.viewer = self
@@ -540,11 +529,19 @@
name
end
- def write_on(f)
+ def write_on(f, file_list, class_list, method_list, overrides = {})
value_hash
+
+ @values['file_list'] = file_list
+ @values['class_list'] = class_list
+ @values['method_list'] = method_list
+
+ @values.update overrides
+
template = RDoc::TemplatePage.new(@template::BODY,
@template::CLASS_PAGE,
@template::METHOD_LIST)
+
template.write_html_on(f, @values)
end
@@ -561,30 +558,29 @@
ml = build_method_summary_list @path
@values["methods"] = ml unless ml.empty?
- il = build_include_list(@context)
+ il = build_include_list @context
@values["includes"] = il unless il.empty?
@values["sections"] = @context.sections.map do |section|
-
secdata = {
"sectitle" => section.title,
"secsequence" => section.sequence,
- "seccomment" => markup(section.comment)
+ "seccomment" => markup(section.comment),
}
- al = build_alias_summary_list(section)
+ al = build_alias_summary_list section
secdata["aliases"] = al unless al.empty?
- co = build_constants_summary_list(section)
+ co = build_constants_summary_list section
secdata["constants"] = co unless co.empty?
- al = build_attribute_list(section)
+ al = build_attribute_list section
secdata["attributes"] = al unless al.empty?
- cl = build_class_list(0, @context, section)
+ cl = build_class_list 0, @context, section
secdata["classlist"] = cl unless cl.empty?
- mdl = build_method_detail_list(section)
+ mdl = build_method_detail_list section
secdata["method_list"] = mdl unless mdl.empty?
secdata
@@ -594,43 +590,45 @@
end
def build_attribute_list(section)
- atts = @context.attributes.sort
- res = []
- atts.each do |att|
+ @context.attributes.sort.map do |att|
next unless att.section == section
- if att.visibility == :public || att.visibility == :protected || @options.show_all
+
+ if att.visibility == :public or att.visibility == :protected or
+ @options.show_all then
+
entry = {
"name" => CGI.escapeHTML(att.name),
"rw" => att.rw,
"a_desc" => markup(att.comment, true)
}
- unless att.visibility == :public || att.visibility == :protected
+
+ unless att.visibility == :public or att.visibility == :protected then
entry["rw"] << "-"
end
- res << entry
+
+ entry
end
- end
- res
+ end.compact
end
def class_attribute_values
h_name = CGI.escapeHTML(name)
- @values["path"] = @path
+ @values["href"] = @path
@values["classmod"] = @is_module ? "Module" : "Class"
- @values["title"] = "#{@values['classmod']}: #{h_name}"
+ @values["title"] = "#{@values['classmod']}: #{h_name} [#{@options.title}]"
c = @context
- c = c.parent while c and !c.diagram
- if c && c.diagram
+ c = c.parent while c and not c.diagram
+
+ if c and c.diagram then
@values["diagram"] = diagram_reference(c.diagram)
end
@values["full_name"] = h_name
- parent_class = @context.superclass
-
- if parent_class
+ if not @context.module? and @context.superclass then
+ parent_class = @context.superclass
@values["parent"] = CGI.escapeHTML(parent_class)
if parent_name
@@ -680,9 +678,10 @@
attr_reader :path
attr_reader :name
+ attr_reader :values
def initialize(context, options, file_dir)
- super(context, options)
+ super context, options
@values = {}
@@ -705,7 +704,7 @@
def filename_to_label
@context.file_relative_name.gsub(/%|\/|\?|\#/) do
- '%%%x' % $&[0].unpack('C')
+ ('%%%x' % $&[0]).unpack('C')
end
end
@@ -755,7 +754,7 @@
}
cl = build_class_list(0, @context, section, file_context)
- @values["classlist"] = cl unless cl.empty?
+ secdata["classlist"] = cl unless cl.empty?
mdl = build_method_detail_list(section)
secdata["method_list"] = mdl unless mdl.empty?
@@ -764,7 +763,7 @@
secdata["aliases"] = al unless al.empty?
co = build_constants_summary_list(section)
- @values["constants"] = co unless co.empty?
+ secdata["constants"] = co unless co.empty?
secdata
end
@@ -772,9 +771,15 @@
@values
end
- def write_on(f)
+ def write_on(f, file_list, class_list, method_list, overrides = {})
value_hash
+ @values['file_list'] = file_list
+ @values['class_list'] = class_list
+ @values['method_list'] = method_list
+
+ @values.update overrides
+
template = RDoc::TemplatePage.new(@template::BODY,
@template::FILE_PAGE,
@template::METHOD_LIST)
@@ -786,7 +791,7 @@
full_path = @context.file_absolute_name
short_name = ::File.basename full_path
- @values["title"] = CGI.escapeHTML("File: #{short_name}")
+ @values["title"] = CGI.escapeHTML("File: #{short_name} [#{@options.title}]")
if @context.diagram then
@values["diagram"] = diagram_reference(@context.diagram)
@@ -816,28 +821,30 @@
attr_reader :img_url
attr_reader :source_code
- @@seq = "M000000"
-
- @@all_methods = []
-
def self.all_methods
@@all_methods
end
def self.reset
@@all_methods = []
+ @@seq = "M000000"
end
+ # Initialize the class variables.
+ self.reset
+
def initialize(context, html_class, options)
+ # TODO: rethink the class hierarchy here...
@context = context
@html_class = html_class
@options = options
+ @@seq = @@seq.succ
+ @seq = @@seq
+
# HACK ugly
@template = options.template_class
- @@seq = @@seq.succ
- @seq = @@seq
@@all_methods << self
context.viewer = self
@@ -846,7 +853,7 @@
@source_code = markup_code(ts)
unless @options.inline_source
@src_url = create_source_code_file(@source_code)
- @img_url = RDoc::Generator.gen_url path, 'source.png'
+ @img_url = RDoc::Markup::ToHtml.gen_relative_url path, 'source.png'
end
end
@@ -861,10 +868,32 @@
if @options.all_one_file
"#" + path
else
- RDoc::Generator.gen_url from_path, path
+ RDoc::Markup::ToHtml.gen_relative_url from_path, path
end
end
+ def formatter
+ @formatter ||= @options.formatter ||
+ RDoc::Markup::ToHtmlCrossref.new(path, self, @options.show_hash)
+ end
+
+ def inspect
+ alias_for = if @context.is_alias_for then
+ " (alias_for #{@context.is_alias_for})"
+ else
+ nil
+ end
+
+ "#<%s:0x%x %s%s%s (%s)%s>" % [
+ self.class, object_id,
+ @context.parent.name,
+ @context.singleton ? '::' : '#',
+ name,
+ @context.visibility,
+ alias_for
+ ]
+ end
+
def name
@context.name
end
@@ -961,7 +990,7 @@
template.write_html_on(f, values)
end
- RDoc::Generator.gen_url path, file_path
+ RDoc::Markup::ToHtml.gen_relative_url path, file_path
end
def <=>(other)
@@ -976,19 +1005,18 @@
src = ""
tokens.each do |t|
next unless t
- # p t.class
# style = STYLE_MAP[t.class]
style = case t
- when RubyToken::TkCONSTANT then "ruby-constant"
- when RubyToken::TkKW then "ruby-keyword kw"
- when RubyToken::TkIVAR then "ruby-ivar"
- when RubyToken::TkOp then "ruby-operator"
- when RubyToken::TkId then "ruby-identifier"
- when RubyToken::TkNode then "ruby-node"
- when RubyToken::TkCOMMENT then "ruby-comment cmt"
- when RubyToken::TkREGEXP then "ruby-regexp re"
- when RubyToken::TkSTRING then "ruby-value str"
- when RubyToken::TkVal then "ruby-value"
+ when RDoc::RubyToken::TkCONSTANT then "ruby-constant"
+ when RDoc::RubyToken::TkKW then "ruby-keyword kw"
+ when RDoc::RubyToken::TkIVAR then "ruby-ivar"
+ when RDoc::RubyToken::TkOp then "ruby-operator"
+ when RDoc::RubyToken::TkId then "ruby-identifier"
+ when RDoc::RubyToken::TkNode then "ruby-node"
+ when RDoc::RubyToken::TkCOMMENT then "ruby-comment cmt"
+ when RDoc::RubyToken::TkREGEXP then "ruby-regexp re"
+ when RDoc::RubyToken::TkSTRING then "ruby-value str"
+ when RDoc::RubyToken::TkVal then "ruby-value"
else
nil
end
@@ -1015,12 +1043,18 @@
first = $1.to_i - 1
last = first + src.count("\n")
size = last.to_s.length
- real_fmt = "%#{size}d: "
- fmt = " " * (size+2)
+ fmt = "%#{size}d: "
+ is_first_line = true
+ line_num = first
src.gsub!(/^/) do
- res = sprintf(fmt, first)
- first += 1
- fmt = real_fmt
+ if is_first_line then
+ is_first_line = false
+ res = " " * (size+2)
+ else
+ res = sprintf(fmt, line_num)
+ end
+
+ line_num += 1
res
end
end
Copied: MacRuby/branches/experimental/lib/rdoc/known_classes.rb (from rev 1886, MacRuby/trunk/lib/rdoc/known_classes.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/known_classes.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/known_classes.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,68 @@
+module RDoc
+
+ ##
+ # Ruby's built-in classes, modules and exceptions
+
+ KNOWN_CLASSES = {
+ "rb_cArray" => "Array",
+ "rb_cBignum" => "Bignum",
+ "rb_cClass" => "Class",
+ "rb_cData" => "Data",
+ "rb_cDir" => "Dir",
+ "rb_cFalseClass" => "FalseClass",
+ "rb_cFile" => "File",
+ "rb_cFixnum" => "Fixnum",
+ "rb_cFloat" => "Float",
+ "rb_cHash" => "Hash",
+ "rb_cIO" => "IO",
+ "rb_cInteger" => "Integer",
+ "rb_cModule" => "Module",
+ "rb_cNilClass" => "NilClass",
+ "rb_cNumeric" => "Numeric",
+ "rb_cObject" => "Object",
+ "rb_cProc" => "Proc",
+ "rb_cRange" => "Range",
+ "rb_cRegexp" => "Regexp",
+ "rb_cRubyVM" => "RubyVM",
+ "rb_cString" => "String",
+ "rb_cStruct" => "Struct",
+ "rb_cSymbol" => "Symbol",
+ "rb_cThread" => "Thread",
+ "rb_cTime" => "Time",
+ "rb_cTrueClass" => "TrueClass",
+
+ "rb_eArgError" => "ArgError",
+ "rb_eEOFError" => "EOFError",
+ "rb_eException" => "Exception",
+ "rb_eFatal" => "Fatal",
+ "rb_eFloatDomainError" => "FloatDomainError",
+ "rb_eIOError" => "IOError",
+ "rb_eIndexError" => "IndexError",
+ "rb_eInterrupt" => "Interrupt",
+ "rb_eLoadError" => "LoadError",
+ "rb_eNameError" => "NameError",
+ "rb_eNoMemError" => "NoMemError",
+ "rb_eNotImpError" => "NotImpError",
+ "rb_eRangeError" => "RangeError",
+ "rb_eRuntimeError" => "RuntimeError",
+ "rb_eScriptError" => "ScriptError",
+ "rb_eSecurityError" => "SecurityError",
+ "rb_eSignal" => "Signal",
+ "rb_eStandardError" => "StandardError",
+ "rb_eSyntaxError" => "SyntaxError",
+ "rb_eSystemCallError" => "SystemCallError",
+ "rb_eSystemExit" => "SystemExit",
+ "rb_eTypeError" => "TypeError",
+ "rb_eZeroDivError" => "ZeroDivError",
+
+ "rb_mComparable" => "Comparable",
+ "rb_mEnumerable" => "Enumerable",
+ "rb_mErrno" => "Errno",
+ "rb_mFileTest" => "FileTest",
+ "rb_mGC" => "GC",
+ "rb_mKernel" => "Kernel",
+ "rb_mMath" => "Math",
+ "rb_mProcess" => "Process"
+ }
+
+end
Modified: MacRuby/branches/experimental/lib/rdoc/markup/attribute_manager.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/attribute_manager.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup/attribute_manager.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -64,7 +64,8 @@
def copy_string(start_pos, end_pos)
res = @str[start_pos...end_pos]
- res.gsub!(/\000/, '')
+ # XXX this doesn't work in MacRuby yet
+ #res.gsub!(/\000/, '')
res
end
@@ -131,7 +132,8 @@
end
def unmask_protected_sequences
- @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
+ #@str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
+ @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1")
end
def initialize
@@ -144,8 +146,6 @@
add_html("b", :BOLD)
add_html("tt", :TT)
add_html("code", :TT)
-
- add_special(/<!--(.*?)-->/, :COMMENT)
end
def add_word_pair(start, stop, name)
@@ -176,21 +176,16 @@
def flow(str)
@str = str
- puts("Before flow, str='#{@str.dump}'") if $DEBUG_RDOC
mask_protected_sequences
@attrs = RDoc::Markup::AttrSpan.new @str.length
- puts("After protecting, str='#{@str.dump}'") if $DEBUG_RDOC
-
convert_attrs(@str, @attrs)
convert_html(@str, @attrs)
convert_specials(str, @attrs)
unmask_protected_sequences
- puts("After flow, str='#{@str.dump}'") if $DEBUG_RDOC
-
return split_into_flow
end
@@ -217,8 +212,6 @@
end
def split_into_flow
- display_attributes if $DEBUG_RDOC
-
res = []
current_attr = 0
str = ""
Modified: MacRuby/branches/experimental/lib/rdoc/markup/fragments.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/fragments.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup/fragments.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -11,7 +11,7 @@
attr_reader :level, :param, :txt
attr_accessor :type
- ######
+ ##
# This is a simple factory system that lets us associate fragement
# types (a string) with a subclass of fragment
Modified: MacRuby/branches/experimental/lib/rdoc/markup/inline.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/inline.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup/inline.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -47,7 +47,7 @@
class AttrChanger
def to_s
- "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
+ "Attr: +#{Attribute.as_string(turn_on)}/-#{Attribute.as_string(turn_on)}"
end
end
Modified: MacRuby/branches/experimental/lib/rdoc/markup/lines.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/lines.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup/lines.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -44,7 +44,8 @@
@deleted = false
# expand tabs
- 1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
+ #1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
+ @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)}
# Strip trailing whitespace
@text.sub!(/\s+$/, '')
Modified: MacRuby/branches/experimental/lib/rdoc/markup/preprocess.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/preprocess.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup/preprocess.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -14,21 +14,25 @@
##
# Look for common options in a chunk of text. Options that we don't handle
- # are passed back to our caller as |directive, param|
+ # are yielded to the caller.
def handle(text)
- text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
+ text.gsub!(/^([ \t]*#?[ \t]*):(\w+):([ \t]*)(.+)?\n/) do
+ next $& if $3.empty? and $4 and $4[0, 1] == ':'
+
prefix = $1
directive = $2.downcase
- param = $3
+ param = $4
case directive
- when "include"
+ when 'include' then
filename = param.split[0]
- include_file(filename, prefix)
+ include_file filename, prefix
else
- yield(directive, param)
+ result = yield directive, param
+ result = "#{prefix}:#{directive}: #{param}\n" unless result
+ result
end
end
end
Modified: MacRuby/branches/experimental/lib/rdoc/markup/to_html.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/to_html.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup/to_html.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,6 @@
require 'rdoc/markup/formatter'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
-require 'rdoc/generator'
require 'cgi'
@@ -21,6 +20,11 @@
def initialize
super
+ # @in_tt - tt nested levels count
+ # @tt_bit - cache
+ @in_tt = 0
+ @tt_bit = RDoc::Markup::Attribute.bitmap_for :TT
+
# external hyperlinks
@markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
@@ -31,8 +35,29 @@
end
##
+ # Converts a target url to one that is relative to a given path
+
+ def self.gen_relative_url(path, target)
+ from = File.dirname path
+ to, to_file = File.split target
+
+ from = from.split "/"
+ to = to.split "/"
+
+ while from.size > 0 and to.size > 0 and from[0] == to[0] do
+ from.shift
+ to.shift
+ end
+
+ from.fill ".."
+ from.concat to
+ from << to_file
+ File.join(*from)
+ end
+
+ ##
# Generate a hyperlink for url, labeled with text. Handle the
- # special cases for img: and link: described under handle_special_HYPEDLINK
+ # special cases for img: and link: described under handle_special_HYPERLINK
def gen_url(url, text)
if url =~ /([A-Za-z]+):(.*)/ then
@@ -48,7 +73,7 @@
url = if path[0, 1] == '#' then # is this meaningful?
path
else
- RDoc::Generator.gen_url @from_path, path
+ self.class.gen_relative_url @from_path, path
end
end
@@ -88,6 +113,20 @@
end
##
+ # are we currently inside <tt> tags?
+
+ def in_tt?
+ @in_tt > 0
+ end
+
+ ##
+ # is +tag+ a <tt> tag?
+
+ def tt?(tag)
+ tag.bit == @tt_bit
+ end
+
+ ##
# Set up the standard mapping of attributes to HTML tags
def init_tags
@@ -216,6 +255,7 @@
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.on)
+ @in_tt += 1 if tt?(tag)
end
end
end
@@ -226,6 +266,7 @@
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
+ @in_tt -= 1 if tt?(tag)
res << annotate(tag.off)
end
end
@@ -251,37 +292,45 @@
res
end
+ def convert_string(item)
+ in_tt? ? convert_string_simple(item) : convert_string_fancy(item)
+ end
+
+ def convert_string_simple(item)
+ CGI.escapeHTML item
+ end
+
##
# some of these patterns are taken from SmartyPants...
- def convert_string(item)
- CGI.escapeHTML(item).
+ def convert_string_fancy(item)
+ # convert ampersand before doing anything else
+ item.gsub(/&/, '&').
# convert -- to em-dash, (-- to en-dash)
gsub(/---?/, '—'). #gsub(/--/, '–').
-
+
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…').
# convert single closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1’').
+ gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1’'). # }
gsub(%r{\'(?=\W|s\b)}, '’').
# convert single opening quote
gsub(/'/, '‘').
# convert double closing quote
- gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1”').
+ gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, '\1”'). # }
# convert double opening quote
- gsub(/'/, '“').
+ gsub(/"/, '“').
# convert copyright
gsub(/\(c\)/, '©').
- # convert and registered trademark
+ # convert registered trademark
gsub(/\(r\)/, '®')
-
end
def convert_special(special)
Modified: MacRuby/branches/experimental/lib/rdoc/markup/to_html_crossref.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/to_html_crossref.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup/to_html_crossref.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -9,25 +9,77 @@
attr_accessor :context
+ # Regular expressions to match class and method references.
+ #
+ # 1.) There can be a '\' in front of text to suppress
+ # any cross-references (note, however, that the single '\'
+ # is written as '\\\\' in order to escape it twice, once
+ # in the Ruby String literal and once in the regexp).
+ # 2.) There can be a '::' in front of class names to reference
+ # from the top-level namespace.
+ # 3.) The method can be followed by parenthesis,
+ # which may or may not have things inside (this
+ # apparently is allowed for Fortran 95, but I also think that this
+ # is a good idea for Ruby, as it is very reasonable to want to
+ # reference a call with arguments).
+ #
+ # NOTE: In order to support Fortran 95 properly, the [A-Z] below
+ # should be changed to [A-Za-z]. This slows down rdoc significantly,
+ # however, and the Fortran 95 support is broken in any case due to
+ # the return in handle_special_CROSSREF if the token consists
+ # entirely of lowercase letters.
+ #
+ # The markup/cross-referencing engine needs a rewrite for
+ # Fortran 95 to be supported properly.
+ CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)'
+ METHOD_REGEXP_STR = '(\w+[!?=]?)(?:\([\.\w+\*\/\+\-\=\<\>]*\))?'
+
+ # Regular expressions matching text that should potentially have
+ # cross-reference links generated are passed to add_special.
+ # Note that these expressions are meant to pick up text for which
+ # cross-references have been suppressed, since the suppression
+ # characters are removed by the code that is triggered.
+ CROSSREF_REGEXP = /(
+ # A::B::C.meth
+ #{CLASS_REGEXP_STR}[\.\#]#{METHOD_REGEXP_STR}
+
+ # Stand-alone method (proceeded by a #)
+ | \\?\##{METHOD_REGEXP_STR}
+
+ # A::B::C
+ # The stuff after CLASS_REGEXP_STR is a
+ # nasty hack. CLASS_REGEXP_STR unfortunately matches
+ # words like dog and cat (these are legal "class"
+ # names in Fortran 95). When a word is flagged as a
+ # potential cross-reference, limitations in the markup
+ # engine suppress other processing, such as typesetting.
+ # This is particularly noticeable for contractions.
+ # In order that words like "can't" not
+ # be flagged as potential cross-references, only
+ # flag potential class cross-references if the character
+ # after the cross-referece is a space or sentence
+ # punctuation.
+ | #{CLASS_REGEXP_STR}(?=[\s\)\.\?\!\,\;]|\z)
+
+ # Things that look like filenames
+ # The key thing is that there must be at least
+ # one special character (period, slash, or
+ # underscore).
+ | [\/\w]+[_\/\.][\w\/\.]+
+
+ # Things that have markup suppressed
+ | \\[^\s]
+ )/x
+
##
# We need to record the html path of our caller so we can generate
# correct relative paths for any hyperlinks that we find
def initialize(from_path, context, show_hash)
+ raise ArgumentError, 'from_path cannot be nil' if from_path.nil?
super()
- # class names, variable names, or instance variables
- @markup.add_special(/(
- # A::B.meth(**) (for operator in Fortran95)
- \w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?
- # meth(**) (for operator in Fortran95)
- | \#\w+(\([.\w\*\/\+\-\=\<\>]+\))?
- | \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
- | \b([A-Z]\w+(::\w+)*) # A::B
- | \#\w+[!?=]? # #meth_name
- | \\?\b\w+([_\/\.]+\w+)*[!?=]? # meth_name
- )/x,
- :CROSSREF)
+ @markup.add_special(CROSSREF_REGEXP, :CROSSREF)
@from_path = from_path
@context = context
@@ -47,28 +99,39 @@
def handle_special_CROSSREF(special)
name = special.text
+ # This ensures that words entirely consisting of lowercase letters will
+ # not have cross-references generated (to suppress lots of
+ # erroneous cross-references to "new" in text, for instance)
+ return name if name =~ /\A[a-z]*\z/
+
return @seen[name] if @seen.include? name
- if name[0,1] == '#' then
+ if name[0, 1] == '#' then
lookup = name[1..-1]
name = lookup unless @show_hash
else
lookup = name
end
+
# Find class, module, or method in class or module.
- if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup then
+ #
+ # Do not, however, use an if/elsif/else chain to do so. Instead, test
+ # each possible pattern until one matches. The reason for this is that a
+ # string like "YAML.txt" could be the txt() class method of class YAML (in
+ # which case it would match the first pattern, which splits the string
+ # into container and method components and looks up both) or a filename
+ # (in which case it would match the last pattern, which just checks
+ # whether the string as a whole is a known symbol).
+
+ if /#{CLASS_REGEXP_STR}[\.\#]#{METHOD_REGEXP_STR}/ =~ lookup then
container = $1
method = $2
ref = @context.find_symbol container, method
- elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup then
- container = $1
- method = $2
- ref = @context.find_symbol container, method
- else
- ref = @context.find_symbol lookup
end
+ ref = @context.find_symbol lookup unless ref
+
out = if lookup =~ /^\\/ then
$'
elsif ref and ref.document_self then
@@ -83,4 +146,3 @@
end
end
-
Copied: MacRuby/branches/experimental/lib/rdoc/markup/to_texinfo.rb (from rev 1886, MacRuby/trunk/lib/rdoc/markup/to_texinfo.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup/to_texinfo.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/markup/to_texinfo.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,69 @@
+require 'rdoc/markup/formatter'
+require 'rdoc/markup/fragments'
+require 'rdoc/markup/inline'
+
+require 'rdoc/markup'
+require 'rdoc/markup/formatter'
+
+##
+# Convert SimpleMarkup to basic TexInfo format
+#
+# TODO: WTF is AttributeManager for?
+#
+class RDoc::Markup::ToTexInfo < RDoc::Markup::Formatter
+
+ def start_accepting
+ @text = []
+ end
+
+ def end_accepting
+ @text.join("\n")
+ end
+
+ def accept_paragraph(attributes, text)
+ @text << format(text)
+ end
+
+ def accept_verbatim(attributes, text)
+ @text << "@verb{|#{format(text)}|}"
+ end
+
+ def accept_heading(attributes, text)
+ heading = ['@majorheading', '@chapheading'][text.head_level - 1] || '@heading'
+ @text << "#{heading} #{format(text)}"
+ end
+
+ def accept_list_start(attributes, text)
+ @text << '@itemize @bullet'
+ end
+
+ def accept_list_end(attributes, text)
+ @text << '@end itemize'
+ end
+
+ def accept_list_item(attributes, text)
+ @text << "@item\n#{format(text)}"
+ end
+
+ def accept_blank_line(attributes, text)
+ @text << "\n"
+ end
+
+ def accept_rule(attributes, text)
+ @text << '-----'
+ end
+
+ def format(text)
+ text.txt.
+ gsub(/@/, "@@").
+ gsub(/\{/, "@{").
+ gsub(/\}/, "@}").
+ # gsub(/,/, "@,"). # technically only required in cross-refs
+ gsub(/\+([\w]+)\+/, "@code{\\1}").
+ gsub(/\<tt\>([^<]+)\<\/tt\>/, "@code{\\1}").
+ gsub(/\*([\w]+)\*/, "@strong{\\1}").
+ gsub(/\<b\>([^<]+)\<\/b\>/, "@strong{\\1}").
+ gsub(/_([\w]+)_/, "@emph{\\1}").
+ gsub(/\<em\>([^<]+)\<\/em\>/, "@emph{\\1}")
+ end
+end
Modified: MacRuby/branches/experimental/lib/rdoc/markup.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/markup.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/markup.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -17,104 +17,9 @@
# RDoc::Markup is intended to be the basis for a family of tools which share
# the common requirement that simple, plain-text should be rendered in a
# variety of different output formats and media. It is envisaged that
-# RDoc::Markup could be the basis for formating RDoc style comment blocks,
+# RDoc::Markup could be the basis for formatting RDoc style comment blocks,
# Wiki entries, and online FAQs.
#
-# = Basic Formatting
-#
-# * RDoc::Markup looks for a document's natural left margin. This is
-# used as the initial margin for the document.
-#
-# * Consecutive lines starting at this margin are considered to be a
-# paragraph.
-#
-# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
-# taken to be the start of a list. The margin in increased to be the first
-# non-space following the list start flag. Subsequent lines should be
-# indented to this \new margin until the list ends. For example:
-#
-# * this is a list with three paragraphs in
-# the first item. This is the first paragraph.
-#
-# And this is the second paragraph.
-#
-# 1. This is an indented, numbered list.
-# 2. This is the second item in that list
-#
-# This is the third conventional paragraph in the
-# first list item.
-#
-# * This is the second item in the original list
-#
-# * You can also construct labeled lists, sometimes called description
-# or definition lists. Do this by putting the label in square brackets
-# and indenting the list body:
-#
-# [cat] a small furry mammal
-# that seems to sleep a lot
-#
-# [ant] a little insect that is known
-# to enjoy picnics
-#
-# A minor variation on labeled lists uses two colons to separate the
-# label from the list body:
-#
-# cat:: a small furry mammal
-# that seems to sleep a lot
-#
-# ant:: a little insect that is known
-# to enjoy picnics
-#
-# This latter style guarantees that the list bodies' left margins are
-# aligned: think of them as a two column table.
-#
-# * Any line that starts to the right of the current margin is treated
-# as verbatim text. This is useful for code listings. The example of a
-# list above is also verbatim text.
-#
-# * A line starting with an equals sign (=) is treated as a
-# heading. Level one headings have one equals sign, level two headings
-# have two,and so on.
-#
-# * A line starting with three or more hyphens (at the current indent)
-# generates a horizontal rule. The more hyphens, the thicker the rule
-# (within reason, and if supported by the output device)
-#
-# * You can use markup within text (except verbatim) to change the
-# appearance of parts of that text. Out of the box, RDoc::Markup
-# supports word-based and general markup.
-#
-# Word-based markup uses flag characters around individual words:
-#
-# [\*word*] displays word in a *bold* font
-# [\_word_] displays word in an _emphasized_ font
-# [\+word+] displays word in a +code+ font
-#
-# General markup affects text between a start delimiter and and end
-# delimiter. Not surprisingly, these delimiters look like HTML markup.
-#
-# [\<b>text...</b>] displays word in a *bold* font
-# [\<em>text...</em>] displays word in an _emphasized_ font
-# [\<i>text...</i>] displays word in an _emphasized_ font
-# [\<tt>text...</tt>] displays word in a +code+ font
-#
-# Unlike conventional Wiki markup, general markup can cross line
-# boundaries. You can turn off the interpretation of markup by
-# preceding the first character with a backslash, so \\\<b>bold
-# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold*
-# respectively.
-#
-# * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are
-# recognized. An HTTP url that references an external image file is
-# converted into an inline <IMG..>. Hyperlinks starting 'link:' are
-# assumed to refer to local files whose path is relative to the --op
-# directory.
-#
-# Hyperlinks can also be of the form <tt>label</tt>[url], in which
-# case the label is used in the displayed text, and <tt>url</tt> is
-# used as the target. If <tt>label</tt> contains multiple words,
-# put it in braces: <em>{multi word label}[</em>url<em>]</em>.
-#
# == Synopsis
#
# This code converts +input_string+ to HTML. The conversion takes place in
@@ -129,7 +34,7 @@
#
# You can extend the RDoc::Markup parser to recognise new markup
# sequences, and to add special processing for text that matches a
-# regular epxression. Here we make WikiWords significant to the parser,
+# regular expression. Here we make WikiWords significant to the parser,
# and also make the sequences {word} and \<no>text...</no> signify
# strike-through text. When then subclass the HTML output class to deal
# with these:
@@ -197,7 +102,7 @@
##
# Add to the sequences used to add formatting to an individual word (such
- # as *bold*). Matching entries will generate attibutes that the output
+ # as *bold*). Matching entries will generate attributes that the output
# formatters can recognize by their +name+.
def add_word_pair(start, stop, name)
Modified: MacRuby/branches/experimental/lib/rdoc/options.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/options.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/options.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -39,7 +39,7 @@
##
# Pattern for additional attr_... style methods
- attr_reader :extra_accessors
+ attr_accessor :extra_accessors
##
# Should we draw fileboxes in diagrams
@@ -62,6 +62,11 @@
attr_accessor :generator
##
+ # Formatter to mark up text with
+
+ attr_accessor :formatter
+
+ ##
# image format for diagrams
attr_reader :image_format
@@ -95,7 +100,7 @@
##
# The name to use for the output
- attr_reader :op_name
+ attr_accessor :op_name
##
# Are we promiscuous about showing module contents across multiple files
@@ -103,11 +108,6 @@
attr_reader :promiscuous
##
- # Don't display progress as we process the files
-
- attr_reader :quiet
-
- ##
# Array of directories to search for files to satisfy an :include:
attr_reader :rdoc_include
@@ -145,18 +145,22 @@
attr_reader :title
##
+ # Verbosity, zero means quiet
+
+ attr_accessor :verbosity
+
+ ##
# URL of web cvs frontend
attr_reader :webcvs
- def initialize(generators) # :nodoc:
+ def initialize(generators = {}) # :nodoc:
@op_dir = "doc"
@op_name = nil
@show_all = false
@main_page = nil
@merge = false
@exclude = []
- @quiet = false
@generators = generators
@generator_name = 'html'
@generator = @generators[@generator_name]
@@ -175,12 +179,12 @@
@extra_accessor_flags = {}
@promiscuous = false
@force_update = false
- @title = "RDoc Documentation"
+ @verbosity = 1
@css = nil
@webcvs = nil
- @charset = 'iso-8859-1'
+ @charset = 'utf-8'
end
##
@@ -192,6 +196,7 @@
opts = OptionParser.new do |opt|
opt.program_name = File.basename $0
opt.version = RDoc::VERSION
+ opt.release = nil
opt.summary_indent = ' ' * 4
opt.banner = <<-EOF
Usage: #{opt.program_name} [options] [names...]
@@ -253,7 +258,7 @@
opt.separator nil
opt.on("--charset=CHARSET", "-c",
- "Specifies the HTML character-set.") do |value|
+ "Specifies the output HTML character-set.") do |value|
@charset = value
end
@@ -279,9 +284,7 @@
opt.on("--exclude=PATTERN", "-x", Regexp,
"Do not process files or directories",
- "matching PATTERN. Files given explicitly",
- "on the command line will never be",
- "excluded.") do |value|
+ "matching PATTERN.") do |value|
@exclude << value
end
@@ -420,9 +423,15 @@
opt.on("--quiet", "-q",
"Don't show progress as we parse.") do |value|
- @quiet = value
+ @verbosity = 0
end
+ opt.on("--verbose", "-v",
+ "Display extra progress as we parse.") do |value|
+ @verbosity = 2
+ end
+
+
opt.separator nil
opt.on("--ri", "-r",
@@ -513,6 +522,8 @@
end
end
+ argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT']
+
opts.parse! argv
@files = argv.dup
@@ -553,6 +564,17 @@
@title ||= string
end
+ ##
+ # Don't display progress as we process the files
+
+ def quiet
+ @verbosity.zero?
+ end
+
+ def quiet=(bool)
+ @verbosity = bool ? 0 : 1
+ end
+
private
##
@@ -571,7 +593,7 @@
end
end
- # Check that the right version of 'dot' is available. Unfortuately this
+ # Check that the right version of 'dot' is available. Unfortunately this
# doesn't work correctly under Windows NT, so we'll bypass the test under
# Windows.
@@ -607,8 +629,8 @@
def check_files
@files.each do |f|
- stat = File.stat f rescue abort("File not found: #{f}")
- abort("File '#{f}' not readable") unless stat.readable?
+ stat = File.stat f
+ raise RDoc::Error, "file '#{f}' not readable" unless stat.readable?
end
end
Deleted: MacRuby/branches/experimental/lib/rdoc/parser/c.rb
===================================================================
--- MacRuby/trunk/lib/rdoc/parser/c.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/parser/c.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,661 +0,0 @@
-require 'rdoc/parser'
-require 'rdoc/parser/ruby'
-require 'rdoc/known_classes'
-
-##
-# We attempt to parse C extension files. Basically we look for
-# the standard patterns that you find in extensions: <tt>rb_define_class,
-# rb_define_method</tt> and so on. We also try to find the corresponding
-# C source for the methods and extract comments, but if we fail
-# we don't worry too much.
-#
-# The comments associated with a Ruby method are extracted from the C
-# comment block associated with the routine that _implements_ that
-# method, that is to say the method whose name is given in the
-# <tt>rb_define_method</tt> call. For example, you might write:
-#
-# /*
-# * Returns a new array that is a one-dimensional flattening of this
-# * array (recursively). That is, for every element that is an array,
-# * extract its elements into the new array.
-# *
-# * s = [ 1, 2, 3 ] #=> [1, 2, 3]
-# * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
-# * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
-# * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
-# */
-# static VALUE
-# rb_ary_flatten(ary)
-# VALUE ary;
-# {
-# ary = rb_obj_dup(ary);
-# rb_ary_flatten_bang(ary);
-# return ary;
-# }
-#
-# ...
-#
-# void
-# Init_Array()
-# {
-# ...
-# rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
-#
-# Here RDoc will determine from the rb_define_method line that there's a
-# method called "flatten" in class Array, and will look for the implementation
-# in the method rb_ary_flatten. It will then use the comment from that
-# method in the HTML output. This method must be in the same source file
-# as the rb_define_method.
-#
-# C classes can be diagrammed (see /tc/dl/ruby/ruby/error.c), and RDoc
-# integrates C and Ruby source into one tree
-#
-# The comment blocks may include special directives:
-#
-# [Document-class: <i>name</i>]
-# This comment block is documentation for the given class. Use this
-# when the <tt>Init_xxx</tt> method is not named after the class.
-#
-# [Document-method: <i>name</i>]
-# This comment documents the named method. Use when RDoc cannot
-# automatically find the method from it's declaration
-#
-# [call-seq: <i>text up to an empty line</i>]
-# Because C source doesn't give descripive names to Ruby-level parameters,
-# you need to document the calling sequence explicitly
-#
-# In addition, RDoc assumes by default that the C method implementing a
-# Ruby function is in the same source file as the rb_define_method call.
-# If this isn't the case, add the comment:
-#
-# rb_define_method(....); // in: filename
-#
-# As an example, we might have an extension that defines multiple classes
-# in its Init_xxx method. We could document them using
-#
-# /*
-# * Document-class: MyClass
-# *
-# * Encapsulate the writing and reading of the configuration
-# * file. ...
-# */
-#
-# /*
-# * Document-method: read_value
-# *
-# * call-seq:
-# * cfg.read_value(key) -> value
-# * cfg.read_value(key} { |key| } -> value
-# *
-# * Return the value corresponding to +key+ from the configuration.
-# * In the second form, if the key isn't found, invoke the
-# * block and return its value.
-# */
-
-class RDoc::Parser::C < RDoc::Parser
-
- parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
-
- @@enclosure_classes = {}
- @@known_bodies = {}
-
- ##
- # Prepare to parse a C file
-
- def initialize(top_level, file_name, content, options, stats)
- super
-
- @known_classes = RDoc::KNOWN_CLASSES.dup
- @content = handle_tab_width handle_ifdefs_in(@content)
- @classes = Hash.new
- @file_dir = File.dirname(@file_name)
- end
-
- def do_aliases
- @content.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
- |var_name, new_name, old_name|
- class_name = @known_classes[var_name] || var_name
- class_obj = find_class(var_name, class_name)
-
- as = class_obj.add_alias RDoc::Alias.new("", old_name, new_name, "")
-
- @stats.add_alias as
- end
- end
-
- def do_classes
- @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
- |var_name, class_name|
- handle_class_module(var_name, "module", class_name, nil, nil)
- end
-
- # The '.' lets us handle SWIG-generated files
- @content.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
- \(
- \s*"(\w+)",
- \s*(\w+)\s*
- \)/mx) do |var_name, class_name, parent|
- handle_class_module(var_name, "class", class_name, parent, nil)
- end
-
- @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
- |var_name, class_name, parent|
- parent = nil if parent == "0"
- handle_class_module(var_name, "class", class_name, parent, nil)
- end
-
- @content.scan(/(\w+)\s* = \s*rb_define_module_under\s*
- \(
- \s*(\w+),
- \s*"(\w+)"
- \s*\)/mx) do |var_name, in_module, class_name|
- handle_class_module(var_name, "module", class_name, nil, in_module)
- end
-
- @content.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
- \(
- \s*(\w+),
- \s*"(\w+)",
- \s*([\w\*\s\(\)\.\->]+)\s* # for SWIG
- \s*\)/mx) do |var_name, in_module, class_name, parent|
- handle_class_module(var_name, "class", class_name, parent, in_module)
- end
- end
-
- def do_constants
- @content.scan(%r{\Wrb_define_
- (
- variable |
- readonly_variable |
- const |
- global_const |
- )
- \s*\(
- (?:\s*(\w+),)?
- \s*"(\w+)",
- \s*(.*?)\s*\)\s*;
- }xm) do |type, var_name, const_name, definition|
- var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
- handle_constants(type, var_name, const_name, definition)
- end
- end
-
- ##
- # Look for includes of the form:
- #
- # rb_include_module(rb_cArray, rb_mEnumerable);
-
- def do_includes
- @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
- if cls = @classes[c]
- m = @known_classes[m] || m
- cls.add_include RDoc::Include.new(m, "")
- end
- end
- end
-
- def do_methods
- @content.scan(%r{rb_define_
- (
- singleton_method |
- method |
- module_function |
- private_method
- )
- \s*\(\s*([\w\.]+),
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
- }xm) do
- |type, var_name, meth_name, meth_body, param_count, source_file|
-
- # Ignore top-object and weird struct.c dynamic stuff
- next if var_name == "ruby_top_self"
- next if var_name == "nstr"
- next if var_name == "envtbl"
- next if var_name == "argf" # it'd be nice to handle this one
-
- var_name = "rb_cObject" if var_name == "rb_mKernel"
- handle_method(type, var_name, meth_name,
- meth_body, param_count, source_file)
- end
-
- @content.scan(%r{rb_define_attr\(
- \s*([\w\.]+),
- \s*"([^"]+)",
- \s*(\d+),
- \s*(\d+)\s*\);
- }xm) do |var_name, attr_name, attr_reader, attr_writer|
- #var_name = "rb_cObject" if var_name == "rb_mKernel"
- handle_attr(var_name, attr_name,
- attr_reader.to_i != 0,
- attr_writer.to_i != 0)
- end
-
- @content.scan(%r{rb_define_global_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)
- (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
- }xm) do |meth_name, meth_body, param_count, source_file|
- handle_method("method", "rb_mKernel", meth_name,
- meth_body, param_count, source_file)
- end
-
- @content.scan(/define_filetest_function\s*\(
- \s*"([^"]+)",
- \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
- \s*(-?\w+)\s*\)/xm) do
- |meth_name, meth_body, param_count|
-
- handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
- handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
- end
- end
-
- def find_attr_comment(attr_name)
- if @content =~ %r{((?>/\*.*?\*/\s+))
- rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi
- $1
- elsif @content =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
- $1
- else
- ''
- end
- end
-
- ##
- # Find the C code corresponding to a Ruby method
-
- def find_body(class_name, meth_name, meth_obj, body, quiet = false)
- case body
- when %r"((?>/\*.*?\*/\s*))(?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name}
- \s*(\([^)]*\))([^;]|$)"xm
- comment, params = $1, $2
- body_text = $&
-
- remove_private_comments(comment) if comment
-
- # see if we can find the whole body
-
- re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
- body_text = $& if /#{re}/m =~ body
-
- # The comment block may have been overridden with a 'Document-method'
- # block. This happens in the interpreter when multiple methods are
- # vectored through to the same C method but those methods are logically
- # distinct (for example Kernel.hash and Kernel.object_id share the same
- # implementation
-
- override_comment = find_override_comment(class_name, meth_obj.name)
- comment = override_comment if override_comment
-
- find_modifiers(comment, meth_obj) if comment
-
-# meth_obj.params = params
- meth_obj.start_collecting_tokens
- meth_obj.add_token(RDoc::RubyToken::Token.new(1,1).set_text(body_text))
- meth_obj.comment = mangle_comment(comment)
- when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
- comment = $1
- find_body(class_name, $2, meth_obj, body, true)
- find_modifiers(comment, meth_obj)
- meth_obj.comment = mangle_comment(comment) + meth_obj.comment
- when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
- unless find_body(class_name, $1, meth_obj, body, true)
- warn "No definition for #{meth_name}" unless @options.quiet
- return false
- end
- else
-
- # No body, but might still have an override comment
- comment = find_override_comment(class_name, meth_obj.name)
-
- if comment
- find_modifiers(comment, meth_obj)
- meth_obj.comment = mangle_comment(comment)
- else
- warn "No definition for #{meth_name}" unless @options.quiet
- return false
- end
- end
- true
- end
-
- def find_class(raw_name, name)
- unless @classes[raw_name]
- if raw_name =~ /^rb_m/
- container = @top_level.add_module RDoc::NormalModule, name
- else
- container = @top_level.add_class RDoc::NormalClass, name, nil
- end
-
- container.record_location @top_level
- @classes[raw_name] = container
- end
- @classes[raw_name]
- end
-
- ##
- # Look for class or module documentation above Init_+class_name+(void),
- # in a Document-class +class_name+ (or module) comment or above an
- # rb_define_class (or module). If a comment is supplied above a matching
- # Init_ and a rb_define_class the Init_ comment is used.
- #
- # /*
- # * This is a comment for Foo
- # */
- # Init_Foo(void) {
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
- # }
- #
- # /*
- # * Document-class: Foo
- # * This is a comment for Foo
- # */
- # Init_foo(void) {
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
- # }
- #
- # /*
- # * This is a comment for Foo
- # */
- # VALUE cFoo = rb_define_class("Foo", rb_cObject);
-
- def find_class_comment(class_name, class_meth)
- comment = nil
- if @content =~ %r{((?>/\*.*?\*/\s+))
- (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi then
- comment = $1
- elsif @content =~ %r{Document-(?:class|module):\s#{class_name}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m
- comment = $1
- else
- if @content =~ /rb_define_(class|module)/m then
- class_name = class_name.split("::").last
- comments = []
- @content.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index|
- comments[index] = chunk
- if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then
- comment = comments[index-1]
- break
- end
- end
- end
- end
- class_meth.comment = mangle_comment(comment) if comment
- end
-
- ##
- # Finds a comment matching +type+ and +const_name+ either above the
- # comment or in the matching Document- section.
-
- def find_const_comment(type, const_name)
- if @content =~ %r{((?>^\s*/\*.*?\*/\s+))
- rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
- $1
- elsif @content =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
- $1
- else
- ''
- end
- end
-
- ##
- # If the comment block contains a section that looks like:
- #
- # call-seq:
- # Array.new
- # Array.new(10)
- #
- # use it for the parameters.
-
- def find_modifiers(comment, meth_obj)
- if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or
- comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '')
- meth_obj.document_self = false
- end
- if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or
- comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '')
- seq = $1
- seq.gsub!(/^\s*\*\s*/, '')
- meth_obj.call_seq = seq
- end
- end
-
- def find_override_comment(class_name, meth_name)
- name = Regexp.escape(meth_name)
- if @content =~ %r{Document-method:\s+#{class_name}(?:\.|::|#)#{name}\s*?\n((?>.*?\*/))}m then
- $1
- elsif @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m then
- $1
- end
- end
-
- def handle_attr(var_name, attr_name, reader, writer)
- rw = ''
- if reader
- #@stats.num_methods += 1
- rw << 'R'
- end
- if writer
- #@stats.num_methods += 1
- rw << 'W'
- end
-
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- if class_obj
- comment = find_attr_comment(attr_name)
- unless comment.empty?
- comment = mangle_comment(comment)
- end
- att = RDoc::Attr.new '', attr_name, rw, comment
- class_obj.add_attribute(att)
- end
- end
-
- def handle_class_module(var_name, class_mod, class_name, parent, in_module)
- parent_name = @known_classes[parent] || parent
-
- if in_module
- enclosure = @classes[in_module] || @@enclosure_classes[in_module]
- unless enclosure
- if enclosure = @known_classes[in_module]
- handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
- enclosure, nil, nil)
- enclosure = @classes[in_module]
- end
- end
- unless enclosure
- warn("Enclosing class/module '#{in_module}' for " +
- "#{class_mod} #{class_name} not known")
- return
- end
- else
- enclosure = @top_level
- end
-
- if class_mod == "class" then
- full_name = enclosure.full_name.to_s + "::#{class_name}"
- if @content =~ %r{Document-class:\s+#{full_name}\s*<\s+([:,\w]+)} then
- parent_name = $1
- end
- cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
- @stats.add_class cm
- else
- cm = enclosure.add_module RDoc::NormalModule, class_name
- @stats.add_module cm
- end
-
- cm.record_location(enclosure.toplevel)
-
- find_class_comment(cm.full_name, cm)
- @classes[var_name] = cm
- @@enclosure_classes[var_name] = cm
- @known_classes[var_name] = cm.full_name
- end
-
- ##
- # Adds constant comments. By providing some_value: at the start ofthe
- # comment you can override the C value of the comment to give a friendly
- # definition.
- #
- # /* 300: The perfect score in bowling */
- # rb_define_const(cFoo, "PERFECT", INT2FIX(300);
- #
- # Will override +INT2FIX(300)+ with the value +300+ in the output RDoc.
- # Values may include quotes and escaped colons (\:).
-
- def handle_constants(type, var_name, const_name, definition)
- #@stats.num_constants += 1
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class(var_name, class_name)
-
- unless class_obj
- warn("Enclosing class/module '#{const_name}' for not known")
- return
- end
-
- comment = find_const_comment(type, const_name)
-
- # In the case of rb_define_const, the definition and comment are in
- # "/* definition: comment */" form. The literal ':' and '\' characters
- # can be escaped with a backslash.
- if type.downcase == 'const' then
- elements = mangle_comment(comment).split(':')
- if elements.nil? or elements.empty? then
- con = RDoc::Constant.new(const_name, definition,
- mangle_comment(comment))
- else
- new_definition = elements[0..-2].join(':')
- if new_definition.empty? then # Default to literal C definition
- new_definition = definition
- else
- new_definition.gsub!("\:", ":")
- new_definition.gsub!("\\", '\\')
- end
- new_definition.sub!(/\A(\s+)/, '')
- new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}"
- con = RDoc::Constant.new(const_name, new_definition,
- mangle_comment(new_comment))
- end
- else
- con = RDoc::Constant.new const_name, definition, mangle_comment(comment)
- end
-
- class_obj.add_constant(con)
- end
-
- ##
- # Removes #ifdefs that would otherwise confuse us
-
- def handle_ifdefs_in(body)
- body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
- end
-
- def handle_method(type, var_name, meth_name, meth_body, param_count,
- source_file = nil)
- class_name = @known_classes[var_name]
-
- return unless class_name
-
- class_obj = find_class var_name, class_name
-
- if class_obj then
- if meth_name == "initialize" then
- meth_name = "new"
- type = "singleton_method"
- end
-
- meth_obj = RDoc::AnyMethod.new '', meth_name
- meth_obj.singleton = %w[singleton_method module_function].include? type
-
- p_count = (Integer(param_count) rescue -1)
-
- if p_count < 0
- meth_obj.params = "(...)"
- elsif p_count == 0
- meth_obj.params = "()"
- else
- meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")"
- end
-
- if source_file then
- file_name = File.join(@file_dir, source_file)
- body = (@@known_bodies[source_file] ||= File.read(file_name))
- else
- body = @content
- end
-
- if find_body(class_name, meth_body, meth_obj, body) and meth_obj.document_self then
- class_obj.add_method meth_obj
- @stats.add_method meth_obj
- end
- end
- end
-
- def handle_tab_width(body)
- if /\t/ =~ body
- tab_width = @options.tab_width
- body.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
- line
- end .join("\n")
- else
- body
- end
- end
-
- ##
- # Remove the /*'s and leading asterisks from C comments
-
- def mangle_comment(comment)
- comment.sub!(%r{/\*+}) { " " * $&.length }
- comment.sub!(%r{\*+/}) { " " * $&.length }
- comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
- comment
- end
-
- ##
- # Removes lines that are commented out that might otherwise get picked up
- # when scanning for classes and methods
-
- def remove_commented_out_lines
- @content.gsub!(%r{//.*rb_define_}, '//')
- end
-
- def remove_private_comments(comment)
- comment.gsub!(/\/?\*--\n(.*?)\/?\*\+\+/m, '')
- comment.sub!(/\/?\*--\n.*/m, '')
- end
-
- ##
- # Extract the classes/modules and methods from a C file and return the
- # corresponding top-level object
-
- def scan
- remove_commented_out_lines
- do_classes
- do_constants
- do_methods
- do_includes
- do_aliases
- @top_level
- end
-
- def warn(msg)
- $stderr.puts
- $stderr.puts msg
- $stderr.flush
- end
-
-end
-
Copied: MacRuby/branches/experimental/lib/rdoc/parser/c.rb (from rev 1886, MacRuby/trunk/lib/rdoc/parser/c.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/parser/c.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/parser/c.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,661 @@
+require 'rdoc/parser'
+require 'rdoc/parser/ruby'
+require 'rdoc/known_classes'
+
+##
+# We attempt to parse C extension files. Basically we look for
+# the standard patterns that you find in extensions: <tt>rb_define_class,
+# rb_define_method</tt> and so on. We also try to find the corresponding
+# C source for the methods and extract comments, but if we fail
+# we don't worry too much.
+#
+# The comments associated with a Ruby method are extracted from the C
+# comment block associated with the routine that _implements_ that
+# method, that is to say the method whose name is given in the
+# <tt>rb_define_method</tt> call. For example, you might write:
+#
+# /*
+# * Returns a new array that is a one-dimensional flattening of this
+# * array (recursively). That is, for every element that is an array,
+# * extract its elements into the new array.
+# *
+# * s = [ 1, 2, 3 ] #=> [1, 2, 3]
+# * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+# * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+# * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+# */
+# static VALUE
+# rb_ary_flatten(ary)
+# VALUE ary;
+# {
+# ary = rb_obj_dup(ary);
+# rb_ary_flatten_bang(ary);
+# return ary;
+# }
+#
+# ...
+#
+# void
+# Init_Array()
+# {
+# ...
+# rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
+#
+# Here RDoc will determine from the rb_define_method line that there's a
+# method called "flatten" in class Array, and will look for the implementation
+# in the method rb_ary_flatten. It will then use the comment from that
+# method in the HTML output. This method must be in the same source file
+# as the rb_define_method.
+#
+# C classes can be diagrammed (see /tc/dl/ruby/ruby/error.c), and RDoc
+# integrates C and Ruby source into one tree
+#
+# The comment blocks may include special directives:
+#
+# [Document-class: <i>name</i>]
+# This comment block is documentation for the given class. Use this
+# when the <tt>Init_xxx</tt> method is not named after the class.
+#
+# [Document-method: <i>name</i>]
+# This comment documents the named method. Use when RDoc cannot
+# automatically find the method from it's declaration
+#
+# [call-seq: <i>text up to an empty line</i>]
+# Because C source doesn't give descripive names to Ruby-level parameters,
+# you need to document the calling sequence explicitly
+#
+# In addition, RDoc assumes by default that the C method implementing a
+# Ruby function is in the same source file as the rb_define_method call.
+# If this isn't the case, add the comment:
+#
+# rb_define_method(....); // in: filename
+#
+# As an example, we might have an extension that defines multiple classes
+# in its Init_xxx method. We could document them using
+#
+# /*
+# * Document-class: MyClass
+# *
+# * Encapsulate the writing and reading of the configuration
+# * file. ...
+# */
+#
+# /*
+# * Document-method: read_value
+# *
+# * call-seq:
+# * cfg.read_value(key) -> value
+# * cfg.read_value(key} { |key| } -> value
+# *
+# * Return the value corresponding to +key+ from the configuration.
+# * In the second form, if the key isn't found, invoke the
+# * block and return its value.
+# */
+
+class RDoc::Parser::C < RDoc::Parser
+
+ parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
+
+ @@enclosure_classes = {}
+ @@known_bodies = {}
+
+ ##
+ # Prepare to parse a C file
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ @known_classes = RDoc::KNOWN_CLASSES.dup
+ @content = handle_tab_width handle_ifdefs_in(@content)
+ @classes = Hash.new
+ @file_dir = File.dirname(@file_name)
+ end
+
+ def do_aliases
+ @content.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
+ |var_name, new_name, old_name|
+ class_name = @known_classes[var_name] || var_name
+ class_obj = find_class(var_name, class_name)
+
+ as = class_obj.add_alias RDoc::Alias.new("", old_name, new_name, "")
+
+ @stats.add_alias as
+ end
+ end
+
+ def do_classes
+ @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
+ |var_name, class_name|
+ handle_class_module(var_name, "module", class_name, nil, nil)
+ end
+
+ # The '.' lets us handle SWIG-generated files
+ @content.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
+ \(
+ \s*"(\w+)",
+ \s*(\w+)\s*
+ \)/mx) do |var_name, class_name, parent|
+ handle_class_module(var_name, "class", class_name, parent, nil)
+ end
+
+ @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
+ |var_name, class_name, parent|
+ parent = nil if parent == "0"
+ handle_class_module(var_name, "class", class_name, parent, nil)
+ end
+
+ @content.scan(/(\w+)\s* = \s*rb_define_module_under\s*
+ \(
+ \s*(\w+),
+ \s*"(\w+)"
+ \s*\)/mx) do |var_name, in_module, class_name|
+ handle_class_module(var_name, "module", class_name, nil, in_module)
+ end
+
+ @content.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
+ \(
+ \s*(\w+),
+ \s*"(\w+)",
+ \s*([\w\*\s\(\)\.\->]+)\s* # for SWIG
+ \s*\)/mx) do |var_name, in_module, class_name, parent|
+ handle_class_module(var_name, "class", class_name, parent, in_module)
+ end
+ end
+
+ def do_constants
+ @content.scan(%r{\Wrb_define_
+ (
+ variable |
+ readonly_variable |
+ const |
+ global_const |
+ )
+ \s*\(
+ (?:\s*(\w+),)?
+ \s*"(\w+)",
+ \s*(.*?)\s*\)\s*;
+ }xm) do |type, var_name, const_name, definition|
+ var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
+ handle_constants(type, var_name, const_name, definition)
+ end
+ end
+
+ ##
+ # Look for includes of the form:
+ #
+ # rb_include_module(rb_cArray, rb_mEnumerable);
+
+ def do_includes
+ @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
+ if cls = @classes[c]
+ m = @known_classes[m] || m
+ cls.add_include RDoc::Include.new(m, "")
+ end
+ end
+ end
+
+ def do_methods
+ @content.scan(%r{rb_define_
+ (
+ singleton_method |
+ method |
+ module_function |
+ private_method
+ )
+ \s*\(\s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ }xm) do
+ |type, var_name, meth_name, meth_body, param_count, source_file|
+
+ # Ignore top-object and weird struct.c dynamic stuff
+ next if var_name == "ruby_top_self"
+ next if var_name == "nstr"
+ next if var_name == "envtbl"
+ next if var_name == "argf" # it'd be nice to handle this one
+
+ var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_method(type, var_name, meth_name,
+ meth_body, param_count, source_file)
+ end
+
+ @content.scan(%r{rb_define_attr\(
+ \s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(\d+),
+ \s*(\d+)\s*\);
+ }xm) do |var_name, attr_name, attr_reader, attr_writer|
+ #var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_attr(var_name, attr_name,
+ attr_reader.to_i != 0,
+ attr_writer.to_i != 0)
+ end
+
+ @content.scan(%r{rb_define_global_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ }xm) do |meth_name, meth_body, param_count, source_file|
+ handle_method("method", "rb_mKernel", meth_name,
+ meth_body, param_count, source_file)
+ end
+
+ @content.scan(/define_filetest_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)/xm) do
+ |meth_name, meth_body, param_count|
+
+ handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
+ handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
+ end
+ end
+
+ def find_attr_comment(attr_name)
+ if @content =~ %r{((?>/\*.*?\*/\s+))
+ rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi
+ $1
+ elsif @content =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
+ $1
+ else
+ ''
+ end
+ end
+
+ ##
+ # Find the C code corresponding to a Ruby method
+
+ def find_body(class_name, meth_name, meth_obj, body, quiet = false)
+ case body
+ when %r"((?>/\*.*?\*/\s*))(?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{meth_name}
+ \s*(\([^)]*\))([^;]|$)"xm
+ comment, params = $1, $2
+ body_text = $&
+
+ remove_private_comments(comment) if comment
+
+ # see if we can find the whole body
+
+ re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
+ body_text = $& if /#{re}/m =~ body
+
+ # The comment block may have been overridden with a 'Document-method'
+ # block. This happens in the interpreter when multiple methods are
+ # vectored through to the same C method but those methods are logically
+ # distinct (for example Kernel.hash and Kernel.object_id share the same
+ # implementation
+
+ override_comment = find_override_comment(class_name, meth_obj.name)
+ comment = override_comment if override_comment
+
+ find_modifiers(comment, meth_obj) if comment
+
+# meth_obj.params = params
+ meth_obj.start_collecting_tokens
+ meth_obj.add_token(RDoc::RubyToken::Token.new(1,1).set_text(body_text))
+ meth_obj.comment = mangle_comment(comment)
+ when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
+ comment = $1
+ find_body(class_name, $2, meth_obj, body, true)
+ find_modifiers(comment, meth_obj)
+ meth_obj.comment = mangle_comment(comment) + meth_obj.comment
+ when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
+ unless find_body(class_name, $1, meth_obj, body, true)
+ warn "No definition for #{meth_name}" unless @options.quiet
+ return false
+ end
+ else
+
+ # No body, but might still have an override comment
+ comment = find_override_comment(class_name, meth_obj.name)
+
+ if comment
+ find_modifiers(comment, meth_obj)
+ meth_obj.comment = mangle_comment(comment)
+ else
+ warn "No definition for #{meth_name}" unless @options.quiet
+ return false
+ end
+ end
+ true
+ end
+
+ def find_class(raw_name, name)
+ unless @classes[raw_name]
+ if raw_name =~ /^rb_m/
+ container = @top_level.add_module RDoc::NormalModule, name
+ else
+ container = @top_level.add_class RDoc::NormalClass, name, nil
+ end
+
+ container.record_location @top_level
+ @classes[raw_name] = container
+ end
+ @classes[raw_name]
+ end
+
+ ##
+ # Look for class or module documentation above Init_+class_name+(void),
+ # in a Document-class +class_name+ (or module) comment or above an
+ # rb_define_class (or module). If a comment is supplied above a matching
+ # Init_ and a rb_define_class the Init_ comment is used.
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # Init_Foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * Document-class: Foo
+ # * This is a comment for Foo
+ # */
+ # Init_foo(void) {
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+ # }
+ #
+ # /*
+ # * This is a comment for Foo
+ # */
+ # VALUE cFoo = rb_define_class("Foo", rb_cObject);
+
+ def find_class_comment(class_name, class_meth)
+ comment = nil
+ if @content =~ %r{((?>/\*.*?\*/\s+))
+ (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi then
+ comment = $1
+ elsif @content =~ %r{Document-(?:class|module):\s#{class_name}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m
+ comment = $1
+ else
+ if @content =~ /rb_define_(class|module)/m then
+ class_name = class_name.split("::").last
+ comments = []
+ @content.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index|
+ comments[index] = chunk
+ if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then
+ comment = comments[index-1]
+ break
+ end
+ end
+ end
+ end
+ class_meth.comment = mangle_comment(comment) if comment
+ end
+
+ ##
+ # Finds a comment matching +type+ and +const_name+ either above the
+ # comment or in the matching Document- section.
+
+ def find_const_comment(type, const_name)
+ if @content =~ %r{((?>^\s*/\*.*?\*/\s+))
+ rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
+ $1
+ elsif @content =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
+ $1
+ else
+ ''
+ end
+ end
+
+ ##
+ # If the comment block contains a section that looks like:
+ #
+ # call-seq:
+ # Array.new
+ # Array.new(10)
+ #
+ # use it for the parameters.
+
+ def find_modifiers(comment, meth_obj)
+ if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or
+ comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '')
+ meth_obj.document_self = false
+ end
+ if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or
+ comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '')
+ seq = $1
+ seq.gsub!(/^\s*\*\s*/, '')
+ meth_obj.call_seq = seq
+ end
+ end
+
+ def find_override_comment(class_name, meth_name)
+ name = Regexp.escape(meth_name)
+ if @content =~ %r{Document-method:\s+#{class_name}(?:\.|::|#)#{name}\s*?\n((?>.*?\*/))}m then
+ $1
+ elsif @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m then
+ $1
+ end
+ end
+
+ def handle_attr(var_name, attr_name, reader, writer)
+ rw = ''
+ if reader
+ #@stats.num_methods += 1
+ rw << 'R'
+ end
+ if writer
+ #@stats.num_methods += 1
+ rw << 'W'
+ end
+
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ if class_obj
+ comment = find_attr_comment(attr_name)
+ unless comment.empty?
+ comment = mangle_comment(comment)
+ end
+ att = RDoc::Attr.new '', attr_name, rw, comment
+ class_obj.add_attribute(att)
+ end
+ end
+
+ def handle_class_module(var_name, class_mod, class_name, parent, in_module)
+ parent_name = @known_classes[parent] || parent
+
+ if in_module
+ enclosure = @classes[in_module] || @@enclosure_classes[in_module]
+ unless enclosure
+ if enclosure = @known_classes[in_module]
+ handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
+ enclosure, nil, nil)
+ enclosure = @classes[in_module]
+ end
+ end
+ unless enclosure
+ warn("Enclosing class/module '#{in_module}' for " +
+ "#{class_mod} #{class_name} not known")
+ return
+ end
+ else
+ enclosure = @top_level
+ end
+
+ if class_mod == "class" then
+ full_name = enclosure.full_name.to_s + "::#{class_name}"
+ if @content =~ %r{Document-class:\s+#{full_name}\s*<\s+([:,\w]+)} then
+ parent_name = $1
+ end
+ cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
+ @stats.add_class cm
+ else
+ cm = enclosure.add_module RDoc::NormalModule, class_name
+ @stats.add_module cm
+ end
+
+ cm.record_location(enclosure.toplevel)
+
+ find_class_comment(cm.full_name, cm)
+ @classes[var_name] = cm
+ @@enclosure_classes[var_name] = cm
+ @known_classes[var_name] = cm.full_name
+ end
+
+ ##
+ # Adds constant comments. By providing some_value: at the start ofthe
+ # comment you can override the C value of the comment to give a friendly
+ # definition.
+ #
+ # /* 300: The perfect score in bowling */
+ # rb_define_const(cFoo, "PERFECT", INT2FIX(300);
+ #
+ # Will override +INT2FIX(300)+ with the value +300+ in the output RDoc.
+ # Values may include quotes and escaped colons (\:).
+
+ def handle_constants(type, var_name, const_name, definition)
+ #@stats.num_constants += 1
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ unless class_obj
+ warn("Enclosing class/module '#{const_name}' for not known")
+ return
+ end
+
+ comment = find_const_comment(type, const_name)
+
+ # In the case of rb_define_const, the definition and comment are in
+ # "/* definition: comment */" form. The literal ':' and '\' characters
+ # can be escaped with a backslash.
+ if type.downcase == 'const' then
+ elements = mangle_comment(comment).split(':')
+ if elements.nil? or elements.empty? then
+ con = RDoc::Constant.new(const_name, definition,
+ mangle_comment(comment))
+ else
+ new_definition = elements[0..-2].join(':')
+ if new_definition.empty? then # Default to literal C definition
+ new_definition = definition
+ else
+ new_definition.gsub!("\:", ":")
+ new_definition.gsub!("\\", '\\')
+ end
+ new_definition.sub!(/\A(\s+)/, '')
+ new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}"
+ con = RDoc::Constant.new(const_name, new_definition,
+ mangle_comment(new_comment))
+ end
+ else
+ con = RDoc::Constant.new const_name, definition, mangle_comment(comment)
+ end
+
+ class_obj.add_constant(con)
+ end
+
+ ##
+ # Removes #ifdefs that would otherwise confuse us
+
+ def handle_ifdefs_in(body)
+ body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
+ end
+
+ def handle_method(type, var_name, meth_name, meth_body, param_count,
+ source_file = nil)
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class var_name, class_name
+
+ if class_obj then
+ if meth_name == "initialize" then
+ meth_name = "new"
+ type = "singleton_method"
+ end
+
+ meth_obj = RDoc::AnyMethod.new '', meth_name
+ meth_obj.singleton = %w[singleton_method module_function].include? type
+
+ p_count = (Integer(param_count) rescue -1)
+
+ if p_count < 0
+ meth_obj.params = "(...)"
+ elsif p_count == 0
+ meth_obj.params = "()"
+ else
+ meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")"
+ end
+
+ if source_file then
+ file_name = File.join(@file_dir, source_file)
+ body = (@@known_bodies[source_file] ||= File.read(file_name))
+ else
+ body = @content
+ end
+
+ if find_body(class_name, meth_body, meth_obj, body) and meth_obj.document_self then
+ class_obj.add_method meth_obj
+ @stats.add_method meth_obj
+ end
+ end
+ end
+
+ def handle_tab_width(body)
+ if /\t/ =~ body
+ tab_width = @options.tab_width
+ body.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
+ line
+ end .join("\n")
+ else
+ body
+ end
+ end
+
+ ##
+ # Remove the /*'s and leading asterisks from C comments
+
+ def mangle_comment(comment)
+ comment.sub!(%r{/\*+}) { " " * $&.length }
+ comment.sub!(%r{\*+/}) { " " * $&.length }
+ comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
+ comment
+ end
+
+ ##
+ # Removes lines that are commented out that might otherwise get picked up
+ # when scanning for classes and methods
+
+ def remove_commented_out_lines
+ @content.gsub!(%r{//.*rb_define_}, '//')
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub!(/\/?\*--\n(.*?)\/?\*\+\+/m, '')
+ comment.sub!(/\/?\*--\n.*/m, '')
+ end
+
+ ##
+ # Extract the classes/modules and methods from a C file and return the
+ # corresponding top-level object
+
+ def scan
+ remove_commented_out_lines
+ do_classes
+ do_constants
+ do_methods
+ do_includes
+ do_aliases
+ @top_level
+ end
+
+ def warn(msg)
+ $stderr.puts
+ $stderr.puts msg
+ $stderr.flush
+ end
+
+end
+
Deleted: MacRuby/branches/experimental/lib/rdoc/parser/f95.rb
===================================================================
--- MacRuby/trunk/lib/rdoc/parser/f95.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/parser/f95.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,1835 +0,0 @@
-require 'rdoc/parser'
-
-##
-# = Fortran95 RDoc Parser
-#
-# == Overview
-#
-# This parser parses Fortran95 files with suffixes "f90", "F90", "f95" and
-# "F95". Fortran95 files are expected to be conformed to Fortran95 standards.
-#
-# == Rules
-#
-# Fundamental rules are same as that of the Ruby parser. But comment markers
-# are '!' not '#'.
-#
-# === Correspondence between RDoc documentation and Fortran95 programs
-#
-# F95 parses main programs, modules, subroutines, functions, derived-types,
-# public variables, public constants, defined operators and defined
-# assignments. These components are described in items of RDoc documentation,
-# as follows.
-#
-# Files :: Files (same as Ruby)
-# Classes:: Modules
-# Methods:: Subroutines, functions, variables, constants, derived-types,
-# defined operators, defined assignments
-# Required files:: Files in which imported modules, external subroutines and
-# external functions are defined.
-# Included Modules:: List of imported modules
-# Attributes:: List of derived-types, List of imported modules all of whose
-# components are published again
-#
-# Components listed in 'Methods' (subroutines, functions, ...) defined in
-# modules are described in the item of 'Classes'. On the other hand,
-# components defined in main programs or as external procedures are described
-# in the item of 'Files'.
-#
-# === Components parsed by default
-#
-# By default, documentation on public components (subroutines, functions,
-# variables, constants, derived-types, defined operators, defined assignments)
-# are generated.
-#
-# With "--all" option, documentation on all components are generated (almost
-# same as the Ruby parser).
-#
-# === Information parsed automatically
-#
-# The following information is automatically parsed.
-#
-# * Types of arguments
-# * Types of variables and constants
-# * Types of variables in the derived types, and initial values
-# * NAMELISTs and types of variables in them, and initial values
-#
-# Aliases by interface statement are described in the item of 'Methods'.
-#
-# Components which are imported from other modules and published again are
-# described in the item of 'Methods'.
-#
-# === Format of comment blocks
-#
-# Comment blocks should be written as follows.
-#
-# Comment blocks are considered to be ended when the line without '!' appears.
-#
-# The indentation is not necessary.
-#
-# ! (Top of file)
-# !
-# ! Comment blocks for the files.
-# !
-# !--
-# ! The comment described in the part enclosed by
-# ! "!--" and "!++" is ignored.
-# !++
-# !
-# module hogehoge
-# !
-# ! Comment blocks for the modules (or the programs).
-# !
-#
-# private
-#
-# logical :: a ! a private variable
-# real, public :: b ! a public variable
-# integer, parameter :: c = 0 ! a public constant
-#
-# public :: c
-# public :: MULTI_ARRAY
-# public :: hoge, foo
-#
-# type MULTI_ARRAY
-# !
-# ! Comment blocks for the derived-types.
-# !
-# real, pointer :: var(:) =>null() ! Comments block for the variables.
-# integer :: num = 0
-# end type MULTI_ARRAY
-#
-# contains
-#
-# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored.
-# & out )
-# !
-# ! Comment blocks for the subroutines or functions
-# !
-# character(*),intent(in):: in ! Comment blocks for the arguments.
-# character(*),intent(out),allocatable,target :: in
-# ! Comment blocks can be
-# ! written under Fortran statements.
-#
-# character(32) :: file ! This comment parsed as a variable in below NAMELIST.
-# integer :: id
-#
-# namelist /varinfo_nml/ file, id
-# !
-# ! Comment blocks for the NAMELISTs.
-# ! Information about variables are described above.
-# !
-#
-# ....
-#
-# end subroutine hoge
-#
-# integer function foo( in )
-# !
-# ! This part is considered as comment block.
-#
-# ! Comment blocks under blank lines are ignored.
-# !
-# integer, intent(in):: inA ! This part is considered as comment block.
-#
-# ! This part is ignored.
-#
-# end function foo
-#
-# subroutine hide( in, &
-# & out ) !:nodoc:
-# !
-# ! If "!:nodoc:" is described at end-of-line in subroutine
-# ! statement as above, the subroutine is ignored.
-# ! This assignment can be used to modules, subroutines,
-# ! functions, variables, constants, derived-types,
-# ! defined operators, defined assignments,
-# ! list of imported modules ("use" statement).
-# !
-#
-# ....
-#
-# end subroutine hide
-#
-# end module hogehoge
-
-class RDoc::Parser::F95 < RDoc::Parser
-
- parse_files_matching(/\.((f|F)9(0|5)|F)$/)
-
- class Token
-
- NO_TEXT = "??".freeze
-
- def initialize(line_no, char_no)
- @line_no = line_no
- @char_no = char_no
- @text = NO_TEXT
- end
- # Because we're used in contexts that expect to return a token,
- # we set the text string and then return ourselves
- def set_text(text)
- @text = text
- self
- end
-
- attr_reader :line_no, :char_no, :text
-
- end
-
- @@external_aliases = []
- @@public_methods = []
-
- ##
- # "false":: Comments are below source code
- # "true" :: Comments are upper source code
-
- COMMENTS_ARE_UPPER = false
-
- ##
- # Internal alias message
-
- INTERNAL_ALIAS_MES = "Alias for"
-
- ##
- # External alias message
-
- EXTERNAL_ALIAS_MES = "The entity is"
-
- ##
- # Define code constructs
-
- def scan
- # remove private comment
- remaining_code = remove_private_comments(@content)
-
- # continuation lines are united to one line
- remaining_code = united_to_one_line(remaining_code)
-
- # semicolons are replaced to line feed
- remaining_code = semicolon_to_linefeed(remaining_code)
-
- # collect comment for file entity
- whole_comment, remaining_code = collect_first_comment(remaining_code)
- @top_level.comment = whole_comment
-
- # String "remaining_code" is converted to Array "remaining_lines"
- remaining_lines = remaining_code.split("\n")
-
- # "module" or "program" parts are parsed (new)
- #
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- module_program_trailing = ""
- module_program_name = ""
- other_block_level_depth = 0
- other_block_searching_flag = nil
- remaining_lines.collect!{|line|
- if !block_searching_flag && !other_block_searching_flag
- if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
- block_searching_flag = :module
- block_searching_lines << line
- module_program_name = $1
- module_program_trailing = find_comments($2)
- next false
- elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?\w/ && !block_start?(line)
- block_searching_flag = :program
- block_searching_lines << line
- module_program_name = $1 || ""
- module_program_trailing = find_comments($2)
- next false
-
- elsif block_start?(line)
- other_block_searching_flag = true
- next line
-
- elsif line =~ /^\s*?!\s?(.*)/
- pre_comment << line
- next line
- else
- pre_comment = []
- next line
- end
- elsif other_block_searching_flag
- other_block_level_depth += 1 if block_start?(line)
- other_block_level_depth -= 1 if block_end?(line)
- if other_block_level_depth < 0
- other_block_level_depth = 0
- other_block_searching_flag = nil
- end
- next line
- end
-
- block_searching_lines << line
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- if level_depth >= 0
- next false
- end
-
- # "module_program_code" is formatted.
- # ":nodoc:" flag is checked.
- #
- module_program_code = block_searching_lines.join("\n")
- module_program_code = remove_empty_head_lines(module_program_code)
- if module_program_trailing =~ /^:nodoc:/
- # next loop to search next block
- level_depth = 0
- block_searching_flag = false
- block_searching_lines = []
- pre_comment = []
- next false
- end
-
- # NormalClass is created, and added to @top_level
- #
- if block_searching_flag == :module
- module_name = module_program_name
- module_code = module_program_code
- module_trailing = module_program_trailing
-
- f9x_module = @top_level.add_module NormalClass, module_name
- f9x_module.record_location @top_level
-
- @stats.add_module f9x_module
-
- f9x_comment = COMMENTS_ARE_UPPER ?
- find_comments(pre_comment.join("\n")) + "\n" + module_trailing :
- module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
- f9x_module.comment = f9x_comment
- parse_program_or_module(f9x_module, module_code)
-
- TopLevel.all_files.each do |name, toplevel|
- if toplevel.include_includes?(module_name, @options.ignore_case)
- if !toplevel.include_requires?(@file_name, @options.ignore_case)
- toplevel.add_require(Require.new(@file_name, ""))
- end
- end
- toplevel.each_classmodule{|m|
- if m.include_includes?(module_name, @options.ignore_case)
- if !m.include_requires?(@file_name, @options.ignore_case)
- m.add_require(Require.new(@file_name, ""))
- end
- end
- }
- end
- elsif block_searching_flag == :program
- program_name = module_program_name
- program_code = module_program_code
- program_trailing = module_program_trailing
- # progress "p" # HACK what stats thingy does this correspond to?
- program_comment = COMMENTS_ARE_UPPER ?
- find_comments(pre_comment.join("\n")) + "\n" + program_trailing :
- program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
- program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
- + program_comment
- @top_level.comment << program_comment
- parse_program_or_module(@top_level, program_code, :private)
- end
-
- # next loop to search next block
- level_depth = 0
- block_searching_flag = false
- block_searching_lines = []
- pre_comment = []
- next false
- }
-
- remaining_lines.delete_if{ |line|
- line == false
- }
-
- # External subprograms and functions are parsed
- #
- parse_program_or_module(@top_level, remaining_lines.join("\n"),
- :public, true)
-
- @top_level
- end # End of scan
-
- private
-
- def parse_program_or_module(container, code,
- visibility=:public, external=nil)
- return unless container
- return unless code
- remaining_lines = code.split("\n")
- remaining_code = "#{code}"
-
- #
- # Parse variables before "contains" in module
- #
- level_depth = 0
- before_contains_lines = []
- before_contains_code = nil
- before_contains_flag = nil
- remaining_lines.each{ |line|
- if !before_contains_flag
- if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
- before_contains_flag = true
- end
- else
- break if line =~ /^\s*?contains\s*?(!.*?)?$/i
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- break if level_depth < 0
- before_contains_lines << line
- end
- }
- before_contains_code = before_contains_lines.join("\n")
- if before_contains_code
- before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
- before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
- end
-
- #
- # Parse global "use"
- #
- use_check_code = "#{before_contains_code}"
- cascaded_modules_list = []
- while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_list = $2 || ""
- used_trailing = $3 || ""
- next if used_trailing =~ /!:nodoc:/
- if !container.include_includes?(used_mod_name, @options.ignore_case)
- # progress "." # HACK what stats thingy does this correspond to?
- container.add_include Include.new(used_mod_name, "")
- end
- if ! (used_list =~ /\,\s*?only\s*?:/i )
- cascaded_modules_list << "\#" + used_mod_name
- end
- end
-
- #
- # Parse public and private, and store information.
- # This information is used when "add_method" and
- # "set_visibility_for" are called.
- #
- visibility_default, visibility_info =
- parse_visibility(remaining_lines.join("\n"), visibility, container)
- @@public_methods.concat visibility_info
- if visibility_default == :public
- if !cascaded_modules_list.empty?
- cascaded_modules =
- Attr.new("Cascaded Modules",
- "Imported modules all of whose components are published again",
- "",
- cascaded_modules_list.join(", "))
- container.add_attribute(cascaded_modules)
- end
- end
-
- #
- # Check rename elements
- #
- use_check_code = "#{before_contains_code}"
- while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
- used_elements.split(",").each{ |used|
- if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
- local = $1
- org = $2
- @@public_methods.collect!{ |pub_meth|
- if local == pub_meth["name"] ||
- local.upcase == pub_meth["name"].upcase &&
- @options.ignore_case
- pub_meth["name"] = org
- pub_meth["local_name"] = local
- end
- pub_meth
- }
- end
- }
- end
-
- #
- # Parse private "use"
- #
- use_check_code = remaining_lines.join("\n")
- while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
- use_check_code = $~.pre_match
- use_check_code << $~.post_match
- used_mod_name = $1.strip.chomp
- used_trailing = $3 || ""
- next if used_trailing =~ /!:nodoc:/
- if !container.include_includes?(used_mod_name, @options.ignore_case)
- # progress "." # HACK what stats thingy does this correspond to?
- container.add_include Include.new(used_mod_name, "")
- end
- end
-
- container.each_includes{ |inc|
- TopLevel.all_files.each do |name, toplevel|
- indicated_mod = toplevel.find_symbol(inc.name,
- nil, @options.ignore_case)
- if indicated_mod
- indicated_name = indicated_mod.parent.file_relative_name
- if !container.include_requires?(indicated_name, @options.ignore_case)
- container.add_require(Require.new(indicated_name, ""))
- end
- break
- end
- end
- }
-
- #
- # Parse derived-types definitions
- #
- derived_types_comment = ""
- remaining_code = remaining_lines.join("\n")
- while remaining_code =~ /^\s*?
- type[\s\,]+(public|private)?\s*?(::)?\s*?
- (\w+)\s*?(!.*?)?$
- (.*?)
- ^\s*?end\s+type.*?$
- /imx
- remaining_code = $~.pre_match
- remaining_code << $~.post_match
- typename = $3.chomp.strip
- type_elements = $5 || ""
- type_code = remove_empty_head_lines($&)
- type_trailing = find_comments($4)
- next if type_trailing =~ /^:nodoc:/
- type_visibility = $1
- type_comment = COMMENTS_ARE_UPPER ?
- find_comments($~.pre_match) + "\n" + type_trailing :
- type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
- type_element_visibility_public = true
- type_code.split("\n").each{ |line|
- if /^\s*?private\s*?$/ =~ line
- type_element_visibility_public = nil
- break
- end
- } if type_code
-
- args_comment = ""
- type_args_info = nil
-
- if @options.show_all
- args_comment = find_arguments(nil, type_code, true)
- else
- type_public_args_list = []
- type_args_info = definition_info(type_code)
- type_args_info.each{ |arg|
- arg_is_public = type_element_visibility_public
- arg_is_public = true if arg.include_attr?("public")
- arg_is_public = nil if arg.include_attr?("private")
- type_public_args_list << arg.varname if arg_is_public
- }
- args_comment = find_arguments(type_public_args_list, type_code)
- end
-
- type = AnyMethod.new("type #{typename}", typename)
- type.singleton = false
- type.params = ""
- type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
- type.comment << args_comment if args_comment
- type.comment << type_comment if type_comment
-
- @stats.add_method type
-
- container.add_method type
-
- set_visibility(container, typename, visibility_default, @@public_methods)
-
- if type_visibility
- type_visibility.gsub!(/\s/,'')
- type_visibility.gsub!(/\,/,'')
- type_visibility.gsub!(/:/,'')
- type_visibility.downcase!
- if type_visibility == "public"
- container.set_visibility_for([typename], :public)
- elsif type_visibility == "private"
- container.set_visibility_for([typename], :private)
- end
- end
-
- check_public_methods(type, container.name)
-
- if @options.show_all
- derived_types_comment << ", " unless derived_types_comment.empty?
- derived_types_comment << typename
- else
- if type.visibility == :public
- derived_types_comment << ", " unless derived_types_comment.empty?
- derived_types_comment << typename
- end
- end
-
- end
-
- if !derived_types_comment.empty?
- derived_types_table =
- Attr.new("Derived Types", "Derived_Types", "",
- derived_types_comment)
- container.add_attribute(derived_types_table)
- end
-
- #
- # move interface scope
- #
- interface_code = ""
- while remaining_code =~ /^\s*?
- interface(
- \s+\w+ |
- \s+operator\s*?\(.*?\) |
- \s+assignment\s*?\(\s*?=\s*?\)
- )?\s*?$
- (.*?)
- ^\s*?end\s+interface.*?$
- /imx
- interface_code << remove_empty_head_lines($&) + "\n"
- remaining_code = $~.pre_match
- remaining_code << $~.post_match
- end
-
- #
- # Parse global constants or variables in modules
- #
- const_var_defs = definition_info(before_contains_code)
- const_var_defs.each{|defitem|
- next if defitem.nodoc
- const_or_var_type = "Variable"
- const_or_var_progress = "v"
- if defitem.include_attr?("parameter")
- const_or_var_type = "Constant"
- const_or_var_progress = "c"
- end
- const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
- const_or_var.singleton = false
- const_or_var.params = ""
- self_comment = find_arguments([defitem.varname], before_contains_code)
- const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
- const_or_var.comment << self_comment if self_comment
-
- @stats.add_method const_or_var_progress
-
- container.add_method const_or_var
-
- set_visibility(container, defitem.varname, visibility_default, @@public_methods)
-
- if defitem.include_attr?("public")
- container.set_visibility_for([defitem.varname], :public)
- elsif defitem.include_attr?("private")
- container.set_visibility_for([defitem.varname], :private)
- end
-
- check_public_methods(const_or_var, container.name)
-
- } if const_var_defs
-
- remaining_lines = remaining_code.split("\n")
-
- # "subroutine" or "function" parts are parsed (new)
- #
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- procedure_type = ""
- contains_lines = []
- contains_flag = nil
- remaining_lines.collect!{|line|
- if !block_searching_flag
- # subroutine
- if line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix
- block_searching_flag = :subroutine
- block_searching_lines << line
-
- procedure_name = $2.chomp.strip
- procedure_params = $3 || ""
- procedure_prefix = $1 || ""
- procedure_trailing = $4 || "!"
- next false
-
- # function
- elsif line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- block_searching_flag = :function
- block_searching_lines << line
-
- procedure_prefix = $1 || ""
- procedure_type = $2 ? $2.chomp.strip : nil
- procedure_name = $8.chomp.strip
- procedure_params = $9 || ""
- procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
- procedure_trailing = $12 || "!"
- next false
- elsif line =~ /^\s*?!\s?(.*)/
- pre_comment << line
- next line
- else
- pre_comment = []
- next line
- end
- end
- contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
- block_searching_lines << line
- contains_lines << line if contains_flag
-
- level_depth += 1 if block_start?(line)
- level_depth -= 1 if block_end?(line)
- if level_depth >= 0
- next false
- end
-
- # "procedure_code" is formatted.
- # ":nodoc:" flag is checked.
- #
- procedure_code = block_searching_lines.join("\n")
- procedure_code = remove_empty_head_lines(procedure_code)
- if procedure_trailing =~ /^!:nodoc:/
- # next loop to search next block
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- procedure_type = ""
- contains_lines = []
- contains_flag = nil
- next false
- end
-
- # AnyMethod is created, and added to container
- #
- subroutine_function = nil
- if block_searching_flag == :subroutine
- subroutine_prefix = procedure_prefix
- subroutine_name = procedure_name
- subroutine_params = procedure_params
- subroutine_trailing = procedure_trailing
- subroutine_code = procedure_code
-
- subroutine_comment = COMMENTS_ARE_UPPER ?
- pre_comment.join("\n") + "\n" + subroutine_trailing :
- subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
- subroutine = AnyMethod.new("subroutine", subroutine_name)
- parse_subprogram(subroutine, subroutine_params,
- subroutine_comment, subroutine_code,
- before_contains_code, nil, subroutine_prefix)
-
- @stats.add_method subroutine
-
- container.add_method subroutine
- subroutine_function = subroutine
-
- elsif block_searching_flag == :function
- function_prefix = procedure_prefix
- function_type = procedure_type
- function_name = procedure_name
- function_params_org = procedure_params
- function_result_arg = procedure_result_arg
- function_trailing = procedure_trailing
- function_code_org = procedure_code
-
- function_comment = COMMENTS_ARE_UPPER ?
- pre_comment.join("\n") + "\n" + function_trailing :
- function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
-
- function_code = "#{function_code_org}"
- if function_type
- function_code << "\n" + function_type + " :: " + function_result_arg
- end
-
- function_params =
- function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
-
- function = AnyMethod.new("function", function_name)
- parse_subprogram(function, function_params,
- function_comment, function_code,
- before_contains_code, true, function_prefix)
-
- # Specific modification due to function
- function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
- function.params << " result(" + function_result_arg + ")"
- function.start_collecting_tokens
- function.add_token Token.new(1,1).set_text(function_code_org)
-
- @stats.add_method function
-
- container.add_method function
- subroutine_function = function
-
- end
-
- # The visibility of procedure is specified
- #
- set_visibility(container, procedure_name,
- visibility_default, @@public_methods)
-
- # The alias for this procedure from external modules
- #
- check_external_aliases(procedure_name,
- subroutine_function.params,
- subroutine_function.comment, subroutine_function) if external
- check_public_methods(subroutine_function, container.name)
-
-
- # contains_lines are parsed as private procedures
- if contains_flag
- parse_program_or_module(container,
- contains_lines.join("\n"), :private)
- end
-
- # next loop to search next block
- level_depth = 0
- block_searching_flag = nil
- block_searching_lines = []
- pre_comment = []
- procedure_trailing = ""
- procedure_name = ""
- procedure_params = ""
- procedure_prefix = ""
- procedure_result_arg = ""
- contains_lines = []
- contains_flag = nil
- next false
- } # End of remaining_lines.collect!{|line|
-
- # Array remains_lines is converted to String remains_code again
- #
- remaining_code = remaining_lines.join("\n")
-
- #
- # Parse interface
- #
- interface_scope = false
- generic_name = ""
- interface_code.split("\n").each{ |line|
- if /^\s*?
- interface(
- \s+\w+|
- \s+operator\s*?\(.*?\)|
- \s+assignment\s*?\(\s*?=\s*?\)
- )?
- \s*?(!.*?)?$
- /ix =~ line
- generic_name = $1 ? $1.strip.chomp : nil
- interface_trailing = $2 || "!"
- interface_scope = true
- interface_scope = false if interface_trailing =~ /!:nodoc:/
-# if generic_name =~ /operator\s*?\((.*?)\)/i
-# operator_name = $1
-# if operator_name && !operator_name.empty?
-# generic_name = "#{operator_name}"
-# end
-# end
-# if generic_name =~ /assignment\s*?\((.*?)\)/i
-# assignment_name = $1
-# if assignment_name && !assignment_name.empty?
-# generic_name = "#{assignment_name}"
-# end
-# end
- end
- if /^\s*?end\s+interface/i =~ line
- interface_scope = false
- generic_name = nil
- end
- # internal alias
- if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
- procedures = $1.strip.chomp
- procedures_trailing = $2 || "!"
- next if procedures_trailing =~ /!:nodoc:/
- procedures.split(",").each{ |proc|
- proc.strip!
- proc.chomp!
- next if generic_name == proc || !generic_name
- old_meth = container.find_symbol(proc, nil, @options.ignore_case)
- next if !old_meth
- nolink = old_meth.visibility == :private ? true : nil
- nolink = nil if @options.show_all
- new_meth =
- initialize_external_method(generic_name, proc,
- old_meth.params, nil,
- old_meth.comment,
- old_meth.clone.token_stream[0].text,
- true, nolink)
- new_meth.singleton = old_meth.singleton
-
- @stats.add_method new_meth
-
- container.add_method new_meth
-
- set_visibility(container, generic_name, visibility_default, @@public_methods)
-
- check_public_methods(new_meth, container.name)
-
- }
- end
-
- # external aliases
- if interface_scope
- # subroutine
- proc = nil
- params = nil
- procedures_trailing = nil
- if line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix
- proc = $2.chomp.strip
- generic_name = proc unless generic_name
- params = $3 || ""
- procedures_trailing = $4 || "!"
-
- # function
- elsif line =~ /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- proc = $8.chomp.strip
- generic_name = proc unless generic_name
- params = $9 || ""
- procedures_trailing = $12 || "!"
- else
- next
- end
- next if procedures_trailing =~ /!:nodoc:/
- indicated_method = nil
- indicated_file = nil
- TopLevel.all_files.each do |name, toplevel|
- indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
- indicated_file = name
- break if indicated_method
- end
-
- if indicated_method
- external_method =
- initialize_external_method(generic_name, proc,
- indicated_method.params,
- indicated_file,
- indicated_method.comment)
-
- @stats.add_method external_method
-
- container.add_method external_method
- set_visibility(container, generic_name, visibility_default, @@public_methods)
- if !container.include_requires?(indicated_file, @options.ignore_case)
- container.add_require(Require.new(indicated_file, ""))
- end
- check_public_methods(external_method, container.name)
-
- else
- @@external_aliases << {
- "new_name" => generic_name,
- "old_name" => proc,
- "file_or_module" => container,
- "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
- }
- end
- end
-
- } if interface_code # End of interface_code.split("\n").each ...
-
- #
- # Already imported methods are removed from @@public_methods.
- # Remainders are assumed to be imported from other modules.
- #
- @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
-
- @@public_methods.each{ |pub_meth|
- next unless pub_meth["file_or_module"].name == container.name
- pub_meth["used_modules"].each{ |used_mod|
- TopLevel.all_classes_and_modules.each{ |modules|
- if modules.name == used_mod ||
- modules.name.upcase == used_mod.upcase &&
- @options.ignore_case
- modules.method_list.each{ |meth|
- if meth.name == pub_meth["name"] ||
- meth.name.upcase == pub_meth["name"].upcase &&
- @options.ignore_case
- new_meth = initialize_public_method(meth,
- modules.name)
- if pub_meth["local_name"]
- new_meth.name = pub_meth["local_name"]
- end
-
- @stats.add_method new_meth
-
- container.add_method new_meth
- end
- }
- end
- }
- }
- }
-
- container
- end # End of parse_program_or_module
-
- ##
- # Parse arguments, comment, code of subroutine and function. Return
- # AnyMethod object.
-
- def parse_subprogram(subprogram, params, comment, code,
- before_contains=nil, function=nil, prefix=nil)
- subprogram.singleton = false
- prefix = "" if !prefix
- arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
- args_comment, params_opt =
- find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
- nil, nil, true)
- params_opt = "( " + params_opt + " ) " if params_opt
- subprogram.params = params_opt || ""
- namelist_comment = find_namelists(code, before_contains)
-
- block_comment = find_comments comment
- if function
- subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
- else
- subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
- end
- subprogram.comment << args_comment if args_comment
- subprogram.comment << block_comment if block_comment
- subprogram.comment << namelist_comment if namelist_comment
-
- # For output source code
- subprogram.start_collecting_tokens
- subprogram.add_token Token.new(1,1).set_text(code)
-
- subprogram
- end
-
- ##
- # Collect comment for file entity
-
- def collect_first_comment(body)
- comment = ""
- not_comment = ""
- comment_start = false
- comment_end = false
- body.split("\n").each{ |line|
- if comment_end
- not_comment << line
- not_comment << "\n"
- elsif /^\s*?!\s?(.*)$/i =~ line
- comment_start = true
- comment << $1
- comment << "\n"
- elsif /^\s*?$/i =~ line
- comment_end = true if comment_start && COMMENTS_ARE_UPPER
- else
- comment_end = true
- not_comment << line
- not_comment << "\n"
- end
- }
- return comment, not_comment
- end
-
-
- ##
- # Return comments of definitions of arguments
- #
- # If "all" argument is true, information of all arguments are returned.
- #
- # If "modified_params" is true, list of arguments are decorated, for
- # example, optional arguments are parenthetic as "[arg]".
-
- def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
- return unless args || all
- indent = "" unless indent
- args = ["all"] if all
- params = "" if modified_params
- comma = ""
- return unless text
- args_rdocforms = "\n"
- remaining_lines = "#{text}"
- definitions = definition_info(remaining_lines)
- args.each{ |arg|
- arg.strip!
- arg.chomp!
- definitions.each { |defitem|
- if arg == defitem.varname.strip.chomp || all
- args_rdocforms << <<-"EOF"
-
-#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> ::
-#{indent} <tt>#{defitem.types.chomp.strip}</tt>
-EOF
- if !defitem.comment.chomp.strip.empty?
- comment = ""
- defitem.comment.split("\n").each{ |line|
- comment << " " + line + "\n"
- }
- args_rdocforms << <<-"EOF"
-
-#{indent} <tt></tt> ::
-#{indent} <tt></tt>
-#{indent} #{comment.chomp.strip}
-EOF
- end
-
- if modified_params
- if defitem.include_attr?("optional")
- params << "#{comma}[#{arg}]"
- else
- params << "#{comma}#{arg}"
- end
- comma = ", "
- end
- end
- }
- }
- if modified_params
- return args_rdocforms, params
- else
- return args_rdocforms
- end
- end
-
- ##
- # Return comments of definitions of namelists
-
- def find_namelists(text, before_contains=nil)
- return nil if !text
- result = ""
- lines = "#{text}"
- before_contains = "" if !before_contains
- while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i
- lines = $~.post_match
- nml_comment = COMMENTS_ARE_UPPER ?
- find_comments($~.pre_match) : find_comments($~.post_match)
- nml_name = $1
- nml_args = $2.split(",")
- result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n"
- result << nml_comment + "\n" if nml_comment
- if lines.split("\n")[0] =~ /^\//i
- lines = "namelist " + lines
- end
- result << find_arguments(nml_args, "#{text}" + "\n" + before_contains)
- end
- return result
- end
-
- ##
- # Comments just after module or subprogram, or arguments are returned. If
- # "COMMENTS_ARE_UPPER" is true, comments just before modules or subprograms
- # are returnd
-
- def find_comments text
- return "" unless text
- lines = text.split("\n")
- lines.reverse! if COMMENTS_ARE_UPPER
- comment_block = Array.new
- lines.each do |line|
- break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
- if COMMENTS_ARE_UPPER
- comment_block.unshift line.sub(/^\s*?!\s?/,"")
- else
- comment_block.push line.sub(/^\s*?!\s?/,"")
- end
- end
- nice_lines = comment_block.join("\n").split "\n\s*?\n"
- nice_lines[0] ||= ""
- nice_lines.shift
- end
-
- ##
- # Create method for internal alias
-
- def initialize_public_method(method, parent)
- return if !method || !parent
-
- new_meth = AnyMethod.new("External Alias for module", method.name)
- new_meth.singleton = method.singleton
- new_meth.params = method.params.clone
- new_meth.comment = remove_trailing_alias(method.comment.clone)
- new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
-
- return new_meth
- end
-
- ##
- # Create method for external alias
- #
- # If argument "internal" is true, file is ignored.
-
- def initialize_external_method(new, old, params, file, comment, token=nil,
- internal=nil, nolink=nil)
- return nil unless new || old
-
- if internal
- external_alias_header = "#{INTERNAL_ALIAS_MES} "
- external_alias_text = external_alias_header + old
- elsif file
- external_alias_header = "#{EXTERNAL_ALIAS_MES} "
- external_alias_text = external_alias_header + file + "#" + old
- else
- return nil
- end
- external_meth = AnyMethod.new(external_alias_text, new)
- external_meth.singleton = false
- external_meth.params = params
- external_comment = remove_trailing_alias(comment) + "\n\n" if comment
- external_meth.comment = external_comment || ""
- if nolink && token
- external_meth.start_collecting_tokens
- external_meth.add_token Token.new(1,1).set_text(token)
- else
- external_meth.comment << external_alias_text
- end
-
- return external_meth
- end
-
- ##
- # Parse visibility
-
- def parse_visibility(code, default, container)
- result = []
- visibility_default = default || :public
-
- used_modules = []
- container.includes.each{|i| used_modules << i.name} if container
-
- remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
- remaining_code.split("\n").each{ |line|
- if /^\s*?private\s*?$/ =~ line
- visibility_default = :private
- break
- end
- } if remaining_code
-
- remaining_code.split("\n").each{ |line|
- if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
- methods = $2.sub(/!.*$/, '')
- methods.split(",").each{ |meth|
- meth.sub!(/!.*$/, '')
- meth.gsub!(/:/, '')
- result << {
- "name" => meth.chomp.strip,
- "visibility" => :private,
- "used_modules" => used_modules.clone,
- "file_or_module" => container,
- "entity_is_discovered" => nil,
- "local_name" => nil
- }
- }
- elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
- methods = $2.sub(/!.*$/, '')
- methods.split(",").each{ |meth|
- meth.sub!(/!.*$/, '')
- meth.gsub!(/:/, '')
- result << {
- "name" => meth.chomp.strip,
- "visibility" => :public,
- "used_modules" => used_modules.clone,
- "file_or_module" => container,
- "entity_is_discovered" => nil,
- "local_name" => nil
- }
- }
- end
- } if remaining_code
-
- if container
- result.each{ |vis_info|
- vis_info["parent"] = container.name
- }
- end
-
- return visibility_default, result
- end
-
- ##
- # Set visibility
- #
- # "subname" element of "visibility_info" is deleted.
-
- def set_visibility(container, subname, visibility_default, visibility_info)
- return unless container || subname || visibility_default || visibility_info
- not_found = true
- visibility_info.collect!{ |info|
- if info["name"] == subname ||
- @options.ignore_case && info["name"].upcase == subname.upcase
- if info["file_or_module"].name == container.name
- container.set_visibility_for([subname], info["visibility"])
- info["entity_is_discovered"] = true
- not_found = false
- end
- end
- info
- }
- if not_found
- return container.set_visibility_for([subname], visibility_default)
- else
- return container
- end
- end
-
- ##
- # Find visibility
-
- def find_visibility(container, subname, visibility_info)
- return nil if !subname || !visibility_info
- visibility_info.each{ |info|
- if info["name"] == subname ||
- @options.ignore_case && info["name"].upcase == subname.upcase
- if info["parent"] == container.name
- return info["visibility"]
- end
- end
- }
- return nil
- end
-
- ##
- # Check external aliases
-
- def check_external_aliases(subname, params, comment, test=nil)
- @@external_aliases.each{ |alias_item|
- if subname == alias_item["old_name"] ||
- subname.upcase == alias_item["old_name"].upcase &&
- @options.ignore_case
-
- new_meth = initialize_external_method(alias_item["new_name"],
- subname, params, @file_name,
- comment)
- new_meth.visibility = alias_item["visibility"]
-
- @stats.add_method new_meth
-
- alias_item["file_or_module"].add_method(new_meth)
-
- if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case)
- alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
- end
- end
- }
- end
-
- ##
- # Check public_methods
-
- def check_public_methods(method, parent)
- return if !method || !parent
- @@public_methods.each{ |alias_item|
- parent_is_used_module = nil
- alias_item["used_modules"].each{ |used_module|
- if used_module == parent ||
- used_module.upcase == parent.upcase &&
- @options.ignore_case
- parent_is_used_module = true
- end
- }
- next if !parent_is_used_module
-
- if method.name == alias_item["name"] ||
- method.name.upcase == alias_item["name"].upcase &&
- @options.ignore_case
-
- new_meth = initialize_public_method(method, parent)
- if alias_item["local_name"]
- new_meth.name = alias_item["local_name"]
- end
-
- @stats.add_method new_meth
-
- alias_item["file_or_module"].add_method new_meth
- end
- }
- end
-
- ##
- # Continuous lines are united.
- #
- # Comments in continuous lines are removed.
-
- def united_to_one_line(f90src)
- return "" unless f90src
- lines = f90src.split("\n")
- previous_continuing = false
- now_continuing = false
- body = ""
- lines.each{ |line|
- words = line.split("")
- next if words.empty? && previous_continuing
- commentout = false
- brank_flag = true ; brank_char = ""
- squote = false ; dquote = false
- ignore = false
- words.collect! { |char|
- if previous_continuing && brank_flag
- now_continuing = true
- ignore = true
- case char
- when "!" ; break
- when " " ; brank_char << char ; next ""
- when "&"
- brank_flag = false
- now_continuing = false
- next ""
- else
- brank_flag = false
- now_continuing = false
- ignore = false
- next brank_char + char
- end
- end
- ignore = false
-
- if now_continuing
- next ""
- elsif !(squote) && !(dquote) && !(commentout)
- case char
- when "!" ; commentout = true ; next char
- when "\""; dquote = true ; next char
- when "\'"; squote = true ; next char
- when "&" ; now_continuing = true ; next ""
- else next char
- end
- elsif commentout
- next char
- elsif squote
- case char
- when "\'"; squote = false ; next char
- else next char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; next char
- else next char
- end
- end
- }
- if !ignore && !previous_continuing || !brank_flag
- if previous_continuing
- body << words.join("")
- else
- body << "\n" + words.join("")
- end
- end
- previous_continuing = now_continuing ? true : nil
- now_continuing = nil
- }
- return body
- end
-
-
- ##
- # Continuous line checker
-
- def continuous_line?(line)
- continuous = false
- if /&\s*?(!.*)?$/ =~ line
- continuous = true
- if comment_out?($~.pre_match)
- continuous = false
- end
- end
- return continuous
- end
-
- ##
- # Comment out checker
-
- def comment_out?(line)
- return nil unless line
- commentout = false
- squote = false ; dquote = false
- line.split("").each { |char|
- if !(squote) && !(dquote)
- case char
- when "!" ; commentout = true ; break
- when "\""; dquote = true
- when "\'"; squote = true
- else next
- end
- elsif squote
- case char
- when "\'"; squote = false
- else next
- end
- elsif dquote
- case char
- when "\""; dquote = false
- else next
- end
- end
- }
- return commentout
- end
-
- ##
- # Semicolons are replaced to line feed.
-
- def semicolon_to_linefeed(text)
- return "" unless text
- lines = text.split("\n")
- lines.collect!{ |line|
- words = line.split("")
- commentout = false
- squote = false ; dquote = false
- words.collect! { |char|
- if !(squote) && !(dquote) && !(commentout)
- case char
- when "!" ; commentout = true ; next char
- when "\""; dquote = true ; next char
- when "\'"; squote = true ; next char
- when ";" ; "\n"
- else next char
- end
- elsif commentout
- next char
- elsif squote
- case char
- when "\'"; squote = false ; next char
- else next char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; next char
- else next char
- end
- end
- }
- words.join("")
- }
- return lines.join("\n")
- end
-
- ##
- # Which "line" is start of block (module, program, block data, subroutine,
- # function) statement ?
-
- def block_start?(line)
- return nil if !line
-
- if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
- line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ \
- /^\s*?
- (recursive|pure|elemental)?\s*?
- subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
- /ix ||
- line =~ \
- /^\s*?
- (recursive|pure|elemental)?\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | type\s*?\([\w\s]+?\)\s+
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | double\s+precision\s+
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
- )?
- function\s+(\w+)\s*?
- (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
- /ix
- return true
- end
-
- return nil
- end
-
- ##
- # Which "line" is end of block (module, program, block data, subroutine,
- # function) statement ?
-
- def block_end?(line)
- return nil if !line
-
- if line =~ /^\s*?end\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i ||
- line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
- return true
- end
-
- return nil
- end
-
- ##
- # Remove "Alias for" in end of comments
-
- def remove_trailing_alias(text)
- return "" if !text
- lines = text.split("\n").reverse
- comment_block = Array.new
- checked = false
- lines.each do |line|
- if !checked
- if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
- /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
- checked = true
- next
- end
- end
- comment_block.unshift line
- end
- nice_lines = comment_block.join("\n")
- nice_lines ||= ""
- return nice_lines
- end
-
- ##
- # Empty lines in header are removed
-
- def remove_empty_head_lines(text)
- return "" unless text
- lines = text.split("\n")
- header = true
- lines.delete_if{ |line|
- header = false if /\S/ =~ line
- header && /^\s*?$/ =~ line
- }
- lines.join("\n")
- end
-
- ##
- # header marker "=", "==", ... are removed
-
- def remove_header_marker(text)
- return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
- end
-
- def remove_private_comments(body)
- body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '')
- return body
- end
-
- ##
- # Information of arguments of subroutines and functions in Fortran95
-
- class Fortran95Definition
-
- # Name of variable
- #
- attr_reader :varname
-
- # Types of variable
- #
- attr_reader :types
-
- # Initial Value
- #
- attr_reader :inivalue
-
- # Suffix of array
- #
- attr_reader :arraysuffix
-
- # Comments
- #
- attr_accessor :comment
-
- # Flag of non documentation
- #
- attr_accessor :nodoc
-
- def initialize(varname, types, inivalue, arraysuffix, comment,
- nodoc=false)
- @varname = varname
- @types = types
- @inivalue = inivalue
- @arraysuffix = arraysuffix
- @comment = comment
- @nodoc = nodoc
- end
-
- def to_s
- return <<-EOF
-<Fortran95Definition:
-varname=#{@varname}, types=#{types},
-inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc},
-comment=
-#{@comment}
->
-EOF
- end
-
- #
- # If attr is included, true is returned
- #
- def include_attr?(attr)
- return if !attr
- @types.split(",").each{ |type|
- return true if type.strip.chomp.upcase == attr.strip.chomp.upcase
- }
- return nil
- end
-
- end # End of Fortran95Definition
-
- ##
- # Parse string argument "text", and Return Array of Fortran95Definition
- # object
-
- def definition_info(text)
- return nil unless text
- lines = "#{text}"
- defs = Array.new
- comment = ""
- trailing_comment = ""
- under_comment_valid = false
- lines.split("\n").each{ |line|
- if /^\s*?!\s?(.*)/ =~ line
- if COMMENTS_ARE_UPPER
- comment << remove_header_marker($1)
- comment << "\n"
- elsif defs[-1] && under_comment_valid
- defs[-1].comment << "\n"
- defs[-1].comment << remove_header_marker($1)
- end
- next
- elsif /^\s*?$/ =~ line
- comment = ""
- under_comment_valid = false
- next
- end
- type = ""
- characters = ""
- if line =~ /^\s*?
- (
- character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | type\s*?\([\w\s]+?\)[\s\,]*
- | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | double\s+precision[\s\,]*
- | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
- )
- (.*?::)?
- (.+)$
- /ix
- characters = $8
- type = $1
- type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
- else
- under_comment_valid = false
- next
- end
- squote = false ; dquote = false ; bracket = 0
- iniflag = false; commentflag = false
- varname = "" ; arraysuffix = "" ; inivalue = ""
- start_pos = defs.size
- characters.split("").each { |char|
- if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
- case char
- when "!" ; commentflag = true
- when "(" ; bracket += 1 ; arraysuffix = char
- when "\""; dquote = true
- when "\'"; squote = true
- when "=" ; iniflag = true ; inivalue << char
- when ","
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- varname = "" ; arraysuffix = "" ; inivalue = ""
- under_comment_valid = true
- when " " ; next
- else ; varname << char
- end
- elsif commentflag
- comment << remove_header_marker(char)
- trailing_comment << remove_header_marker(char)
- elsif iniflag
- if dquote
- case char
- when "\"" ; dquote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif squote
- case char
- when "\'" ; squote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif bracket > 0
- case char
- when "(" ; bracket += 1 ; inivalue << char
- when ")" ; bracket -= 1 ; inivalue << char
- else ; inivalue << char
- end
- else
- case char
- when ","
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- varname = "" ; arraysuffix = "" ; inivalue = ""
- iniflag = false
- under_comment_valid = true
- when "(" ; bracket += 1 ; inivalue << char
- when "\""; dquote = true ; inivalue << char
- when "\'"; squote = true ; inivalue << char
- when "!" ; commentflag = true
- else ; inivalue << char
- end
- end
- elsif !(squote) && !(dquote) && bracket > 0
- case char
- when "(" ; bracket += 1 ; arraysuffix << char
- when ")" ; bracket -= 1 ; arraysuffix << char
- else ; arraysuffix << char
- end
- elsif squote
- case char
- when "\'"; squote = false ; inivalue << char
- else ; inivalue << char
- end
- elsif dquote
- case char
- when "\""; dquote = false ; inivalue << char
- else ; inivalue << char
- end
- end
- }
- defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
- if trailing_comment =~ /^:nodoc:/
- defs[start_pos..-1].collect!{ |defitem|
- defitem.nodoc = true
- }
- end
- varname = "" ; arraysuffix = "" ; inivalue = ""
- comment = ""
- under_comment_valid = true
- trailing_comment = ""
- }
- return defs
- end
-
-end
-
Copied: MacRuby/branches/experimental/lib/rdoc/parser/f95.rb (from rev 1886, MacRuby/trunk/lib/rdoc/parser/f95.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/parser/f95.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/parser/f95.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,1835 @@
+require 'rdoc/parser'
+
+##
+# = Fortran95 RDoc Parser
+#
+# == Overview
+#
+# This parser parses Fortran95 files with suffixes "f90", "F90", "f95" and
+# "F95". Fortran95 files are expected to be conformed to Fortran95 standards.
+#
+# == Rules
+#
+# Fundamental rules are same as that of the Ruby parser. But comment markers
+# are '!' not '#'.
+#
+# === Correspondence between RDoc documentation and Fortran95 programs
+#
+# F95 parses main programs, modules, subroutines, functions, derived-types,
+# public variables, public constants, defined operators and defined
+# assignments. These components are described in items of RDoc documentation,
+# as follows.
+#
+# Files :: Files (same as Ruby)
+# Classes:: Modules
+# Methods:: Subroutines, functions, variables, constants, derived-types,
+# defined operators, defined assignments
+# Required files:: Files in which imported modules, external subroutines and
+# external functions are defined.
+# Included Modules:: List of imported modules
+# Attributes:: List of derived-types, List of imported modules all of whose
+# components are published again
+#
+# Components listed in 'Methods' (subroutines, functions, ...) defined in
+# modules are described in the item of 'Classes'. On the other hand,
+# components defined in main programs or as external procedures are described
+# in the item of 'Files'.
+#
+# === Components parsed by default
+#
+# By default, documentation on public components (subroutines, functions,
+# variables, constants, derived-types, defined operators, defined assignments)
+# are generated.
+#
+# With "--all" option, documentation on all components are generated (almost
+# same as the Ruby parser).
+#
+# === Information parsed automatically
+#
+# The following information is automatically parsed.
+#
+# * Types of arguments
+# * Types of variables and constants
+# * Types of variables in the derived types, and initial values
+# * NAMELISTs and types of variables in them, and initial values
+#
+# Aliases by interface statement are described in the item of 'Methods'.
+#
+# Components which are imported from other modules and published again are
+# described in the item of 'Methods'.
+#
+# === Format of comment blocks
+#
+# Comment blocks should be written as follows.
+#
+# Comment blocks are considered to be ended when the line without '!' appears.
+#
+# The indentation is not necessary.
+#
+# ! (Top of file)
+# !
+# ! Comment blocks for the files.
+# !
+# !--
+# ! The comment described in the part enclosed by
+# ! "!--" and "!++" is ignored.
+# !++
+# !
+# module hogehoge
+# !
+# ! Comment blocks for the modules (or the programs).
+# !
+#
+# private
+#
+# logical :: a ! a private variable
+# real, public :: b ! a public variable
+# integer, parameter :: c = 0 ! a public constant
+#
+# public :: c
+# public :: MULTI_ARRAY
+# public :: hoge, foo
+#
+# type MULTI_ARRAY
+# !
+# ! Comment blocks for the derived-types.
+# !
+# real, pointer :: var(:) =>null() ! Comments block for the variables.
+# integer :: num = 0
+# end type MULTI_ARRAY
+#
+# contains
+#
+# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored.
+# & out )
+# !
+# ! Comment blocks for the subroutines or functions
+# !
+# character(*),intent(in):: in ! Comment blocks for the arguments.
+# character(*),intent(out),allocatable,target :: in
+# ! Comment blocks can be
+# ! written under Fortran statements.
+#
+# character(32) :: file ! This comment parsed as a variable in below NAMELIST.
+# integer :: id
+#
+# namelist /varinfo_nml/ file, id
+# !
+# ! Comment blocks for the NAMELISTs.
+# ! Information about variables are described above.
+# !
+#
+# ....
+#
+# end subroutine hoge
+#
+# integer function foo( in )
+# !
+# ! This part is considered as comment block.
+#
+# ! Comment blocks under blank lines are ignored.
+# !
+# integer, intent(in):: inA ! This part is considered as comment block.
+#
+# ! This part is ignored.
+#
+# end function foo
+#
+# subroutine hide( in, &
+# & out ) !:nodoc:
+# !
+# ! If "!:nodoc:" is described at end-of-line in subroutine
+# ! statement as above, the subroutine is ignored.
+# ! This assignment can be used to modules, subroutines,
+# ! functions, variables, constants, derived-types,
+# ! defined operators, defined assignments,
+# ! list of imported modules ("use" statement).
+# !
+#
+# ....
+#
+# end subroutine hide
+#
+# end module hogehoge
+
+class RDoc::Parser::F95 < RDoc::Parser
+
+ parse_files_matching(/\.((f|F)9(0|5)|F)$/)
+
+ class Token
+
+ NO_TEXT = "??".freeze
+
+ def initialize(line_no, char_no)
+ @line_no = line_no
+ @char_no = char_no
+ @text = NO_TEXT
+ end
+ # Because we're used in contexts that expect to return a token,
+ # we set the text string and then return ourselves
+ def set_text(text)
+ @text = text
+ self
+ end
+
+ attr_reader :line_no, :char_no, :text
+
+ end
+
+ @@external_aliases = []
+ @@public_methods = []
+
+ ##
+ # "false":: Comments are below source code
+ # "true" :: Comments are upper source code
+
+ COMMENTS_ARE_UPPER = false
+
+ ##
+ # Internal alias message
+
+ INTERNAL_ALIAS_MES = "Alias for"
+
+ ##
+ # External alias message
+
+ EXTERNAL_ALIAS_MES = "The entity is"
+
+ ##
+ # Define code constructs
+
+ def scan
+ # remove private comment
+ remaining_code = remove_private_comments(@content)
+
+ # continuation lines are united to one line
+ remaining_code = united_to_one_line(remaining_code)
+
+ # semicolons are replaced to line feed
+ remaining_code = semicolon_to_linefeed(remaining_code)
+
+ # collect comment for file entity
+ whole_comment, remaining_code = collect_first_comment(remaining_code)
+ @top_level.comment = whole_comment
+
+ # String "remaining_code" is converted to Array "remaining_lines"
+ remaining_lines = remaining_code.split("\n")
+
+ # "module" or "program" parts are parsed (new)
+ #
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ module_program_trailing = ""
+ module_program_name = ""
+ other_block_level_depth = 0
+ other_block_searching_flag = nil
+ remaining_lines.collect!{|line|
+ if !block_searching_flag && !other_block_searching_flag
+ if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
+ block_searching_flag = :module
+ block_searching_lines << line
+ module_program_name = $1
+ module_program_trailing = find_comments($2)
+ next false
+ elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?\w/ && !block_start?(line)
+ block_searching_flag = :program
+ block_searching_lines << line
+ module_program_name = $1 || ""
+ module_program_trailing = find_comments($2)
+ next false
+
+ elsif block_start?(line)
+ other_block_searching_flag = true
+ next line
+
+ elsif line =~ /^\s*?!\s?(.*)/
+ pre_comment << line
+ next line
+ else
+ pre_comment = []
+ next line
+ end
+ elsif other_block_searching_flag
+ other_block_level_depth += 1 if block_start?(line)
+ other_block_level_depth -= 1 if block_end?(line)
+ if other_block_level_depth < 0
+ other_block_level_depth = 0
+ other_block_searching_flag = nil
+ end
+ next line
+ end
+
+ block_searching_lines << line
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ if level_depth >= 0
+ next false
+ end
+
+ # "module_program_code" is formatted.
+ # ":nodoc:" flag is checked.
+ #
+ module_program_code = block_searching_lines.join("\n")
+ module_program_code = remove_empty_head_lines(module_program_code)
+ if module_program_trailing =~ /^:nodoc:/
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = false
+ block_searching_lines = []
+ pre_comment = []
+ next false
+ end
+
+ # NormalClass is created, and added to @top_level
+ #
+ if block_searching_flag == :module
+ module_name = module_program_name
+ module_code = module_program_code
+ module_trailing = module_program_trailing
+
+ f9x_module = @top_level.add_module NormalClass, module_name
+ f9x_module.record_location @top_level
+
+ @stats.add_module f9x_module
+
+ f9x_comment = COMMENTS_ARE_UPPER ?
+ find_comments(pre_comment.join("\n")) + "\n" + module_trailing :
+ module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
+ f9x_module.comment = f9x_comment
+ parse_program_or_module(f9x_module, module_code)
+
+ TopLevel.all_files.each do |name, toplevel|
+ if toplevel.include_includes?(module_name, @options.ignore_case)
+ if !toplevel.include_requires?(@file_name, @options.ignore_case)
+ toplevel.add_require(Require.new(@file_name, ""))
+ end
+ end
+ toplevel.each_classmodule{|m|
+ if m.include_includes?(module_name, @options.ignore_case)
+ if !m.include_requires?(@file_name, @options.ignore_case)
+ m.add_require(Require.new(@file_name, ""))
+ end
+ end
+ }
+ end
+ elsif block_searching_flag == :program
+ program_name = module_program_name
+ program_code = module_program_code
+ program_trailing = module_program_trailing
+ # progress "p" # HACK what stats thingy does this correspond to?
+ program_comment = COMMENTS_ARE_UPPER ?
+ find_comments(pre_comment.join("\n")) + "\n" + program_trailing :
+ program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
+ program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
+ + program_comment
+ @top_level.comment << program_comment
+ parse_program_or_module(@top_level, program_code, :private)
+ end
+
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = false
+ block_searching_lines = []
+ pre_comment = []
+ next false
+ }
+
+ remaining_lines.delete_if{ |line|
+ line == false
+ }
+
+ # External subprograms and functions are parsed
+ #
+ parse_program_or_module(@top_level, remaining_lines.join("\n"),
+ :public, true)
+
+ @top_level
+ end # End of scan
+
+ private
+
+ def parse_program_or_module(container, code,
+ visibility=:public, external=nil)
+ return unless container
+ return unless code
+ remaining_lines = code.split("\n")
+ remaining_code = "#{code}"
+
+ #
+ # Parse variables before "contains" in module
+ #
+ level_depth = 0
+ before_contains_lines = []
+ before_contains_code = nil
+ before_contains_flag = nil
+ remaining_lines.each{ |line|
+ if !before_contains_flag
+ if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
+ before_contains_flag = true
+ end
+ else
+ break if line =~ /^\s*?contains\s*?(!.*?)?$/i
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ break if level_depth < 0
+ before_contains_lines << line
+ end
+ }
+ before_contains_code = before_contains_lines.join("\n")
+ if before_contains_code
+ before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
+ before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
+ end
+
+ #
+ # Parse global "use"
+ #
+ use_check_code = "#{before_contains_code}"
+ cascaded_modules_list = []
+ while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_list = $2 || ""
+ used_trailing = $3 || ""
+ next if used_trailing =~ /!:nodoc:/
+ if !container.include_includes?(used_mod_name, @options.ignore_case)
+ # progress "." # HACK what stats thingy does this correspond to?
+ container.add_include Include.new(used_mod_name, "")
+ end
+ if ! (used_list =~ /\,\s*?only\s*?:/i )
+ cascaded_modules_list << "\#" + used_mod_name
+ end
+ end
+
+ #
+ # Parse public and private, and store information.
+ # This information is used when "add_method" and
+ # "set_visibility_for" are called.
+ #
+ visibility_default, visibility_info =
+ parse_visibility(remaining_lines.join("\n"), visibility, container)
+ @@public_methods.concat visibility_info
+ if visibility_default == :public
+ if !cascaded_modules_list.empty?
+ cascaded_modules =
+ Attr.new("Cascaded Modules",
+ "Imported modules all of whose components are published again",
+ "",
+ cascaded_modules_list.join(", "))
+ container.add_attribute(cascaded_modules)
+ end
+ end
+
+ #
+ # Check rename elements
+ #
+ use_check_code = "#{before_contains_code}"
+ while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
+ used_elements.split(",").each{ |used|
+ if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
+ local = $1
+ org = $2
+ @@public_methods.collect!{ |pub_meth|
+ if local == pub_meth["name"] ||
+ local.upcase == pub_meth["name"].upcase &&
+ @options.ignore_case
+ pub_meth["name"] = org
+ pub_meth["local_name"] = local
+ end
+ pub_meth
+ }
+ end
+ }
+ end
+
+ #
+ # Parse private "use"
+ #
+ use_check_code = remaining_lines.join("\n")
+ while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_trailing = $3 || ""
+ next if used_trailing =~ /!:nodoc:/
+ if !container.include_includes?(used_mod_name, @options.ignore_case)
+ # progress "." # HACK what stats thingy does this correspond to?
+ container.add_include Include.new(used_mod_name, "")
+ end
+ end
+
+ container.each_includes{ |inc|
+ TopLevel.all_files.each do |name, toplevel|
+ indicated_mod = toplevel.find_symbol(inc.name,
+ nil, @options.ignore_case)
+ if indicated_mod
+ indicated_name = indicated_mod.parent.file_relative_name
+ if !container.include_requires?(indicated_name, @options.ignore_case)
+ container.add_require(Require.new(indicated_name, ""))
+ end
+ break
+ end
+ end
+ }
+
+ #
+ # Parse derived-types definitions
+ #
+ derived_types_comment = ""
+ remaining_code = remaining_lines.join("\n")
+ while remaining_code =~ /^\s*?
+ type[\s\,]+(public|private)?\s*?(::)?\s*?
+ (\w+)\s*?(!.*?)?$
+ (.*?)
+ ^\s*?end\s+type.*?$
+ /imx
+ remaining_code = $~.pre_match
+ remaining_code << $~.post_match
+ typename = $3.chomp.strip
+ type_elements = $5 || ""
+ type_code = remove_empty_head_lines($&)
+ type_trailing = find_comments($4)
+ next if type_trailing =~ /^:nodoc:/
+ type_visibility = $1
+ type_comment = COMMENTS_ARE_UPPER ?
+ find_comments($~.pre_match) + "\n" + type_trailing :
+ type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
+ type_element_visibility_public = true
+ type_code.split("\n").each{ |line|
+ if /^\s*?private\s*?$/ =~ line
+ type_element_visibility_public = nil
+ break
+ end
+ } if type_code
+
+ args_comment = ""
+ type_args_info = nil
+
+ if @options.show_all
+ args_comment = find_arguments(nil, type_code, true)
+ else
+ type_public_args_list = []
+ type_args_info = definition_info(type_code)
+ type_args_info.each{ |arg|
+ arg_is_public = type_element_visibility_public
+ arg_is_public = true if arg.include_attr?("public")
+ arg_is_public = nil if arg.include_attr?("private")
+ type_public_args_list << arg.varname if arg_is_public
+ }
+ args_comment = find_arguments(type_public_args_list, type_code)
+ end
+
+ type = AnyMethod.new("type #{typename}", typename)
+ type.singleton = false
+ type.params = ""
+ type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
+ type.comment << args_comment if args_comment
+ type.comment << type_comment if type_comment
+
+ @stats.add_method type
+
+ container.add_method type
+
+ set_visibility(container, typename, visibility_default, @@public_methods)
+
+ if type_visibility
+ type_visibility.gsub!(/\s/,'')
+ type_visibility.gsub!(/\,/,'')
+ type_visibility.gsub!(/:/,'')
+ type_visibility.downcase!
+ if type_visibility == "public"
+ container.set_visibility_for([typename], :public)
+ elsif type_visibility == "private"
+ container.set_visibility_for([typename], :private)
+ end
+ end
+
+ check_public_methods(type, container.name)
+
+ if @options.show_all
+ derived_types_comment << ", " unless derived_types_comment.empty?
+ derived_types_comment << typename
+ else
+ if type.visibility == :public
+ derived_types_comment << ", " unless derived_types_comment.empty?
+ derived_types_comment << typename
+ end
+ end
+
+ end
+
+ if !derived_types_comment.empty?
+ derived_types_table =
+ Attr.new("Derived Types", "Derived_Types", "",
+ derived_types_comment)
+ container.add_attribute(derived_types_table)
+ end
+
+ #
+ # move interface scope
+ #
+ interface_code = ""
+ while remaining_code =~ /^\s*?
+ interface(
+ \s+\w+ |
+ \s+operator\s*?\(.*?\) |
+ \s+assignment\s*?\(\s*?=\s*?\)
+ )?\s*?$
+ (.*?)
+ ^\s*?end\s+interface.*?$
+ /imx
+ interface_code << remove_empty_head_lines($&) + "\n"
+ remaining_code = $~.pre_match
+ remaining_code << $~.post_match
+ end
+
+ #
+ # Parse global constants or variables in modules
+ #
+ const_var_defs = definition_info(before_contains_code)
+ const_var_defs.each{|defitem|
+ next if defitem.nodoc
+ const_or_var_type = "Variable"
+ const_or_var_progress = "v"
+ if defitem.include_attr?("parameter")
+ const_or_var_type = "Constant"
+ const_or_var_progress = "c"
+ end
+ const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
+ const_or_var.singleton = false
+ const_or_var.params = ""
+ self_comment = find_arguments([defitem.varname], before_contains_code)
+ const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
+ const_or_var.comment << self_comment if self_comment
+
+ @stats.add_method const_or_var_progress
+
+ container.add_method const_or_var
+
+ set_visibility(container, defitem.varname, visibility_default, @@public_methods)
+
+ if defitem.include_attr?("public")
+ container.set_visibility_for([defitem.varname], :public)
+ elsif defitem.include_attr?("private")
+ container.set_visibility_for([defitem.varname], :private)
+ end
+
+ check_public_methods(const_or_var, container.name)
+
+ } if const_var_defs
+
+ remaining_lines = remaining_code.split("\n")
+
+ # "subroutine" or "function" parts are parsed (new)
+ #
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ procedure_type = ""
+ contains_lines = []
+ contains_flag = nil
+ remaining_lines.collect!{|line|
+ if !block_searching_flag
+ # subroutine
+ if line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix
+ block_searching_flag = :subroutine
+ block_searching_lines << line
+
+ procedure_name = $2.chomp.strip
+ procedure_params = $3 || ""
+ procedure_prefix = $1 || ""
+ procedure_trailing = $4 || "!"
+ next false
+
+ # function
+ elsif line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ block_searching_flag = :function
+ block_searching_lines << line
+
+ procedure_prefix = $1 || ""
+ procedure_type = $2 ? $2.chomp.strip : nil
+ procedure_name = $8.chomp.strip
+ procedure_params = $9 || ""
+ procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
+ procedure_trailing = $12 || "!"
+ next false
+ elsif line =~ /^\s*?!\s?(.*)/
+ pre_comment << line
+ next line
+ else
+ pre_comment = []
+ next line
+ end
+ end
+ contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
+ block_searching_lines << line
+ contains_lines << line if contains_flag
+
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ if level_depth >= 0
+ next false
+ end
+
+ # "procedure_code" is formatted.
+ # ":nodoc:" flag is checked.
+ #
+ procedure_code = block_searching_lines.join("\n")
+ procedure_code = remove_empty_head_lines(procedure_code)
+ if procedure_trailing =~ /^!:nodoc:/
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ procedure_type = ""
+ contains_lines = []
+ contains_flag = nil
+ next false
+ end
+
+ # AnyMethod is created, and added to container
+ #
+ subroutine_function = nil
+ if block_searching_flag == :subroutine
+ subroutine_prefix = procedure_prefix
+ subroutine_name = procedure_name
+ subroutine_params = procedure_params
+ subroutine_trailing = procedure_trailing
+ subroutine_code = procedure_code
+
+ subroutine_comment = COMMENTS_ARE_UPPER ?
+ pre_comment.join("\n") + "\n" + subroutine_trailing :
+ subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
+ subroutine = AnyMethod.new("subroutine", subroutine_name)
+ parse_subprogram(subroutine, subroutine_params,
+ subroutine_comment, subroutine_code,
+ before_contains_code, nil, subroutine_prefix)
+
+ @stats.add_method subroutine
+
+ container.add_method subroutine
+ subroutine_function = subroutine
+
+ elsif block_searching_flag == :function
+ function_prefix = procedure_prefix
+ function_type = procedure_type
+ function_name = procedure_name
+ function_params_org = procedure_params
+ function_result_arg = procedure_result_arg
+ function_trailing = procedure_trailing
+ function_code_org = procedure_code
+
+ function_comment = COMMENTS_ARE_UPPER ?
+ pre_comment.join("\n") + "\n" + function_trailing :
+ function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
+
+ function_code = "#{function_code_org}"
+ if function_type
+ function_code << "\n" + function_type + " :: " + function_result_arg
+ end
+
+ function_params =
+ function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
+
+ function = AnyMethod.new("function", function_name)
+ parse_subprogram(function, function_params,
+ function_comment, function_code,
+ before_contains_code, true, function_prefix)
+
+ # Specific modification due to function
+ function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
+ function.params << " result(" + function_result_arg + ")"
+ function.start_collecting_tokens
+ function.add_token Token.new(1,1).set_text(function_code_org)
+
+ @stats.add_method function
+
+ container.add_method function
+ subroutine_function = function
+
+ end
+
+ # The visibility of procedure is specified
+ #
+ set_visibility(container, procedure_name,
+ visibility_default, @@public_methods)
+
+ # The alias for this procedure from external modules
+ #
+ check_external_aliases(procedure_name,
+ subroutine_function.params,
+ subroutine_function.comment, subroutine_function) if external
+ check_public_methods(subroutine_function, container.name)
+
+
+ # contains_lines are parsed as private procedures
+ if contains_flag
+ parse_program_or_module(container,
+ contains_lines.join("\n"), :private)
+ end
+
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ contains_lines = []
+ contains_flag = nil
+ next false
+ } # End of remaining_lines.collect!{|line|
+
+ # Array remains_lines is converted to String remains_code again
+ #
+ remaining_code = remaining_lines.join("\n")
+
+ #
+ # Parse interface
+ #
+ interface_scope = false
+ generic_name = ""
+ interface_code.split("\n").each{ |line|
+ if /^\s*?
+ interface(
+ \s+\w+|
+ \s+operator\s*?\(.*?\)|
+ \s+assignment\s*?\(\s*?=\s*?\)
+ )?
+ \s*?(!.*?)?$
+ /ix =~ line
+ generic_name = $1 ? $1.strip.chomp : nil
+ interface_trailing = $2 || "!"
+ interface_scope = true
+ interface_scope = false if interface_trailing =~ /!:nodoc:/
+# if generic_name =~ /operator\s*?\((.*?)\)/i
+# operator_name = $1
+# if operator_name && !operator_name.empty?
+# generic_name = "#{operator_name}"
+# end
+# end
+# if generic_name =~ /assignment\s*?\((.*?)\)/i
+# assignment_name = $1
+# if assignment_name && !assignment_name.empty?
+# generic_name = "#{assignment_name}"
+# end
+# end
+ end
+ if /^\s*?end\s+interface/i =~ line
+ interface_scope = false
+ generic_name = nil
+ end
+ # internal alias
+ if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
+ procedures = $1.strip.chomp
+ procedures_trailing = $2 || "!"
+ next if procedures_trailing =~ /!:nodoc:/
+ procedures.split(",").each{ |proc|
+ proc.strip!
+ proc.chomp!
+ next if generic_name == proc || !generic_name
+ old_meth = container.find_symbol(proc, nil, @options.ignore_case)
+ next if !old_meth
+ nolink = old_meth.visibility == :private ? true : nil
+ nolink = nil if @options.show_all
+ new_meth =
+ initialize_external_method(generic_name, proc,
+ old_meth.params, nil,
+ old_meth.comment,
+ old_meth.clone.token_stream[0].text,
+ true, nolink)
+ new_meth.singleton = old_meth.singleton
+
+ @stats.add_method new_meth
+
+ container.add_method new_meth
+
+ set_visibility(container, generic_name, visibility_default, @@public_methods)
+
+ check_public_methods(new_meth, container.name)
+
+ }
+ end
+
+ # external aliases
+ if interface_scope
+ # subroutine
+ proc = nil
+ params = nil
+ procedures_trailing = nil
+ if line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix
+ proc = $2.chomp.strip
+ generic_name = proc unless generic_name
+ params = $3 || ""
+ procedures_trailing = $4 || "!"
+
+ # function
+ elsif line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ proc = $8.chomp.strip
+ generic_name = proc unless generic_name
+ params = $9 || ""
+ procedures_trailing = $12 || "!"
+ else
+ next
+ end
+ next if procedures_trailing =~ /!:nodoc:/
+ indicated_method = nil
+ indicated_file = nil
+ TopLevel.all_files.each do |name, toplevel|
+ indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
+ indicated_file = name
+ break if indicated_method
+ end
+
+ if indicated_method
+ external_method =
+ initialize_external_method(generic_name, proc,
+ indicated_method.params,
+ indicated_file,
+ indicated_method.comment)
+
+ @stats.add_method external_method
+
+ container.add_method external_method
+ set_visibility(container, generic_name, visibility_default, @@public_methods)
+ if !container.include_requires?(indicated_file, @options.ignore_case)
+ container.add_require(Require.new(indicated_file, ""))
+ end
+ check_public_methods(external_method, container.name)
+
+ else
+ @@external_aliases << {
+ "new_name" => generic_name,
+ "old_name" => proc,
+ "file_or_module" => container,
+ "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
+ }
+ end
+ end
+
+ } if interface_code # End of interface_code.split("\n").each ...
+
+ #
+ # Already imported methods are removed from @@public_methods.
+ # Remainders are assumed to be imported from other modules.
+ #
+ @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
+
+ @@public_methods.each{ |pub_meth|
+ next unless pub_meth["file_or_module"].name == container.name
+ pub_meth["used_modules"].each{ |used_mod|
+ TopLevel.all_classes_and_modules.each{ |modules|
+ if modules.name == used_mod ||
+ modules.name.upcase == used_mod.upcase &&
+ @options.ignore_case
+ modules.method_list.each{ |meth|
+ if meth.name == pub_meth["name"] ||
+ meth.name.upcase == pub_meth["name"].upcase &&
+ @options.ignore_case
+ new_meth = initialize_public_method(meth,
+ modules.name)
+ if pub_meth["local_name"]
+ new_meth.name = pub_meth["local_name"]
+ end
+
+ @stats.add_method new_meth
+
+ container.add_method new_meth
+ end
+ }
+ end
+ }
+ }
+ }
+
+ container
+ end # End of parse_program_or_module
+
+ ##
+ # Parse arguments, comment, code of subroutine and function. Return
+ # AnyMethod object.
+
+ def parse_subprogram(subprogram, params, comment, code,
+ before_contains=nil, function=nil, prefix=nil)
+ subprogram.singleton = false
+ prefix = "" if !prefix
+ arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
+ args_comment, params_opt =
+ find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
+ nil, nil, true)
+ params_opt = "( " + params_opt + " ) " if params_opt
+ subprogram.params = params_opt || ""
+ namelist_comment = find_namelists(code, before_contains)
+
+ block_comment = find_comments comment
+ if function
+ subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
+ else
+ subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
+ end
+ subprogram.comment << args_comment if args_comment
+ subprogram.comment << block_comment if block_comment
+ subprogram.comment << namelist_comment if namelist_comment
+
+ # For output source code
+ subprogram.start_collecting_tokens
+ subprogram.add_token Token.new(1,1).set_text(code)
+
+ subprogram
+ end
+
+ ##
+ # Collect comment for file entity
+
+ def collect_first_comment(body)
+ comment = ""
+ not_comment = ""
+ comment_start = false
+ comment_end = false
+ body.split("\n").each{ |line|
+ if comment_end
+ not_comment << line
+ not_comment << "\n"
+ elsif /^\s*?!\s?(.*)$/i =~ line
+ comment_start = true
+ comment << $1
+ comment << "\n"
+ elsif /^\s*?$/i =~ line
+ comment_end = true if comment_start && COMMENTS_ARE_UPPER
+ else
+ comment_end = true
+ not_comment << line
+ not_comment << "\n"
+ end
+ }
+ return comment, not_comment
+ end
+
+
+ ##
+ # Return comments of definitions of arguments
+ #
+ # If "all" argument is true, information of all arguments are returned.
+ #
+ # If "modified_params" is true, list of arguments are decorated, for
+ # example, optional arguments are parenthetic as "[arg]".
+
+ def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
+ return unless args || all
+ indent = "" unless indent
+ args = ["all"] if all
+ params = "" if modified_params
+ comma = ""
+ return unless text
+ args_rdocforms = "\n"
+ remaining_lines = "#{text}"
+ definitions = definition_info(remaining_lines)
+ args.each{ |arg|
+ arg.strip!
+ arg.chomp!
+ definitions.each { |defitem|
+ if arg == defitem.varname.strip.chomp || all
+ args_rdocforms << <<-"EOF"
+
+#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> ::
+#{indent} <tt>#{defitem.types.chomp.strip}</tt>
+EOF
+ if !defitem.comment.chomp.strip.empty?
+ comment = ""
+ defitem.comment.split("\n").each{ |line|
+ comment << " " + line + "\n"
+ }
+ args_rdocforms << <<-"EOF"
+
+#{indent} <tt></tt> ::
+#{indent} <tt></tt>
+#{indent} #{comment.chomp.strip}
+EOF
+ end
+
+ if modified_params
+ if defitem.include_attr?("optional")
+ params << "#{comma}[#{arg}]"
+ else
+ params << "#{comma}#{arg}"
+ end
+ comma = ", "
+ end
+ end
+ }
+ }
+ if modified_params
+ return args_rdocforms, params
+ else
+ return args_rdocforms
+ end
+ end
+
+ ##
+ # Return comments of definitions of namelists
+
+ def find_namelists(text, before_contains=nil)
+ return nil if !text
+ result = ""
+ lines = "#{text}"
+ before_contains = "" if !before_contains
+ while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i
+ lines = $~.post_match
+ nml_comment = COMMENTS_ARE_UPPER ?
+ find_comments($~.pre_match) : find_comments($~.post_match)
+ nml_name = $1
+ nml_args = $2.split(",")
+ result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n"
+ result << nml_comment + "\n" if nml_comment
+ if lines.split("\n")[0] =~ /^\//i
+ lines = "namelist " + lines
+ end
+ result << find_arguments(nml_args, "#{text}" + "\n" + before_contains)
+ end
+ return result
+ end
+
+ ##
+ # Comments just after module or subprogram, or arguments are returned. If
+ # "COMMENTS_ARE_UPPER" is true, comments just before modules or subprograms
+ # are returnd
+
+ def find_comments text
+ return "" unless text
+ lines = text.split("\n")
+ lines.reverse! if COMMENTS_ARE_UPPER
+ comment_block = Array.new
+ lines.each do |line|
+ break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
+ if COMMENTS_ARE_UPPER
+ comment_block.unshift line.sub(/^\s*?!\s?/,"")
+ else
+ comment_block.push line.sub(/^\s*?!\s?/,"")
+ end
+ end
+ nice_lines = comment_block.join("\n").split "\n\s*?\n"
+ nice_lines[0] ||= ""
+ nice_lines.shift
+ end
+
+ ##
+ # Create method for internal alias
+
+ def initialize_public_method(method, parent)
+ return if !method || !parent
+
+ new_meth = AnyMethod.new("External Alias for module", method.name)
+ new_meth.singleton = method.singleton
+ new_meth.params = method.params.clone
+ new_meth.comment = remove_trailing_alias(method.comment.clone)
+ new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
+
+ return new_meth
+ end
+
+ ##
+ # Create method for external alias
+ #
+ # If argument "internal" is true, file is ignored.
+
+ def initialize_external_method(new, old, params, file, comment, token=nil,
+ internal=nil, nolink=nil)
+ return nil unless new || old
+
+ if internal
+ external_alias_header = "#{INTERNAL_ALIAS_MES} "
+ external_alias_text = external_alias_header + old
+ elsif file
+ external_alias_header = "#{EXTERNAL_ALIAS_MES} "
+ external_alias_text = external_alias_header + file + "#" + old
+ else
+ return nil
+ end
+ external_meth = AnyMethod.new(external_alias_text, new)
+ external_meth.singleton = false
+ external_meth.params = params
+ external_comment = remove_trailing_alias(comment) + "\n\n" if comment
+ external_meth.comment = external_comment || ""
+ if nolink && token
+ external_meth.start_collecting_tokens
+ external_meth.add_token Token.new(1,1).set_text(token)
+ else
+ external_meth.comment << external_alias_text
+ end
+
+ return external_meth
+ end
+
+ ##
+ # Parse visibility
+
+ def parse_visibility(code, default, container)
+ result = []
+ visibility_default = default || :public
+
+ used_modules = []
+ container.includes.each{|i| used_modules << i.name} if container
+
+ remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
+ remaining_code.split("\n").each{ |line|
+ if /^\s*?private\s*?$/ =~ line
+ visibility_default = :private
+ break
+ end
+ } if remaining_code
+
+ remaining_code.split("\n").each{ |line|
+ if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
+ methods = $2.sub(/!.*$/, '')
+ methods.split(",").each{ |meth|
+ meth.sub!(/!.*$/, '')
+ meth.gsub!(/:/, '')
+ result << {
+ "name" => meth.chomp.strip,
+ "visibility" => :private,
+ "used_modules" => used_modules.clone,
+ "file_or_module" => container,
+ "entity_is_discovered" => nil,
+ "local_name" => nil
+ }
+ }
+ elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
+ methods = $2.sub(/!.*$/, '')
+ methods.split(",").each{ |meth|
+ meth.sub!(/!.*$/, '')
+ meth.gsub!(/:/, '')
+ result << {
+ "name" => meth.chomp.strip,
+ "visibility" => :public,
+ "used_modules" => used_modules.clone,
+ "file_or_module" => container,
+ "entity_is_discovered" => nil,
+ "local_name" => nil
+ }
+ }
+ end
+ } if remaining_code
+
+ if container
+ result.each{ |vis_info|
+ vis_info["parent"] = container.name
+ }
+ end
+
+ return visibility_default, result
+ end
+
+ ##
+ # Set visibility
+ #
+ # "subname" element of "visibility_info" is deleted.
+
+ def set_visibility(container, subname, visibility_default, visibility_info)
+ return unless container || subname || visibility_default || visibility_info
+ not_found = true
+ visibility_info.collect!{ |info|
+ if info["name"] == subname ||
+ @options.ignore_case && info["name"].upcase == subname.upcase
+ if info["file_or_module"].name == container.name
+ container.set_visibility_for([subname], info["visibility"])
+ info["entity_is_discovered"] = true
+ not_found = false
+ end
+ end
+ info
+ }
+ if not_found
+ return container.set_visibility_for([subname], visibility_default)
+ else
+ return container
+ end
+ end
+
+ ##
+ # Find visibility
+
+ def find_visibility(container, subname, visibility_info)
+ return nil if !subname || !visibility_info
+ visibility_info.each{ |info|
+ if info["name"] == subname ||
+ @options.ignore_case && info["name"].upcase == subname.upcase
+ if info["parent"] == container.name
+ return info["visibility"]
+ end
+ end
+ }
+ return nil
+ end
+
+ ##
+ # Check external aliases
+
+ def check_external_aliases(subname, params, comment, test=nil)
+ @@external_aliases.each{ |alias_item|
+ if subname == alias_item["old_name"] ||
+ subname.upcase == alias_item["old_name"].upcase &&
+ @options.ignore_case
+
+ new_meth = initialize_external_method(alias_item["new_name"],
+ subname, params, @file_name,
+ comment)
+ new_meth.visibility = alias_item["visibility"]
+
+ @stats.add_method new_meth
+
+ alias_item["file_or_module"].add_method(new_meth)
+
+ if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case)
+ alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
+ end
+ end
+ }
+ end
+
+ ##
+ # Check public_methods
+
+ def check_public_methods(method, parent)
+ return if !method || !parent
+ @@public_methods.each{ |alias_item|
+ parent_is_used_module = nil
+ alias_item["used_modules"].each{ |used_module|
+ if used_module == parent ||
+ used_module.upcase == parent.upcase &&
+ @options.ignore_case
+ parent_is_used_module = true
+ end
+ }
+ next if !parent_is_used_module
+
+ if method.name == alias_item["name"] ||
+ method.name.upcase == alias_item["name"].upcase &&
+ @options.ignore_case
+
+ new_meth = initialize_public_method(method, parent)
+ if alias_item["local_name"]
+ new_meth.name = alias_item["local_name"]
+ end
+
+ @stats.add_method new_meth
+
+ alias_item["file_or_module"].add_method new_meth
+ end
+ }
+ end
+
+ ##
+ # Continuous lines are united.
+ #
+ # Comments in continuous lines are removed.
+
+ def united_to_one_line(f90src)
+ return "" unless f90src
+ lines = f90src.split("\n")
+ previous_continuing = false
+ now_continuing = false
+ body = ""
+ lines.each{ |line|
+ words = line.split("")
+ next if words.empty? && previous_continuing
+ commentout = false
+ brank_flag = true ; brank_char = ""
+ squote = false ; dquote = false
+ ignore = false
+ words.collect! { |char|
+ if previous_continuing && brank_flag
+ now_continuing = true
+ ignore = true
+ case char
+ when "!" ; break
+ when " " ; brank_char << char ; next ""
+ when "&"
+ brank_flag = false
+ now_continuing = false
+ next ""
+ else
+ brank_flag = false
+ now_continuing = false
+ ignore = false
+ next brank_char + char
+ end
+ end
+ ignore = false
+
+ if now_continuing
+ next ""
+ elsif !(squote) && !(dquote) && !(commentout)
+ case char
+ when "!" ; commentout = true ; next char
+ when "\""; dquote = true ; next char
+ when "\'"; squote = true ; next char
+ when "&" ; now_continuing = true ; next ""
+ else next char
+ end
+ elsif commentout
+ next char
+ elsif squote
+ case char
+ when "\'"; squote = false ; next char
+ else next char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; next char
+ else next char
+ end
+ end
+ }
+ if !ignore && !previous_continuing || !brank_flag
+ if previous_continuing
+ body << words.join("")
+ else
+ body << "\n" + words.join("")
+ end
+ end
+ previous_continuing = now_continuing ? true : nil
+ now_continuing = nil
+ }
+ return body
+ end
+
+
+ ##
+ # Continuous line checker
+
+ def continuous_line?(line)
+ continuous = false
+ if /&\s*?(!.*)?$/ =~ line
+ continuous = true
+ if comment_out?($~.pre_match)
+ continuous = false
+ end
+ end
+ return continuous
+ end
+
+ ##
+ # Comment out checker
+
+ def comment_out?(line)
+ return nil unless line
+ commentout = false
+ squote = false ; dquote = false
+ line.split("").each { |char|
+ if !(squote) && !(dquote)
+ case char
+ when "!" ; commentout = true ; break
+ when "\""; dquote = true
+ when "\'"; squote = true
+ else next
+ end
+ elsif squote
+ case char
+ when "\'"; squote = false
+ else next
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false
+ else next
+ end
+ end
+ }
+ return commentout
+ end
+
+ ##
+ # Semicolons are replaced to line feed.
+
+ def semicolon_to_linefeed(text)
+ return "" unless text
+ lines = text.split("\n")
+ lines.collect!{ |line|
+ words = line.split("")
+ commentout = false
+ squote = false ; dquote = false
+ words.collect! { |char|
+ if !(squote) && !(dquote) && !(commentout)
+ case char
+ when "!" ; commentout = true ; next char
+ when "\""; dquote = true ; next char
+ when "\'"; squote = true ; next char
+ when ";" ; "\n"
+ else next char
+ end
+ elsif commentout
+ next char
+ elsif squote
+ case char
+ when "\'"; squote = false ; next char
+ else next char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; next char
+ else next char
+ end
+ end
+ }
+ words.join("")
+ }
+ return lines.join("\n")
+ end
+
+ ##
+ # Which "line" is start of block (module, program, block data, subroutine,
+ # function) statement ?
+
+ def block_start?(line)
+ return nil if !line
+
+ if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ \
+ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix ||
+ line =~ \
+ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ return true
+ end
+
+ return nil
+ end
+
+ ##
+ # Which "line" is end of block (module, program, block data, subroutine,
+ # function) statement ?
+
+ def block_end?(line)
+ return nil if !line
+
+ if line =~ /^\s*?end\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
+ return true
+ end
+
+ return nil
+ end
+
+ ##
+ # Remove "Alias for" in end of comments
+
+ def remove_trailing_alias(text)
+ return "" if !text
+ lines = text.split("\n").reverse
+ comment_block = Array.new
+ checked = false
+ lines.each do |line|
+ if !checked
+ if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
+ /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
+ checked = true
+ next
+ end
+ end
+ comment_block.unshift line
+ end
+ nice_lines = comment_block.join("\n")
+ nice_lines ||= ""
+ return nice_lines
+ end
+
+ ##
+ # Empty lines in header are removed
+
+ def remove_empty_head_lines(text)
+ return "" unless text
+ lines = text.split("\n")
+ header = true
+ lines.delete_if{ |line|
+ header = false if /\S/ =~ line
+ header && /^\s*?$/ =~ line
+ }
+ lines.join("\n")
+ end
+
+ ##
+ # header marker "=", "==", ... are removed
+
+ def remove_header_marker(text)
+ return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
+ end
+
+ def remove_private_comments(body)
+ body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '')
+ return body
+ end
+
+ ##
+ # Information of arguments of subroutines and functions in Fortran95
+
+ class Fortran95Definition
+
+ # Name of variable
+ #
+ attr_reader :varname
+
+ # Types of variable
+ #
+ attr_reader :types
+
+ # Initial Value
+ #
+ attr_reader :inivalue
+
+ # Suffix of array
+ #
+ attr_reader :arraysuffix
+
+ # Comments
+ #
+ attr_accessor :comment
+
+ # Flag of non documentation
+ #
+ attr_accessor :nodoc
+
+ def initialize(varname, types, inivalue, arraysuffix, comment,
+ nodoc=false)
+ @varname = varname
+ @types = types
+ @inivalue = inivalue
+ @arraysuffix = arraysuffix
+ @comment = comment
+ @nodoc = nodoc
+ end
+
+ def to_s
+ return <<-EOF
+<Fortran95Definition:
+varname=#{@varname}, types=#{types},
+inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc},
+comment=
+#{@comment}
+>
+EOF
+ end
+
+ #
+ # If attr is included, true is returned
+ #
+ def include_attr?(attr)
+ return if !attr
+ @types.split(",").each{ |type|
+ return true if type.strip.chomp.upcase == attr.strip.chomp.upcase
+ }
+ return nil
+ end
+
+ end # End of Fortran95Definition
+
+ ##
+ # Parse string argument "text", and Return Array of Fortran95Definition
+ # object
+
+ def definition_info(text)
+ return nil unless text
+ lines = "#{text}"
+ defs = Array.new
+ comment = ""
+ trailing_comment = ""
+ under_comment_valid = false
+ lines.split("\n").each{ |line|
+ if /^\s*?!\s?(.*)/ =~ line
+ if COMMENTS_ARE_UPPER
+ comment << remove_header_marker($1)
+ comment << "\n"
+ elsif defs[-1] && under_comment_valid
+ defs[-1].comment << "\n"
+ defs[-1].comment << remove_header_marker($1)
+ end
+ next
+ elsif /^\s*?$/ =~ line
+ comment = ""
+ under_comment_valid = false
+ next
+ end
+ type = ""
+ characters = ""
+ if line =~ /^\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | type\s*?\([\w\s]+?\)[\s\,]*
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | double\s+precision[\s\,]*
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ )
+ (.*?::)?
+ (.+)$
+ /ix
+ characters = $8
+ type = $1
+ type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
+ else
+ under_comment_valid = false
+ next
+ end
+ squote = false ; dquote = false ; bracket = 0
+ iniflag = false; commentflag = false
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ start_pos = defs.size
+ characters.split("").each { |char|
+ if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
+ case char
+ when "!" ; commentflag = true
+ when "(" ; bracket += 1 ; arraysuffix = char
+ when "\""; dquote = true
+ when "\'"; squote = true
+ when "=" ; iniflag = true ; inivalue << char
+ when ","
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ under_comment_valid = true
+ when " " ; next
+ else ; varname << char
+ end
+ elsif commentflag
+ comment << remove_header_marker(char)
+ trailing_comment << remove_header_marker(char)
+ elsif iniflag
+ if dquote
+ case char
+ when "\"" ; dquote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif squote
+ case char
+ when "\'" ; squote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif bracket > 0
+ case char
+ when "(" ; bracket += 1 ; inivalue << char
+ when ")" ; bracket -= 1 ; inivalue << char
+ else ; inivalue << char
+ end
+ else
+ case char
+ when ","
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ iniflag = false
+ under_comment_valid = true
+ when "(" ; bracket += 1 ; inivalue << char
+ when "\""; dquote = true ; inivalue << char
+ when "\'"; squote = true ; inivalue << char
+ when "!" ; commentflag = true
+ else ; inivalue << char
+ end
+ end
+ elsif !(squote) && !(dquote) && bracket > 0
+ case char
+ when "(" ; bracket += 1 ; arraysuffix << char
+ when ")" ; bracket -= 1 ; arraysuffix << char
+ else ; arraysuffix << char
+ end
+ elsif squote
+ case char
+ when "\'"; squote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ end
+ }
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ if trailing_comment =~ /^:nodoc:/
+ defs[start_pos..-1].collect!{ |defitem|
+ defitem.nodoc = true
+ }
+ end
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ comment = ""
+ under_comment_valid = true
+ trailing_comment = ""
+ }
+ return defs
+ end
+
+end
+
Deleted: MacRuby/branches/experimental/lib/rdoc/parser/perl.rb
===================================================================
--- MacRuby/trunk/lib/rdoc/parser/perl.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/parser/perl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,165 +0,0 @@
-require 'rdoc/parser'
-
-##
-#
-# This is an attamept to write a basic parser for Perl's
-# POD (Plain old Documentation) format. Ruby code must
-# co-exist with Perl, and some tasks are easier in Perl
-# than Ruby because of existing libraries.
-#
-# One difficult is that Perl POD has no means of identifying
-# the classes (packages) and methods (subs) with which it
-# is associated, it is more like literate programming in so
-# far as it just happens to be in the same place as the code,
-# but need not be.
-#
-# We would like to support all the markup the POD provides
-# so that it will convert happily to HTML. At the moment
-# I don't think I can do that: time constraints.
-#
-
-class RDoc::Parser::PerlPOD < RDoc::Parser
-
- parse_files_matching(/.p[lm]$/)
-
- ##
- # Prepare to parse a perl file
-
- def initialize(top_level, file_name, content, options, stats)
- super
-
- preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
-
- preprocess.handle @content do |directive, param|
- warn "Unrecognized directive '#{directive}' in #{@file_name}"
- end
- end
-
- ##
- # Extract the Pod(-like) comments from the code.
- # At its most basic there will ne no need to distinguish
- # between the different types of header, etc.
- #
- # This uses a simple finite state machine, in a very
- # procedural pattern. I could "replace case with polymorphism"
- # but I think it would obscure the intent, scatter the
- # code all over tha place. This machine is necessary
- # because POD requires that directives be preceded by
- # blank lines, so reading line by line is necessary,
- # and preserving state about what is seen is necesary.
-
- def scan
-
- @top_level.comment ||= ""
- state=:code_blank
- line_number = 0
- line = nil
-
- # This started out as a really long nested case statement,
- # which also led to repetitive code. I'd like to avoid that
- # so I'm using a "table" instead.
-
- # Firstly we need some procs to do the transition and processing
- # work. Because these are procs they are closures, and they can
- # use variables in the local scope.
- #
- # First, the "nothing to see here" stuff.
- code_noop = lambda do
- if line =~ /^\s+$/
- state = :code_blank
- end
- end
-
- pod_noop = lambda do
- if line =~ /^\s+$/
- state = :pod_blank
- end
- @top_level.comment += filter(line)
- end
-
- begin_noop = lambda do
- if line =~ /^\s+$/
- state = :begin_blank
- end
- @top_level.comment += filter(line)
- end
-
- # Now for the blocks that process code and comments...
-
- transit_to_pod = lambda do
- case line
- when /^=(?:pod|head\d+)/
- state = :pod_no_blank
- @top_level.comment += filter(line)
- when /^=over/
- state = :over_no_blank
- @top_level.comment += filter(line)
- when /^=(?:begin|for)/
- state = :begin_no_blank
- end
- end
-
- process_pod = lambda do
- case line
- when /^\s*$/
- state = :pod_blank
- @top_level.comment += filter(line)
- when /^=cut/
- state = :code_no_blank
- when /^=end/
- $stderr.puts "'=end' unexpected at #{line_number} in #{@file_name}"
- else
- @top_level.comment += filter(line)
- end
- end
-
-
- process_begin = lambda do
- case line
- when /^\s*$/
- state = :begin_blank
- @top_level.comment += filter(line)
- when /^=end/
- state = :code_no_blank
- when /^=cut/
- $stderr.puts "'=cut' unexpected at #{line_number} in #{@file_name}"
- else
- @top_level.comment += filter(line)
- end
-
- end
-
-
- transitions = { :code_no_blank => code_noop,
- :code_blank => transit_to_pod,
- :pod_no_blank => pod_noop,
- :pod_blank => process_pod,
- :begin_no_blank => begin_noop,
- :begin_blank => process_begin}
- @content.each_line do |l|
- line = l
- line_number += 1
- transitions[state].call
- end # each line
-
- @top_level
- end
-
- # Filter the perl markup that does the same as the rdoc
- # filtering. Only basic for now. Will probably need a
- # proper parser to cope with C<<...>> etc
- def filter(comment)
- return '' if comment =~ /^=pod\s*$/
- comment.gsub!(/^=pod/, '==')
- comment.gsub!(/^=head(\d+)/) do
- "=" * $1.to_i
- end
- comment.gsub!(/=item/, '');
- comment.gsub!(/C<(.*?)>/, '<tt>\1</tt>');
- comment.gsub!(/I<(.*?)>/, '<i>\1</i>');
- comment.gsub!(/B<(.*?)>/, '<b>\1</b>');
- comment
- end
-
-end
-
Copied: MacRuby/branches/experimental/lib/rdoc/parser/perl.rb (from rev 1886, MacRuby/trunk/lib/rdoc/parser/perl.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/parser/perl.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/parser/perl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,165 @@
+require 'rdoc/parser'
+
+##
+#
+# This is an attamept to write a basic parser for Perl's
+# POD (Plain old Documentation) format. Ruby code must
+# co-exist with Perl, and some tasks are easier in Perl
+# than Ruby because of existing libraries.
+#
+# One difficult is that Perl POD has no means of identifying
+# the classes (packages) and methods (subs) with which it
+# is associated, it is more like literate programming in so
+# far as it just happens to be in the same place as the code,
+# but need not be.
+#
+# We would like to support all the markup the POD provides
+# so that it will convert happily to HTML. At the moment
+# I don't think I can do that: time constraints.
+#
+
+class RDoc::Parser::PerlPOD < RDoc::Parser
+
+ parse_files_matching(/.p[lm]$/)
+
+ ##
+ # Prepare to parse a perl file
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
+
+ preprocess.handle @content do |directive, param|
+ warn "Unrecognized directive '#{directive}' in #{@file_name}"
+ end
+ end
+
+ ##
+ # Extract the Pod(-like) comments from the code.
+ # At its most basic there will ne no need to distinguish
+ # between the different types of header, etc.
+ #
+ # This uses a simple finite state machine, in a very
+ # procedural pattern. I could "replace case with polymorphism"
+ # but I think it would obscure the intent, scatter the
+ # code all over tha place. This machine is necessary
+ # because POD requires that directives be preceded by
+ # blank lines, so reading line by line is necessary,
+ # and preserving state about what is seen is necesary.
+
+ def scan
+
+ @top_level.comment ||= ""
+ state=:code_blank
+ line_number = 0
+ line = nil
+
+ # This started out as a really long nested case statement,
+ # which also led to repetitive code. I'd like to avoid that
+ # so I'm using a "table" instead.
+
+ # Firstly we need some procs to do the transition and processing
+ # work. Because these are procs they are closures, and they can
+ # use variables in the local scope.
+ #
+ # First, the "nothing to see here" stuff.
+ code_noop = lambda do
+ if line =~ /^\s+$/
+ state = :code_blank
+ end
+ end
+
+ pod_noop = lambda do
+ if line =~ /^\s+$/
+ state = :pod_blank
+ end
+ @top_level.comment += filter(line)
+ end
+
+ begin_noop = lambda do
+ if line =~ /^\s+$/
+ state = :begin_blank
+ end
+ @top_level.comment += filter(line)
+ end
+
+ # Now for the blocks that process code and comments...
+
+ transit_to_pod = lambda do
+ case line
+ when /^=(?:pod|head\d+)/
+ state = :pod_no_blank
+ @top_level.comment += filter(line)
+ when /^=over/
+ state = :over_no_blank
+ @top_level.comment += filter(line)
+ when /^=(?:begin|for)/
+ state = :begin_no_blank
+ end
+ end
+
+ process_pod = lambda do
+ case line
+ when /^\s*$/
+ state = :pod_blank
+ @top_level.comment += filter(line)
+ when /^=cut/
+ state = :code_no_blank
+ when /^=end/
+ $stderr.puts "'=end' unexpected at #{line_number} in #{@file_name}"
+ else
+ @top_level.comment += filter(line)
+ end
+ end
+
+
+ process_begin = lambda do
+ case line
+ when /^\s*$/
+ state = :begin_blank
+ @top_level.comment += filter(line)
+ when /^=end/
+ state = :code_no_blank
+ when /^=cut/
+ $stderr.puts "'=cut' unexpected at #{line_number} in #{@file_name}"
+ else
+ @top_level.comment += filter(line)
+ end
+
+ end
+
+
+ transitions = { :code_no_blank => code_noop,
+ :code_blank => transit_to_pod,
+ :pod_no_blank => pod_noop,
+ :pod_blank => process_pod,
+ :begin_no_blank => begin_noop,
+ :begin_blank => process_begin}
+ @content.each_line do |l|
+ line = l
+ line_number += 1
+ transitions[state].call
+ end # each line
+
+ @top_level
+ end
+
+ # Filter the perl markup that does the same as the rdoc
+ # filtering. Only basic for now. Will probably need a
+ # proper parser to cope with C<<...>> etc
+ def filter(comment)
+ return '' if comment =~ /^=pod\s*$/
+ comment.gsub!(/^=pod/, '==')
+ comment.gsub!(/^=head(\d+)/) do
+ "=" * $1.to_i
+ end
+ comment.gsub!(/=item/, '');
+ comment.gsub!(/C<(.*?)>/, '<tt>\1</tt>');
+ comment.gsub!(/I<(.*?)>/, '<i>\1</i>');
+ comment.gsub!(/B<(.*?)>/, '<b>\1</b>');
+ comment
+ end
+
+end
+
Deleted: MacRuby/branches/experimental/lib/rdoc/parser/ruby.rb
===================================================================
--- MacRuby/trunk/lib/rdoc/parser/ruby.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/parser/ruby.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,2829 +0,0 @@
-##
-# This file contains stuff stolen outright from:
-#
-# rtags.rb -
-# ruby-lex.rb - ruby lexcal analyzer
-# ruby-token.rb - ruby tokens
-# by Keiju ISHITSUKA (Nippon Rational Inc.)
-#
-
-require 'e2mmap'
-require 'irb/slex'
-
-require 'rdoc/code_objects'
-require 'rdoc/tokenstream'
-require 'rdoc/markup/preprocess'
-require 'rdoc/parser'
-
-$TOKEN_DEBUG ||= nil
-#$TOKEN_DEBUG = $DEBUG_RDOC
-
-##
-# Definitions of all tokens involved in the lexical analysis
-
-module RDoc::RubyToken
-
- EXPR_BEG = :EXPR_BEG
- EXPR_MID = :EXPR_MID
- EXPR_END = :EXPR_END
- EXPR_ARG = :EXPR_ARG
- EXPR_FNAME = :EXPR_FNAME
- EXPR_DOT = :EXPR_DOT
- EXPR_CLASS = :EXPR_CLASS
-
- class Token
- NO_TEXT = "??".freeze
-
- attr_accessor :text
- attr_reader :line_no
- attr_reader :char_no
-
- def initialize(line_no, char_no)
- @line_no = line_no
- @char_no = char_no
- @text = NO_TEXT
- end
-
- def ==(other)
- self.class == other.class and
- other.line_no == @line_no and
- other.char_no == @char_no and
- other.text == @text
- end
-
- ##
- # Because we're used in contexts that expect to return a token, we set the
- # text string and then return ourselves
-
- def set_text(text)
- @text = text
- self
- end
-
- end
-
- class TkNode < Token
- attr :node
- end
-
- class TkId < Token
- def initialize(line_no, char_no, name)
- super(line_no, char_no)
- @name = name
- end
- attr :name
- end
-
- class TkKW < TkId
- end
-
- class TkVal < Token
- def initialize(line_no, char_no, value = nil)
- super(line_no, char_no)
- set_text(value)
- end
- end
-
- class TkOp < Token
- def name
- self.class.op_name
- end
- end
-
- class TkOPASGN < TkOp
- def initialize(line_no, char_no, op)
- super(line_no, char_no)
- op = TkReading2Token[op] unless Symbol === op
- @op = op
- end
- attr :op
- end
-
- class TkUnknownChar < Token
- def initialize(line_no, char_no, id)
- super(line_no, char_no)
- @name = char_no.chr
- end
- attr :name
- end
-
- class TkError < Token
- end
-
- def set_token_position(line, char)
- @prev_line_no = line
- @prev_char_no = char
- end
-
- def Token(token, value = nil)
- tk = nil
- case token
- when String, Symbol
- source = String === token ? TkReading2Token : TkSymbol2Token
- raise TkReading2TokenNoKey, token if (tk = source[token]).nil?
- tk = Token(tk[0], value)
- else
- tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty?
- token.new(@prev_line_no, @prev_char_no)
- else
- token.new(@prev_line_no, @prev_char_no, value)
- end
- end
- tk
- end
-
- TokenDefinitions = [
- [:TkCLASS, TkKW, "class", EXPR_CLASS],
- [:TkMODULE, TkKW, "module", EXPR_CLASS],
- [:TkDEF, TkKW, "def", EXPR_FNAME],
- [:TkUNDEF, TkKW, "undef", EXPR_FNAME],
- [:TkBEGIN, TkKW, "begin", EXPR_BEG],
- [:TkRESCUE, TkKW, "rescue", EXPR_MID],
- [:TkENSURE, TkKW, "ensure", EXPR_BEG],
- [:TkEND, TkKW, "end", EXPR_END],
- [:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD],
- [:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD],
- [:TkTHEN, TkKW, "then", EXPR_BEG],
- [:TkELSIF, TkKW, "elsif", EXPR_BEG],
- [:TkELSE, TkKW, "else", EXPR_BEG],
- [:TkCASE, TkKW, "case", EXPR_BEG],
- [:TkWHEN, TkKW, "when", EXPR_BEG],
- [:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD],
- [:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD],
- [:TkFOR, TkKW, "for", EXPR_BEG],
- [:TkBREAK, TkKW, "break", EXPR_END],
- [:TkNEXT, TkKW, "next", EXPR_END],
- [:TkREDO, TkKW, "redo", EXPR_END],
- [:TkRETRY, TkKW, "retry", EXPR_END],
- [:TkIN, TkKW, "in", EXPR_BEG],
- [:TkDO, TkKW, "do", EXPR_BEG],
- [:TkRETURN, TkKW, "return", EXPR_MID],
- [:TkYIELD, TkKW, "yield", EXPR_END],
- [:TkSUPER, TkKW, "super", EXPR_END],
- [:TkSELF, TkKW, "self", EXPR_END],
- [:TkNIL, TkKW, "nil", EXPR_END],
- [:TkTRUE, TkKW, "true", EXPR_END],
- [:TkFALSE, TkKW, "false", EXPR_END],
- [:TkAND, TkKW, "and", EXPR_BEG],
- [:TkOR, TkKW, "or", EXPR_BEG],
- [:TkNOT, TkKW, "not", EXPR_BEG],
- [:TkIF_MOD, TkKW],
- [:TkUNLESS_MOD, TkKW],
- [:TkWHILE_MOD, TkKW],
- [:TkUNTIL_MOD, TkKW],
- [:TkALIAS, TkKW, "alias", EXPR_FNAME],
- [:TkDEFINED, TkKW, "defined?", EXPR_END],
- [:TklBEGIN, TkKW, "BEGIN", EXPR_END],
- [:TklEND, TkKW, "END", EXPR_END],
- [:Tk__LINE__, TkKW, "__LINE__", EXPR_END],
- [:Tk__FILE__, TkKW, "__FILE__", EXPR_END],
-
- [:TkIDENTIFIER, TkId],
- [:TkFID, TkId],
- [:TkGVAR, TkId],
- [:TkIVAR, TkId],
- [:TkCONSTANT, TkId],
-
- [:TkINTEGER, TkVal],
- [:TkFLOAT, TkVal],
- [:TkSTRING, TkVal],
- [:TkXSTRING, TkVal],
- [:TkREGEXP, TkVal],
- [:TkCOMMENT, TkVal],
-
- [:TkDSTRING, TkNode],
- [:TkDXSTRING, TkNode],
- [:TkDREGEXP, TkNode],
- [:TkNTH_REF, TkId],
- [:TkBACK_REF, TkId],
-
- [:TkUPLUS, TkOp, "+@"],
- [:TkUMINUS, TkOp, "-@"],
- [:TkPOW, TkOp, "**"],
- [:TkCMP, TkOp, "<=>"],
- [:TkEQ, TkOp, "=="],
- [:TkEQQ, TkOp, "==="],
- [:TkNEQ, TkOp, "!="],
- [:TkGEQ, TkOp, ">="],
- [:TkLEQ, TkOp, "<="],
- [:TkANDOP, TkOp, "&&"],
- [:TkOROP, TkOp, "||"],
- [:TkMATCH, TkOp, "=~"],
- [:TkNMATCH, TkOp, "!~"],
- [:TkDOT2, TkOp, ".."],
- [:TkDOT3, TkOp, "..."],
- [:TkAREF, TkOp, "[]"],
- [:TkASET, TkOp, "[]="],
- [:TkLSHFT, TkOp, "<<"],
- [:TkRSHFT, TkOp, ">>"],
- [:TkCOLON2, TkOp],
- [:TkCOLON3, TkOp],
-# [:OPASGN, TkOp], # +=, -= etc. #
- [:TkASSOC, TkOp, "=>"],
- [:TkQUESTION, TkOp, "?"], #?
- [:TkCOLON, TkOp, ":"], #:
-
- [:TkfLPAREN], # func( #
- [:TkfLBRACK], # func[ #
- [:TkfLBRACE], # func{ #
- [:TkSTAR], # *arg
- [:TkAMPER], # &arg #
- [:TkSYMBOL, TkId], # :SYMBOL
- [:TkSYMBEG, TkId],
- [:TkGT, TkOp, ">"],
- [:TkLT, TkOp, "<"],
- [:TkPLUS, TkOp, "+"],
- [:TkMINUS, TkOp, "-"],
- [:TkMULT, TkOp, "*"],
- [:TkDIV, TkOp, "/"],
- [:TkMOD, TkOp, "%"],
- [:TkBITOR, TkOp, "|"],
- [:TkBITXOR, TkOp, "^"],
- [:TkBITAND, TkOp, "&"],
- [:TkBITNOT, TkOp, "~"],
- [:TkNOTOP, TkOp, "!"],
-
- [:TkBACKQUOTE, TkOp, "`"],
-
- [:TkASSIGN, Token, "="],
- [:TkDOT, Token, "."],
- [:TkLPAREN, Token, "("], #(exp)
- [:TkLBRACK, Token, "["], #[arry]
- [:TkLBRACE, Token, "{"], #{hash}
- [:TkRPAREN, Token, ")"],
- [:TkRBRACK, Token, "]"],
- [:TkRBRACE, Token, "}"],
- [:TkCOMMA, Token, ","],
- [:TkSEMICOLON, Token, ";"],
-
- [:TkRD_COMMENT],
- [:TkSPACE],
- [:TkNL],
- [:TkEND_OF_SCRIPT],
-
- [:TkBACKSLASH, TkUnknownChar, "\\"],
- [:TkAT, TkUnknownChar, "@"],
- [:TkDOLLAR, TkUnknownChar, "\$"], #"
- ]
-
- # {reading => token_class}
- # {reading => [token_class, *opt]}
- TkReading2Token = {}
- TkSymbol2Token = {}
-
- def self.def_token(token_n, super_token = Token, reading = nil, *opts)
- token_n = token_n.id2name unless String === token_n
-
- fail AlreadyDefinedToken, token_n if const_defined?(token_n)
-
- token_c = Class.new super_token
- const_set token_n, token_c
-# token_c.inspect
-
- if reading
- if TkReading2Token[reading]
- fail TkReading2TokenDuplicateError, token_n, reading
- end
- if opts.empty?
- TkReading2Token[reading] = [token_c]
- else
- TkReading2Token[reading] = [token_c].concat(opts)
- end
- end
- TkSymbol2Token[token_n.intern] = token_c
-
- if token_c <= TkOp
- token_c.class_eval %{
- def self.op_name; "#{reading}"; end
- }
- end
- end
-
- for defs in TokenDefinitions
- def_token(*defs)
- end
-
- NEWLINE_TOKEN = TkNL.new(0,0)
- NEWLINE_TOKEN.set_text("\n")
-
-end
-
-##
-# Lexical analyzer for Ruby source
-
-class RDoc::RubyLex
-
- ##
- # Read an input stream character by character. We allow for unlimited
- # ungetting of characters just read.
- #
- # We simplify the implementation greatly by reading the entire input
- # into a buffer initially, and then simply traversing it using
- # pointers.
- #
- # We also have to allow for the <i>here document diversion</i>. This
- # little gem comes about when the lexer encounters a here
- # document. At this point we effectively need to split the input
- # stream into two parts: one to read the body of the here document,
- # the other to read the rest of the input line where the here
- # document was initially encountered. For example, we might have
- #
- # do_something(<<-A, <<-B)
- # stuff
- # for
- # A
- # stuff
- # for
- # B
- #
- # When the lexer encounters the <<A, it reads until the end of the
- # line, and keeps it around for later. It then reads the body of the
- # here document. Once complete, it needs to read the rest of the
- # original line, but then skip the here document body.
- #
-
- class BufferedReader
-
- attr_reader :line_num
-
- def initialize(content, options)
- @options = options
-
- if /\t/ =~ content
- tab_width = @options.tab_width
- content = content.split(/\n/).map do |line|
- 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
- line
- end .join("\n")
- end
- @content = content
- @content << "\n" unless @content[-1,1] == "\n"
- @size = @content.size
- @offset = 0
- @hwm = 0
- @line_num = 1
- @read_back_offset = 0
- @last_newline = 0
- @newline_pending = false
- end
-
- def column
- @offset - @last_newline
- end
-
- def getc
- return nil if @offset >= @size
- ch = @content[@offset, 1]
-
- @offset += 1
- @hwm = @offset if @hwm < @offset
-
- if @newline_pending
- @line_num += 1
- @last_newline = @offset - 1
- @newline_pending = false
- end
-
- if ch == "\n"
- @newline_pending = true
- end
- ch
- end
-
- def getc_already_read
- getc
- end
-
- def ungetc(ch)
- raise "unget past beginning of file" if @offset <= 0
- @offset -= 1
- if @content[@offset] == ?\n
- @newline_pending = false
- end
- end
-
- def get_read
- res = @content[@read_back_offset... at offset]
- @read_back_offset = @offset
- res
- end
-
- def peek(at)
- pos = @offset + at
- if pos >= @size
- nil
- else
- @content[pos, 1]
- end
- end
-
- def peek_equal(str)
- @content[@offset, str.length] == str
- end
-
- def divert_read_from(reserve)
- @content[@offset, 0] = reserve
- @size = @content.size
- end
- end
-
- # end of nested class BufferedReader
-
- extend Exception2MessageMapper
- def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
- def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
- def_exception(:TkReading2TokenDuplicateError,
- "key duplicate(token_n='%s', key='%s')")
- def_exception(:SyntaxError, "%s")
-
- include RDoc::RubyToken
- include IRB
-
- attr_reader :continue
- attr_reader :lex_state
-
- def self.debug?
- false
- end
-
- def initialize(content, options)
- lex_init
-
- @options = options
-
- @reader = BufferedReader.new content, @options
-
- @exp_line_no = @line_no = 1
- @base_char_no = 0
- @indent = 0
-
- @ltype = nil
- @quoted = nil
- @lex_state = EXPR_BEG
- @space_seen = false
-
- @continue = false
- @line = ""
-
- @skip_space = false
- @read_auto_clean_up = false
- @exception_on_syntax_error = true
- end
-
- attr_accessor :skip_space
- attr_accessor :read_auto_clean_up
- attr_accessor :exception_on_syntax_error
- attr_reader :indent
-
- # io functions
- def line_no
- @reader.line_num
- end
-
- def char_no
- @reader.column
- end
-
- def get_read
- @reader.get_read
- end
-
- def getc
- @reader.getc
- end
-
- def getc_of_rests
- @reader.getc_already_read
- end
-
- def gets
- c = getc or return
- l = ""
- begin
- l.concat c unless c == "\r"
- break if c == "\n"
- end while c = getc
- l
- end
-
-
- def ungetc(c = nil)
- @reader.ungetc(c)
- end
-
- def peek_equal?(str)
- @reader.peek_equal(str)
- end
-
- def peek(i = 0)
- @reader.peek(i)
- end
-
- def lex
- until (TkNL === (tk = token) or TkEND_OF_SCRIPT === tk) and
- not @continue or tk.nil?
- end
-
- line = get_read
-
- if line == "" and TkEND_OF_SCRIPT === tk or tk.nil? then
- nil
- else
- line
- end
- end
-
- def token
- set_token_position(line_no, char_no)
- begin
- begin
- tk = @OP.match(self)
- @space_seen = TkSPACE === tk
- rescue SyntaxError => e
- raise RDoc::Error, "syntax error: #{e.message}" if
- @exception_on_syntax_error
-
- tk = TkError.new(line_no, char_no)
- end
- end while @skip_space and TkSPACE === tk
- if @read_auto_clean_up
- get_read
- end
-# throw :eof unless tk
- tk
- end
-
- ENINDENT_CLAUSE = [
- "case", "class", "def", "do", "for", "if",
- "module", "unless", "until", "while", "begin" #, "when"
- ]
- DEINDENT_CLAUSE = ["end" #, "when"
- ]
-
- PERCENT_LTYPE = {
- "q" => "\'",
- "Q" => "\"",
- "x" => "\`",
- "r" => "/",
- "w" => "]"
- }
-
- PERCENT_PAREN = {
- "{" => "}",
- "[" => "]",
- "<" => ">",
- "(" => ")"
- }
-
- Ltype2Token = {
- "\'" => TkSTRING,
- "\"" => TkSTRING,
- "\`" => TkXSTRING,
- "/" => TkREGEXP,
- "]" => TkDSTRING
- }
- Ltype2Token.default = TkSTRING
-
- DLtype2Token = {
- "\"" => TkDSTRING,
- "\`" => TkDXSTRING,
- "/" => TkDREGEXP,
- }
-
- def lex_init()
- @OP = IRB::SLex.new
- @OP.def_rules("\0", "\004", "\032") do |chars, io|
- Token(TkEND_OF_SCRIPT).set_text(chars)
- end
-
- @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |chars, io|
- @space_seen = TRUE
- while (ch = getc) =~ /[ \t\f\r\13]/
- chars << ch
- end
- ungetc
- Token(TkSPACE).set_text(chars)
- end
-
- @OP.def_rule("#") do
- |op, io|
- identify_comment
- end
-
- @OP.def_rule("=begin", proc{@prev_char_no == 0 && peek(0) =~ /\s/}) do
- |op, io|
- str = op
- @ltype = "="
-
-
- begin
- line = ""
- begin
- ch = getc
- line << ch
- end until ch == "\n"
- str << line
- end until line =~ /^=end/
-
- ungetc
-
- @ltype = nil
-
- if str =~ /\A=begin\s+rdoc/i
- str.sub!(/\A=begin.*\n/, '')
- str.sub!(/^=end.*/m, '')
- Token(TkCOMMENT).set_text(str)
- else
- Token(TkRD_COMMENT)#.set_text(str)
- end
- end
-
- @OP.def_rule("\n") do
- print "\\n\n" if RDoc::RubyLex.debug?
- case @lex_state
- when EXPR_BEG, EXPR_FNAME, EXPR_DOT
- @continue = TRUE
- else
- @continue = FALSE
- @lex_state = EXPR_BEG
- end
- Token(TkNL).set_text("\n")
- end
-
- @OP.def_rules("*", "**",
- "!", "!=", "!~",
- "=", "==", "===",
- "=~", "<=>",
- "<", "<=",
- ">", ">=", ">>") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- @OP.def_rules("<<") do
- |op, io|
- tk = nil
- if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
- (@lex_state != EXPR_ARG || @space_seen)
- c = peek(0)
- if /[-\w_\"\'\`]/ =~ c
- tk = identify_here_document
- end
- end
- if !tk
- @lex_state = EXPR_BEG
- tk = Token(op).set_text(op)
- end
- tk
- end
-
- @OP.def_rules("'", '"') do
- |op, io|
- identify_string(op)
- end
-
- @OP.def_rules("`") do
- |op, io|
- if @lex_state == EXPR_FNAME
- Token(op).set_text(op)
- else
- identify_string(op)
- end
- end
-
- @OP.def_rules('?') do
- |op, io|
- if @lex_state == EXPR_END
- @lex_state = EXPR_BEG
- Token(TkQUESTION).set_text(op)
- else
- ch = getc
- if @lex_state == EXPR_ARG && ch !~ /\s/
- ungetc
- @lex_state = EXPR_BEG
- Token(TkQUESTION).set_text(op)
- else
- str = op
- str << ch
- if (ch == '\\') #'
- str << read_escape
- end
- @lex_state = EXPR_END
- Token(TkINTEGER).set_text(str)
- end
- end
- end
-
- @OP.def_rules("&", "&&", "|", "||") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- @OP.def_rules("+=", "-=", "*=", "**=",
- "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
- |op, io|
- @lex_state = EXPR_BEG
- op =~ /^(.*)=$/
- Token(TkOPASGN, $1).set_text(op)
- end
-
- @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) do |op, io|
- Token(TkUPLUS).set_text(op)
- end
-
- @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) do |op, io|
- Token(TkUMINUS).set_text(op)
- end
-
- @OP.def_rules("+", "-") do
- |op, io|
- catch(:RET) do
- if @lex_state == EXPR_ARG
- if @space_seen and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = EXPR_BEG
- end
- elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
- throw :RET, identify_number(op)
- else
- @lex_state = EXPR_BEG
- end
- Token(op).set_text(op)
- end
- end
-
- @OP.def_rule(".") do
- @lex_state = EXPR_BEG
- if peek(0) =~ /[0-9]/
- ungetc
- identify_number("")
- else
- # for obj.if
- @lex_state = EXPR_DOT
- Token(TkDOT).set_text(".")
- end
- end
-
- @OP.def_rules("..", "...") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- lex_int2
- end
-
- def lex_int2
- @OP.def_rules("]", "}", ")") do
- |op, io|
- @lex_state = EXPR_END
- @indent -= 1
- Token(op).set_text(op)
- end
-
- @OP.def_rule(":") do
- if @lex_state == EXPR_END || peek(0) =~ /\s/
- @lex_state = EXPR_BEG
- tk = Token(TkCOLON)
- else
- @lex_state = EXPR_FNAME
- tk = Token(TkSYMBEG)
- end
- tk.set_text(":")
- end
-
- @OP.def_rule("::") do
- if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
- @lex_state = EXPR_BEG
- tk = Token(TkCOLON3)
- else
- @lex_state = EXPR_DOT
- tk = Token(TkCOLON2)
- end
- tk.set_text("::")
- end
-
- @OP.def_rule("/") do
- |op, io|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_string(op)
- elsif peek(0) == '='
- getc
- @lex_state = EXPR_BEG
- Token(TkOPASGN, :/).set_text("/=") #")
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_string(op)
- else
- @lex_state = EXPR_BEG
- Token("/").set_text(op)
- end
- end
-
- @OP.def_rules("^") do
- @lex_state = EXPR_BEG
- Token("^").set_text("^")
- end
-
- @OP.def_rules(",", ";") do
- |op, io|
- @lex_state = EXPR_BEG
- Token(op).set_text(op)
- end
-
- @OP.def_rule("~") do
- @lex_state = EXPR_BEG
- Token("~").set_text("~")
- end
-
- @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) do
- @lex_state = EXPR_BEG
- Token("~").set_text("~@")
- end
-
- @OP.def_rule("(") do
- @indent += 1
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- @lex_state = EXPR_BEG
- tk = Token(TkfLPAREN)
- else
- @lex_state = EXPR_BEG
- tk = Token(TkLPAREN)
- end
- tk.set_text("(")
- end
-
- @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) do
- Token("[]").set_text("[]")
- end
-
- @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) do
- Token("[]=").set_text("[]=")
- end
-
- @OP.def_rule("[") do
- @indent += 1
- if @lex_state == EXPR_FNAME
- t = Token(TkfLBRACK)
- else
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- t = Token(TkLBRACK)
- elsif @lex_state == EXPR_ARG && @space_seen
- t = Token(TkLBRACK)
- else
- t = Token(TkfLBRACK)
- end
- @lex_state = EXPR_BEG
- end
- t.set_text("[")
- end
-
- @OP.def_rule("{") do
- @indent += 1
- if @lex_state != EXPR_END && @lex_state != EXPR_ARG
- t = Token(TkLBRACE)
- else
- t = Token(TkfLBRACE)
- end
- @lex_state = EXPR_BEG
- t.set_text("{")
- end
-
- @OP.def_rule('\\') do #'
- if getc == "\n"
- @space_seen = true
- @continue = true
- Token(TkSPACE).set_text("\\\n")
- else
- ungetc
- Token("\\").set_text("\\") #"
- end
- end
-
- @OP.def_rule('%') do
- |op, io|
- if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
- identify_quotation('%')
- elsif peek(0) == '='
- getc
- Token(TkOPASGN, "%").set_text("%=")
- elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
- identify_quotation('%')
- else
- @lex_state = EXPR_BEG
- Token("%").set_text("%")
- end
- end
-
- @OP.def_rule('$') do #'
- identify_gvar
- end
-
- @OP.def_rule('@') do
- if peek(0) =~ /[@\w_]/
- ungetc
- identify_identifier
- else
- Token("@").set_text("@")
- end
- end
-
- @OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do
- throw :eof
- end
-
- @OP.def_rule("") do
- |op, io|
- printf "MATCH: start %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
- if peek(0) =~ /[0-9]/
- t = identify_number("")
- elsif peek(0) =~ /[\w_]/
- t = identify_identifier
- end
- printf "MATCH: end %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
- t
- end
- end
-
- def identify_gvar
- @lex_state = EXPR_END
- str = "$"
-
- tk = case ch = getc
- when /[~_*$?!@\/\\;,=:<>".]/ #"
- str << ch
- Token(TkGVAR, str)
-
- when "-"
- str << "-" << getc
- Token(TkGVAR, str)
-
- when "&", "`", "'", "+"
- str << ch
- Token(TkBACK_REF, str)
-
- when /[1-9]/
- str << ch
- while (ch = getc) =~ /[0-9]/
- str << ch
- end
- ungetc
- Token(TkNTH_REF)
- when /\w/
- ungetc
- ungetc
- return identify_identifier
- else
- ungetc
- Token("$")
- end
- tk.set_text(str)
- end
-
- def identify_identifier
- token = ""
- token.concat getc if peek(0) =~ /[$@]/
- token.concat getc if peek(0) == "@"
-
- while (ch = getc) =~ /\w|_/
- print ":", ch, ":" if RDoc::RubyLex.debug?
- token.concat ch
- end
- ungetc
-
- if ch == "!" or ch == "?"
- token.concat getc
- end
- # fix token
-
- # $stderr.puts "identifier - #{token}, state = #@lex_state"
-
- case token
- when /^\$/
- return Token(TkGVAR, token).set_text(token)
- when /^\@/
- @lex_state = EXPR_END
- return Token(TkIVAR, token).set_text(token)
- end
-
- if @lex_state != EXPR_DOT
- print token, "\n" if RDoc::RubyLex.debug?
-
- token_c, *trans = TkReading2Token[token]
- if token_c
- # reserved word?
-
- if (@lex_state != EXPR_BEG &&
- @lex_state != EXPR_FNAME &&
- trans[1])
- # modifiers
- token_c = TkSymbol2Token[trans[1]]
- @lex_state = trans[0]
- else
- if @lex_state != EXPR_FNAME
- if ENINDENT_CLAUSE.include?(token)
- @indent += 1
- elsif DEINDENT_CLAUSE.include?(token)
- @indent -= 1
- end
- @lex_state = trans[0]
- else
- @lex_state = EXPR_END
- end
- end
- return Token(token_c, token).set_text(token)
- end
- end
-
- if @lex_state == EXPR_FNAME
- @lex_state = EXPR_END
- if peek(0) == '='
- token.concat getc
- end
- elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
- @lex_state = EXPR_ARG
- else
- @lex_state = EXPR_END
- end
-
- if token[0, 1] =~ /[A-Z]/
- return Token(TkCONSTANT, token).set_text(token)
- elsif token[token.size - 1, 1] =~ /[!?]/
- return Token(TkFID, token).set_text(token)
- else
- return Token(TkIDENTIFIER, token).set_text(token)
- end
- end
-
- def identify_here_document
- ch = getc
- if ch == "-"
- ch = getc
- indent = true
- end
- if /['"`]/ =~ ch # '
- lt = ch
- quoted = ""
- while (c = getc) && c != lt
- quoted.concat c
- end
- else
- lt = '"'
- quoted = ch.dup
- while (c = getc) && c =~ /\w/
- quoted.concat c
- end
- ungetc
- end
-
- ltback, @ltype = @ltype, lt
- reserve = ""
-
- while ch = getc
- reserve << ch
- if ch == "\\" #"
- ch = getc
- reserve << ch
- elsif ch == "\n"
- break
- end
- end
-
- str = ""
- while (l = gets)
- l.chomp!
- l.strip! if indent
- break if l == quoted
- str << l.chomp << "\n"
- end
-
- @reader.divert_read_from(reserve)
-
- @ltype = ltback
- @lex_state = EXPR_END
- Token(Ltype2Token[lt], str).set_text(str.dump)
- end
-
- def identify_quotation(initial_char)
- ch = getc
- if lt = PERCENT_LTYPE[ch]
- initial_char += ch
- ch = getc
- elsif ch =~ /\W/
- lt = "\""
- else
- fail SyntaxError, "unknown type of %string ('#{ch}')"
- end
-# if ch !~ /\W/
-# ungetc
-# next
-# end
- #@ltype = lt
- @quoted = ch unless @quoted = PERCENT_PAREN[ch]
- identify_string(lt, @quoted, ch, initial_char)
- end
-
- def identify_number(start)
- str = start.dup
-
- if start == "+" or start == "-" or start == ""
- start = getc
- str << start
- end
-
- @lex_state = EXPR_END
-
- if start == "0"
- if peek(0) == "x"
- ch = getc
- str << ch
- match = /[0-9a-f_]/
- else
- match = /[0-7_]/
- end
- while ch = getc
- if ch !~ match
- ungetc
- break
- else
- str << ch
- end
- end
- return Token(TkINTEGER).set_text(str)
- end
-
- type = TkINTEGER
- allow_point = TRUE
- allow_e = TRUE
- while ch = getc
- case ch
- when /[0-9_]/
- str << ch
-
- when allow_point && "."
- type = TkFLOAT
- if peek(0) !~ /[0-9]/
- ungetc
- break
- end
- str << ch
- allow_point = false
-
- when allow_e && "e", allow_e && "E"
- str << ch
- type = TkFLOAT
- if peek(0) =~ /[+-]/
- str << getc
- end
- allow_e = false
- allow_point = false
- else
- ungetc
- break
- end
- end
- Token(type).set_text(str)
- end
-
- def identify_string(ltype, quoted = ltype, opener=nil, initial_char = nil)
- @ltype = ltype
- @quoted = quoted
- subtype = nil
-
- str = ""
- str << initial_char if initial_char
- str << (opener||quoted)
-
- nest = 0
- begin
- while ch = getc
- str << ch
- if @quoted == ch
- if nest == 0
- break
- else
- nest -= 1
- end
- elsif opener == ch
- nest += 1
- elsif @ltype != "'" && @ltype != "]" and ch == "#"
- ch = getc
- if ch == "{"
- subtype = true
- str << ch << skip_inner_expression
- else
- ungetc(ch)
- end
- elsif ch == '\\' #'
- str << read_escape
- end
- end
- if @ltype == "/"
- if peek(0) =~ /i|o|n|e|s/
- str << getc
- end
- end
- if subtype
- Token(DLtype2Token[ltype], str)
- else
- Token(Ltype2Token[ltype], str)
- end.set_text(str)
- ensure
- @ltype = nil
- @quoted = nil
- @lex_state = EXPR_END
- end
- end
-
- def skip_inner_expression
- res = ""
- nest = 0
- while (ch = getc)
- res << ch
- if ch == '}'
- break if nest.zero?
- nest -= 1
- elsif ch == '{'
- nest += 1
- end
- end
- res
- end
-
- def identify_comment
- @ltype = "#"
- comment = "#"
- while ch = getc
- if ch == "\\"
- ch = getc
- if ch == "\n"
- ch = " "
- else
- comment << "\\"
- end
- else
- if ch == "\n"
- @ltype = nil
- ungetc
- break
- end
- end
- comment << ch
- end
- return Token(TkCOMMENT).set_text(comment)
- end
-
- def read_escape
- res = ""
- case ch = getc
- when /[0-7]/
- ungetc ch
- 3.times do
- case ch = getc
- when /[0-7]/
- when nil
- break
- else
- ungetc
- break
- end
- res << ch
- end
-
- when "x"
- res << ch
- 2.times do
- case ch = getc
- when /[0-9a-fA-F]/
- when nil
- break
- else
- ungetc
- break
- end
- res << ch
- end
-
- when "M"
- res << ch
- if (ch = getc) != '-'
- ungetc
- else
- res << ch
- if (ch = getc) == "\\" #"
- res << ch
- res << read_escape
- else
- res << ch
- end
- end
-
- when "C", "c" #, "^"
- res << ch
- if ch == "C" and (ch = getc) != "-"
- ungetc
- else
- res << ch
- if (ch = getc) == "\\" #"
- res << ch
- res << read_escape
- else
- res << ch
- end
- end
- else
- res << ch
- end
- res
- end
-end
-
-##
-# Extracts code elements from a source file returning a TopLevel object
-# containing the constituent file elements.
-#
-# This file is based on rtags
-#
-# RubyParser understands how to document:
-# * classes
-# * modules
-# * methods
-# * constants
-# * aliases
-# * private, public, protected
-# * private_class_function, public_class_function
-# * module_function
-# * attr, attr_reader, attr_writer, attr_accessor
-# * extra accessors given on the command line
-# * metaprogrammed methods
-# * require
-# * include
-#
-# == Method Arguments
-#
-#--
-# NOTE: I don't think this works, needs tests, remove the paragraph following
-# this block when known to work
-#
-# The parser extracts the arguments from the method definition. You can
-# override this with a custom argument definition using the :args: directive:
-#
-# ##
-# # This method tries over and over until it is tired
-#
-# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
-# puts thing_to_try
-# go_go_go thing_to_try, tries - 1
-# end
-#
-# If you have a more-complex set of overrides you can use the :call-seq:
-# directive:
-#++
-#
-# The parser extracts the arguments from the method definition. You can
-# override this with a custom argument definition using the :call-seq:
-# directive:
-#
-# ##
-# # This method can be called with a range or an offset and length
-# #
-# # :call-seq:
-# # my_method(Range)
-# # my_method(offset, length)
-#
-# def my_method(*args)
-# end
-#
-# The parser extracts +yield+ expressions from method bodies to gather the
-# yielded argument names. If your method manually calls a block instead of
-# yielding or you want to override the discovered argument names use
-# the :yields: directive:
-#
-# ##
-# # My method is awesome
-#
-# def my_method(&block) # :yields: happy, times
-# block.call 1, 2
-# end
-#
-# == Metaprogrammed Methods
-#
-# To pick up a metaprogrammed method, the parser looks for a comment starting
-# with '##' before an identifier:
-#
-# ##
-# # This is a meta-programmed method!
-#
-# add_my_method :meta_method, :arg1, :arg2
-#
-# The parser looks at the token after the identifier to determine the name, in
-# this example, :meta_method. If a name cannot be found, a warning is printed
-# and 'unknown is used.
-#
-# You can force the name of a method using the :method: directive:
-#
-# ##
-# # :method: woo_hoo!
-#
-# By default, meta-methods are instance methods. To indicate that a method is
-# a singleton method instead use the :singleton-method: directive:
-#
-# ##
-# # :singleton-method:
-#
-# You can also use the :singleton-method: directive with a name:
-#
-# ##
-# # :singleton-method: woo_hoo!
-#
-# == Hidden methods
-#
-# You can provide documentation for methods that don't appear using
-# the :method: and :singleton-method: directives:
-#
-# ##
-# # :method: ghost_method
-# # There is a method here, but you can't see it!
-#
-# ##
-# # this is a comment for a regular method
-#
-# def regular_method() end
-#
-# Note that by default, the :method: directive will be ignored if there is a
-# standard rdocable item following it.
-
-class RDoc::Parser::Ruby < RDoc::Parser
-
- parse_files_matching(/\.rbw?$/)
-
- include RDoc::RubyToken
- include RDoc::TokenStream
-
- NORMAL = "::"
- SINGLE = "<<"
-
- def initialize(top_level, file_name, content, options, stats)
- super
-
- @size = 0
- @token_listeners = nil
- @scanner = RDoc::RubyLex.new content, @options
- @scanner.exception_on_syntax_error = false
-
- reset
- end
-
- def add_token_listener(obj)
- @token_listeners ||= []
- @token_listeners << obj
- end
-
- ##
- # Look for the first comment in a file that isn't a shebang line.
-
- def collect_first_comment
- skip_tkspace
- res = ''
- first_line = true
-
- tk = get_tk
-
- while TkCOMMENT === tk
- if first_line and tk.text =~ /\A#!/ then
- skip_tkspace
- tk = get_tk
- elsif first_line and tk.text =~ /\A#\s*-\*-/ then
- first_line = false
- skip_tkspace
- tk = get_tk
- else
- first_line = false
- res << tk.text << "\n"
- tk = get_tk
-
- if TkNL === tk then
- skip_tkspace false
- tk = get_tk
- end
- end
- end
-
- unget_tk tk
-
- res
- end
-
- def error(msg)
- msg = make_message msg
- $stderr.puts msg
- exit(1)
- end
-
- ##
- # Look for a 'call-seq' in the comment, and override the normal parameter
- # stuff
-
- def extract_call_seq(comment, meth)
- if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '') then
- seq = $1
- seq.gsub!(/^\s*\#\s*/, '')
- meth.call_seq = seq
- end
-
- meth
- end
-
- def get_bool
- skip_tkspace
- tk = get_tk
- case tk
- when TkTRUE
- true
- when TkFALSE, TkNIL
- false
- else
- unget_tk tk
- true
- end
- end
-
- ##
- # Look for the name of a class of module (optionally with a leading :: or
- # with :: separated named) and return the ultimate name and container
-
- def get_class_or_module(container)
- skip_tkspace
- name_t = get_tk
-
- # class ::A -> A is in the top level
- if TkCOLON2 === name_t then
- name_t = get_tk
- container = @top_level
- end
-
- skip_tkspace(false)
-
- while TkCOLON2 === peek_tk do
- prev_container = container
- container = container.find_module_named(name_t.name)
- if !container
-# warn("Couldn't find module #{name_t.name}")
- container = prev_container.add_module RDoc::NormalModule, name_t.name
- end
- get_tk
- name_t = get_tk
- end
- skip_tkspace(false)
- return [container, name_t]
- end
-
- ##
- # Return a superclass, which can be either a constant of an expression
-
- def get_class_specification
- tk = get_tk
- return "self" if TkSELF === tk
-
- res = ""
- while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
- res += tk.text
- tk = get_tk
- end
-
- unget_tk(tk)
- skip_tkspace(false)
-
- get_tkread # empty out read buffer
-
- tk = get_tk
-
- case tk
- when TkNL, TkCOMMENT, TkSEMICOLON then
- unget_tk(tk)
- return res
- end
-
- res += parse_call_parameters(tk)
- res
- end
-
- ##
- # Parse a constant, which might be qualified by one or more class or module
- # names
-
- def get_constant
- res = ""
- skip_tkspace(false)
- tk = get_tk
-
- while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
- res += tk.text
- tk = get_tk
- end
-
-# if res.empty?
-# warn("Unexpected token #{tk} in constant")
-# end
- unget_tk(tk)
- res
- end
-
- ##
- # Get a constant that may be surrounded by parens
-
- def get_constant_with_optional_parens
- skip_tkspace(false)
- nest = 0
- while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do
- get_tk
- skip_tkspace(true)
- nest += 1
- end
-
- name = get_constant
-
- while nest > 0
- skip_tkspace(true)
- tk = get_tk
- nest -= 1 if TkRPAREN === tk
- end
- name
- end
-
- def get_symbol_or_name
- tk = get_tk
- case tk
- when TkSYMBOL
- tk.text.sub(/^:/, '')
- when TkId, TkOp
- tk.name
- when TkSTRING
- tk.text
- else
- raise "Name or symbol expected (got #{tk})"
- end
- end
-
- def get_tk
- tk = nil
- if @tokens.empty?
- tk = @scanner.token
- @read.push @scanner.get_read
- puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
- else
- @read.push @unget_read.shift
- tk = @tokens.shift
- puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
- end
-
- if TkSYMBEG === tk then
- set_token_position(tk.line_no, tk.char_no)
- tk1 = get_tk
- if TkId === tk1 or TkOp === tk1 or TkSTRING === tk1 then
- if tk1.respond_to?(:name)
- tk = Token(TkSYMBOL).set_text(":" + tk1.name)
- else
- tk = Token(TkSYMBOL).set_text(":" + tk1.text)
- end
- # remove the identifier we just read (we're about to
- # replace it with a symbol)
- @token_listeners.each do |obj|
- obj.pop_token
- end if @token_listeners
- else
- warn("':' not followed by identifier or operator")
- tk = tk1
- end
- end
-
- # inform any listeners of our shiny new token
- @token_listeners.each do |obj|
- obj.add_token(tk)
- end if @token_listeners
-
- tk
- end
-
- def get_tkread
- read = @read.join("")
- @read = []
- read
- end
-
- ##
- # Look for directives in a normal comment block:
- #
- # #-- - don't display comment from this point forward
- #
- # This routine modifies it's parameter
-
- def look_for_directives_in(context, comment)
- preprocess = RDoc::Markup::PreProcess.new(@file_name,
- @options.rdoc_include)
-
- preprocess.handle(comment) do |directive, param|
- case directive
- when 'enddoc' then
- throw :enddoc
- when 'main' then
- @options.main_page = param
- ''
- when 'method', 'singleton-method' then
- false # ignore
- when 'section' then
- context.set_current_section(param, comment)
- comment.replace ''
- break
- when 'startdoc' then
- context.start_doc
- context.force_documentation = true
- ''
- when 'stopdoc' then
- context.stop_doc
- ''
- when 'title' then
- @options.title = param
- ''
- else
- warn "Unrecognized directive '#{directive}'"
- false
- end
- end
-
- remove_private_comments(comment)
- end
-
- def make_message(msg)
- prefix = "\n" + @file_name + ":"
- if @scanner
- prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
- end
- return prefix + msg
- end
-
- def parse_attr(context, single, tk, comment)
- args = parse_symbol_arg(1)
- if args.size > 0
- name = args[0]
- rw = "R"
- skip_tkspace(false)
- tk = get_tk
- if TkCOMMA === tk then
- rw = "RW" if get_bool
- else
- unget_tk tk
- end
- att = RDoc::Attr.new get_tkread, name, rw, comment
- read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
- if att.document_self
- context.add_attribute(att)
- end
- else
- warn("'attr' ignored - looks like a variable")
- end
- end
-
- def parse_attr_accessor(context, single, tk, comment)
- args = parse_symbol_arg
- read = get_tkread
- rw = "?"
-
- # If nodoc is given, don't document any of them
-
- tmp = RDoc::CodeObject.new
- read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
- return unless tmp.document_self
-
- case tk.name
- when "attr_reader" then rw = "R"
- when "attr_writer" then rw = "W"
- when "attr_accessor" then rw = "RW"
- else
- rw = @options.extra_accessor_flags[tk.name]
- rw = '?' if rw.nil?
- end
-
- for name in args
- att = RDoc::Attr.new get_tkread, name, rw, comment
- context.add_attribute att
- end
- end
-
- def parse_alias(context, single, tk, comment)
- skip_tkspace
- if TkLPAREN === peek_tk then
- get_tk
- skip_tkspace
- end
- new_name = get_symbol_or_name
- @scanner.instance_eval{@lex_state = EXPR_FNAME}
- skip_tkspace
- if TkCOMMA === peek_tk then
- get_tk
- skip_tkspace
- end
- old_name = get_symbol_or_name
-
- al = RDoc::Alias.new get_tkread, old_name, new_name, comment
- read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
- if al.document_self
- context.add_alias(al)
- end
- end
-
- def parse_call_parameters(tk)
- end_token = case tk
- when TkLPAREN, TkfLPAREN
- TkRPAREN
- when TkRPAREN
- return ""
- else
- TkNL
- end
- nest = 0
-
- loop do
- case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN
- nest += 1
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest <= 0
- else
- break unless @scanner.continue
- end
- when TkCOMMENT
- unget_tk(tk)
- break
- end
- tk = get_tk
- end
- res = get_tkread.tr("\n", " ").strip
- res = "" if res == ";"
- res
- end
-
- def parse_class(container, single, tk, comment)
- container, name_t = get_class_or_module(container)
-
- case name_t
- when TkCONSTANT
- name = name_t.name
- superclass = "Object"
-
- if TkLT === peek_tk then
- get_tk
- skip_tkspace(true)
- superclass = get_class_specification
- superclass = "<unknown>" if superclass.empty?
- end
-
- cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
- cls = container.add_class cls_type, name, superclass
-
- @stats.add_class cls
-
- read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
- cls.record_location @top_level
-
- parse_statements cls
- cls.comment = comment
-
- when TkLSHFT
- case name = get_class_specification
- when "self", container.name
- parse_statements(container, SINGLE)
- else
- other = RDoc::TopLevel.find_class_named(name)
- unless other
- # other = @top_level.add_class(NormalClass, name, nil)
- # other.record_location(@top_level)
- # other.comment = comment
- other = RDoc::NormalClass.new "Dummy", nil
- end
-
- @stats.add_class other
-
- read_documentation_modifiers other, RDoc::CLASS_MODIFIERS
- parse_statements(other, SINGLE)
- end
-
- else
- warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}")
- end
- end
-
- def parse_constant(container, single, tk, comment)
- name = tk.name
- skip_tkspace(false)
- eq_tk = get_tk
-
- unless TkASSIGN === eq_tk then
- unget_tk(eq_tk)
- return
- end
-
-
- nest = 0
- get_tkread
-
- tk = get_tk
- if TkGT === tk then
- unget_tk(tk)
- unget_tk(eq_tk)
- return
- end
-
- loop do
- case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO
- nest += 1
- when TkRPAREN, TkRBRACE, TkRBRACK, TkEND
- nest -= 1
- when TkCOMMENT
- if nest <= 0 && @scanner.lex_state == EXPR_END
- unget_tk(tk)
- break
- end
- when TkNL
- if (nest <= 0) && ((@scanner.lex_state == EXPR_END) || (!@scanner.continue))
- unget_tk(tk)
- break
- end
- end
- tk = get_tk
- end
-
- res = get_tkread.tr("\n", " ").strip
- res = "" if res == ";"
-
- con = RDoc::Constant.new name, res, comment
- read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
-
- if con.document_self
- container.add_constant(con)
- end
- end
-
- def parse_comment(container, tk, comment)
- line_no = tk.line_no
- column = tk.char_no
-
- singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
-
- if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
- name = $1 unless $1.empty?
- else
- return nil
- end
-
- meth = RDoc::GhostMethod.new get_tkread, name
- meth.singleton = singleton
-
- @stats.add_method meth
-
- meth.start_collecting_tokens
- indent = TkSPACE.new 1, 1
- indent.set_text " " * column
-
- position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
- meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
-
- meth.params = ''
-
- extract_call_seq comment, meth
-
- container.add_method meth if meth.document_self
-
- meth.comment = comment
- end
-
- def parse_include(context, comment)
- loop do
- skip_tkspace_comment
-
- name = get_constant_with_optional_parens
- context.add_include RDoc::Include.new(name, comment) unless name.empty?
-
- return unless TkCOMMA === peek_tk
- get_tk
- end
- end
-
- ##
- # Parses a meta-programmed method
-
- def parse_meta_method(container, single, tk, comment)
- line_no = tk.line_no
- column = tk.char_no
-
- start_collecting_tokens
- add_token tk
- add_token_listener self
-
- skip_tkspace false
-
- singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
-
- if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
- name = $1 unless $1.empty?
- end
-
- if name.nil? then
- name_t = get_tk
- case name_t
- when TkSYMBOL then
- name = name_t.text[1..-1]
- when TkSTRING then
- name = name_t.text[1..-2]
- else
- warn "#{container.top_level.file_relative_name}:#{name_t.line_no} unknown name token #{name_t.inspect} for meta-method"
- name = 'unknown'
- end
- end
-
- meth = RDoc::MetaMethod.new get_tkread, name
- meth.singleton = singleton
-
- @stats.add_method meth
-
- remove_token_listener self
-
- meth.start_collecting_tokens
- indent = TkSPACE.new 1, 1
- indent.set_text " " * column
-
- position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
- meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
- meth.add_tokens @token_stream
-
- add_token_listener meth
-
- meth.params = ''
-
- extract_call_seq comment, meth
-
- container.add_method meth if meth.document_self
-
- last_tk = tk
-
- while tk = get_tk do
- case tk
- when TkSEMICOLON then
- break
- when TkNL then
- break unless last_tk and TkCOMMA === last_tk
- when TkSPACE then
- # expression continues
- else
- last_tk = tk
- end
- end
-
- remove_token_listener meth
-
- meth.comment = comment
- end
-
- ##
- # Parses a method
-
- def parse_method(container, single, tk, comment)
- line_no = tk.line_no
- column = tk.char_no
-
- start_collecting_tokens
- add_token(tk)
- add_token_listener(self)
-
- @scanner.instance_eval do @lex_state = EXPR_FNAME end
-
- skip_tkspace(false)
- name_t = get_tk
- back_tk = skip_tkspace
- meth = nil
- added_container = false
-
- dot = get_tk
- if TkDOT === dot or TkCOLON2 === dot then
- @scanner.instance_eval do @lex_state = EXPR_FNAME end
- skip_tkspace
- name_t2 = get_tk
-
- case name_t
- when TkSELF then
- name = name_t2.name
- when TkCONSTANT then
- name = name_t2.name
- prev_container = container
- container = container.find_module_named(name_t.name)
- unless container then
- added_container = true
- obj = name_t.name.split("::").inject(Object) do |state, item|
- state.const_get(item)
- end rescue nil
-
- type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
-
- unless [Class, Module].include?(obj.class) then
- warn("Couldn't find #{name_t.name}. Assuming it's a module")
- end
-
- if type == RDoc::NormalClass then
- container = prev_container.add_class(type, name_t.name, obj.superclass.name)
- else
- container = prev_container.add_module(type, name_t.name)
- end
-
- container.record_location @top_level
- end
- else
- # warn("Unexpected token '#{name_t2.inspect}'")
- # break
- skip_method(container)
- return
- end
-
- meth = RDoc::AnyMethod.new(get_tkread, name)
- meth.singleton = true
- else
- unget_tk dot
- back_tk.reverse_each do |token|
- unget_tk token
- end
- name = name_t.name
-
- meth = RDoc::AnyMethod.new get_tkread, name
- meth.singleton = (single == SINGLE)
- end
-
- @stats.add_method meth
-
- remove_token_listener self
-
- meth.start_collecting_tokens
- indent = TkSPACE.new 1, 1
- indent.set_text " " * column
-
- token = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
- meth.add_tokens [token, NEWLINE_TOKEN, indent]
- meth.add_tokens @token_stream
-
- add_token_listener meth
-
- @scanner.instance_eval do @continue = false end
- parse_method_parameters meth
-
- if meth.document_self then
- container.add_method meth
- elsif added_container then
- container.document_self = false
- end
-
- # Having now read the method parameters and documentation modifiers, we
- # now know whether we have to rename #initialize to ::new
-
- if name == "initialize" && !meth.singleton then
- if meth.dont_rename_initialize then
- meth.visibility = :protected
- else
- meth.singleton = true
- meth.name = "new"
- meth.visibility = :public
- end
- end
-
- parse_statements(container, single, meth)
-
- remove_token_listener(meth)
-
- extract_call_seq comment, meth
-
- meth.comment = comment
- end
-
- def parse_method_or_yield_parameters(method = nil,
- modifiers = RDoc::METHOD_MODIFIERS)
- skip_tkspace(false)
- tk = get_tk
-
- # Little hack going on here. In the statement
- # f = 2*(1+yield)
- # We see the RPAREN as the next token, so we need
- # to exit early. This still won't catch all cases
- # (such as "a = yield + 1"
- end_token = case tk
- when TkLPAREN, TkfLPAREN
- TkRPAREN
- when TkRPAREN
- return ""
- else
- TkNL
- end
- nest = 0
-
- loop do
- case tk
- when TkSEMICOLON
- break
- when TkLBRACE
- nest += 1
- when TkRBRACE
- # we might have a.each {|i| yield i }
- unget_tk(tk) if nest.zero?
- nest -= 1
- break if nest <= 0
- when TkLPAREN, TkfLPAREN
- nest += 1
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest <= 0
- else
- break unless @scanner.continue
- end
- when method && method.block_params.nil? && TkCOMMENT
- unget_tk(tk)
- read_documentation_modifiers(method, modifiers)
- end
- tk = get_tk
- end
- res = get_tkread.tr("\n", " ").strip
- res = "" if res == ";"
- res
- end
-
- ##
- # Capture the method's parameters. Along the way, look for a comment
- # containing:
- #
- # # yields: ....
- #
- # and add this as the block_params for the method
-
- def parse_method_parameters(method)
- res = parse_method_or_yield_parameters(method)
- res = "(" + res + ")" unless res[0] == ?(
- method.params = res unless method.params
- if method.block_params.nil?
- skip_tkspace(false)
- read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
- end
- end
-
- def parse_module(container, single, tk, comment)
- container, name_t = get_class_or_module(container)
-
- name = name_t.name
-
- mod = container.add_module RDoc::NormalModule, name
- mod.record_location @top_level
-
- @stats.add_module mod
-
- read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
- parse_statements(mod)
- mod.comment = comment
- end
-
- def parse_require(context, comment)
- skip_tkspace_comment
- tk = get_tk
- if TkLPAREN === tk then
- skip_tkspace_comment
- tk = get_tk
- end
-
- name = nil
- case tk
- when TkSTRING
- name = tk.text
- # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
- # name = tk.name
- when TkDSTRING
- warn "Skipping require of dynamic string: #{tk.text}"
- # else
- # warn "'require' used as variable"
- end
- if name
- context.add_require RDoc::Require.new(name, comment)
- else
- unget_tk(tk)
- end
- end
-
- def parse_statements(container, single = NORMAL, current_method = nil,
- comment = '')
- nest = 1
- save_visibility = container.visibility
-
- non_comment_seen = true
-
- while tk = get_tk do
- keep_comment = false
-
- non_comment_seen = true unless TkCOMMENT === tk
-
- case tk
- when TkNL then
- skip_tkspace true # Skip blanks and newlines
- tk = get_tk
-
- if TkCOMMENT === tk then
- if non_comment_seen then
- # Look for RDoc in a comment about to be thrown away
- parse_comment container, tk, comment unless comment.empty?
-
- comment = ''
- non_comment_seen = false
- end
-
- while TkCOMMENT === tk do
- comment << tk.text << "\n"
- tk = get_tk # this is the newline
- skip_tkspace(false) # leading spaces
- tk = get_tk
- end
-
- unless comment.empty? then
- look_for_directives_in container, comment
-
- if container.done_documenting then
- container.ongoing_visibility = save_visibility
- end
- end
-
- keep_comment = true
- else
- non_comment_seen = true
- end
-
- unget_tk tk
- keep_comment = true
-
- when TkCLASS then
- if container.document_children then
- parse_class container, single, tk, comment
- else
- nest += 1
- end
-
- when TkMODULE then
- if container.document_children then
- parse_module container, single, tk, comment
- else
- nest += 1
- end
-
- when TkDEF then
- if container.document_self then
- parse_method container, single, tk, comment
- else
- nest += 1
- end
-
- when TkCONSTANT then
- if container.document_self then
- parse_constant container, single, tk, comment
- end
-
- when TkALIAS then
- if container.document_self then
- parse_alias container, single, tk, comment
- end
-
- when TkYIELD then
- if current_method.nil? then
- warn "Warning: yield outside of method" if container.document_self
- else
- parse_yield container, single, tk, current_method
- end
-
- # Until and While can have a 'do', which shouldn't increase the nesting.
- # We can't solve the general case, but we can handle most occurrences by
- # ignoring a do at the end of a line.
-
- when TkUNTIL, TkWHILE then
- nest += 1
- skip_optional_do_after_expression
-
- # 'for' is trickier
- when TkFOR then
- nest += 1
- skip_for_variable
- skip_optional_do_after_expression
-
- when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN then
- nest += 1
-
- when TkIDENTIFIER then
- if nest == 1 and current_method.nil? then
- case tk.name
- when 'private', 'protected', 'public', 'private_class_method',
- 'public_class_method', 'module_function' then
- parse_visibility container, single, tk
- keep_comment = true
- when 'attr' then
- parse_attr container, single, tk, comment
- when /^attr_(reader|writer|accessor)$/, @options.extra_accessors then
- parse_attr_accessor container, single, tk, comment
- when 'alias_method' then
- if container.document_self then
- parse_alias container, single, tk, comment
- end
- else
- if container.document_self and comment =~ /\A#\#$/ then
- parse_meta_method container, single, tk, comment
- end
- end
- end
-
- case tk.name
- when "require" then
- parse_require container, comment
- when "include" then
- parse_include container, comment
- end
-
- when TkEND then
- nest -= 1
- if nest == 0 then
- read_documentation_modifiers container, RDoc::CLASS_MODIFIERS
- container.ongoing_visibility = save_visibility
- return
- end
-
- end
-
- comment = '' unless keep_comment
-
- begin
- get_tkread
- skip_tkspace(false)
- end while peek_tk == TkNL
- end
- end
-
- def parse_symbol_arg(no = nil)
- args = []
- skip_tkspace_comment
- case tk = get_tk
- when TkLPAREN
- loop do
- skip_tkspace_comment
- if tk1 = parse_symbol_in_arg
- args.push tk1
- break if no and args.size >= no
- end
-
- skip_tkspace_comment
- case tk2 = get_tk
- when TkRPAREN
- break
- when TkCOMMA
- else
- warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
- break
- end
- end
- else
- unget_tk tk
- if tk = parse_symbol_in_arg
- args.push tk
- return args if no and args.size >= no
- end
-
- loop do
- skip_tkspace(false)
-
- tk1 = get_tk
- unless TkCOMMA === tk1 then
- unget_tk tk1
- break
- end
-
- skip_tkspace_comment
- if tk = parse_symbol_in_arg
- args.push tk
- break if no and args.size >= no
- end
- end
- end
- args
- end
-
- def parse_symbol_in_arg
- case tk = get_tk
- when TkSYMBOL
- tk.text.sub(/^:/, '')
- when TkSTRING
- eval @read[-1]
- else
- warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
- nil
- end
- end
-
- def parse_toplevel_statements(container)
- comment = collect_first_comment
- look_for_directives_in(container, comment)
- container.comment = comment unless comment.empty?
- parse_statements container, NORMAL, nil, comment
- end
-
- def parse_visibility(container, single, tk)
- singleton = (single == SINGLE)
-
- vis_type = tk.name
-
- vis = case vis_type
- when 'private' then :private
- when 'protected' then :protected
- when 'public' then :public
- when 'private_class_method' then
- singleton = true
- :private
- when 'public_class_method' then
- singleton = true
- :public
- when 'module_function' then
- singleton = true
- :public
- else
- raise "Invalid visibility: #{tk.name}"
- end
-
- skip_tkspace_comment false
-
- case peek_tk
- # Ryan Davis suggested the extension to ignore modifiers, because he
- # often writes
- #
- # protected unless $TESTING
- #
- when TkNL, TkUNLESS_MOD, TkIF_MOD, TkSEMICOLON then
- container.ongoing_visibility = vis
- else
- if vis_type == 'module_function' then
- args = parse_symbol_arg
- container.set_visibility_for args, :private, false
-
- module_functions = []
-
- container.methods_matching args do |m|
- s_m = m.dup
- s_m.singleton = true if RDoc::AnyMethod === s_m
- s_m.visibility = :public
- module_functions << s_m
- end
-
- module_functions.each do |s_m|
- case s_m
- when RDoc::AnyMethod then
- container.add_method s_m
- when RDoc::Attr then
- container.add_attribute s_m
- end
- end
- else
- args = parse_symbol_arg
- container.set_visibility_for args, vis, singleton
- end
- end
- end
-
- def parse_yield_parameters
- parse_method_or_yield_parameters
- end
-
- def parse_yield(context, single, tk, method)
- if method.block_params.nil?
- get_tkread
- @scanner.instance_eval{@continue = false}
- method.block_params = parse_yield_parameters
- end
- end
-
- def peek_read
- @read.join('')
- end
-
- ##
- # Peek at the next token, but don't remove it from the stream
-
- def peek_tk
- unget_tk(tk = get_tk)
- tk
- end
-
- ##
- # Directives are modifier comments that can appear after class, module, or
- # method names. For example:
- #
- # def fred # :yields: a, b
- #
- # or:
- #
- # class MyClass # :nodoc:
- #
- # We return the directive name and any parameters as a two element array
-
- def read_directive(allowed)
- tk = get_tk
- result = nil
- if TkCOMMENT === tk
- if tk.text =~ /\s*:?(\w+):\s*(.*)/
- directive = $1.downcase
- if allowed.include?(directive)
- result = [directive, $2]
- end
- end
- else
- unget_tk(tk)
- end
- result
- end
-
- def read_documentation_modifiers(context, allow)
- dir = read_directive(allow)
-
- case dir[0]
- when "notnew", "not_new", "not-new" then
- context.dont_rename_initialize = true
-
- when "nodoc" then
- context.document_self = false
- if dir[1].downcase == "all"
- context.document_children = false
- end
-
- when "doc" then
- context.document_self = true
- context.force_documentation = true
-
- when "yield", "yields" then
- unless context.params.nil?
- context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
- end
-
- context.block_params = dir[1]
-
- when "arg", "args" then
- context.params = dir[1]
- end if dir
- end
-
- def remove_private_comments(comment)
- comment.gsub!(/^#--\n.*?^#\+\+/m, '')
- comment.sub!(/^#--\n.*/m, '')
- end
-
- def remove_token_listener(obj)
- @token_listeners.delete(obj)
- end
-
- def reset
- @tokens = []
- @unget_read = []
- @read = []
- end
-
- def scan
- reset
-
- catch(:eof) do
- catch(:enddoc) do
- begin
- parse_toplevel_statements(@top_level)
- rescue Exception => e
- $stderr.puts <<-EOF
-
-
-RDoc failure in #{@file_name} at or around line #{@scanner.line_no} column
-#{@scanner.char_no}
-
-Before reporting this, could you check that the file you're documenting
-compiles cleanly--RDoc is not a full Ruby parser, and gets confused easily if
-fed invalid programs.
-
-The internal error was:
-
- EOF
-
- e.set_backtrace(e.backtrace[0,4])
- raise
- end
- end
- end
-
- @top_level
- end
-
- ##
- # while, until, and for have an optional do
-
- def skip_optional_do_after_expression
- skip_tkspace(false)
- tk = get_tk
- case tk
- when TkLPAREN, TkfLPAREN
- end_token = TkRPAREN
- else
- end_token = TkNL
- end
-
- nest = 0
- @scanner.instance_eval{@continue = false}
-
- loop do
- case tk
- when TkSEMICOLON
- break
- when TkLPAREN, TkfLPAREN
- nest += 1
- when TkDO
- break if nest.zero?
- when end_token
- if end_token == TkRPAREN
- nest -= 1
- break if @scanner.lex_state == EXPR_END and nest.zero?
- else
- break unless @scanner.continue
- end
- end
- tk = get_tk
- end
- skip_tkspace(false)
-
- get_tk if TkDO === peek_tk
- end
-
- ##
- # skip the var [in] part of a 'for' statement
-
- def skip_for_variable
- skip_tkspace(false)
- tk = get_tk
- skip_tkspace(false)
- tk = get_tk
- unget_tk(tk) unless TkIN === tk
- end
-
- def skip_method(container)
- meth = RDoc::AnyMethod.new "", "anon"
- parse_method_parameters(meth)
- parse_statements(container, false, meth)
- end
-
- ##
- # Skip spaces
-
- def skip_tkspace(skip_nl = true)
- tokens = []
-
- while TkSPACE === (tk = get_tk) or (skip_nl and TkNL === tk) do
- tokens.push tk
- end
-
- unget_tk(tk)
- tokens
- end
-
- ##
- # Skip spaces until a comment is found
-
- def skip_tkspace_comment(skip_nl = true)
- loop do
- skip_tkspace(skip_nl)
- return unless TkCOMMENT === peek_tk
- get_tk
- end
- end
-
- def unget_tk(tk)
- @tokens.unshift tk
- @unget_read.unshift @read.pop
-
- # Remove this token from any listeners
- @token_listeners.each do |obj|
- obj.pop_token
- end if @token_listeners
- end
-
- def warn(msg)
- return if @options.quiet
- msg = make_message msg
- $stderr.puts msg
- end
-
-end
-
Copied: MacRuby/branches/experimental/lib/rdoc/parser/ruby.rb (from rev 1886, MacRuby/trunk/lib/rdoc/parser/ruby.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/parser/ruby.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/parser/ruby.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,2829 @@
+##
+# This file contains stuff stolen outright from:
+#
+# rtags.rb -
+# ruby-lex.rb - ruby lexcal analyzer
+# ruby-token.rb - ruby tokens
+# by Keiju ISHITSUKA (Nippon Rational Inc.)
+#
+
+require 'e2mmap'
+require 'irb/slex'
+
+require 'rdoc/code_objects'
+require 'rdoc/tokenstream'
+require 'rdoc/markup/preprocess'
+require 'rdoc/parser'
+
+$TOKEN_DEBUG ||= nil
+#$TOKEN_DEBUG = $DEBUG_RDOC
+
+##
+# Definitions of all tokens involved in the lexical analysis
+
+module RDoc::RubyToken
+
+ EXPR_BEG = :EXPR_BEG
+ EXPR_MID = :EXPR_MID
+ EXPR_END = :EXPR_END
+ EXPR_ARG = :EXPR_ARG
+ EXPR_FNAME = :EXPR_FNAME
+ EXPR_DOT = :EXPR_DOT
+ EXPR_CLASS = :EXPR_CLASS
+
+ class Token
+ NO_TEXT = "??".freeze
+
+ attr_accessor :text
+ attr_reader :line_no
+ attr_reader :char_no
+
+ def initialize(line_no, char_no)
+ @line_no = line_no
+ @char_no = char_no
+ @text = NO_TEXT
+ end
+
+ def ==(other)
+ self.class == other.class and
+ other.line_no == @line_no and
+ other.char_no == @char_no and
+ other.text == @text
+ end
+
+ ##
+ # Because we're used in contexts that expect to return a token, we set the
+ # text string and then return ourselves
+
+ def set_text(text)
+ @text = text
+ self
+ end
+
+ end
+
+ class TkNode < Token
+ attr :node
+ end
+
+ class TkId < Token
+ def initialize(line_no, char_no, name)
+ super(line_no, char_no)
+ @name = name
+ end
+ attr :name
+ end
+
+ class TkKW < TkId
+ end
+
+ class TkVal < Token
+ def initialize(line_no, char_no, value = nil)
+ super(line_no, char_no)
+ set_text(value)
+ end
+ end
+
+ class TkOp < Token
+ def name
+ self.class.op_name
+ end
+ end
+
+ class TkOPASGN < TkOp
+ def initialize(line_no, char_no, op)
+ super(line_no, char_no)
+ op = TkReading2Token[op] unless Symbol === op
+ @op = op
+ end
+ attr :op
+ end
+
+ class TkUnknownChar < Token
+ def initialize(line_no, char_no, id)
+ super(line_no, char_no)
+ @name = char_no.chr
+ end
+ attr :name
+ end
+
+ class TkError < Token
+ end
+
+ def set_token_position(line, char)
+ @prev_line_no = line
+ @prev_char_no = char
+ end
+
+ def Token(token, value = nil)
+ tk = nil
+ case token
+ when String, Symbol
+ source = String === token ? TkReading2Token : TkSymbol2Token
+ raise TkReading2TokenNoKey, token if (tk = source[token]).nil?
+ tk = Token(tk[0], value)
+ else
+ tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty?
+ token.new(@prev_line_no, @prev_char_no)
+ else
+ token.new(@prev_line_no, @prev_char_no, value)
+ end
+ end
+ tk
+ end
+
+ TokenDefinitions = [
+ [:TkCLASS, TkKW, "class", EXPR_CLASS],
+ [:TkMODULE, TkKW, "module", EXPR_CLASS],
+ [:TkDEF, TkKW, "def", EXPR_FNAME],
+ [:TkUNDEF, TkKW, "undef", EXPR_FNAME],
+ [:TkBEGIN, TkKW, "begin", EXPR_BEG],
+ [:TkRESCUE, TkKW, "rescue", EXPR_MID],
+ [:TkENSURE, TkKW, "ensure", EXPR_BEG],
+ [:TkEND, TkKW, "end", EXPR_END],
+ [:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD],
+ [:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD],
+ [:TkTHEN, TkKW, "then", EXPR_BEG],
+ [:TkELSIF, TkKW, "elsif", EXPR_BEG],
+ [:TkELSE, TkKW, "else", EXPR_BEG],
+ [:TkCASE, TkKW, "case", EXPR_BEG],
+ [:TkWHEN, TkKW, "when", EXPR_BEG],
+ [:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD],
+ [:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD],
+ [:TkFOR, TkKW, "for", EXPR_BEG],
+ [:TkBREAK, TkKW, "break", EXPR_END],
+ [:TkNEXT, TkKW, "next", EXPR_END],
+ [:TkREDO, TkKW, "redo", EXPR_END],
+ [:TkRETRY, TkKW, "retry", EXPR_END],
+ [:TkIN, TkKW, "in", EXPR_BEG],
+ [:TkDO, TkKW, "do", EXPR_BEG],
+ [:TkRETURN, TkKW, "return", EXPR_MID],
+ [:TkYIELD, TkKW, "yield", EXPR_END],
+ [:TkSUPER, TkKW, "super", EXPR_END],
+ [:TkSELF, TkKW, "self", EXPR_END],
+ [:TkNIL, TkKW, "nil", EXPR_END],
+ [:TkTRUE, TkKW, "true", EXPR_END],
+ [:TkFALSE, TkKW, "false", EXPR_END],
+ [:TkAND, TkKW, "and", EXPR_BEG],
+ [:TkOR, TkKW, "or", EXPR_BEG],
+ [:TkNOT, TkKW, "not", EXPR_BEG],
+ [:TkIF_MOD, TkKW],
+ [:TkUNLESS_MOD, TkKW],
+ [:TkWHILE_MOD, TkKW],
+ [:TkUNTIL_MOD, TkKW],
+ [:TkALIAS, TkKW, "alias", EXPR_FNAME],
+ [:TkDEFINED, TkKW, "defined?", EXPR_END],
+ [:TklBEGIN, TkKW, "BEGIN", EXPR_END],
+ [:TklEND, TkKW, "END", EXPR_END],
+ [:Tk__LINE__, TkKW, "__LINE__", EXPR_END],
+ [:Tk__FILE__, TkKW, "__FILE__", EXPR_END],
+
+ [:TkIDENTIFIER, TkId],
+ [:TkFID, TkId],
+ [:TkGVAR, TkId],
+ [:TkIVAR, TkId],
+ [:TkCONSTANT, TkId],
+
+ [:TkINTEGER, TkVal],
+ [:TkFLOAT, TkVal],
+ [:TkSTRING, TkVal],
+ [:TkXSTRING, TkVal],
+ [:TkREGEXP, TkVal],
+ [:TkCOMMENT, TkVal],
+
+ [:TkDSTRING, TkNode],
+ [:TkDXSTRING, TkNode],
+ [:TkDREGEXP, TkNode],
+ [:TkNTH_REF, TkId],
+ [:TkBACK_REF, TkId],
+
+ [:TkUPLUS, TkOp, "+@"],
+ [:TkUMINUS, TkOp, "-@"],
+ [:TkPOW, TkOp, "**"],
+ [:TkCMP, TkOp, "<=>"],
+ [:TkEQ, TkOp, "=="],
+ [:TkEQQ, TkOp, "==="],
+ [:TkNEQ, TkOp, "!="],
+ [:TkGEQ, TkOp, ">="],
+ [:TkLEQ, TkOp, "<="],
+ [:TkANDOP, TkOp, "&&"],
+ [:TkOROP, TkOp, "||"],
+ [:TkMATCH, TkOp, "=~"],
+ [:TkNMATCH, TkOp, "!~"],
+ [:TkDOT2, TkOp, ".."],
+ [:TkDOT3, TkOp, "..."],
+ [:TkAREF, TkOp, "[]"],
+ [:TkASET, TkOp, "[]="],
+ [:TkLSHFT, TkOp, "<<"],
+ [:TkRSHFT, TkOp, ">>"],
+ [:TkCOLON2, TkOp],
+ [:TkCOLON3, TkOp],
+# [:OPASGN, TkOp], # +=, -= etc. #
+ [:TkASSOC, TkOp, "=>"],
+ [:TkQUESTION, TkOp, "?"], #?
+ [:TkCOLON, TkOp, ":"], #:
+
+ [:TkfLPAREN], # func( #
+ [:TkfLBRACK], # func[ #
+ [:TkfLBRACE], # func{ #
+ [:TkSTAR], # *arg
+ [:TkAMPER], # &arg #
+ [:TkSYMBOL, TkId], # :SYMBOL
+ [:TkSYMBEG, TkId],
+ [:TkGT, TkOp, ">"],
+ [:TkLT, TkOp, "<"],
+ [:TkPLUS, TkOp, "+"],
+ [:TkMINUS, TkOp, "-"],
+ [:TkMULT, TkOp, "*"],
+ [:TkDIV, TkOp, "/"],
+ [:TkMOD, TkOp, "%"],
+ [:TkBITOR, TkOp, "|"],
+ [:TkBITXOR, TkOp, "^"],
+ [:TkBITAND, TkOp, "&"],
+ [:TkBITNOT, TkOp, "~"],
+ [:TkNOTOP, TkOp, "!"],
+
+ [:TkBACKQUOTE, TkOp, "`"],
+
+ [:TkASSIGN, Token, "="],
+ [:TkDOT, Token, "."],
+ [:TkLPAREN, Token, "("], #(exp)
+ [:TkLBRACK, Token, "["], #[arry]
+ [:TkLBRACE, Token, "{"], #{hash}
+ [:TkRPAREN, Token, ")"],
+ [:TkRBRACK, Token, "]"],
+ [:TkRBRACE, Token, "}"],
+ [:TkCOMMA, Token, ","],
+ [:TkSEMICOLON, Token, ";"],
+
+ [:TkRD_COMMENT],
+ [:TkSPACE],
+ [:TkNL],
+ [:TkEND_OF_SCRIPT],
+
+ [:TkBACKSLASH, TkUnknownChar, "\\"],
+ [:TkAT, TkUnknownChar, "@"],
+ [:TkDOLLAR, TkUnknownChar, "\$"], #"
+ ]
+
+ # {reading => token_class}
+ # {reading => [token_class, *opt]}
+ TkReading2Token = {}
+ TkSymbol2Token = {}
+
+ def self.def_token(token_n, super_token = Token, reading = nil, *opts)
+ token_n = token_n.id2name unless String === token_n
+
+ fail AlreadyDefinedToken, token_n if const_defined?(token_n)
+
+ token_c = Class.new super_token
+ const_set token_n, token_c
+# token_c.inspect
+
+ if reading
+ if TkReading2Token[reading]
+ fail TkReading2TokenDuplicateError, token_n, reading
+ end
+ if opts.empty?
+ TkReading2Token[reading] = [token_c]
+ else
+ TkReading2Token[reading] = [token_c].concat(opts)
+ end
+ end
+ TkSymbol2Token[token_n.intern] = token_c
+
+ if token_c <= TkOp
+ token_c.class_eval %{
+ def self.op_name; "#{reading}"; end
+ }
+ end
+ end
+
+ for defs in TokenDefinitions
+ def_token(*defs)
+ end
+
+ NEWLINE_TOKEN = TkNL.new(0,0)
+ NEWLINE_TOKEN.set_text("\n")
+
+end
+
+##
+# Lexical analyzer for Ruby source
+
+class RDoc::RubyLex
+
+ ##
+ # Read an input stream character by character. We allow for unlimited
+ # ungetting of characters just read.
+ #
+ # We simplify the implementation greatly by reading the entire input
+ # into a buffer initially, and then simply traversing it using
+ # pointers.
+ #
+ # We also have to allow for the <i>here document diversion</i>. This
+ # little gem comes about when the lexer encounters a here
+ # document. At this point we effectively need to split the input
+ # stream into two parts: one to read the body of the here document,
+ # the other to read the rest of the input line where the here
+ # document was initially encountered. For example, we might have
+ #
+ # do_something(<<-A, <<-B)
+ # stuff
+ # for
+ # A
+ # stuff
+ # for
+ # B
+ #
+ # When the lexer encounters the <<A, it reads until the end of the
+ # line, and keeps it around for later. It then reads the body of the
+ # here document. Once complete, it needs to read the rest of the
+ # original line, but then skip the here document body.
+ #
+
+ class BufferedReader
+
+ attr_reader :line_num
+
+ def initialize(content, options)
+ @options = options
+
+ if /\t/ =~ content
+ tab_width = @options.tab_width
+ content = content.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
+ line
+ end .join("\n")
+ end
+ @content = content
+ @content << "\n" unless @content[-1,1] == "\n"
+ @size = @content.size
+ @offset = 0
+ @hwm = 0
+ @line_num = 1
+ @read_back_offset = 0
+ @last_newline = 0
+ @newline_pending = false
+ end
+
+ def column
+ @offset - @last_newline
+ end
+
+ def getc
+ return nil if @offset >= @size
+ ch = @content[@offset, 1]
+
+ @offset += 1
+ @hwm = @offset if @hwm < @offset
+
+ if @newline_pending
+ @line_num += 1
+ @last_newline = @offset - 1
+ @newline_pending = false
+ end
+
+ if ch == "\n"
+ @newline_pending = true
+ end
+ ch
+ end
+
+ def getc_already_read
+ getc
+ end
+
+ def ungetc(ch)
+ raise "unget past beginning of file" if @offset <= 0
+ @offset -= 1
+ if @content[@offset] == ?\n
+ @newline_pending = false
+ end
+ end
+
+ def get_read
+ res = @content[@read_back_offset... at offset]
+ @read_back_offset = @offset
+ res
+ end
+
+ def peek(at)
+ pos = @offset + at
+ if pos >= @size
+ nil
+ else
+ @content[pos, 1]
+ end
+ end
+
+ def peek_equal(str)
+ @content[@offset, str.length] == str
+ end
+
+ def divert_read_from(reserve)
+ @content[@offset, 0] = reserve
+ @size = @content.size
+ end
+ end
+
+ # end of nested class BufferedReader
+
+ extend Exception2MessageMapper
+ def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
+ def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
+ def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
+ def_exception(:TkReading2TokenDuplicateError,
+ "key duplicate(token_n='%s', key='%s')")
+ def_exception(:SyntaxError, "%s")
+
+ include RDoc::RubyToken
+ include IRB
+
+ attr_reader :continue
+ attr_reader :lex_state
+
+ def self.debug?
+ false
+ end
+
+ def initialize(content, options)
+ lex_init
+
+ @options = options
+
+ @reader = BufferedReader.new content, @options
+
+ @exp_line_no = @line_no = 1
+ @base_char_no = 0
+ @indent = 0
+
+ @ltype = nil
+ @quoted = nil
+ @lex_state = EXPR_BEG
+ @space_seen = false
+
+ @continue = false
+ @line = ""
+
+ @skip_space = false
+ @read_auto_clean_up = false
+ @exception_on_syntax_error = true
+ end
+
+ attr_accessor :skip_space
+ attr_accessor :read_auto_clean_up
+ attr_accessor :exception_on_syntax_error
+ attr_reader :indent
+
+ # io functions
+ def line_no
+ @reader.line_num
+ end
+
+ def char_no
+ @reader.column
+ end
+
+ def get_read
+ @reader.get_read
+ end
+
+ def getc
+ @reader.getc
+ end
+
+ def getc_of_rests
+ @reader.getc_already_read
+ end
+
+ def gets
+ c = getc or return
+ l = ""
+ begin
+ l.concat c unless c == "\r"
+ break if c == "\n"
+ end while c = getc
+ l
+ end
+
+
+ def ungetc(c = nil)
+ @reader.ungetc(c)
+ end
+
+ def peek_equal?(str)
+ @reader.peek_equal(str)
+ end
+
+ def peek(i = 0)
+ @reader.peek(i)
+ end
+
+ def lex
+ until (TkNL === (tk = token) or TkEND_OF_SCRIPT === tk) and
+ not @continue or tk.nil?
+ end
+
+ line = get_read
+
+ if line == "" and TkEND_OF_SCRIPT === tk or tk.nil? then
+ nil
+ else
+ line
+ end
+ end
+
+ def token
+ set_token_position(line_no, char_no)
+ begin
+ begin
+ tk = @OP.match(self)
+ @space_seen = TkSPACE === tk
+ rescue SyntaxError => e
+ raise RDoc::Error, "syntax error: #{e.message}" if
+ @exception_on_syntax_error
+
+ tk = TkError.new(line_no, char_no)
+ end
+ end while @skip_space and TkSPACE === tk
+ if @read_auto_clean_up
+ get_read
+ end
+# throw :eof unless tk
+ tk
+ end
+
+ ENINDENT_CLAUSE = [
+ "case", "class", "def", "do", "for", "if",
+ "module", "unless", "until", "while", "begin" #, "when"
+ ]
+ DEINDENT_CLAUSE = ["end" #, "when"
+ ]
+
+ PERCENT_LTYPE = {
+ "q" => "\'",
+ "Q" => "\"",
+ "x" => "\`",
+ "r" => "/",
+ "w" => "]"
+ }
+
+ PERCENT_PAREN = {
+ "{" => "}",
+ "[" => "]",
+ "<" => ">",
+ "(" => ")"
+ }
+
+ Ltype2Token = {
+ "\'" => TkSTRING,
+ "\"" => TkSTRING,
+ "\`" => TkXSTRING,
+ "/" => TkREGEXP,
+ "]" => TkDSTRING
+ }
+ Ltype2Token.default = TkSTRING
+
+ DLtype2Token = {
+ "\"" => TkDSTRING,
+ "\`" => TkDXSTRING,
+ "/" => TkDREGEXP,
+ }
+
+ def lex_init()
+ @OP = IRB::SLex.new
+ @OP.def_rules("\0", "\004", "\032") do |chars, io|
+ Token(TkEND_OF_SCRIPT).set_text(chars)
+ end
+
+ @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |chars, io|
+ @space_seen = TRUE
+ while (ch = getc) =~ /[ \t\f\r\13]/
+ chars << ch
+ end
+ ungetc
+ Token(TkSPACE).set_text(chars)
+ end
+
+ @OP.def_rule("#") do
+ |op, io|
+ identify_comment
+ end
+
+ @OP.def_rule("=begin", proc{@prev_char_no == 0 && peek(0) =~ /\s/}) do
+ |op, io|
+ str = op
+ @ltype = "="
+
+
+ begin
+ line = ""
+ begin
+ ch = getc
+ line << ch
+ end until ch == "\n"
+ str << line
+ end until line =~ /^=end/
+
+ ungetc
+
+ @ltype = nil
+
+ if str =~ /\A=begin\s+rdoc/i
+ str.sub!(/\A=begin.*\n/, '')
+ str.sub!(/^=end.*/m, '')
+ Token(TkCOMMENT).set_text(str)
+ else
+ Token(TkRD_COMMENT)#.set_text(str)
+ end
+ end
+
+ @OP.def_rule("\n") do
+ print "\\n\n" if RDoc::RubyLex.debug?
+ case @lex_state
+ when EXPR_BEG, EXPR_FNAME, EXPR_DOT
+ @continue = TRUE
+ else
+ @continue = FALSE
+ @lex_state = EXPR_BEG
+ end
+ Token(TkNL).set_text("\n")
+ end
+
+ @OP.def_rules("*", "**",
+ "!", "!=", "!~",
+ "=", "==", "===",
+ "=~", "<=>",
+ "<", "<=",
+ ">", ">=", ">>") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rules("<<") do
+ |op, io|
+ tk = nil
+ if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
+ (@lex_state != EXPR_ARG || @space_seen)
+ c = peek(0)
+ if /[-\w_\"\'\`]/ =~ c
+ tk = identify_here_document
+ end
+ end
+ if !tk
+ @lex_state = EXPR_BEG
+ tk = Token(op).set_text(op)
+ end
+ tk
+ end
+
+ @OP.def_rules("'", '"') do
+ |op, io|
+ identify_string(op)
+ end
+
+ @OP.def_rules("`") do
+ |op, io|
+ if @lex_state == EXPR_FNAME
+ Token(op).set_text(op)
+ else
+ identify_string(op)
+ end
+ end
+
+ @OP.def_rules('?') do
+ |op, io|
+ if @lex_state == EXPR_END
+ @lex_state = EXPR_BEG
+ Token(TkQUESTION).set_text(op)
+ else
+ ch = getc
+ if @lex_state == EXPR_ARG && ch !~ /\s/
+ ungetc
+ @lex_state = EXPR_BEG
+ Token(TkQUESTION).set_text(op)
+ else
+ str = op
+ str << ch
+ if (ch == '\\') #'
+ str << read_escape
+ end
+ @lex_state = EXPR_END
+ Token(TkINTEGER).set_text(str)
+ end
+ end
+ end
+
+ @OP.def_rules("&", "&&", "|", "||") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rules("+=", "-=", "*=", "**=",
+ "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ op =~ /^(.*)=$/
+ Token(TkOPASGN, $1).set_text(op)
+ end
+
+ @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) do |op, io|
+ Token(TkUPLUS).set_text(op)
+ end
+
+ @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) do |op, io|
+ Token(TkUMINUS).set_text(op)
+ end
+
+ @OP.def_rules("+", "-") do
+ |op, io|
+ catch(:RET) do
+ if @lex_state == EXPR_ARG
+ if @space_seen and peek(0) =~ /[0-9]/
+ throw :RET, identify_number(op)
+ else
+ @lex_state = EXPR_BEG
+ end
+ elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
+ throw :RET, identify_number(op)
+ else
+ @lex_state = EXPR_BEG
+ end
+ Token(op).set_text(op)
+ end
+ end
+
+ @OP.def_rule(".") do
+ @lex_state = EXPR_BEG
+ if peek(0) =~ /[0-9]/
+ ungetc
+ identify_number("")
+ else
+ # for obj.if
+ @lex_state = EXPR_DOT
+ Token(TkDOT).set_text(".")
+ end
+ end
+
+ @OP.def_rules("..", "...") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ lex_int2
+ end
+
+ def lex_int2
+ @OP.def_rules("]", "}", ")") do
+ |op, io|
+ @lex_state = EXPR_END
+ @indent -= 1
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rule(":") do
+ if @lex_state == EXPR_END || peek(0) =~ /\s/
+ @lex_state = EXPR_BEG
+ tk = Token(TkCOLON)
+ else
+ @lex_state = EXPR_FNAME
+ tk = Token(TkSYMBEG)
+ end
+ tk.set_text(":")
+ end
+
+ @OP.def_rule("::") do
+ if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
+ @lex_state = EXPR_BEG
+ tk = Token(TkCOLON3)
+ else
+ @lex_state = EXPR_DOT
+ tk = Token(TkCOLON2)
+ end
+ tk.set_text("::")
+ end
+
+ @OP.def_rule("/") do
+ |op, io|
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ identify_string(op)
+ elsif peek(0) == '='
+ getc
+ @lex_state = EXPR_BEG
+ Token(TkOPASGN, :/).set_text("/=") #")
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
+ identify_string(op)
+ else
+ @lex_state = EXPR_BEG
+ Token("/").set_text(op)
+ end
+ end
+
+ @OP.def_rules("^") do
+ @lex_state = EXPR_BEG
+ Token("^").set_text("^")
+ end
+
+ @OP.def_rules(",", ";") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rule("~") do
+ @lex_state = EXPR_BEG
+ Token("~").set_text("~")
+ end
+
+ @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) do
+ @lex_state = EXPR_BEG
+ Token("~").set_text("~@")
+ end
+
+ @OP.def_rule("(") do
+ @indent += 1
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ @lex_state = EXPR_BEG
+ tk = Token(TkfLPAREN)
+ else
+ @lex_state = EXPR_BEG
+ tk = Token(TkLPAREN)
+ end
+ tk.set_text("(")
+ end
+
+ @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) do
+ Token("[]").set_text("[]")
+ end
+
+ @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) do
+ Token("[]=").set_text("[]=")
+ end
+
+ @OP.def_rule("[") do
+ @indent += 1
+ if @lex_state == EXPR_FNAME
+ t = Token(TkfLBRACK)
+ else
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ t = Token(TkLBRACK)
+ elsif @lex_state == EXPR_ARG && @space_seen
+ t = Token(TkLBRACK)
+ else
+ t = Token(TkfLBRACK)
+ end
+ @lex_state = EXPR_BEG
+ end
+ t.set_text("[")
+ end
+
+ @OP.def_rule("{") do
+ @indent += 1
+ if @lex_state != EXPR_END && @lex_state != EXPR_ARG
+ t = Token(TkLBRACE)
+ else
+ t = Token(TkfLBRACE)
+ end
+ @lex_state = EXPR_BEG
+ t.set_text("{")
+ end
+
+ @OP.def_rule('\\') do #'
+ if getc == "\n"
+ @space_seen = true
+ @continue = true
+ Token(TkSPACE).set_text("\\\n")
+ else
+ ungetc
+ Token("\\").set_text("\\") #"
+ end
+ end
+
+ @OP.def_rule('%') do
+ |op, io|
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ identify_quotation('%')
+ elsif peek(0) == '='
+ getc
+ Token(TkOPASGN, "%").set_text("%=")
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
+ identify_quotation('%')
+ else
+ @lex_state = EXPR_BEG
+ Token("%").set_text("%")
+ end
+ end
+
+ @OP.def_rule('$') do #'
+ identify_gvar
+ end
+
+ @OP.def_rule('@') do
+ if peek(0) =~ /[@\w_]/
+ ungetc
+ identify_identifier
+ else
+ Token("@").set_text("@")
+ end
+ end
+
+ @OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do
+ throw :eof
+ end
+
+ @OP.def_rule("") do
+ |op, io|
+ printf "MATCH: start %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
+ if peek(0) =~ /[0-9]/
+ t = identify_number("")
+ elsif peek(0) =~ /[\w_]/
+ t = identify_identifier
+ end
+ printf "MATCH: end %s: %s\n", op, io.inspect if RDoc::RubyLex.debug?
+ t
+ end
+ end
+
+ def identify_gvar
+ @lex_state = EXPR_END
+ str = "$"
+
+ tk = case ch = getc
+ when /[~_*$?!@\/\\;,=:<>".]/ #"
+ str << ch
+ Token(TkGVAR, str)
+
+ when "-"
+ str << "-" << getc
+ Token(TkGVAR, str)
+
+ when "&", "`", "'", "+"
+ str << ch
+ Token(TkBACK_REF, str)
+
+ when /[1-9]/
+ str << ch
+ while (ch = getc) =~ /[0-9]/
+ str << ch
+ end
+ ungetc
+ Token(TkNTH_REF)
+ when /\w/
+ ungetc
+ ungetc
+ return identify_identifier
+ else
+ ungetc
+ Token("$")
+ end
+ tk.set_text(str)
+ end
+
+ def identify_identifier
+ token = ""
+ token.concat getc if peek(0) =~ /[$@]/
+ token.concat getc if peek(0) == "@"
+
+ while (ch = getc) =~ /\w|_/
+ print ":", ch, ":" if RDoc::RubyLex.debug?
+ token.concat ch
+ end
+ ungetc
+
+ if ch == "!" or ch == "?"
+ token.concat getc
+ end
+ # fix token
+
+ # $stderr.puts "identifier - #{token}, state = #@lex_state"
+
+ case token
+ when /^\$/
+ return Token(TkGVAR, token).set_text(token)
+ when /^\@/
+ @lex_state = EXPR_END
+ return Token(TkIVAR, token).set_text(token)
+ end
+
+ if @lex_state != EXPR_DOT
+ print token, "\n" if RDoc::RubyLex.debug?
+
+ token_c, *trans = TkReading2Token[token]
+ if token_c
+ # reserved word?
+
+ if (@lex_state != EXPR_BEG &&
+ @lex_state != EXPR_FNAME &&
+ trans[1])
+ # modifiers
+ token_c = TkSymbol2Token[trans[1]]
+ @lex_state = trans[0]
+ else
+ if @lex_state != EXPR_FNAME
+ if ENINDENT_CLAUSE.include?(token)
+ @indent += 1
+ elsif DEINDENT_CLAUSE.include?(token)
+ @indent -= 1
+ end
+ @lex_state = trans[0]
+ else
+ @lex_state = EXPR_END
+ end
+ end
+ return Token(token_c, token).set_text(token)
+ end
+ end
+
+ if @lex_state == EXPR_FNAME
+ @lex_state = EXPR_END
+ if peek(0) == '='
+ token.concat getc
+ end
+ elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_END
+ end
+
+ if token[0, 1] =~ /[A-Z]/
+ return Token(TkCONSTANT, token).set_text(token)
+ elsif token[token.size - 1, 1] =~ /[!?]/
+ return Token(TkFID, token).set_text(token)
+ else
+ return Token(TkIDENTIFIER, token).set_text(token)
+ end
+ end
+
+ def identify_here_document
+ ch = getc
+ if ch == "-"
+ ch = getc
+ indent = true
+ end
+ if /['"`]/ =~ ch # '
+ lt = ch
+ quoted = ""
+ while (c = getc) && c != lt
+ quoted.concat c
+ end
+ else
+ lt = '"'
+ quoted = ch.dup
+ while (c = getc) && c =~ /\w/
+ quoted.concat c
+ end
+ ungetc
+ end
+
+ ltback, @ltype = @ltype, lt
+ reserve = ""
+
+ while ch = getc
+ reserve << ch
+ if ch == "\\" #"
+ ch = getc
+ reserve << ch
+ elsif ch == "\n"
+ break
+ end
+ end
+
+ str = ""
+ while (l = gets)
+ l.chomp!
+ l.strip! if indent
+ break if l == quoted
+ str << l.chomp << "\n"
+ end
+
+ @reader.divert_read_from(reserve)
+
+ @ltype = ltback
+ @lex_state = EXPR_END
+ Token(Ltype2Token[lt], str).set_text(str.dump)
+ end
+
+ def identify_quotation(initial_char)
+ ch = getc
+ if lt = PERCENT_LTYPE[ch]
+ initial_char += ch
+ ch = getc
+ elsif ch =~ /\W/
+ lt = "\""
+ else
+ fail SyntaxError, "unknown type of %string ('#{ch}')"
+ end
+# if ch !~ /\W/
+# ungetc
+# next
+# end
+ #@ltype = lt
+ @quoted = ch unless @quoted = PERCENT_PAREN[ch]
+ identify_string(lt, @quoted, ch, initial_char)
+ end
+
+ def identify_number(start)
+ str = start.dup
+
+ if start == "+" or start == "-" or start == ""
+ start = getc
+ str << start
+ end
+
+ @lex_state = EXPR_END
+
+ if start == "0"
+ if peek(0) == "x"
+ ch = getc
+ str << ch
+ match = /[0-9a-f_]/
+ else
+ match = /[0-7_]/
+ end
+ while ch = getc
+ if ch !~ match
+ ungetc
+ break
+ else
+ str << ch
+ end
+ end
+ return Token(TkINTEGER).set_text(str)
+ end
+
+ type = TkINTEGER
+ allow_point = TRUE
+ allow_e = TRUE
+ while ch = getc
+ case ch
+ when /[0-9_]/
+ str << ch
+
+ when allow_point && "."
+ type = TkFLOAT
+ if peek(0) !~ /[0-9]/
+ ungetc
+ break
+ end
+ str << ch
+ allow_point = false
+
+ when allow_e && "e", allow_e && "E"
+ str << ch
+ type = TkFLOAT
+ if peek(0) =~ /[+-]/
+ str << getc
+ end
+ allow_e = false
+ allow_point = false
+ else
+ ungetc
+ break
+ end
+ end
+ Token(type).set_text(str)
+ end
+
+ def identify_string(ltype, quoted = ltype, opener=nil, initial_char = nil)
+ @ltype = ltype
+ @quoted = quoted
+ subtype = nil
+
+ str = ""
+ str << initial_char if initial_char
+ str << (opener||quoted)
+
+ nest = 0
+ begin
+ while ch = getc
+ str << ch
+ if @quoted == ch
+ if nest == 0
+ break
+ else
+ nest -= 1
+ end
+ elsif opener == ch
+ nest += 1
+ elsif @ltype != "'" && @ltype != "]" and ch == "#"
+ ch = getc
+ if ch == "{"
+ subtype = true
+ str << ch << skip_inner_expression
+ else
+ ungetc(ch)
+ end
+ elsif ch == '\\' #'
+ str << read_escape
+ end
+ end
+ if @ltype == "/"
+ if peek(0) =~ /i|o|n|e|s/
+ str << getc
+ end
+ end
+ if subtype
+ Token(DLtype2Token[ltype], str)
+ else
+ Token(Ltype2Token[ltype], str)
+ end.set_text(str)
+ ensure
+ @ltype = nil
+ @quoted = nil
+ @lex_state = EXPR_END
+ end
+ end
+
+ def skip_inner_expression
+ res = ""
+ nest = 0
+ while (ch = getc)
+ res << ch
+ if ch == '}'
+ break if nest.zero?
+ nest -= 1
+ elsif ch == '{'
+ nest += 1
+ end
+ end
+ res
+ end
+
+ def identify_comment
+ @ltype = "#"
+ comment = "#"
+ while ch = getc
+ if ch == "\\"
+ ch = getc
+ if ch == "\n"
+ ch = " "
+ else
+ comment << "\\"
+ end
+ else
+ if ch == "\n"
+ @ltype = nil
+ ungetc
+ break
+ end
+ end
+ comment << ch
+ end
+ return Token(TkCOMMENT).set_text(comment)
+ end
+
+ def read_escape
+ res = ""
+ case ch = getc
+ when /[0-7]/
+ ungetc ch
+ 3.times do
+ case ch = getc
+ when /[0-7]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
+ res << ch
+ end
+
+ when "x"
+ res << ch
+ 2.times do
+ case ch = getc
+ when /[0-9a-fA-F]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
+ res << ch
+ end
+
+ when "M"
+ res << ch
+ if (ch = getc) != '-'
+ ungetc
+ else
+ res << ch
+ if (ch = getc) == "\\" #"
+ res << ch
+ res << read_escape
+ else
+ res << ch
+ end
+ end
+
+ when "C", "c" #, "^"
+ res << ch
+ if ch == "C" and (ch = getc) != "-"
+ ungetc
+ else
+ res << ch
+ if (ch = getc) == "\\" #"
+ res << ch
+ res << read_escape
+ else
+ res << ch
+ end
+ end
+ else
+ res << ch
+ end
+ res
+ end
+end
+
+##
+# Extracts code elements from a source file returning a TopLevel object
+# containing the constituent file elements.
+#
+# This file is based on rtags
+#
+# RubyParser understands how to document:
+# * classes
+# * modules
+# * methods
+# * constants
+# * aliases
+# * private, public, protected
+# * private_class_function, public_class_function
+# * module_function
+# * attr, attr_reader, attr_writer, attr_accessor
+# * extra accessors given on the command line
+# * metaprogrammed methods
+# * require
+# * include
+#
+# == Method Arguments
+#
+#--
+# NOTE: I don't think this works, needs tests, remove the paragraph following
+# this block when known to work
+#
+# The parser extracts the arguments from the method definition. You can
+# override this with a custom argument definition using the :args: directive:
+#
+# ##
+# # This method tries over and over until it is tired
+#
+# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
+# puts thing_to_try
+# go_go_go thing_to_try, tries - 1
+# end
+#
+# If you have a more-complex set of overrides you can use the :call-seq:
+# directive:
+#++
+#
+# The parser extracts the arguments from the method definition. You can
+# override this with a custom argument definition using the :call-seq:
+# directive:
+#
+# ##
+# # This method can be called with a range or an offset and length
+# #
+# # :call-seq:
+# # my_method(Range)
+# # my_method(offset, length)
+#
+# def my_method(*args)
+# end
+#
+# The parser extracts +yield+ expressions from method bodies to gather the
+# yielded argument names. If your method manually calls a block instead of
+# yielding or you want to override the discovered argument names use
+# the :yields: directive:
+#
+# ##
+# # My method is awesome
+#
+# def my_method(&block) # :yields: happy, times
+# block.call 1, 2
+# end
+#
+# == Metaprogrammed Methods
+#
+# To pick up a metaprogrammed method, the parser looks for a comment starting
+# with '##' before an identifier:
+#
+# ##
+# # This is a meta-programmed method!
+#
+# add_my_method :meta_method, :arg1, :arg2
+#
+# The parser looks at the token after the identifier to determine the name, in
+# this example, :meta_method. If a name cannot be found, a warning is printed
+# and 'unknown is used.
+#
+# You can force the name of a method using the :method: directive:
+#
+# ##
+# # :method: woo_hoo!
+#
+# By default, meta-methods are instance methods. To indicate that a method is
+# a singleton method instead use the :singleton-method: directive:
+#
+# ##
+# # :singleton-method:
+#
+# You can also use the :singleton-method: directive with a name:
+#
+# ##
+# # :singleton-method: woo_hoo!
+#
+# == Hidden methods
+#
+# You can provide documentation for methods that don't appear using
+# the :method: and :singleton-method: directives:
+#
+# ##
+# # :method: ghost_method
+# # There is a method here, but you can't see it!
+#
+# ##
+# # this is a comment for a regular method
+#
+# def regular_method() end
+#
+# Note that by default, the :method: directive will be ignored if there is a
+# standard rdocable item following it.
+
+class RDoc::Parser::Ruby < RDoc::Parser
+
+ parse_files_matching(/\.rbw?$/)
+
+ include RDoc::RubyToken
+ include RDoc::TokenStream
+
+ NORMAL = "::"
+ SINGLE = "<<"
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ @size = 0
+ @token_listeners = nil
+ @scanner = RDoc::RubyLex.new content, @options
+ @scanner.exception_on_syntax_error = false
+
+ reset
+ end
+
+ def add_token_listener(obj)
+ @token_listeners ||= []
+ @token_listeners << obj
+ end
+
+ ##
+ # Look for the first comment in a file that isn't a shebang line.
+
+ def collect_first_comment
+ skip_tkspace
+ res = ''
+ first_line = true
+
+ tk = get_tk
+
+ while TkCOMMENT === tk
+ if first_line and tk.text =~ /\A#!/ then
+ skip_tkspace
+ tk = get_tk
+ elsif first_line and tk.text =~ /\A#\s*-\*-/ then
+ first_line = false
+ skip_tkspace
+ tk = get_tk
+ else
+ first_line = false
+ res << tk.text << "\n"
+ tk = get_tk
+
+ if TkNL === tk then
+ skip_tkspace false
+ tk = get_tk
+ end
+ end
+ end
+
+ unget_tk tk
+
+ res
+ end
+
+ def error(msg)
+ msg = make_message msg
+ $stderr.puts msg
+ exit(1)
+ end
+
+ ##
+ # Look for a 'call-seq' in the comment, and override the normal parameter
+ # stuff
+
+ def extract_call_seq(comment, meth)
+ if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '') then
+ seq = $1
+ seq.gsub!(/^\s*\#\s*/, '')
+ meth.call_seq = seq
+ end
+
+ meth
+ end
+
+ def get_bool
+ skip_tkspace
+ tk = get_tk
+ case tk
+ when TkTRUE
+ true
+ when TkFALSE, TkNIL
+ false
+ else
+ unget_tk tk
+ true
+ end
+ end
+
+ ##
+ # Look for the name of a class of module (optionally with a leading :: or
+ # with :: separated named) and return the ultimate name and container
+
+ def get_class_or_module(container)
+ skip_tkspace
+ name_t = get_tk
+
+ # class ::A -> A is in the top level
+ if TkCOLON2 === name_t then
+ name_t = get_tk
+ container = @top_level
+ end
+
+ skip_tkspace(false)
+
+ while TkCOLON2 === peek_tk do
+ prev_container = container
+ container = container.find_module_named(name_t.name)
+ if !container
+# warn("Couldn't find module #{name_t.name}")
+ container = prev_container.add_module RDoc::NormalModule, name_t.name
+ end
+ get_tk
+ name_t = get_tk
+ end
+ skip_tkspace(false)
+ return [container, name_t]
+ end
+
+ ##
+ # Return a superclass, which can be either a constant of an expression
+
+ def get_class_specification
+ tk = get_tk
+ return "self" if TkSELF === tk
+
+ res = ""
+ while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
+ res += tk.text
+ tk = get_tk
+ end
+
+ unget_tk(tk)
+ skip_tkspace(false)
+
+ get_tkread # empty out read buffer
+
+ tk = get_tk
+
+ case tk
+ when TkNL, TkCOMMENT, TkSEMICOLON then
+ unget_tk(tk)
+ return res
+ end
+
+ res += parse_call_parameters(tk)
+ res
+ end
+
+ ##
+ # Parse a constant, which might be qualified by one or more class or module
+ # names
+
+ def get_constant
+ res = ""
+ skip_tkspace(false)
+ tk = get_tk
+
+ while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do
+ res += tk.text
+ tk = get_tk
+ end
+
+# if res.empty?
+# warn("Unexpected token #{tk} in constant")
+# end
+ unget_tk(tk)
+ res
+ end
+
+ ##
+ # Get a constant that may be surrounded by parens
+
+ def get_constant_with_optional_parens
+ skip_tkspace(false)
+ nest = 0
+ while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do
+ get_tk
+ skip_tkspace(true)
+ nest += 1
+ end
+
+ name = get_constant
+
+ while nest > 0
+ skip_tkspace(true)
+ tk = get_tk
+ nest -= 1 if TkRPAREN === tk
+ end
+ name
+ end
+
+ def get_symbol_or_name
+ tk = get_tk
+ case tk
+ when TkSYMBOL
+ tk.text.sub(/^:/, '')
+ when TkId, TkOp
+ tk.name
+ when TkSTRING
+ tk.text
+ else
+ raise "Name or symbol expected (got #{tk})"
+ end
+ end
+
+ def get_tk
+ tk = nil
+ if @tokens.empty?
+ tk = @scanner.token
+ @read.push @scanner.get_read
+ puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
+ else
+ @read.push @unget_read.shift
+ tk = @tokens.shift
+ puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
+ end
+
+ if TkSYMBEG === tk then
+ set_token_position(tk.line_no, tk.char_no)
+ tk1 = get_tk
+ if TkId === tk1 or TkOp === tk1 or TkSTRING === tk1 then
+ if tk1.respond_to?(:name)
+ tk = Token(TkSYMBOL).set_text(":" + tk1.name)
+ else
+ tk = Token(TkSYMBOL).set_text(":" + tk1.text)
+ end
+ # remove the identifier we just read (we're about to
+ # replace it with a symbol)
+ @token_listeners.each do |obj|
+ obj.pop_token
+ end if @token_listeners
+ else
+ warn("':' not followed by identifier or operator")
+ tk = tk1
+ end
+ end
+
+ # inform any listeners of our shiny new token
+ @token_listeners.each do |obj|
+ obj.add_token(tk)
+ end if @token_listeners
+
+ tk
+ end
+
+ def get_tkread
+ read = @read.join("")
+ @read = []
+ read
+ end
+
+ ##
+ # Look for directives in a normal comment block:
+ #
+ # #-- - don't display comment from this point forward
+ #
+ # This routine modifies it's parameter
+
+ def look_for_directives_in(context, comment)
+ preprocess = RDoc::Markup::PreProcess.new(@file_name,
+ @options.rdoc_include)
+
+ preprocess.handle(comment) do |directive, param|
+ case directive
+ when 'enddoc' then
+ throw :enddoc
+ when 'main' then
+ @options.main_page = param
+ ''
+ when 'method', 'singleton-method' then
+ false # ignore
+ when 'section' then
+ context.set_current_section(param, comment)
+ comment.replace ''
+ break
+ when 'startdoc' then
+ context.start_doc
+ context.force_documentation = true
+ ''
+ when 'stopdoc' then
+ context.stop_doc
+ ''
+ when 'title' then
+ @options.title = param
+ ''
+ else
+ warn "Unrecognized directive '#{directive}'"
+ false
+ end
+ end
+
+ remove_private_comments(comment)
+ end
+
+ def make_message(msg)
+ prefix = "\n" + @file_name + ":"
+ if @scanner
+ prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
+ end
+ return prefix + msg
+ end
+
+ def parse_attr(context, single, tk, comment)
+ args = parse_symbol_arg(1)
+ if args.size > 0
+ name = args[0]
+ rw = "R"
+ skip_tkspace(false)
+ tk = get_tk
+ if TkCOMMA === tk then
+ rw = "RW" if get_bool
+ else
+ unget_tk tk
+ end
+ att = RDoc::Attr.new get_tkread, name, rw, comment
+ read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
+ if att.document_self
+ context.add_attribute(att)
+ end
+ else
+ warn("'attr' ignored - looks like a variable")
+ end
+ end
+
+ def parse_attr_accessor(context, single, tk, comment)
+ args = parse_symbol_arg
+ read = get_tkread
+ rw = "?"
+
+ # If nodoc is given, don't document any of them
+
+ tmp = RDoc::CodeObject.new
+ read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
+ return unless tmp.document_self
+
+ case tk.name
+ when "attr_reader" then rw = "R"
+ when "attr_writer" then rw = "W"
+ when "attr_accessor" then rw = "RW"
+ else
+ rw = @options.extra_accessor_flags[tk.name]
+ rw = '?' if rw.nil?
+ end
+
+ for name in args
+ att = RDoc::Attr.new get_tkread, name, rw, comment
+ context.add_attribute att
+ end
+ end
+
+ def parse_alias(context, single, tk, comment)
+ skip_tkspace
+ if TkLPAREN === peek_tk then
+ get_tk
+ skip_tkspace
+ end
+ new_name = get_symbol_or_name
+ @scanner.instance_eval{@lex_state = EXPR_FNAME}
+ skip_tkspace
+ if TkCOMMA === peek_tk then
+ get_tk
+ skip_tkspace
+ end
+ old_name = get_symbol_or_name
+
+ al = RDoc::Alias.new get_tkread, old_name, new_name, comment
+ read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
+ if al.document_self
+ context.add_alias(al)
+ end
+ end
+
+ def parse_call_parameters(tk)
+ end_token = case tk
+ when TkLPAREN, TkfLPAREN
+ TkRPAREN
+ when TkRPAREN
+ return ""
+ else
+ TkNL
+ end
+ nest = 0
+
+ loop do
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest <= 0
+ else
+ break unless @scanner.continue
+ end
+ when TkCOMMENT
+ unget_tk(tk)
+ break
+ end
+ tk = get_tk
+ end
+ res = get_tkread.tr("\n", " ").strip
+ res = "" if res == ";"
+ res
+ end
+
+ def parse_class(container, single, tk, comment)
+ container, name_t = get_class_or_module(container)
+
+ case name_t
+ when TkCONSTANT
+ name = name_t.name
+ superclass = "Object"
+
+ if TkLT === peek_tk then
+ get_tk
+ skip_tkspace(true)
+ superclass = get_class_specification
+ superclass = "<unknown>" if superclass.empty?
+ end
+
+ cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
+ cls = container.add_class cls_type, name, superclass
+
+ @stats.add_class cls
+
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
+ cls.record_location @top_level
+
+ parse_statements cls
+ cls.comment = comment
+
+ when TkLSHFT
+ case name = get_class_specification
+ when "self", container.name
+ parse_statements(container, SINGLE)
+ else
+ other = RDoc::TopLevel.find_class_named(name)
+ unless other
+ # other = @top_level.add_class(NormalClass, name, nil)
+ # other.record_location(@top_level)
+ # other.comment = comment
+ other = RDoc::NormalClass.new "Dummy", nil
+ end
+
+ @stats.add_class other
+
+ read_documentation_modifiers other, RDoc::CLASS_MODIFIERS
+ parse_statements(other, SINGLE)
+ end
+
+ else
+ warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}")
+ end
+ end
+
+ def parse_constant(container, single, tk, comment)
+ name = tk.name
+ skip_tkspace(false)
+ eq_tk = get_tk
+
+ unless TkASSIGN === eq_tk then
+ unget_tk(eq_tk)
+ return
+ end
+
+
+ nest = 0
+ get_tkread
+
+ tk = get_tk
+ if TkGT === tk then
+ unget_tk(tk)
+ unget_tk(eq_tk)
+ return
+ end
+
+ loop do
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN, TkLBRACE, TkLBRACK, TkDO
+ nest += 1
+ when TkRPAREN, TkRBRACE, TkRBRACK, TkEND
+ nest -= 1
+ when TkCOMMENT
+ if nest <= 0 && @scanner.lex_state == EXPR_END
+ unget_tk(tk)
+ break
+ end
+ when TkNL
+ if (nest <= 0) && ((@scanner.lex_state == EXPR_END) || (!@scanner.continue))
+ unget_tk(tk)
+ break
+ end
+ end
+ tk = get_tk
+ end
+
+ res = get_tkread.tr("\n", " ").strip
+ res = "" if res == ";"
+
+ con = RDoc::Constant.new name, res, comment
+ read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
+
+ if con.document_self
+ container.add_constant(con)
+ end
+ end
+
+ def parse_comment(container, tk, comment)
+ line_no = tk.line_no
+ column = tk.char_no
+
+ singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
+
+ if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
+ name = $1 unless $1.empty?
+ else
+ return nil
+ end
+
+ meth = RDoc::GhostMethod.new get_tkread, name
+ meth.singleton = singleton
+
+ @stats.add_method meth
+
+ meth.start_collecting_tokens
+ indent = TkSPACE.new 1, 1
+ indent.set_text " " * column
+
+ position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
+ meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
+
+ meth.params = ''
+
+ extract_call_seq comment, meth
+
+ container.add_method meth if meth.document_self
+
+ meth.comment = comment
+ end
+
+ def parse_include(context, comment)
+ loop do
+ skip_tkspace_comment
+
+ name = get_constant_with_optional_parens
+ context.add_include RDoc::Include.new(name, comment) unless name.empty?
+
+ return unless TkCOMMA === peek_tk
+ get_tk
+ end
+ end
+
+ ##
+ # Parses a meta-programmed method
+
+ def parse_meta_method(container, single, tk, comment)
+ line_no = tk.line_no
+ column = tk.char_no
+
+ start_collecting_tokens
+ add_token tk
+ add_token_listener self
+
+ skip_tkspace false
+
+ singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
+
+ if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
+ name = $1 unless $1.empty?
+ end
+
+ if name.nil? then
+ name_t = get_tk
+ case name_t
+ when TkSYMBOL then
+ name = name_t.text[1..-1]
+ when TkSTRING then
+ name = name_t.text[1..-2]
+ else
+ warn "#{container.top_level.file_relative_name}:#{name_t.line_no} unknown name token #{name_t.inspect} for meta-method"
+ name = 'unknown'
+ end
+ end
+
+ meth = RDoc::MetaMethod.new get_tkread, name
+ meth.singleton = singleton
+
+ @stats.add_method meth
+
+ remove_token_listener self
+
+ meth.start_collecting_tokens
+ indent = TkSPACE.new 1, 1
+ indent.set_text " " * column
+
+ position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
+ meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
+ meth.add_tokens @token_stream
+
+ add_token_listener meth
+
+ meth.params = ''
+
+ extract_call_seq comment, meth
+
+ container.add_method meth if meth.document_self
+
+ last_tk = tk
+
+ while tk = get_tk do
+ case tk
+ when TkSEMICOLON then
+ break
+ when TkNL then
+ break unless last_tk and TkCOMMA === last_tk
+ when TkSPACE then
+ # expression continues
+ else
+ last_tk = tk
+ end
+ end
+
+ remove_token_listener meth
+
+ meth.comment = comment
+ end
+
+ ##
+ # Parses a method
+
+ def parse_method(container, single, tk, comment)
+ line_no = tk.line_no
+ column = tk.char_no
+
+ start_collecting_tokens
+ add_token(tk)
+ add_token_listener(self)
+
+ @scanner.instance_eval do @lex_state = EXPR_FNAME end
+
+ skip_tkspace(false)
+ name_t = get_tk
+ back_tk = skip_tkspace
+ meth = nil
+ added_container = false
+
+ dot = get_tk
+ if TkDOT === dot or TkCOLON2 === dot then
+ @scanner.instance_eval do @lex_state = EXPR_FNAME end
+ skip_tkspace
+ name_t2 = get_tk
+
+ case name_t
+ when TkSELF then
+ name = name_t2.name
+ when TkCONSTANT then
+ name = name_t2.name
+ prev_container = container
+ container = container.find_module_named(name_t.name)
+ unless container then
+ added_container = true
+ obj = name_t.name.split("::").inject(Object) do |state, item|
+ state.const_get(item)
+ end rescue nil
+
+ type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
+
+ unless [Class, Module].include?(obj.class) then
+ warn("Couldn't find #{name_t.name}. Assuming it's a module")
+ end
+
+ if type == RDoc::NormalClass then
+ container = prev_container.add_class(type, name_t.name, obj.superclass.name)
+ else
+ container = prev_container.add_module(type, name_t.name)
+ end
+
+ container.record_location @top_level
+ end
+ else
+ # warn("Unexpected token '#{name_t2.inspect}'")
+ # break
+ skip_method(container)
+ return
+ end
+
+ meth = RDoc::AnyMethod.new(get_tkread, name)
+ meth.singleton = true
+ else
+ unget_tk dot
+ back_tk.reverse_each do |token|
+ unget_tk token
+ end
+ name = name_t.name
+
+ meth = RDoc::AnyMethod.new get_tkread, name
+ meth.singleton = (single == SINGLE)
+ end
+
+ @stats.add_method meth
+
+ remove_token_listener self
+
+ meth.start_collecting_tokens
+ indent = TkSPACE.new 1, 1
+ indent.set_text " " * column
+
+ token = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}")
+ meth.add_tokens [token, NEWLINE_TOKEN, indent]
+ meth.add_tokens @token_stream
+
+ add_token_listener meth
+
+ @scanner.instance_eval do @continue = false end
+ parse_method_parameters meth
+
+ if meth.document_self then
+ container.add_method meth
+ elsif added_container then
+ container.document_self = false
+ end
+
+ # Having now read the method parameters and documentation modifiers, we
+ # now know whether we have to rename #initialize to ::new
+
+ if name == "initialize" && !meth.singleton then
+ if meth.dont_rename_initialize then
+ meth.visibility = :protected
+ else
+ meth.singleton = true
+ meth.name = "new"
+ meth.visibility = :public
+ end
+ end
+
+ parse_statements(container, single, meth)
+
+ remove_token_listener(meth)
+
+ extract_call_seq comment, meth
+
+ meth.comment = comment
+ end
+
+ def parse_method_or_yield_parameters(method = nil,
+ modifiers = RDoc::METHOD_MODIFIERS)
+ skip_tkspace(false)
+ tk = get_tk
+
+ # Little hack going on here. In the statement
+ # f = 2*(1+yield)
+ # We see the RPAREN as the next token, so we need
+ # to exit early. This still won't catch all cases
+ # (such as "a = yield + 1"
+ end_token = case tk
+ when TkLPAREN, TkfLPAREN
+ TkRPAREN
+ when TkRPAREN
+ return ""
+ else
+ TkNL
+ end
+ nest = 0
+
+ loop do
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLBRACE
+ nest += 1
+ when TkRBRACE
+ # we might have a.each {|i| yield i }
+ unget_tk(tk) if nest.zero?
+ nest -= 1
+ break if nest <= 0
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest <= 0
+ else
+ break unless @scanner.continue
+ end
+ when method && method.block_params.nil? && TkCOMMENT
+ unget_tk(tk)
+ read_documentation_modifiers(method, modifiers)
+ end
+ tk = get_tk
+ end
+ res = get_tkread.tr("\n", " ").strip
+ res = "" if res == ";"
+ res
+ end
+
+ ##
+ # Capture the method's parameters. Along the way, look for a comment
+ # containing:
+ #
+ # # yields: ....
+ #
+ # and add this as the block_params for the method
+
+ def parse_method_parameters(method)
+ res = parse_method_or_yield_parameters(method)
+ res = "(" + res + ")" unless res[0] == ?(
+ method.params = res unless method.params
+ if method.block_params.nil?
+ skip_tkspace(false)
+ read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
+ end
+ end
+
+ def parse_module(container, single, tk, comment)
+ container, name_t = get_class_or_module(container)
+
+ name = name_t.name
+
+ mod = container.add_module RDoc::NormalModule, name
+ mod.record_location @top_level
+
+ @stats.add_module mod
+
+ read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
+ parse_statements(mod)
+ mod.comment = comment
+ end
+
+ def parse_require(context, comment)
+ skip_tkspace_comment
+ tk = get_tk
+ if TkLPAREN === tk then
+ skip_tkspace_comment
+ tk = get_tk
+ end
+
+ name = nil
+ case tk
+ when TkSTRING
+ name = tk.text
+ # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
+ # name = tk.name
+ when TkDSTRING
+ warn "Skipping require of dynamic string: #{tk.text}"
+ # else
+ # warn "'require' used as variable"
+ end
+ if name
+ context.add_require RDoc::Require.new(name, comment)
+ else
+ unget_tk(tk)
+ end
+ end
+
+ def parse_statements(container, single = NORMAL, current_method = nil,
+ comment = '')
+ nest = 1
+ save_visibility = container.visibility
+
+ non_comment_seen = true
+
+ while tk = get_tk do
+ keep_comment = false
+
+ non_comment_seen = true unless TkCOMMENT === tk
+
+ case tk
+ when TkNL then
+ skip_tkspace true # Skip blanks and newlines
+ tk = get_tk
+
+ if TkCOMMENT === tk then
+ if non_comment_seen then
+ # Look for RDoc in a comment about to be thrown away
+ parse_comment container, tk, comment unless comment.empty?
+
+ comment = ''
+ non_comment_seen = false
+ end
+
+ while TkCOMMENT === tk do
+ comment << tk.text << "\n"
+ tk = get_tk # this is the newline
+ skip_tkspace(false) # leading spaces
+ tk = get_tk
+ end
+
+ unless comment.empty? then
+ look_for_directives_in container, comment
+
+ if container.done_documenting then
+ container.ongoing_visibility = save_visibility
+ end
+ end
+
+ keep_comment = true
+ else
+ non_comment_seen = true
+ end
+
+ unget_tk tk
+ keep_comment = true
+
+ when TkCLASS then
+ if container.document_children then
+ parse_class container, single, tk, comment
+ else
+ nest += 1
+ end
+
+ when TkMODULE then
+ if container.document_children then
+ parse_module container, single, tk, comment
+ else
+ nest += 1
+ end
+
+ when TkDEF then
+ if container.document_self then
+ parse_method container, single, tk, comment
+ else
+ nest += 1
+ end
+
+ when TkCONSTANT then
+ if container.document_self then
+ parse_constant container, single, tk, comment
+ end
+
+ when TkALIAS then
+ if container.document_self then
+ parse_alias container, single, tk, comment
+ end
+
+ when TkYIELD then
+ if current_method.nil? then
+ warn "Warning: yield outside of method" if container.document_self
+ else
+ parse_yield container, single, tk, current_method
+ end
+
+ # Until and While can have a 'do', which shouldn't increase the nesting.
+ # We can't solve the general case, but we can handle most occurrences by
+ # ignoring a do at the end of a line.
+
+ when TkUNTIL, TkWHILE then
+ nest += 1
+ skip_optional_do_after_expression
+
+ # 'for' is trickier
+ when TkFOR then
+ nest += 1
+ skip_for_variable
+ skip_optional_do_after_expression
+
+ when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN then
+ nest += 1
+
+ when TkIDENTIFIER then
+ if nest == 1 and current_method.nil? then
+ case tk.name
+ when 'private', 'protected', 'public', 'private_class_method',
+ 'public_class_method', 'module_function' then
+ parse_visibility container, single, tk
+ keep_comment = true
+ when 'attr' then
+ parse_attr container, single, tk, comment
+ when /^attr_(reader|writer|accessor)$/, @options.extra_accessors then
+ parse_attr_accessor container, single, tk, comment
+ when 'alias_method' then
+ if container.document_self then
+ parse_alias container, single, tk, comment
+ end
+ else
+ if container.document_self and comment =~ /\A#\#$/ then
+ parse_meta_method container, single, tk, comment
+ end
+ end
+ end
+
+ case tk.name
+ when "require" then
+ parse_require container, comment
+ when "include" then
+ parse_include container, comment
+ end
+
+ when TkEND then
+ nest -= 1
+ if nest == 0 then
+ read_documentation_modifiers container, RDoc::CLASS_MODIFIERS
+ container.ongoing_visibility = save_visibility
+ return
+ end
+
+ end
+
+ comment = '' unless keep_comment
+
+ begin
+ get_tkread
+ skip_tkspace(false)
+ end while peek_tk == TkNL
+ end
+ end
+
+ def parse_symbol_arg(no = nil)
+ args = []
+ skip_tkspace_comment
+ case tk = get_tk
+ when TkLPAREN
+ loop do
+ skip_tkspace_comment
+ if tk1 = parse_symbol_in_arg
+ args.push tk1
+ break if no and args.size >= no
+ end
+
+ skip_tkspace_comment
+ case tk2 = get_tk
+ when TkRPAREN
+ break
+ when TkCOMMA
+ else
+ warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
+ break
+ end
+ end
+ else
+ unget_tk tk
+ if tk = parse_symbol_in_arg
+ args.push tk
+ return args if no and args.size >= no
+ end
+
+ loop do
+ skip_tkspace(false)
+
+ tk1 = get_tk
+ unless TkCOMMA === tk1 then
+ unget_tk tk1
+ break
+ end
+
+ skip_tkspace_comment
+ if tk = parse_symbol_in_arg
+ args.push tk
+ break if no and args.size >= no
+ end
+ end
+ end
+ args
+ end
+
+ def parse_symbol_in_arg
+ case tk = get_tk
+ when TkSYMBOL
+ tk.text.sub(/^:/, '')
+ when TkSTRING
+ eval @read[-1]
+ else
+ warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
+ nil
+ end
+ end
+
+ def parse_toplevel_statements(container)
+ comment = collect_first_comment
+ look_for_directives_in(container, comment)
+ container.comment = comment unless comment.empty?
+ parse_statements container, NORMAL, nil, comment
+ end
+
+ def parse_visibility(container, single, tk)
+ singleton = (single == SINGLE)
+
+ vis_type = tk.name
+
+ vis = case vis_type
+ when 'private' then :private
+ when 'protected' then :protected
+ when 'public' then :public
+ when 'private_class_method' then
+ singleton = true
+ :private
+ when 'public_class_method' then
+ singleton = true
+ :public
+ when 'module_function' then
+ singleton = true
+ :public
+ else
+ raise "Invalid visibility: #{tk.name}"
+ end
+
+ skip_tkspace_comment false
+
+ case peek_tk
+ # Ryan Davis suggested the extension to ignore modifiers, because he
+ # often writes
+ #
+ # protected unless $TESTING
+ #
+ when TkNL, TkUNLESS_MOD, TkIF_MOD, TkSEMICOLON then
+ container.ongoing_visibility = vis
+ else
+ if vis_type == 'module_function' then
+ args = parse_symbol_arg
+ container.set_visibility_for args, :private, false
+
+ module_functions = []
+
+ container.methods_matching args do |m|
+ s_m = m.dup
+ s_m.singleton = true if RDoc::AnyMethod === s_m
+ s_m.visibility = :public
+ module_functions << s_m
+ end
+
+ module_functions.each do |s_m|
+ case s_m
+ when RDoc::AnyMethod then
+ container.add_method s_m
+ when RDoc::Attr then
+ container.add_attribute s_m
+ end
+ end
+ else
+ args = parse_symbol_arg
+ container.set_visibility_for args, vis, singleton
+ end
+ end
+ end
+
+ def parse_yield_parameters
+ parse_method_or_yield_parameters
+ end
+
+ def parse_yield(context, single, tk, method)
+ if method.block_params.nil?
+ get_tkread
+ @scanner.instance_eval{@continue = false}
+ method.block_params = parse_yield_parameters
+ end
+ end
+
+ def peek_read
+ @read.join('')
+ end
+
+ ##
+ # Peek at the next token, but don't remove it from the stream
+
+ def peek_tk
+ unget_tk(tk = get_tk)
+ tk
+ end
+
+ ##
+ # Directives are modifier comments that can appear after class, module, or
+ # method names. For example:
+ #
+ # def fred # :yields: a, b
+ #
+ # or:
+ #
+ # class MyClass # :nodoc:
+ #
+ # We return the directive name and any parameters as a two element array
+
+ def read_directive(allowed)
+ tk = get_tk
+ result = nil
+ if TkCOMMENT === tk
+ if tk.text =~ /\s*:?(\w+):\s*(.*)/
+ directive = $1.downcase
+ if allowed.include?(directive)
+ result = [directive, $2]
+ end
+ end
+ else
+ unget_tk(tk)
+ end
+ result
+ end
+
+ def read_documentation_modifiers(context, allow)
+ dir = read_directive(allow)
+
+ case dir[0]
+ when "notnew", "not_new", "not-new" then
+ context.dont_rename_initialize = true
+
+ when "nodoc" then
+ context.document_self = false
+ if dir[1].downcase == "all"
+ context.document_children = false
+ end
+
+ when "doc" then
+ context.document_self = true
+ context.force_documentation = true
+
+ when "yield", "yields" then
+ unless context.params.nil?
+ context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
+ end
+
+ context.block_params = dir[1]
+
+ when "arg", "args" then
+ context.params = dir[1]
+ end if dir
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub!(/^#--\n.*?^#\+\+/m, '')
+ comment.sub!(/^#--\n.*/m, '')
+ end
+
+ def remove_token_listener(obj)
+ @token_listeners.delete(obj)
+ end
+
+ def reset
+ @tokens = []
+ @unget_read = []
+ @read = []
+ end
+
+ def scan
+ reset
+
+ catch(:eof) do
+ catch(:enddoc) do
+ begin
+ parse_toplevel_statements(@top_level)
+ rescue Exception => e
+ $stderr.puts <<-EOF
+
+
+RDoc failure in #{@file_name} at or around line #{@scanner.line_no} column
+#{@scanner.char_no}
+
+Before reporting this, could you check that the file you're documenting
+compiles cleanly--RDoc is not a full Ruby parser, and gets confused easily if
+fed invalid programs.
+
+The internal error was:
+
+ EOF
+
+ e.set_backtrace(e.backtrace[0,4])
+ raise
+ end
+ end
+ end
+
+ @top_level
+ end
+
+ ##
+ # while, until, and for have an optional do
+
+ def skip_optional_do_after_expression
+ skip_tkspace(false)
+ tk = get_tk
+ case tk
+ when TkLPAREN, TkfLPAREN
+ end_token = TkRPAREN
+ else
+ end_token = TkNL
+ end
+
+ nest = 0
+ @scanner.instance_eval{@continue = false}
+
+ loop do
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when TkDO
+ break if nest.zero?
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest.zero?
+ else
+ break unless @scanner.continue
+ end
+ end
+ tk = get_tk
+ end
+ skip_tkspace(false)
+
+ get_tk if TkDO === peek_tk
+ end
+
+ ##
+ # skip the var [in] part of a 'for' statement
+
+ def skip_for_variable
+ skip_tkspace(false)
+ tk = get_tk
+ skip_tkspace(false)
+ tk = get_tk
+ unget_tk(tk) unless TkIN === tk
+ end
+
+ def skip_method(container)
+ meth = RDoc::AnyMethod.new "", "anon"
+ parse_method_parameters(meth)
+ parse_statements(container, false, meth)
+ end
+
+ ##
+ # Skip spaces
+
+ def skip_tkspace(skip_nl = true)
+ tokens = []
+
+ while TkSPACE === (tk = get_tk) or (skip_nl and TkNL === tk) do
+ tokens.push tk
+ end
+
+ unget_tk(tk)
+ tokens
+ end
+
+ ##
+ # Skip spaces until a comment is found
+
+ def skip_tkspace_comment(skip_nl = true)
+ loop do
+ skip_tkspace(skip_nl)
+ return unless TkCOMMENT === peek_tk
+ get_tk
+ end
+ end
+
+ def unget_tk(tk)
+ @tokens.unshift tk
+ @unget_read.unshift @read.pop
+
+ # Remove this token from any listeners
+ @token_listeners.each do |obj|
+ obj.pop_token
+ end if @token_listeners
+ end
+
+ def warn(msg)
+ return if @options.quiet
+ msg = make_message msg
+ $stderr.puts msg
+ end
+
+end
+
Deleted: MacRuby/branches/experimental/lib/rdoc/parser/simple.rb
===================================================================
--- MacRuby/trunk/lib/rdoc/parser/simple.rb 2009-06-19 21:09:10 UTC (rev 1886)
+++ MacRuby/branches/experimental/lib/rdoc/parser/simple.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,38 +0,0 @@
-require 'rdoc/parser'
-
-##
-# Parse a non-source file. We basically take the whole thing as one big
-# comment. If the first character in the file is '#', we strip leading pound
-# signs.
-
-class RDoc::Parser::Simple < RDoc::Parser
-
- parse_files_matching(//)
-
- ##
- # Prepare to parse a plain file
-
- def initialize(top_level, file_name, content, options, stats)
- super
-
- preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
-
- preprocess.handle @content do |directive, param|
- warn "Unrecognized directive '#{directive}' in #{@file_name}"
- end
- end
-
- ##
- # Extract the file contents and attach them to the toplevel as a comment
-
- def scan
- @top_level.comment = remove_private_comments(@content)
- @top_level
- end
-
- def remove_private_comments(comment)
- comment.gsub(/^--\n.*?^\+\+/m, '').sub(/^--\n.*/m, '')
- end
-
-end
-
Copied: MacRuby/branches/experimental/lib/rdoc/parser/simple.rb (from rev 1886, MacRuby/trunk/lib/rdoc/parser/simple.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/parser/simple.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/parser/simple.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,38 @@
+require 'rdoc/parser'
+
+##
+# Parse a non-source file. We basically take the whole thing as one big
+# comment. If the first character in the file is '#', we strip leading pound
+# signs.
+
+class RDoc::Parser::Simple < RDoc::Parser
+
+ parse_files_matching(//)
+
+ ##
+ # Prepare to parse a plain file
+
+ def initialize(top_level, file_name, content, options, stats)
+ super
+
+ preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
+
+ preprocess.handle @content do |directive, param|
+ warn "Unrecognized directive '#{directive}' in #{@file_name}"
+ end
+ end
+
+ ##
+ # Extract the file contents and attach them to the toplevel as a comment
+
+ def scan
+ @top_level.comment = remove_private_comments(@content)
+ @top_level
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub(/^--\n.*?^\+\+/m, '').sub(/^--\n.*/m, '')
+ end
+
+end
+
Copied: MacRuby/branches/experimental/lib/rdoc/parser.rb (from rev 1886, MacRuby/trunk/lib/rdoc/parser.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/parser.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rdoc/parser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,146 @@
+require 'rdoc'
+require 'rdoc/code_objects'
+require 'rdoc/markup/preprocess'
+require 'rdoc/stats'
+
+##
+# A parser is simple a class that implements
+#
+# #initialize(file_name, body, options)
+#
+# and
+#
+# #scan
+#
+# The initialize method takes a file name to be used, the body of the file,
+# and an RDoc::Options object. The scan method is then called to return an
+# appropriately parsed TopLevel code object.
+#
+# The ParseFactory is used to redirect to the correct parser given a
+# filename extension. This magic works because individual parsers have to
+# register themselves with us as they are loaded in. The do this using the
+# following incantation
+#
+# require "rdoc/parser"
+#
+# class RDoc::Parser::Xyz < RDoc::Parser
+# parse_files_matching /\.xyz$/ # <<<<
+#
+# def initialize(file_name, body, options)
+# ...
+# end
+#
+# def scan
+# ...
+# end
+# end
+#
+# Just to make life interesting, if we suspect a plain text file, we also
+# look for a shebang line just in case it's a potential shell script
+
+class RDoc::Parser
+
+ @parsers = []
+
+ class << self
+ attr_reader :parsers
+ end
+
+ ##
+ # Alias an extension to another extension. After this call, files ending
+ # "new_ext" will be parsed using the same parser as "old_ext"
+
+ def self.alias_extension(old_ext, new_ext)
+ old_ext = old_ext.sub(/^\.(.*)/, '\1')
+ new_ext = new_ext.sub(/^\.(.*)/, '\1')
+
+ parser = can_parse "xxx.#{old_ext}"
+ return false unless parser
+
+ RDoc::Parser.parsers.unshift [/\.#{new_ext}$/, parser]
+
+ true
+ end
+
+ ##
+ # Shamelessly stolen from the ptools gem (since RDoc cannot depend on
+ # the gem).
+
+ def self.binary?(file)
+=begin
+ # XXX: this currently doesn't work in MacRuby
+ s = (File.read(file, File.stat(file).blksize, 0, :mode => "rb") || "").split(//)
+
+ if s.size > 0 then
+ ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
+ else
+ false
+ end
+=end
+ false
+ end
+ private_class_method :binary?
+
+ ##
+ # Return a parser that can handle a particular extension
+
+ def self.can_parse(file_name)
+ parser = RDoc::Parser.parsers.find { |regexp,| regexp =~ file_name }.last
+
+ #
+ # The default parser should *NOT* parse binary files.
+ #
+ if parser == RDoc::Parser::Simple then
+ if binary? file_name then
+ return nil
+ end
+ end
+
+ return parser
+ end
+
+ ##
+ # Find the correct parser for a particular file name. Return a SimpleParser
+ # for ones that we don't know
+
+ def self.for(top_level, file_name, body, options, stats)
+ # If no extension, look for shebang
+ if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)} then
+ shebang = $1
+ case shebang
+ when %r{env\s+ruby}, %r{/ruby}
+ file_name = "dummy.rb"
+ end
+ end
+
+ parser = can_parse file_name
+
+ #
+ # This method must return a parser.
+ #
+ if !parser then
+ parser = RDoc::Parser::Simple
+ end
+
+ parser.new top_level, file_name, body, options, stats
+ end
+
+ ##
+ # Record which file types this parser can understand.
+
+ def self.parse_files_matching(regexp)
+ RDoc::Parser.parsers.unshift [regexp, self]
+ end
+
+ def initialize(top_level, file_name, content, options, stats)
+ @top_level = top_level
+ @file_name = file_name
+ @content = content
+ @options = options
+ @stats = stats
+ end
+
+end
+
+require 'rdoc/parser/simple'
+
Modified: MacRuby/branches/experimental/lib/rdoc/rdoc.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/rdoc.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/rdoc.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,10 +1,14 @@
require 'rdoc'
-require 'rdoc/parsers/parse_rb.rb'
-require 'rdoc/parsers/parse_c.rb'
-require 'rdoc/parsers/parse_f95.rb'
-require 'rdoc/parsers/parse_simple.rb'
+require 'rdoc/parser'
+# Simple must come first
+require 'rdoc/parser/simple'
+require 'rdoc/parser/ruby'
+require 'rdoc/parser/c'
+require 'rdoc/parser/f95'
+require 'rdoc/parser/perl'
+
require 'rdoc/stats'
require 'rdoc/options'
@@ -17,22 +21,25 @@
module RDoc
##
- # Encapsulate the production of rdoc documentation. Basically
- # you can use this as you would invoke rdoc from the command
- # line:
+ # Encapsulate the production of rdoc documentation. Basically you can use
+ # this as you would invoke rdoc from the command line:
#
- # rdoc = RDoc::RDoc.new
- # rdoc.document(args)
+ # rdoc = RDoc::RDoc.new
+ # rdoc.document(args)
#
- # where _args_ is an array of strings, each corresponding to
- # an argument you'd give rdoc on the command line. See rdoc/rdoc.rb
- # for details.
+ # Where +args+ is an array of strings, each corresponding to an argument
+ # you'd give rdoc on the command line. See rdoc/rdoc.rb for details.
class RDoc
Generator = Struct.new(:file_name, :class_name, :key)
##
+ # Accessor for statistics. Available after each call to parse_files
+
+ attr_reader :stats
+
+ ##
# This is the list of output generator that we support
GENERATORS = {}
@@ -54,7 +61,7 @@
end
def initialize
- @stats = Stats.new
+ @stats = nil
end
##
@@ -134,7 +141,7 @@
# subdirectories.
#
# The effect of this is that if you want a file with a non-standard
- # extension parsed, you must name it explicity.
+ # extension parsed, you must name it explicitly.
def normalized_file_list(options, relative_files, force_doc = false,
exclude_pattern = nil)
@@ -146,7 +153,10 @@
case type = stat.ftype
when "file"
next if @last_created and stat.mtime < @last_created
- file_list << rel_file_name.sub(/^\.\//, '') if force_doc || ParserFactory.can_parse(rel_file_name)
+
+ if force_doc or ::RDoc::Parser.can_parse(rel_file_name) then
+ file_list << rel_file_name.sub(/^\.\//, '')
+ end
when "directory"
next if rel_file_name == "CVS" || rel_file_name == ".svn"
dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME)
@@ -179,35 +189,40 @@
# Parse each file on the command line, recursively entering directories.
def parse_files(options)
+ @stats = Stats.new options.verbosity
+
files = options.files
files = ["."] if files.empty?
- file_list = normalized_file_list(options, files, true)
+ file_list = normalized_file_list(options, files, true, options.exclude)
return [] if file_list.empty?
file_info = []
- width = file_list.map { |name| name.length }.max + 1
- file_list.each do |fn|
- $stderr.printf("\n%*s: ", width, fn) unless options.quiet
+ file_list.each do |filename|
+ @stats.add_file filename
content = if RUBY_VERSION >= '1.9' then
- File.open(fn, "r:ascii-8bit") { |f| f.read }
+ File.open(filename, "r:ascii-8bit") { |f| f.read }
else
- File.read fn
+ File.read filename
end
- if /coding:\s*(\S+)/ =~ content[/\A(?:.*\n){0,2}/]
- if enc = Encoding.find($1)
- content.force_encoding(enc)
+ if defined? Encoding then
+ if /coding:\s*(\S+)/ =~ content[/\A(?:.*\n){0,2}/]
+ if enc = ::Encoding.find($1)
+ content.force_encoding(enc)
+ end
end
end
- top_level = TopLevel.new(fn)
- parser = ParserFactory.parser_for(top_level, fn, content, options, @stats)
+ top_level = ::RDoc::TopLevel.new filename
+
+ parser = ::RDoc::Parser.for top_level, filename, content, options,
+ @stats
+
file_info << parser.scan
- @stats.num_files += 1
end
file_info
@@ -241,17 +256,19 @@
file_info = parse_files @options
+ @options.title = "RDoc Documentation"
+
if file_info.empty?
$stderr.puts "\nNo newer files." unless @options.quiet
else
- gen = @options.generator
+ @gen = @options.generator
- $stderr.puts "\nGenerating #{gen.key.upcase}..." unless @options.quiet
+ $stderr.puts "\nGenerating #{@gen.key.upcase}..." unless @options.quiet
- require gen.file_name
+ require @gen.file_name
- gen_class = ::RDoc::Generator.const_get gen.class_name
- gen = gen_class.for @options
+ gen_class = ::RDoc::Generator.const_get @gen.class_name
+ @gen = gen_class.for @options
pwd = Dir.pwd
@@ -259,7 +276,7 @@
begin
Diagram.new(file_info, @options).draw if @options.diagram
- gen.generate(file_info)
+ @gen.generate(file_info)
update_output_dir(".", start_time)
ensure
Dir.chdir(pwd)
@@ -272,6 +289,5 @@
end
end
end
-
end
Modified: MacRuby/branches/experimental/lib/rdoc/ri/cache.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/cache.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/cache.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -14,7 +14,7 @@
@inferior_classes = []
end
- # We found this class in more tha one place, so add
+ # We found this class in more than one place, so add
# in the name from there.
def add_path(path)
@path_names << path
@@ -37,10 +37,10 @@
if name =~ /^(.*?)-(c|i).yaml$/
external_name = $1
is_class_method = $2 == "c"
- internal_name = RiWriter.external_to_internal(external_name)
+ internal_name = RDoc::RI::Writer.external_to_internal(external_name)
list = is_class_method ? @class_methods : @instance_methods
path = File.join(dir, name)
- list << MethodEntry.new(path, internal_name, is_class_method, self)
+ list << RDoc::RI::MethodEntry.new(path, internal_name, is_class_method, self)
else
full_name = File.join(dir, name)
if File.directory?(full_name)
@@ -48,7 +48,7 @@
if inf_class
inf_class.add_path(full_name)
else
- inf_class = ClassEntry.new(full_name, name, self)
+ inf_class = RDoc::RI::ClassEntry.new(full_name, name, self)
@inferior_classes << inf_class
end
inf_class.load_from(full_name)
@@ -168,7 +168,7 @@
end
##
-# We represent everything know about all 'ri' files accessible to this program
+# We represent everything known about all 'ri' files accessible to this program
class RDoc::RI::Cache
@@ -185,4 +185,3 @@
end
end
-
Modified: MacRuby/branches/experimental/lib/rdoc/ri/descriptions.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/descriptions.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/descriptions.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,11 +2,10 @@
require 'rdoc/markup/fragments'
require 'rdoc/ri'
-#--
+##
# Descriptions are created by RDoc (in ri_generator) and written out in
# serialized form into the documentation tree. ri then reads these to generate
# the documentation
-#++
class RDoc::RI::NamedThing
attr_reader :name
@@ -78,12 +77,14 @@
class RDoc::RI::ModuleDescription < RDoc::RI::Description
attr_accessor :class_methods
+ attr_accessor :class_method_extensions
attr_accessor :instance_methods
+ attr_accessor :instance_method_extensions
attr_accessor :attributes
attr_accessor :constants
attr_accessor :includes
- # merge in another class desscription into this one
+ # merge in another class description into this one
def merge_in(old)
merge(@class_methods, old.class_methods)
merge(@instance_methods, old.instance_methods)
@@ -94,8 +95,12 @@
@comment = old.comment
else
unless old.comment.nil? or old.comment.empty? then
- @comment << RDoc::Markup::Flow::RULE.new
- @comment.concat old.comment
+ if @comment.nil? or @comment.empty? then
+ @comment = old.comment
+ else
+ @comment << RDoc::Markup::Flow::RULE.new
+ @comment.concat old.comment
+ end
end
end
end
@@ -145,6 +150,7 @@
attr_accessor :aliases
attr_accessor :is_alias_for
attr_accessor :params
+ attr_accessor :source_path
end
Modified: MacRuby/branches/experimental/lib/rdoc/ri/display.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/display.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/display.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,5 +1,15 @@
require 'rdoc/ri'
+# readline support might not be present, so be careful
+# when requiring it.
+begin
+ require('readline')
+ require('abbrev')
+ CAN_USE_READLINE = true # HACK use an RDoc namespace constant
+rescue LoadError
+ CAN_USE_READLINE = false
+end
+
##
# This is a kind of 'flag' module. If you want to write your own 'ri' display
# module (perhaps because you're writing an IDE), you write a class which
@@ -41,9 +51,9 @@
# Display information about +klass+. Fetches additional information from
# +ri_reader+ as necessary.
- def display_class_info(klass, ri_reader)
+ def display_class_info(klass)
page do
- superclass = klass.superclass_string
+ superclass = klass.superclass
if superclass
superclass = " < " + superclass
@@ -61,17 +71,11 @@
@formatter.blankline
@formatter.display_heading("Includes:", 2, "")
incs = []
+
klass.includes.each do |inc|
- inc_desc = ri_reader.find_class_by_name(inc.name)
- if inc_desc
- str = inc.name + "("
- str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
- str << ")"
- incs << str
- else
- incs << inc.name
- end
- end
+ incs << inc.name
+ end
+
@formatter.wrap(incs.sort.join(', '))
end
@@ -82,42 +86,19 @@
constants = klass.constants.sort_by { |constant| constant.name }
constants.each do |constant|
+ @formatter.wrap "#{constant.name} = #{constant.value}"
if constant.comment then
- @formatter.wrap "#{constant.name}:"
-
@formatter.indent do
@formatter.display_flow constant.comment
end
else
- @formatter.wrap constant.name
+ @formatter.break_to_newline
end
end
end
- class_data = [
- :class_methods,
- :class_method_extensions,
- :instance_methods,
- :instance_method_extensions,
- ]
-
- class_data.each do |data_type|
- data = klass.send data_type
-
- unless data.empty? then
- @formatter.blankline
-
- heading = data_type.to_s.split('_').join(' ').capitalize << ':'
- @formatter.display_heading heading, 2, ''
-
- data = data.map { |item| item.name }.sort.join ', '
- @formatter.wrap data
- end
- end
-
unless klass.attributes.empty? then
@formatter.blankline
-
@formatter.display_heading 'Attributes:', 2, ''
attributes = klass.attributes.sort_by { |attribute| attribute.name }
@@ -130,13 +111,121 @@
end
else
@formatter.wrap "#{attribute.name} (#{attribute.rw})"
+ @formatter.break_to_newline
end
end
end
+
+ return display_class_method_list(klass)
end
end
+
+ ##
+ # Given a Hash mapping a class' methods to method types (returned by
+ # display_class_method_list), this method allows the user to
+ # choose one of the methods.
+
+ def get_class_method_choice(method_map)
+ if CAN_USE_READLINE
+ # prepare abbreviations for tab completion
+ abbreviations = method_map.keys.abbrev
+ Readline.completion_proc = proc do |string|
+ abbreviations.values.uniq.grep(/^#{string}/)
+ end
+ end
+
+ @formatter.raw_print_line "\nEnter the method name you want.\n"
+ @formatter.raw_print_line "Class methods can be preceeded by '::' and instance methods by '#'.\n"
+ if CAN_USE_READLINE
+ @formatter.raw_print_line "You can use tab to autocomplete.\n"
+ @formatter.raw_print_line "Enter a blank line to exit.\n"
+
+ choice_string = Readline.readline(">> ").strip
+ else
+ @formatter.raw_print_line "Enter a blank line to exit.\n"
+ @formatter.raw_print_line ">> "
+ choice_string = $stdin.gets.strip
+ end
+
+ if choice_string == ''
+ return nil
+ else
+ class_or_instance = method_map[choice_string]
+
+ if class_or_instance
+ # If the user's choice is not preceeded by a '::' or a '#', figure
+ # out whether they want a class or an instance method and decorate
+ # the choice appropriately.
+ if(choice_string =~ /^[a-zA-Z]/)
+ if(class_or_instance == :class)
+ choice_string = "::#{choice_string}"
+ else
+ choice_string = "##{choice_string}"
+ end
+ end
+
+ return choice_string
+ else
+ @formatter.raw_print_line "No method matched '#{choice_string}'.\n"
+ return nil
+ end
+ end
+ end
+
+
##
+ # Display methods on +klass+
+ # Returns a hash mapping method name to method contents (HACK?)
+
+ def display_class_method_list(klass)
+ method_map = {}
+
+ class_data = [
+ :class_methods,
+ :class_method_extensions,
+ :instance_methods,
+ :instance_method_extensions,
+ ]
+
+ class_data.each do |data_type|
+ data = klass.send data_type
+
+ unless data.nil? or data.empty? then
+ @formatter.blankline
+
+ heading = data_type.to_s.split('_').join(' ').capitalize << ':'
+ @formatter.display_heading heading, 2, ''
+
+ method_names = []
+ data.each do |item|
+ method_names << item.name
+
+ if(data_type == :class_methods ||
+ data_type == :class_method_extensions) then
+ method_map["::#{item.name}"] = :class
+ method_map[item.name] = :class
+ else
+ #
+ # Since we iterate over instance methods after class methods,
+ # an instance method always will overwrite the unqualified
+ # class method entry for a class method of the same name.
+ #
+ method_map["##{item.name}"] = :instance
+ method_map[item.name] = :instance
+ end
+ end
+ method_names.sort!
+
+ @formatter.wrap method_names.join(', ')
+ end
+ end
+
+ method_map
+ end
+ private :display_class_method_list
+
+ ##
# Display an Array of RDoc::Markup::Flow objects, +flow+.
def display_flow(flow)
@@ -172,10 +261,42 @@
def display_method_list(methods)
page do
@formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:"
+ @formatter.blankline
+ methods.each do |method|
+ @formatter.raw_print_line "#{method.full_name} [#{method.source_path}]\n"
+ end
+ end
+ end
+
+ ##
+ # Display a list of +methods+ and allow the user to select one of them.
+
+ def display_method_list_choice(methods)
+ page do
+ @formatter.wrap "More than one method matched your request. Please choose one of the possible matches."
@formatter.blankline
- @formatter.wrap methods.map { |m| m.full_name }.join(", ")
+ methods.each_with_index do |method, index|
+ @formatter.raw_print_line "%3d %s [%s]\n" % [index + 1, method.full_name, method.source_path]
+ end
+
+ @formatter.raw_print_line ">> "
+
+ choice = $stdin.gets.strip!
+
+ if(choice == '')
+ return
+ end
+
+ choice = choice.to_i
+
+ if ((choice == 0) || (choice > methods.size)) then
+ @formatter.raw_print_line "Invalid choice!\n"
+ else
+ method = methods[choice - 1]
+ display_method_info(method)
+ end
end
end
@@ -198,10 +319,8 @@
@formatter.break_to_newline
end
- if method.source_path then
- @formatter.blankline
- @formatter.wrap("Extension from #{method.source_path}")
- end
+ @formatter.blankline
+ @formatter.wrap("From #{method.source_path}")
end
##
@@ -271,4 +390,3 @@
end
end
-
Modified: MacRuby/branches/experimental/lib/rdoc/ri/driver.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/driver.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/driver.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -11,25 +11,97 @@
class RDoc::RI::Driver
- def self.process_args(argv)
+ #
+ # This class offers both Hash and OpenStruct functionality.
+ # We convert from the Core Hash to this before calling any of
+ # the display methods, in order to give the display methods
+ # a cleaner API for accessing the data.
+ #
+ class OpenStructHash < Hash
+ #
+ # This method converts from a Hash to an OpenStructHash.
+ #
+ def self.convert(object)
+ case object
+ when Hash then
+ new_hash = new # Convert Hash -> OpenStructHash
+
+ object.each do |key, value|
+ new_hash[key] = convert(value)
+ end
+
+ new_hash
+ when Array then
+ object.map do |element|
+ convert(element)
+ end
+ else
+ object
+ end
+ end
+
+ def merge_enums(other)
+ other.each do |k, v|
+ if self[k] then
+ case v
+ when Array then
+ # HACK dunno
+ if String === self[k] and self[k].empty? then
+ self[k] = v
+ else
+ self[k] += v
+ end
+ when Hash then
+ self[k].update v
+ else
+ # do nothing
+ end
+ else
+ self[k] = v
+ end
+ end
+ end
+
+ def method_missing method, *args
+ self[method.to_s]
+ end
+ end
+
+ class Error < RDoc::RI::Error; end
+
+ class NotFoundError < Error
+ def message
+ "Nothing known about #{super}"
+ end
+ end
+
+ attr_accessor :homepath # :nodoc:
+
+ def self.default_options
options = {}
options[:use_stdout] = !$stdout.tty?
options[:width] = 72
options[:formatter] = RDoc::RI::Formatter.for 'plain'
- options[:list_classes] = false
- options[:list_names] = false
+ options[:interactive] = false
+ options[:use_cache] = true
- # By default all paths are used. If any of these are true, only those
- # directories are used.
- use_system = false
- use_site = false
- use_home = false
- use_gems = false
- doc_dirs = []
+ # By default all standard paths are used.
+ options[:use_system] = true
+ options[:use_site] = true
+ options[:use_home] = true
+ options[:use_gems] = true
+ options[:extra_doc_dirs] = []
+ return options
+ end
+
+ def self.process_args(argv)
+ options = default_options
+
opts = OptionParser.new do |opt|
opt.program_name = File.basename $0
opt.version = RDoc::VERSION
+ opt.release = nil
opt.summary_indent = ' ' * 4
directories = [
@@ -84,88 +156,116 @@
opt.separator "Options:"
opt.separator nil
- opt.on("--classes", "-c",
- "Display the names of classes and modules we",
- "know about.") do |value|
- options[:list_classes] = value
+ opt.on("--fmt=FORMAT", "--format=FORMAT", "-f",
+ RDoc::RI::Formatter::FORMATTERS.keys,
+ "Format to use when displaying output:",
+ " #{RDoc::RI::Formatter.list}",
+ "Use 'bs' (backspace) with most pager",
+ "programs. To use ANSI, either disable the",
+ "pager or tell the pager to allow control",
+ "characters.") do |value|
+ options[:formatter] = RDoc::RI::Formatter.for value
end
opt.separator nil
opt.on("--doc-dir=DIRNAME", "-d", Array,
- "List of directories to search for",
- "documentation. If not specified, we search",
- "the standard rdoc/ri directories. May be",
- "repeated.") do |value|
+ "List of directories from which to source",
+ "documentation in addition to the standard",
+ "directories. May be repeated.") do |value|
value.each do |dir|
unless File.directory? dir then
raise OptionParser::InvalidArgument, "#{dir} is not a directory"
end
+
+ options[:extra_doc_dirs] << File.expand_path(dir)
end
+ end
- doc_dirs.concat value
+ opt.separator nil
+
+ opt.on("--[no-]use-cache",
+ "Whether or not to use ri's cache.",
+ "True by default.") do |value|
+ options[:use_cache] = value
end
opt.separator nil
- opt.on("--fmt=FORMAT", "--format=FORMAT", "-f",
- RDoc::RI::Formatter::FORMATTERS.keys,
- "Format to use when displaying output:",
- " #{RDoc::RI::Formatter.list}",
- "Use 'bs' (backspace) with most pager",
- "programs. To use ANSI, either disable the",
- "pager or tell the pager to allow control",
- "characters.") do |value|
- options[:formatter] = RDoc::RI::Formatter.for value
+ opt.on("--no-standard-docs",
+ "Do not include documentation from",
+ "the Ruby standard library, site_lib,",
+ "installed gems, or ~/.rdoc.",
+ "Equivalent to specifying",
+ "the options --no-system, --no-site, --no-gems,",
+ "and --no-home") do
+ options[:use_system] = false
+ options[:use_site] = false
+ options[:use_gems] = false
+ options[:use_home] = false
end
opt.separator nil
- unless RDoc::RI::Paths::GEMDIRS.empty? then
- opt.on("--[no-]gems",
- "Include documentation from RubyGems.") do |value|
- use_gems = value
- end
+ opt.on("--[no-]system",
+ "Include documentation from Ruby's standard",
+ "library. Defaults to true.") do |value|
+ options[:use_system] = value
end
opt.separator nil
- opt.on("--[no-]home",
- "Include documentation stored in ~/.rdoc.") do |value|
- use_home = value
+ opt.on("--[no-]site",
+ "Include documentation from libraries",
+ "installed in site_lib.",
+ "Defaults to true.") do |value|
+ options[:use_site] = value
end
opt.separator nil
- opt.on("--[no-]list-names", "-l",
- "List all the names known to RDoc, one per",
- "line.") do |value|
- options[:list_names] = value
+ opt.on("--[no-]gems",
+ "Include documentation from RubyGems.",
+ "Defaults to true.") do |value|
+ options[:use_gems] = value
end
opt.separator nil
- opt.on("--no-pager", "-T",
- "Send output directly to stdout.") do |value|
- options[:use_stdout] = !value
+ opt.on("--[no-]home",
+ "Include documentation stored in ~/.rdoc.",
+ "Defaults to true.") do |value|
+ options[:use_home] = value
end
opt.separator nil
- opt.on("--[no-]site",
- "Include documentation from libraries",
- "installed in site_lib.") do |value|
- use_site = value
+ opt.on("--list-doc-dirs",
+ "List the directories from which ri will",
+ "source documentation on stdout and exit.") do
+ options[:list_doc_dirs] = true
end
opt.separator nil
- opt.on("--[no-]system",
- "Include documentation from Ruby's standard",
- "library.") do |value|
- use_system = value
+ opt.on("--no-pager", "-T",
+ "Send output directly to stdout,",
+ "rather than to a pager.") do
+ options[:use_stdout] = true
end
+ opt.on("--interactive", "-i",
+ "This makes ri go into interactive mode.",
+ "When ri is in interactive mode it will",
+ "allow the user to disambiguate lists of",
+ "methods in case multiple methods match",
+ "against a method search string. It also",
+ "will allow the user to enter in a method",
+ "name (with auto-completion, if readline",
+ "is supported) when viewing a class.") do
+ options[:interactive] = true
+ end
+
opt.separator nil
opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
@@ -180,10 +280,10 @@
options[:names] = argv
- options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home,
- use_gems, *doc_dirs)
- options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site,
- use_home, use_gems, *doc_dirs)
+ options[:formatter] ||= RDoc::RI::Formatter.for('plain')
+ options[:use_stdout] ||= !$stdout.tty?
+ options[:use_stdout] ||= options[:interactive]
+ options[:width] ||= 72
options
@@ -200,22 +300,30 @@
ri.run
end
- def initialize(options={})
- options[:formatter] ||= RDoc::RI::Formatter.for('plain')
- options[:use_stdout] ||= !$stdout.tty?
- options[:width] ||= 72
+ def initialize(initial_options={})
+ options = self.class.default_options.update(initial_options)
+
@names = options[:names]
+ @class_cache_name = 'classes'
- @class_cache_name = 'classes'
- @all_dirs = RDoc::RI::Paths.path(true, true, true, true)
+ @doc_dirs = RDoc::RI::Paths.path(options[:use_system],
+ options[:use_site],
+ options[:use_home],
+ options[:use_gems],
+ options[:extra_doc_dirs])
+
@homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first
@homepath = @homepath.sub(/\.rdoc/, '.ri')
- @sys_dirs = RDoc::RI::Paths.raw_path(true, false, false, false)
+ @sys_dir = RDoc::RI::Paths.raw_path(true, false, false, false).first
+ @list_doc_dirs = options[:list_doc_dirs]
FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path
+ @cache_doc_dirs_path = File.join cache_file_path, ".doc_dirs"
+ @use_cache = options[:use_cache]
@class_cache = nil
+ @interactive = options[:interactive]
@display = RDoc::RI::DefaultDisplay.new(options[:formatter],
options[:width],
options[:use_stdout])
@@ -224,29 +332,94 @@
def class_cache
return @class_cache if @class_cache
- newest = map_dirs('created.rid', :all) do |f|
+ # Get the documentation directories used to make the cache in order to see
+ # whether the cache is valid for the current ri instantiation.
+ if(File.readable?(@cache_doc_dirs_path))
+ cache_doc_dirs = IO.read(@cache_doc_dirs_path).split("\n")
+ else
+ cache_doc_dirs = []
+ end
+
+ newest = map_dirs('created.rid') do |f|
File.mtime f if test ?f, f
end.max
+ # An up to date cache file must have been created more recently than
+ # the last modification of any of the documentation directories. It also
+ # must have been created with the same documentation directories
+ # as those from which ri currently is sourcing documentation.
up_to_date = (File.exist?(class_cache_file_path) and
- newest and newest < File.mtime(class_cache_file_path))
+ newest and newest < File.mtime(class_cache_file_path) and
+ (cache_doc_dirs == @doc_dirs))
- @class_cache = if up_to_date then
- load_cache_for @class_cache_name
- else
- class_cache = {}
+ if up_to_date and @use_cache then
+ open class_cache_file_path, 'rb' do |fp|
+ begin
+ @class_cache = Marshal.load fp.read
+ rescue
+ #
+ # This shouldn't be necessary, since the up_to_date logic above
+ # should force the cache to be recreated when a new version of
+ # rdoc is installed. This seems like a worthwhile enhancement
+ # to ri's robustness, however.
+ #
+ $stderr.puts "Error reading the class cache; recreating the class cache!"
+ @class_cache = create_class_cache
+ end
+ end
+ else
+ @class_cache = create_class_cache
+ end
- classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] }
- populate_class_cache class_cache, classes
+ @class_cache
+ end
- classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] }
- warn "Updating class cache with #{classes.size} classes..."
+ def create_class_cache
+ class_cache = OpenStructHash.new
- populate_class_cache class_cache, classes, true
- write_cache class_cache, class_cache_file_path
- end
+ if(@use_cache)
+ # Dump the documentation directories to a file in the cache, so that
+ # we only will use the cache for future instantiations with identical
+ # documentation directories.
+ File.open @cache_doc_dirs_path, "wb" do |fp|
+ fp << @doc_dirs.join("\n")
+ end
+ end
+
+ classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] }
+ warn "Updating class cache with #{classes.size} classes..."
+ populate_class_cache class_cache, classes
+
+ write_cache class_cache, class_cache_file_path
+
+ class_cache
end
+ def populate_class_cache(class_cache, classes, extension = false)
+ classes.each do |cdesc|
+ desc = read_yaml cdesc
+ klassname = desc["full_name"]
+
+ unless class_cache.has_key? klassname then
+ desc["display_name"] = "Class"
+ desc["sources"] = [cdesc]
+ desc["instance_method_extensions"] = []
+ desc["class_method_extensions"] = []
+ class_cache[klassname] = desc
+ else
+ klass = class_cache[klassname]
+
+ if extension then
+ desc["instance_method_extensions"] = desc.delete "instance_methods"
+ desc["class_method_extensions"] = desc.delete "class_methods"
+ end
+
+ klass.merge_enums desc
+ klass["sources"] << cdesc
+ end
+ end
+ end
+
def class_cache_file_path
File.join cache_file_path, @class_cache_name
end
@@ -261,133 +434,202 @@
def display_class(name)
klass = class_cache[name]
- @display.display_class_info klass, class_cache
+ @display.display_class_info klass
end
+ def display_method(method)
+ @display.display_method_info method
+ end
+
+ def get_info_for(arg)
+ @names = [arg]
+ run
+ end
+
def load_cache_for(klassname)
path = cache_file_for klassname
+ cache = nil
+
if File.exist? path and
- File.mtime(path) >= File.mtime(class_cache_file_path) then
- File.open path, 'rb' do |fp|
- Marshal.load fp.read
+ File.mtime(path) >= File.mtime(class_cache_file_path) and
+ @use_cache then
+ open path, 'rb' do |fp|
+ begin
+ cache = Marshal.load fp.read
+ rescue
+ #
+ # The cache somehow is bad. Recreate the cache.
+ #
+ $stderr.puts "Error reading the cache for #{klassname}; recreating the cache!"
+ cache = create_cache_for klassname, path
+ end
end
else
- class_cache = nil
+ cache = create_cache_for klassname, path
+ end
- File.open class_cache_file_path, 'rb' do |fp|
- class_cache = Marshal.load fp.read
- end
+ cache
+ end
- klass = class_cache[klassname]
- return nil unless klass
+ def create_cache_for(klassname, path)
+ klass = class_cache[klassname]
+ return nil unless klass
- method_files = klass["sources"]
- cache = {}
+ method_files = klass["sources"]
+ cache = OpenStructHash.new
- sys_dir = @sys_dirs.first
- method_files.each do |f|
- system_file = f.index(sys_dir) == 0
- Dir[File.join(File.dirname(f), "*")].each do |yaml|
- next unless yaml =~ /yaml$/
- next if yaml =~ /cdesc-[^\/]+yaml$/
- method = read_yaml yaml
- name = method["full_name"]
- ext_path = f
- ext_path = "gem #{$1}" if f =~ %r%gems/[\d.]+/doc/([^/]+)%
- method["source_path"] = ext_path unless system_file
- cache[name] = method
+ method_files.each do |f|
+ system_file = f.index(@sys_dir) == 0
+ Dir[File.join(File.dirname(f), "*")].each do |yaml|
+ next unless yaml =~ /yaml$/
+ next if yaml =~ /cdesc-[^\/]+yaml$/
+
+ method = read_yaml yaml
+
+ if system_file then
+ method["source_path"] = "Ruby #{RDoc::RI::Paths::VERSION}"
+ else
+ if(f =~ %r%gems/[\d.]+/doc/([^/]+)%) then
+ ext_path = "gem #{$1}"
+ else
+ ext_path = f
+ end
+
+ method["source_path"] = ext_path
end
+
+ name = method["full_name"]
+ cache[name] = method
end
+ end
- write_cache cache, path
+ write_cache cache, path
+ end
+
+ ##
+ # Finds the next ancestor of +orig_klass+ after +klass+.
+
+ def lookup_ancestor(klass, orig_klass)
+ # This is a bit hacky, but ri will go into an infinite
+ # loop otherwise, since Object has an Object ancestor
+ # for some reason. Depending on the documentation state, I've seen
+ # Kernel as an ancestor of Object and not as an ancestor of Object.
+ if ((orig_klass == "Object") &&
+ ((klass == "Kernel") || (klass == "Object")))
+ return nil
end
+
+ cache = class_cache[orig_klass]
+
+ return nil unless cache
+
+ ancestors = [orig_klass]
+ ancestors.push(*cache.includes.map { |inc| inc['name'] })
+ ancestors << cache.superclass
+
+ ancestor_index = ancestors.index(klass)
+
+ if ancestor_index
+ ancestor = ancestors[ancestors.index(klass) + 1]
+ return ancestor if ancestor
+ end
+
+ lookup_ancestor klass, cache.superclass
end
- def map_dirs(file_name, system=false)
- dirs = if system == :all then
- @all_dirs
- else
- if system then
- @sys_dirs
- else
- @all_dirs - @sys_dirs
- end
- end
+ ##
+ # Finds the method
- dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
+ def lookup_method(name, klass)
+ cache = load_cache_for klass
+ return nil unless cache
+
+ method = cache[name.gsub('.', '#')]
+ method = cache[name.gsub('.', '::')] unless method
+ method
end
- def populate_class_cache(class_cache, classes, extension = false)
- classes.each do |cdesc|
- desc = read_yaml cdesc
- klassname = desc["full_name"]
+ def map_dirs(file_name)
+ @doc_dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
+ end
- unless class_cache.has_key? klassname then
- desc["display_name"] = "Class"
- desc["sources"] = [cdesc]
- desc["instance_method_extensions"] = []
- desc["class_method_extensions"] = []
- class_cache[klassname] = desc
- else
- klass = class_cache[klassname]
+ ##
+ # Extract the class and method name parts from +name+ like Foo::Bar#baz
- if extension then
- desc["instance_method_extensions"] = desc.delete "instance_methods"
- desc["class_method_extensions"] = desc.delete "class_methods"
- end
+ def parse_name(name)
+ parts = name.split(/(::|\#|\.)/)
- klass.merge_enums desc
- klass["sources"] << cdesc
- end
+ if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
+ meth = parts.pop
+ parts.pop
end
+
+ klass = parts.join
+
+ [klass, meth]
end
def read_yaml(path)
data = File.read path
+
+ # Necessary to be backward-compatible with documentation generated
+ # by earliar RDoc versions.
data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '')
data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/,
' !ruby/\1:RDoc::Markup::\2')
- YAML.load data
+ OpenStructHash.convert(YAML.load(data))
end
- def get_info_for(arg)
- @names = [arg]
- run
- end
-
def run
- if @names.empty? then
+ if(@list_doc_dirs)
+ puts @doc_dirs.join("\n")
+ elsif @names.empty? then
@display.list_known_classes class_cache.keys.sort
else
@names.each do |name|
- case name
- when /::|\#|\./ then
- if class_cache.key? name then
- display_class name
- else
- meth = nil
+ if class_cache.key? name then
+ method_map = display_class name
+ if(@interactive)
+ method_name = @display.get_class_method_choice(method_map)
- parts = name.split(/::|\#|\./)
- meth = parts.pop unless parts.last =~ /^[A-Z]/
- klass = parts.join '::'
+ if(method_name != nil)
+ method = lookup_method "#{name}#{method_name}", name
+ display_method method
+ end
+ end
+ elsif name =~ /::|\#|\./ then
+ klass, = parse_name name
- cache = load_cache_for klass
- # HACK Does not support F.n
- abort "Nothing known about #{name}" unless cache
- method = cache[name.gsub(/\./, '#')]
- abort "Nothing known about #{name}" unless method
- @display.display_method_info method
+ orig_klass = klass
+ orig_name = name
+
+ loop do
+ method = lookup_method name, klass
+
+ break method if method
+
+ ancestor = lookup_ancestor klass, orig_klass
+
+ break unless ancestor
+
+ name = name.sub klass, ancestor
+ klass = ancestor
end
+
+ raise NotFoundError, orig_name unless method
+
+ display_method method
else
- if class_cache.key? name then
- display_class name
+ methods = select_methods(/#{name}/)
+
+ if methods.size == 0
+ raise NotFoundError, name
+ elsif methods.size == 1
+ display_method methods[0]
else
- methods = select_methods(/^#{name}/)
- if methods.size == 0
- abort "Nothing known about #{name}"
- elsif methods.size == 1
- @display.display_method_info methods.first
+ if(@interactive)
+ @display.display_method_list_choice methods
else
@display.display_method_list methods
end
@@ -395,6 +637,8 @@
end
end
end
+ rescue NotFoundError => e
+ abort e.message
end
def select_methods(pattern)
@@ -413,40 +657,13 @@
end
def write_cache(cache, path)
- File.open path, "wb" do |cache_file|
- Marshal.dump cache, cache_file
+ if(@use_cache)
+ File.open path, "wb" do |cache_file|
+ Marshal.dump cache, cache_file
+ end
end
cache
end
end
-
-class Hash # HACK don't add stuff to Hash.
- def method_missing method, *args
- self[method.to_s]
- end
-
- def merge_enums(other)
- other.each do |k,v|
- if self[k] then
- case v
- when Array then
- # HACK dunno
- if String === self[k] and self[k].empty? then
- self[k] = v
- else
- self[k] += v
- end
- when Hash then
- self[k].merge! v
- else
- # do nothing
- end
- else
- self[k] = v
- end
- end
- end
-end
-
Modified: MacRuby/branches/experimental/lib/rdoc/ri/formatter.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/formatter.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/formatter.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -93,7 +93,7 @@
end
def raw_print_line(txt)
- @output.puts txt
+ @output.print txt
end
##
Modified: MacRuby/branches/experimental/lib/rdoc/ri/paths.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/paths.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/paths.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -26,9 +26,9 @@
DOC_DIR = "doc/rdoc"
- version = RbConfig::CONFIG['ruby_version']
+ VERSION = RbConfig::CONFIG['ruby_version']
- base = File.join(RbConfig::CONFIG['datadir'], "ri", version)
+ base = File.join(RbConfig::CONFIG['datadir'], "ri", VERSION)
SYSDIR = File.join(base, "system")
SITEDIR = File.join(base, "site")
homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH']
@@ -39,12 +39,8 @@
HOMEDIR = nil
end
- # This is the search path for 'ri'
- PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)}
-
begin
- require 'rubygems' unless defined?(Gem) and defined?(Gem::Enable) and
- Gem::Enable
+ require 'rubygems'# unless defined?(Gem)
# HACK dup'd from Gem.latest_partials and friends
all_paths = []
@@ -67,7 +63,6 @@
end
GEMDIRS = ri_paths.map { |k,v| v.last }.sort
- GEMDIRS.each { |dir| PATH << dir }
rescue LoadError
GEMDIRS = []
end
@@ -85,9 +80,6 @@
# found.
def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs)
- return PATH unless use_system or use_site or use_home or use_gems or
- not extra_dirs.empty?
-
path = []
path << extra_dirs unless extra_dirs.empty?
path << SYSDIR if use_system
@@ -97,6 +89,4 @@
return path.flatten.compact
end
-
end
-
Modified: MacRuby/branches/experimental/lib/rdoc/ri/reader.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/reader.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/reader.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -45,7 +45,7 @@
def get_method(method_entry)
path = method_entry.path_name
- File.open(path) { |f| RI::Description.deserialize(f) }
+ File.open(path) { |f| RDoc::RI::Description.deserialize(f) }
end
##
@@ -54,8 +54,8 @@
def get_class(class_entry)
result = nil
for path in class_entry.path_names
- path = RiWriter.class_desc_path(path, class_entry)
- desc = File.open(path) {|f| RI::Description.deserialize(f) }
+ path = RDoc::RI::Writer.class_desc_path(path, class_entry)
+ desc = File.open(path) {|f| RDoc::RI::Description.deserialize(f) }
if result
result.merge_in(desc)
else
Modified: MacRuby/branches/experimental/lib/rdoc/ri/util.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri/util.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri/util.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,5 @@
require 'rdoc/ri'
-class RDoc::RI::Error < RuntimeError; end
-
##
# Break argument into its constituent class or module names, an
# optional method type, and a method name
Modified: MacRuby/branches/experimental/lib/rdoc/ri.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/ri.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/ri.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,4 +1,8 @@
require 'rdoc'
-module RDoc::RI; end
+module RDoc::RI
+ class Error < RDoc::Error; end
+
+end
+
Modified: MacRuby/branches/experimental/lib/rdoc/stats.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc/stats.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc/stats.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -5,21 +5,111 @@
class RDoc::Stats
- attr_accessor :num_files, :num_classes, :num_modules, :num_methods
+ attr_reader :num_classes
+ attr_reader :num_files
+ attr_reader :num_methods
+ attr_reader :num_modules
- def initialize
- @num_files = @num_classes = @num_modules = @num_methods = 0
+ def initialize(verbosity = 1)
+ @num_classes = 0
+ @num_files = 0
+ @num_methods = 0
+ @num_modules = 0
+
@start = Time.now
+
+ @display = case verbosity
+ when 0 then Quiet.new
+ when 1 then Normal.new
+ else Verbose.new
+ end
end
+ def add_alias(as)
+ @display.print_alias as
+ @num_methods += 1
+ end
+
+ def add_class(klass)
+ @display.print_class klass
+ @num_classes += 1
+ end
+
+ def add_file(file)
+ @display.print_file file
+ @num_files += 1
+ end
+
+ def add_method(method)
+ @display.print_method method
+ @num_methods += 1
+ end
+
+ def add_module(mod)
+ @display.print_module mod
+ @num_modules += 1
+ end
+
def print
puts "Files: #@num_files"
puts "Classes: #@num_classes"
puts "Modules: #@num_modules"
puts "Methods: #@num_methods"
- puts "Elapsed: " + sprintf("%0.3fs", Time.now - @start)
+ puts "Elapsed: " + sprintf("%0.1fs", Time.now - @start)
end
+ class Quiet
+ def print_alias(*) end
+ def print_class(*) end
+ def print_file(*) end
+ def print_method(*) end
+ def print_module(*) end
+ end
+
+ class Normal
+ def print_alias(as)
+ print 'a'
+ end
+
+ def print_class(klass)
+ print 'C'
+ end
+
+ def print_file(file)
+ print "\n#{file}: "
+ end
+
+ def print_method(method)
+ print 'm'
+ end
+
+ def print_module(mod)
+ print 'M'
+ end
+ end
+
+ class Verbose
+ def print_alias(as)
+ puts "\t\talias #{as.new_name} #{as.old_name}"
+ end
+
+ def print_class(klass)
+ puts "\tclass #{klass.full_name}"
+ end
+
+ def print_file(file)
+ puts file
+ end
+
+ def print_method(method)
+ puts "\t\t#{method.singleton ? '::' : '#'}#{method.name}"
+ end
+
+ def print_module(mod)
+ puts "\tmodule #{mod.full_name}"
+ end
+ end
+
end
Modified: MacRuby/branches/experimental/lib/rdoc.rb
===================================================================
--- MacRuby/branches/experimental/lib/rdoc.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rdoc.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,87 +1,111 @@
$DEBUG_RDOC = nil
##
-# = RDOC - Ruby Documentation System
-#
+# = \RDoc - Ruby Documentation System
+#
# This package contains RDoc and RDoc::Markup. RDoc is an application that
-# produces documentation for one or more Ruby source files. We work similarly
+# produces documentation for one or more Ruby source files. It works similarly
# to JavaDoc, parsing the source, and extracting the definition for classes,
-# modules, and methods (along with includes and requires). We associate with
+# modules, and methods (along with includes and requires). It associates with
# these optional documentation contained in the immediately preceding comment
-# block, and then render the result using a pluggable output formatter.
+# block, and then renders the result using a pluggable output formatter.
# RDoc::Markup is a library that converts plain text into various output
# formats. The markup library is used to interpret the comment blocks that
# RDoc uses to document methods, classes, and so on.
-#
+#
# == Roadmap
-#
+#
# * If you want to use RDoc to create documentation for your Ruby source files,
# read on.
-# * If you want to include extensions written in C, see RDoc::C_Parser
-# * For information on the various markups available in comment blocks, see
-# RDoc::Markup.
-# * If you want to drive RDoc programatically, see RDoc::RDoc.
+# * If you want to include extensions written in C, see RDoc::Parser::C
+# * If you want to drive RDoc programmatically, see RDoc::RDoc.
# * If you want to use the library to format text blocks into HTML, have a look
# at RDoc::Markup.
# * If you want to try writing your own HTML output template, see
# RDoc::Generator::HTML
-#
+#
# == Summary
-#
-# Once installed, you can create documentation using the 'rdoc' command
-# (the command is 'rdoc.bat' under Windows)
-#
+#
+# Once installed, you can create documentation using the +rdoc+ command
+#
# % rdoc [options] [names...]
-#
-# Type "rdoc --help" for an up-to-date option summary.
-#
+#
+# For an up-to-date option summary, type
+# % rdoc --help
+#
# A typical use might be to generate documentation for a package of Ruby
-# source (such as rdoc itself).
-#
+# source (such as RDoc itself).
+#
# % rdoc
-#
+#
# This command generates documentation for all the Ruby and C source
# files in and below the current directory. These will be stored in a
-# documentation tree starting in the subdirectory 'doc'.
-#
+# documentation tree starting in the subdirectory +doc+.
+#
# You can make this slightly more useful for your readers by having the
# index page contain the documentation for the primary file. In our
# case, we could type
-#
+#
# % rdoc --main rdoc.rb
-#
+#
# You'll find information on the various formatting tricks you can use
# in comment blocks in the documentation this generates.
-#
+#
# RDoc uses file extensions to determine how to process each file. File names
-# ending +.rb+ and <tt>.rbw</tt> are assumed to be Ruby source. Files
+# ending +.rb+ and +.rbw+ are assumed to be Ruby source. Files
# ending +.c+ are parsed as C files. All other files are assumed to
# contain just Markup-style markup (with or without leading '#' comment
# markers). If directory names are passed to RDoc, they are scanned
# recursively for C and Ruby source files only.
+#
+# == \Options
+# rdoc can be passed a variety of command-line options. In addition,
+# options can be specified via the +RDOCOPT+ environment variable, which
+# functions similarly to the +RUBYOPT+ environment variable.
+#
+# % export RDOCOPT="-S"
+#
+# will make rdoc default to inline method source code. Command-line options
+# always will override those in +RDOCOPT+.
+#
+# Run
#
-# = Markup
-#
-# For information on how to make lists, hyperlinks, etc. with RDoc, see
-# RDoc::Markup.
-#
-# Comment blocks can be written fairly naturally, either using '#' on
+# % rdoc --help
+#
+# for full details on rdoc's options.
+#
+# Here are some of the most commonly used options.
+# [-d, --diagram]
+# Generate diagrams showing modules and
+# classes. You need dot V1.8.6 or later to
+# use the --diagram option correctly. Dot is
+# available from http://graphviz.org
+#
+# [-S, --inline-source]
+# Show method source code inline, rather than via a popup link.
+#
+# [-T, --template=NAME]
+# Set the template used when generating output.
+#
+# == Documenting Source Code
+#
+# Comment blocks can be written fairly naturally, either using +#+ on
# successive lines of the comment, or by including the comment in
-# an =begin/=end block. If you use the latter form, the =begin line must be
+# a =begin/=end block. If you use the latter form, the =begin line must be
# flagged with an RDoc tag:
-#
+#
# =begin rdoc
# Documentation to be processed by RDoc.
#
# ...
# =end
-#
+#
# RDoc stops processing comments if it finds a comment line containing
# a <tt>--</tt>. This can be used to separate external from internal
# comments, or to stop a comment being associated with a method, class, or
# module. Commenting can be turned back on with a line that starts with a
# <tt>++</tt>.
-#
+#
# ##
# # Extract the age and calculate the date-of-birth.
# #--
@@ -92,47 +116,141 @@
# def get_dob(person)
# # ...
# end
-#
-# Names of classes, source files, and any method names containing an
+#
+# Names of classes, files, and any method names containing an
# underscore or preceded by a hash character are automatically hyperlinked
-# from comment text to their description.
-#
+# from comment text to their description.
+#
# Method parameter lists are extracted and displayed with the method
# description. If a method calls +yield+, then the parameters passed to yield
# will also be displayed:
-#
+#
# def fred
# ...
# yield line, address
-#
+#
# This will get documented as:
-#
+#
# fred() { |line, address| ... }
-#
+#
# You can override this using a comment containing ':yields: ...' immediately
# after the method definition
-#
+#
# def fred # :yields: index, position
# # ...
#
# yield line, address
-#
+#
# which will get documented as
-#
+#
# fred() { |index, position| ... }
-#
+#
# +:yields:+ is an example of a documentation directive. These appear
# immediately after the start of the document element they are modifying.
-#
+#
+# == \Markup
+#
+# * The markup engine looks for a document's natural left margin. This is
+# used as the initial margin for the document.
+#
+# * Consecutive lines starting at this margin are considered to be a
+# paragraph.
+#
+# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
+# taken to be the start of a list. The margin in increased to be the first
+# non-space following the list start flag. Subsequent lines should be
+# indented to this new margin until the list ends. For example:
+#
+# * this is a list with three paragraphs in
+# the first item. This is the first paragraph.
+#
+# And this is the second paragraph.
+#
+# 1. This is an indented, numbered list.
+# 2. This is the second item in that list
+#
+# This is the third conventional paragraph in the
+# first list item.
+#
+# * This is the second item in the original list
+#
+# * You can also construct labeled lists, sometimes called description
+# or definition lists. Do this by putting the label in square brackets
+# and indenting the list body:
+#
+# [cat] a small furry mammal
+# that seems to sleep a lot
+#
+# [ant] a little insect that is known
+# to enjoy picnics
+#
+# A minor variation on labeled lists uses two colons to separate the
+# label from the list body:
+#
+# cat:: a small furry mammal
+# that seems to sleep a lot
+#
+# ant:: a little insect that is known
+# to enjoy picnics
+#
+# This latter style guarantees that the list bodies' left margins are
+# aligned: think of them as a two column table.
+#
+# * Any line that starts to the right of the current margin is treated
+# as verbatim text. This is useful for code listings. The example of a
+# list above is also verbatim text.
+#
+# * A line starting with an equals sign (=) is treated as a
+# heading. Level one headings have one equals sign, level two headings
+# have two,and so on.
+#
+# * A line starting with three or more hyphens (at the current indent)
+# generates a horizontal rule. The more hyphens, the thicker the rule
+# (within reason, and if supported by the output device)
+#
+# * You can use markup within text (except verbatim) to change the
+# appearance of parts of that text. Out of the box, RDoc::Markup
+# supports word-based and general markup.
+#
+# Word-based markup uses flag characters around individual words:
+#
+# [\*word*] displays word in a *bold* font
+# [\_word_] displays word in an _emphasized_ font
+# [\+word+] displays word in a +code+ font
+#
+# General markup affects text between a start delimiter and and end
+# delimiter. Not surprisingly, these delimiters look like HTML markup.
+#
+# [\<b>text...</b>] displays word in a *bold* font
+# [\<em>text...</em>] displays word in an _emphasized_ font
+# [\\<i>text...</i>] displays word in an <i>italicized</i> font
+# [\<tt>text...</tt>] displays word in a +code+ font
+#
+# Unlike conventional Wiki markup, general markup can cross line
+# boundaries. You can turn off the interpretation of markup by
+# preceding the first character with a backslash. This only works for
+# simple markup, not HTML-style markup.
+#
+# * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are
+# recognized. An HTTP url that references an external image file is
+# converted into an inline <IMG..>. Hyperlinks starting 'link:' are
+# assumed to refer to local files whose path is relative to the --op
+# directory.
+#
+# Hyperlinks can also be of the form <tt>label</tt>[url], in which
+# case the label is used in the displayed text, and +url+ is
+# used as the target. If +label+ contains multiple words,
+# put it in braces: <em>{multi word label}[</em>url<em>]</em>.
+#
# == Directives
-#
+#
# [+:nodoc:+ / +:nodoc:+ all]
-# Don't include this element in the documentation. For classes
-# and modules, the methods, aliases, constants, and attributes
-# directly within the affected class or module will also be
-# omitted. By default, though, modules and classes within that
-# class of module _will_ be documented. This is turned off by
-# adding the +all+ modifier.
+# This directive prevents documentation for the element from
+# being generated. For classes and modules, the methods, aliases,
+# constants, and attributes directly within the affected class or
+# module also will be omitted. By default, though, modules and
+# classes within that class of module _will_ be documented. This is
+# turned off by adding the +all+ modifier.
#
# module MyModule # :nodoc:
# class Input
@@ -143,27 +261,27 @@
# class Output
# end
# end
-#
-# In the above code, only class +MyModule::Input+ will be documented.
-# :nodoc: is global across all files the class or module appears in, so use
-# :stopdoc:/:startdoc: to only omit documentation for a particular set of
-# methods, etc.
-#
+#
+# In the above code, only class <tt>MyModule::Input</tt> will be documented.
+# The +:nodoc:+ directive is global across all files for the class or module
+# to which it applies, so use +:stopdoc:+/+:startdoc:+ to suppress
+# documentation only for a particular set of methods, etc.
+#
# [+:doc:+]
-# Force a method or attribute to be documented even if it wouldn't otherwise
-# be. Useful if, for example, you want to include documentation of a
+# Forces a method or attribute to be documented even if it wouldn't be
+# otherwise. Useful if, for example, you want to include documentation of a
# particular private method.
-#
+#
# [+:notnew:+]
# Only applicable to the +initialize+ instance method. Normally RDoc
-# assumes that the documentation and parameters for #initialize are
-# actually for the ::new method, and so fakes out a ::new for the class.
-# The :notnew: modifier stops this. Remember that #initialize is protected,
-# so you won't see the documentation unless you use the -a command line
+# assumes that the documentation and parameters for +initialize+ are
+# actually for the +new+ method, and so fakes out a +new+ for the class.
+# The +:notnew:+ modifier stops this. Remember that +initialize+ is private,
+# so you won't see the documentation unless you use the +-a+ command line
# option.
-#
+#
# Comment blocks can contain other directives:
-#
+#
# [<tt>:section: title</tt>]
# Starts a new section in the output. The title following +:section:+ is
# used as the section heading, and the remainder of the comment containing
@@ -178,66 +296,66 @@
# # This is the section that I wrote.
# # See it glisten in the noon-day sun.
# # ----------------------------------------
-#
+#
# [+:call-seq:+]
# Lines up to the next blank line in the comment are treated as the method's
# calling sequence, overriding the default parsing of method parameters and
# yield arguments.
-#
+#
# [+:include:+ _filename_]
# \Include the contents of the named file at this point. The file will be
# searched for in the directories listed by the +--include+ option, or in
# the current directory by default. The contents of the file will be
-# shifted to have the same indentation as the ':' at the start of the
-# :include: directive.
-#
+# shifted to have the same indentation as the ':' at the start of
+# the :include: directive.
+#
# [+:title:+ _text_]
# Sets the title for the document. Equivalent to the <tt>--title</tt>
# command line parameter. (The command line parameter overrides any :title:
# directive in the source).
-#
+#
# [+:enddoc:+]
# Document nothing further at the current level.
-#
+#
# [+:main:+ _name_]
# Equivalent to the <tt>--main</tt> command line parameter.
-#
+#
# [+:stopdoc:+ / +:startdoc:+]
# Stop and start adding new documentation elements to the current container.
# For example, if a class has a number of constants that you don't want to
# document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the
-# last. If you don't specifiy a +:startdoc:+ by the end of the container,
+# last. If you don't specify a +:startdoc:+ by the end of the container,
# disables documentation for the entire class or module.
-#
-# = Other stuff
-#
+#
+# == Other stuff
+#
# RDoc is currently being maintained by Eric Hodel <drbrain at segment7.net>
#
# Dave Thomas <dave at pragmaticprogrammer.com> is the original author of RDoc.
-#
+#
# == Credits
-#
+#
# * The Ruby parser in rdoc/parse.rb is based heavily on the outstanding
# work of Keiju ISHITSUKA of Nippon Rational Inc, who produced the Ruby
# parser for irb and the rtags package.
-#
+#
# * Code to diagram classes and modules was written by Sergey A Yanovitsky
# (Jah) of Enticla.
-#
+#
# * Charset patch from MoonWolf.
-#
+#
# * Rich Kilmer wrote the kilmer.rb output template.
-#
+#
# * Dan Brickley led the design of the RDF format.
-#
+#
# == License
-#
+#
# RDoc is Copyright (c) 2001-2003 Dave Thomas, The Pragmatic Programmers. It
# is free software, and may be redistributed under the terms specified
# in the README file of the Ruby distribution.
-#
+#
# == Warranty
-#
+#
# This software is provided "as is" and without any express or implied
# warranties, including, without limitation, the implied warranties of
# merchantibility and fitness for a particular purpose.
@@ -254,7 +372,7 @@
##
# RDoc version you are using
- VERSION = "2.0.0"
+ VERSION = "2.2.2"
##
# Name of the dotfile that contains the description of files to be processed
Modified: MacRuby/branches/experimental/lib/resolv-replace.rb
===================================================================
--- MacRuby/branches/experimental/lib/resolv-replace.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/resolv-replace.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -23,7 +23,8 @@
class UDPSocket
alias original_resolv_bind bind
def bind(host, port)
- original_resolv_bind(IPSocket.getaddress(host), port)
+ host = IPSocket.getaddress(host) if host != ""
+ original_resolv_bind(host, port)
end
alias original_resolv_connect connect
Modified: MacRuby/branches/experimental/lib/resolv.rb
===================================================================
--- MacRuby/branches/experimental/lib/resolv.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/resolv.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,29 +3,34 @@
require 'timeout'
require 'thread'
+begin
+ require 'securerandom'
+rescue LoadError
+end
+
# Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can
# handle multiple DNS requests concurrently without blocking. The ruby
# interpreter.
#
# See also resolv-replace.rb to replace the libc resolver with # Resolv.
-#
+#
# Resolv can look up various DNS resources using the DNS module directly.
-#
+#
# Examples:
-#
+#
# p Resolv.getaddress "www.ruby-lang.org"
# p Resolv.getname "210.251.121.214"
-#
+#
# Resolv::DNS.open do |dns|
# ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
# p ress.map { |r| r.address }
# ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
# p ress.map { |r| [r.exchange.to_s, r.preference] }
# end
-#
-#
+#
+#
# == Bugs
-#
+#
# * NIS is not supported.
# * /etc/nsswitch.conf is not supported.
@@ -33,14 +38,14 @@
##
# Looks up the first IP address for +name+.
-
+
def self.getaddress(name)
DefaultResolver.getaddress(name)
end
##
# Looks up all IP address for +name+.
-
+
def self.getaddresses(name)
DefaultResolver.getaddresses(name)
end
@@ -82,7 +87,7 @@
##
# Looks up the first IP address for +name+.
-
+
def getaddress(name)
each_address(name) {|address| return address}
raise ResolvError.new("no address for #{name}")
@@ -90,7 +95,7 @@
##
# Looks up all IP address for +name+.
-
+
def getaddresses(name)
ret = []
each_address(name) {|address| ret << address}
@@ -252,7 +257,7 @@
end
##
- # Iterates over all hostnames for +address+ retrived from the hosts file.
+ # Iterates over all hostnames for +address+ retrieved from the hosts file.
def each_name(address, &proc)
lazy_initialize
@@ -285,11 +290,6 @@
UDPSize = 512
##
- # Group of DNS resolver threads
-
- DNSThreadGroup = ThreadGroup.new
-
- ##
# Creates a new DNS resolver. See Resolv::DNS.new for argument details.
#
# Yields the created DNS resolver to the block, if given, otherwise
@@ -309,7 +309,7 @@
# Creates a new DNS resolver.
#
# +config_info+ can be:
- #
+ #
# nil:: Uses /etc/resolv.conf.
# String:: Path to a file using /etc/resolv.conf's format.
# Hash:: Must contain :nameserver, :search and :ndots keys.
@@ -330,13 +330,6 @@
@mutex.synchronize {
unless @initialized
@config.lazy_initialize
-
- if nameserver = @config.single?
- @requester = Requester::ConnectedUDP.new(nameserver)
- else
- @requester = Requester::UnconnectedUDP.new
- end
-
@initialized = true
end
}
@@ -349,8 +342,6 @@
def close
@mutex.synchronize {
if @initialized
- @requester.close if @requester
- @requester = nil
@initialized = false
end
}
@@ -466,7 +457,7 @@
##
# Looks up all +typeclass+ DNS resources for +name+. See #getresource for
# argument details.
-
+
def getresources(name, typeclass)
ret = []
each_resource(name, typeclass) {|resource| ret << resource}
@@ -476,10 +467,10 @@
##
# Iterates over all +typeclass+ DNS resources for +name+. See
# #getresource for argument details.
-
+
def each_resource(name, typeclass, &proc)
lazy_initialize
- q = Queue.new
+ requester = make_requester
senders = {}
begin
@config.resolv(name) {|candidate, tout, nameserver|
@@ -488,11 +479,9 @@
msg.add_question(candidate, typeclass)
unless sender = senders[[candidate, nameserver]]
sender = senders[[candidate, nameserver]] =
- @requester.sender(msg, candidate, q, nameserver)
+ requester.sender(msg, candidate, nameserver)
end
- sender.send
- reply = reply_name = nil
- timeout(tout, ResolvTimeout) { reply, reply_name = q.pop }
+ reply, reply_name = requester.request(sender, tout)
case reply.rcode
when RCode::NoError
extract_resources(reply, reply_name, typeclass, &proc)
@@ -504,10 +493,18 @@
end
}
ensure
- @requester.delete(q)
+ requester.close
end
end
+ def make_requester # :nodoc:
+ if nameserver = @config.single?
+ Requester::ConnectedUDP.new(nameserver)
+ else
+ Requester::UnconnectedUDP.new
+ end
+ end
+
def extract_resources(msg, name, typeclass) # :nodoc:
if typeclass < Resource::ANY
n0 = Name.create(name)
@@ -539,91 +536,145 @@
}
end
+ if defined? SecureRandom
+ def self.random(arg) # :nodoc:
+ begin
+ SecureRandom.random_number(arg)
+ rescue NotImplementedError
+ rand(arg)
+ end
+ end
+ else
+ def self.random(arg) # :nodoc:
+ rand(arg)
+ end
+ end
+
+
+ def self.rangerand(range) # :nodoc:
+ base = range.begin
+ len = range.end - range.begin
+ if !range.exclude_end?
+ len += 1
+ end
+ base + random(len)
+ end
+
+ RequestID = {}
+ RequestIDMutex = Mutex.new
+
+ def self.allocate_request_id(host, port) # :nodoc:
+ id = nil
+ RequestIDMutex.synchronize {
+ h = (RequestID[[host, port]] ||= {})
+ begin
+ id = rangerand(0x0000..0xffff)
+ end while h[id]
+ h[id] = true
+ }
+ id
+ end
+
+ def self.free_request_id(host, port, id) # :nodoc:
+ RequestIDMutex.synchronize {
+ key = [host, port]
+ if h = RequestID[key]
+ h.delete id
+ if h.empty?
+ RequestID.delete key
+ end
+ end
+ }
+ end
+
+ def self.bind_random_port(udpsock) # :nodoc:
+ begin
+ port = rangerand(1024..65535)
+ udpsock.bind("", port)
+ rescue Errno::EADDRINUSE
+ retry
+ end
+ end
+
class Requester # :nodoc:
def initialize
@senders = {}
+ @sock = nil
end
- def close
- thread, sock, @thread, @sock = @thread, @sock
- begin
- if thread
- thread.kill
- thread.join
+ def request(sender, tout)
+ timelimit = Time.now + tout
+ sender.send
+ while (now = Time.now) < timelimit
+ timeout = timelimit - now
+ if !IO.select([@sock], nil, nil, timeout)
+ raise ResolvTimeout
end
- ensure
- sock.close if sock
+ reply, from = recv_reply
+ begin
+ msg = Message.decode(reply)
+ rescue DecodeError
+ next # broken DNS message ignored
+ end
+ if s = @senders[[from,msg.id]]
+ break
+ else
+ # unexpected DNS message ignored
+ end
end
+ return msg, s.data
end
- def delete(arg)
- case arg
- when Sender
- @senders.delete_if {|k, s| s == arg }
- when Queue
- @senders.delete_if {|k, s| s.queue == arg }
- else
- raise ArgumentError.new("neither Sender or Queue: #{arg}")
- end
+ def close
+ sock = @sock
+ @sock = nil
+ sock.close if sock
end
class Sender # :nodoc:
- def initialize(msg, data, sock, queue)
+ def initialize(msg, data, sock)
@msg = msg
@data = data
@sock = sock
- @queue = queue
end
- attr_reader :queue
-
- def recv(msg)
- @queue.push([msg, @data])
- end
end
class UnconnectedUDP < Requester # :nodoc:
def initialize
super()
@sock = UDPSocket.new
- @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
- @id = {}
- @id.default = -1
- @thread = Thread.new {
- DNSThreadGroup.add Thread.current
- loop {
- reply, from = @sock.recvfrom(UDPSize)
- msg = begin
- Message.decode(reply)
- rescue DecodeError
- STDERR.print("DNS message decoding error: #{reply.inspect}\n")
- next
- end
- if s = @senders[[[from[3],from[1]],msg.id]]
- s.recv msg
- else
- #STDERR.print("non-handled DNS message: #{msg.inspect} from #{from.inspect}\n")
- end
- }
- }
+ @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
+ DNS.bind_random_port(@sock)
end
- def sender(msg, data, queue, host, port=Port)
+ def recv_reply
+ reply, from = @sock.recvfrom(UDPSize)
+ return reply, [from[3],from[1]]
+ end
+
+ def sender(msg, data, host, port=Port)
service = [host, port]
- id = Thread.exclusive {
- @id[service] = (@id[service] + 1) & 0xffff
- }
+ id = DNS.allocate_request_id(host, port)
request = msg.encode
request[0,2] = [id].pack('n')
return @senders[[service, id]] =
- Sender.new(request, data, @sock, host, port, queue)
+ Sender.new(request, data, @sock, host, port)
end
+ def close
+ super
+ @senders.each_key {|service, id|
+ DNS.free_request_id(service[0], service[1], id)
+ }
+ end
+
class Sender < Requester::Sender # :nodoc:
- def initialize(msg, data, sock, host, port, queue)
- super(msg, data, sock, queue)
+ def initialize(msg, data, sock, host, port)
+ super(msg, data, sock)
@host = host
@port = port
end
+ attr_reader :data
def send
@sock.send(@msg, 0, @host, @port)
@@ -637,42 +688,38 @@
@host = host
@port = port
@sock = UDPSocket.new(host.index(':') ? Socket::AF_INET6 : Socket::AF_INET)
+ DNS.bind_random_port(@sock)
@sock.connect(host, port)
- @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
- @id = -1
- @thread = Thread.new {
- DNSThreadGroup.add Thread.current
- loop {
- reply = @sock.recv(UDPSize)
- msg = begin
- Message.decode(reply)
- rescue DecodeError
- STDERR.print("DNS message decoding error: #{reply.inspect}")
- next
- end
- if s = @senders[msg.id]
- s.recv msg
- else
- #STDERR.print("non-handled DNS message: #{msg.inspect}")
- end
- }
- }
+ @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
end
- def sender(msg, data, queue, host=@host, port=@port)
+ def recv_reply
+ reply = @sock.recv(UDPSize)
+ return reply, nil
+ end
+
+ def sender(msg, data, host=@host, port=@port)
unless host == @host && port == @port
raise RequestError.new("host/port don't match: #{host}:#{port}")
end
- id = Thread.exclusive { @id = (@id + 1) & 0xffff }
+ id = DNS.allocate_request_id(@host, @port)
request = msg.encode
request[0,2] = [id].pack('n')
- return @senders[id] = Sender.new(request, data, @sock, queue)
+ return @senders[[nil,id]] = Sender.new(request, data, @sock)
end
+ def close
+ super
+ @senders.each_key {|from, id|
+ DNS.free_request_id(@host, @port, id)
+ }
+ end
+
class Sender < Requester::Sender # :nodoc:
def send
@sock.send(@msg, 0)
end
+ attr_reader :data
end
end
@@ -681,39 +728,25 @@
super()
@host = host
@port = port
- @sock = TCPSocket.new
- @sock.connect(host, port)
- @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
- @id = -1
+ @sock = TCPSocket.new(@host, @port)
+ @sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::F_SETFD
@senders = {}
- @thread = Thread.new {
- DNSThreadGroup.add Thread.current
- loop {
- len = @sock.read(2).unpack('n')
- reply = @sock.read(len)
- msg = begin
- Message.decode(reply)
- rescue DecodeError
- STDERR.print("DNS message decoding error: #{reply.inspect}")
- next
- end
- if s = @senders[msg.id]
- s.push msg
- else
- #STDERR.print("non-handled DNS message: #{msg.inspect}")
- end
- }
- }
end
- def sender(msg, data, queue, host=@host, port=@port)
+ def recv_reply
+ len = @sock.read(2).unpack('n')[0]
+ reply = @sock.read(len)
+ return reply, nil
+ end
+
+ def sender(msg, data, host=@host, port=@port)
unless host == @host && port == @port
raise RequestError.new("host/port don't match: #{host}:#{port}")
end
- id = Thread.exclusive { @id = (@id + 1) & 0xffff }
+ id = DNS.allocate_request_id(@host, @port)
request = msg.encode
request[0,2] = [request.length, id].pack('nn')
- return @senders[id] = Sender.new(request, data, @sock, queue)
+ return @senders[[nil,id]] = Sender.new(request, data, @sock)
end
class Sender < Requester::Sender # :nodoc:
@@ -721,7 +754,15 @@
@sock.print(@msg)
@sock.flush
end
+ attr_reader :data
end
+
+ def close
+ super
+ @senders.each_key {|from,id|
+ DNS.free_request_id(@host, @port, id)
+ }
+ end
end
##
@@ -996,7 +1037,7 @@
# A representation of a DNS name.
class Name
-
+
##
# Creates a new DNS name from +arg+. +arg+ can be:
#
@@ -1419,11 +1460,11 @@
class Query
def encode_rdata(msg) # :nodoc:
- raise EncodeError.new("#{self.class} is query.")
+ raise EncodeError.new("#{self.class} is query.")
end
def self.decode_rdata(msg) # :nodoc:
- raise DecodeError.new("#{self.class} is query.")
+ raise DecodeError.new("#{self.class} is query.")
end
end
@@ -1898,7 +1939,7 @@
def initialize(address)
@address = IPv6.create(address)
end
-
+
##
# The Resolv::IPv6 address for this AAAA.
@@ -1915,7 +1956,7 @@
##
# SRV resource record defined in RFC 2782
- #
+ #
# These records identify the hostname and port that a service is
# available at.
@@ -2026,7 +2067,7 @@
end
##
- # A String reperesentation of this IPv4 address.
+ # A String representation of this IPv4 address.
##
# The raw IPv4 address as a String.
Modified: MacRuby/branches/experimental/lib/rexml/attlistdecl.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/attlistdecl.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/attlistdecl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,60 +3,60 @@
require 'rexml/source'
module REXML
- # This class needs:
- # * Documentation
- # * Work! Not all types of attlists are intelligently parsed, so we just
- # spew back out what we get in. This works, but it would be better if
- # we formatted the output ourselves.
- #
- # AttlistDecls provide *just* enough support to allow namespace
- # declarations. If you need some sort of generalized support, or have an
- # interesting idea about how to map the hideous, terrible design of DTD
- # AttlistDecls onto an intuitive Ruby interface, let me know. I'm desperate
- # for anything to make DTDs more palateable.
- class AttlistDecl < Child
- include Enumerable
+ # This class needs:
+ # * Documentation
+ # * Work! Not all types of attlists are intelligently parsed, so we just
+ # spew back out what we get in. This works, but it would be better if
+ # we formatted the output ourselves.
+ #
+ # AttlistDecls provide *just* enough support to allow namespace
+ # declarations. If you need some sort of generalized support, or have an
+ # interesting idea about how to map the hideous, terrible design of DTD
+ # AttlistDecls onto an intuitive Ruby interface, let me know. I'm desperate
+ # for anything to make DTDs more palateable.
+ class AttlistDecl < Child
+ include Enumerable
- # What is this? Got me.
- attr_reader :element_name
+ # What is this? Got me.
+ attr_reader :element_name
- # Create an AttlistDecl, pulling the information from a Source. Notice
- # that this isn't very convenient; to create an AttlistDecl, you basically
- # have to format it yourself, and then have the initializer parse it.
- # Sorry, but for the forseeable future, DTD support in REXML is pretty
- # weak on convenience. Have I mentioned how much I hate DTDs?
- def initialize(source)
- super()
- if (source.kind_of? Array)
- @element_name, @pairs, @contents = *source
- end
- end
-
- # Access the attlist attribute/value pairs.
- # value = attlist_decl[ attribute_name ]
- def [](key)
- @pairs[key]
- end
+ # Create an AttlistDecl, pulling the information from a Source. Notice
+ # that this isn't very convenient; to create an AttlistDecl, you basically
+ # have to format it yourself, and then have the initializer parse it.
+ # Sorry, but for the forseeable future, DTD support in REXML is pretty
+ # weak on convenience. Have I mentioned how much I hate DTDs?
+ def initialize(source)
+ super()
+ if (source.kind_of? Array)
+ @element_name, @pairs, @contents = *source
+ end
+ end
+
+ # Access the attlist attribute/value pairs.
+ # value = attlist_decl[ attribute_name ]
+ def [](key)
+ @pairs[key]
+ end
- # Whether an attlist declaration includes the given attribute definition
- # if attlist_decl.include? "xmlns:foobar"
- def include?(key)
- @pairs.keys.include? key
- end
+ # Whether an attlist declaration includes the given attribute definition
+ # if attlist_decl.include? "xmlns:foobar"
+ def include?(key)
+ @pairs.keys.include? key
+ end
- # Itterate over the key/value pairs:
- # attlist_decl.each { |attribute_name, attribute_value| ... }
- def each(&block)
- @pairs.each(&block)
- end
+ # Iterate over the key/value pairs:
+ # attlist_decl.each { |attribute_name, attribute_value| ... }
+ def each(&block)
+ @pairs.each(&block)
+ end
- # Write out exactly what we got in.
- def write out, indent=-1
- out << @contents
- end
+ # Write out exactly what we got in.
+ def write out, indent=-1
+ out << @contents
+ end
- def node_type
- :attlistdecl
- end
- end
+ def node_type
+ :attlistdecl
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/attribute.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/attribute.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/attribute.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,24 +2,24 @@
require 'rexml/text'
module REXML
- # Defines an Element Attribute; IE, a attribute=value pair, as in:
- # <element attribute="value"/>. Attributes can be in their own
- # namespaces. General users of REXML will not interact with the
- # Attribute class much.
- class Attribute
- include Node
- include Namespace
+ # Defines an Element Attribute; IE, a attribute=value pair, as in:
+ # <element attribute="value"/>. Attributes can be in their own
+ # namespaces. General users of REXML will not interact with the
+ # Attribute class much.
+ class Attribute
+ include Node
+ include Namespace
- # The element to which this attribute belongs
- attr_reader :element
- # The normalized value of this attribute. That is, the attribute with
- # entities intact.
- attr_writer :normalized
- PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um
+ # The element to which this attribute belongs
+ attr_reader :element
+ # The normalized value of this attribute. That is, the attribute with
+ # entities intact.
+ attr_writer :normalized
+ PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um
NEEDS_A_SECOND_CHECK = /(<|&((#{Entity::NAME});|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));)?)/um
- # Constructor.
+ # Constructor.
# FIXME: The parser doesn't catch illegal characters in attributes
#
# first::
@@ -36,137 +36,137 @@
# Ignored unless +first+ is a String; otherwise, may be the Element
# parent of this attribute, or nil.
#
- #
- # Attribute.new( attribute_to_clone )
- # Attribute.new( attribute_to_clone, parent_element )
- # Attribute.new( "attr", "attr_value" )
- # Attribute.new( "attr", "attr_value", parent_element )
- def initialize( first, second=nil, parent=nil )
- @normalized = @unnormalized = @element = nil
- if first.kind_of? Attribute
- self.name = first.expanded_name
- @unnormalized = first.value
- if second.kind_of? Element
- @element = second
- else
- @element = first.element
- end
- elsif first.kind_of? String
- @element = parent
- self.name = first
- @normalized = second.to_s
- else
- raise "illegal argument #{first.class.name} to Attribute constructor"
- end
- end
+ #
+ # Attribute.new( attribute_to_clone )
+ # Attribute.new( attribute_to_clone, parent_element )
+ # Attribute.new( "attr", "attr_value" )
+ # Attribute.new( "attr", "attr_value", parent_element )
+ def initialize( first, second=nil, parent=nil )
+ @normalized = @unnormalized = @element = nil
+ if first.kind_of? Attribute
+ self.name = first.expanded_name
+ @unnormalized = first.value
+ if second.kind_of? Element
+ @element = second
+ else
+ @element = first.element
+ end
+ elsif first.kind_of? String
+ @element = parent
+ self.name = first
+ @normalized = second.to_s
+ else
+ raise "illegal argument #{first.class.name} to Attribute constructor"
+ end
+ end
- # Returns the namespace of the attribute.
- #
- # e = Element.new( "elns:myelement" )
- # e.add_attribute( "nsa:a", "aval" )
- # e.add_attribute( "b", "bval" )
- # e.attributes.get_attribute( "a" ).prefix # -> "nsa"
- # e.attributes.get_attribute( "b" ).prefix # -> "elns"
- # a = Attribute.new( "x", "y" )
- # a.prefix # -> ""
- def prefix
- pf = super
- if pf == ""
- pf = @element.prefix if @element
- end
- pf
- end
+ # Returns the namespace of the attribute.
+ #
+ # e = Element.new( "elns:myelement" )
+ # e.add_attribute( "nsa:a", "aval" )
+ # e.add_attribute( "b", "bval" )
+ # e.attributes.get_attribute( "a" ).prefix # -> "nsa"
+ # e.attributes.get_attribute( "b" ).prefix # -> "elns"
+ # a = Attribute.new( "x", "y" )
+ # a.prefix # -> ""
+ def prefix
+ pf = super
+ if pf == ""
+ pf = @element.prefix if @element
+ end
+ pf
+ end
- # Returns the namespace URL, if defined, or nil otherwise
- #
- # e = Element.new("el")
- # e.add_attributes({"xmlns:ns", "http://url"})
- # e.namespace( "ns" ) # -> "http://url"
- def namespace arg=nil
- arg = prefix if arg.nil?
- @element.namespace arg
- end
+ # Returns the namespace URL, if defined, or nil otherwise
+ #
+ # e = Element.new("el")
+ # e.add_attributes({"xmlns:ns", "http://url"})
+ # e.namespace( "ns" ) # -> "http://url"
+ def namespace arg=nil
+ arg = prefix if arg.nil?
+ @element.namespace arg
+ end
- # Returns true if other is an Attribute and has the same name and value,
- # false otherwise.
- def ==( other )
- other.kind_of?(Attribute) and other.name==name and other.value==value
- end
+ # Returns true if other is an Attribute and has the same name and value,
+ # false otherwise.
+ def ==( other )
+ other.kind_of?(Attribute) and other.name==name and other.value==value
+ end
- # Creates (and returns) a hash from both the name and value
- def hash
- name.hash + value.hash
- end
+ # Creates (and returns) a hash from both the name and value
+ def hash
+ name.hash + value.hash
+ end
- # Returns this attribute out as XML source, expanding the name
- #
- # a = Attribute.new( "x", "y" )
- # a.to_string # -> "x='y'"
- # b = Attribute.new( "ns:x", "y" )
- # b.to_string # -> "ns:x='y'"
- def to_string
- if @element and @element.context and @element.context[:attribute_quote] == :quote
- %Q^#@expanded_name="#{to_s().gsub(/"/, '"e;')}"^
- else
- "#@expanded_name='#{to_s().gsub(/'/, ''')}'"
- end
- end
+ # Returns this attribute out as XML source, expanding the name
+ #
+ # a = Attribute.new( "x", "y" )
+ # a.to_string # -> "x='y'"
+ # b = Attribute.new( "ns:x", "y" )
+ # b.to_string # -> "ns:x='y'"
+ def to_string
+ if @element and @element.context and @element.context[:attribute_quote] == :quote
+ %Q^#@expanded_name="#{to_s().gsub(/"/, '"e;')}"^
+ else
+ "#@expanded_name='#{to_s().gsub(/'/, ''')}'"
+ end
+ end
def doctype
- if @element
- doc = @element.document
- doctype = doc.doctype if doc
- end
+ if @element
+ doc = @element.document
+ doctype = doc.doctype if doc
+ end
end
- # Returns the attribute value, with entities replaced
- def to_s
- return @normalized if @normalized
+ # Returns the attribute value, with entities replaced
+ def to_s
+ return @normalized if @normalized
- @normalized = Text::normalize( @unnormalized, doctype )
- @unnormalized = nil
+ @normalized = Text::normalize( @unnormalized, doctype )
+ @unnormalized = nil
@normalized
- end
+ end
- # Returns the UNNORMALIZED value of this attribute. That is, entities
- # have been expanded to their values
- def value
- return @unnormalized if @unnormalized
- @unnormalized = Text::unnormalize( @normalized, doctype )
- @normalized = nil
+ # Returns the UNNORMALIZED value of this attribute. That is, entities
+ # have been expanded to their values
+ def value
+ return @unnormalized if @unnormalized
+ @unnormalized = Text::unnormalize( @normalized, doctype )
+ @normalized = nil
@unnormalized
- end
+ end
- # Returns a copy of this attribute
- def clone
- Attribute.new self
- end
+ # Returns a copy of this attribute
+ def clone
+ Attribute.new self
+ end
- # Sets the element of which this object is an attribute. Normally, this
- # is not directly called.
- #
- # Returns this attribute
- def element=( element )
- @element = element
+ # Sets the element of which this object is an attribute. Normally, this
+ # is not directly called.
+ #
+ # Returns this attribute
+ def element=( element )
+ @element = element
if @normalized
Text.check( @normalized, NEEDS_A_SECOND_CHECK, doctype )
end
- self
- end
+ self
+ end
- # Removes this Attribute from the tree, and returns true if successfull
- #
- # This method is usually not called directly.
- def remove
- @element.attributes.delete self.name unless @element.nil?
- end
+ # Removes this Attribute from the tree, and returns true if successfull
+ #
+ # This method is usually not called directly.
+ def remove
+ @element.attributes.delete self.name unless @element.nil?
+ end
- # Writes this attribute (EG, puts 'key="value"' to the output)
- def write( output, indent=-1 )
- output << to_string
- end
+ # Writes this attribute (EG, puts 'key="value"' to the output)
+ def write( output, indent=-1 )
+ output << to_string
+ end
def node_type
:attribute
@@ -183,6 +183,6 @@
path += "/@#{self.expanded_name}"
return path
end
- end
+ end
end
#vim:ts=2 sw=2 noexpandtab:
Modified: MacRuby/branches/experimental/lib/rexml/cdata.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/cdata.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/cdata.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,39 +1,39 @@
require "rexml/text"
module REXML
- class CData < Text
- START = '<![CDATA['
- STOP = ']]>'
- ILLEGAL = /(\]\]>)/
+ class CData < Text
+ START = '<![CDATA['
+ STOP = ']]>'
+ ILLEGAL = /(\]\]>)/
- # Constructor. CData is data between <![CDATA[ ... ]]>
- #
- # _Examples_
- # CData.new( source )
- # CData.new( "Here is some CDATA" )
- # CData.new( "Some unprocessed data", respect_whitespace_TF, parent_element )
- def initialize( first, whitespace=true, parent=nil )
- super( first, whitespace, parent, false, true, ILLEGAL )
- end
+ # Constructor. CData is data between <![CDATA[ ... ]]>
+ #
+ # _Examples_
+ # CData.new( source )
+ # CData.new( "Here is some CDATA" )
+ # CData.new( "Some unprocessed data", respect_whitespace_TF, parent_element )
+ def initialize( first, whitespace=true, parent=nil )
+ super( first, whitespace, parent, false, true, ILLEGAL )
+ end
- # Make a copy of this object
- #
- # _Examples_
- # c = CData.new( "Some text" )
- # d = c.clone
- # d.to_s # -> "Some text"
- def clone
- CData.new self
- end
+ # Make a copy of this object
+ #
+ # _Examples_
+ # c = CData.new( "Some text" )
+ # d = c.clone
+ # d.to_s # -> "Some text"
+ def clone
+ CData.new self
+ end
- # Returns the content of this CData object
- #
- # _Examples_
- # c = CData.new( "Some text" )
- # c.to_s # -> "Some text"
- def to_s
- @string
- end
+ # Returns the content of this CData object
+ #
+ # _Examples_
+ # c = CData.new( "Some text" )
+ # c.to_s # -> "Some text"
+ def to_s
+ @string
+ end
def value
@string
@@ -42,26 +42,26 @@
# == DEPRECATED
# See the rexml/formatters package
#
- # Generates XML output of this object
- #
- # output::
- # Where to write the string. Defaults to $stdout
- # indent::
+ # Generates XML output of this object
+ #
+ # output::
+ # Where to write the string. Defaults to $stdout
+ # indent::
# The amount to indent this node by
- # transitive::
+ # transitive::
# Ignored
- # ie_hack::
+ # ie_hack::
# Ignored
- #
- # _Examples_
- # c = CData.new( " Some text " )
- # c.write( $stdout ) #-> <![CDATA[ Some text ]]>
- def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
+ #
+ # _Examples_
+ # c = CData.new( " Some text " )
+ # c.write( $stdout ) #-> <![CDATA[ Some text ]]>
+ def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
Kernel.warn( "#{self.class.name}.write is deprecated" )
- indent( output, indent )
- output << START
- output << @string
- output << STOP
- end
- end
+ indent( output, indent )
+ output << START
+ output << @string
+ output << STOP
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/child.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/child.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/child.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,96 +1,96 @@
require "rexml/node"
module REXML
- ##
- # A Child object is something contained by a parent, and this class
- # contains methods to support that. Most user code will not use this
- # class directly.
- class Child
- include Node
- attr_reader :parent # The Parent of this object
+ ##
+ # A Child object is something contained by a parent, and this class
+ # contains methods to support that. Most user code will not use this
+ # class directly.
+ class Child
+ include Node
+ attr_reader :parent # The Parent of this object
- # Constructor. Any inheritors of this class should call super to make
- # sure this method is called.
- # parent::
- # if supplied, the parent of this child will be set to the
- # supplied value, and self will be added to the parent
- def initialize( parent = nil )
- @parent = nil
- # Declare @parent, but don't define it. The next line sets the
- # parent.
- parent.add( self ) if parent
- end
+ # Constructor. Any inheritors of this class should call super to make
+ # sure this method is called.
+ # parent::
+ # if supplied, the parent of this child will be set to the
+ # supplied value, and self will be added to the parent
+ def initialize( parent = nil )
+ @parent = nil
+ # Declare @parent, but don't define it. The next line sets the
+ # parent.
+ parent.add( self ) if parent
+ end
- # Replaces this object with another object. Basically, calls
- # Parent.replace_child
- #
- # Returns:: self
- def replace_with( child )
- @parent.replace_child( self, child )
- self
- end
+ # Replaces this object with another object. Basically, calls
+ # Parent.replace_child
+ #
+ # Returns:: self
+ def replace_with( child )
+ @parent.replace_child( self, child )
+ self
+ end
- # Removes this child from the parent.
- #
- # Returns:: self
- def remove
- unless @parent.nil?
- @parent.delete self
- end
- self
- end
+ # Removes this child from the parent.
+ #
+ # Returns:: self
+ def remove
+ unless @parent.nil?
+ @parent.delete self
+ end
+ self
+ end
- # Sets the parent of this child to the supplied argument.
- #
- # other::
- # Must be a Parent object. If this object is the same object as the
- # existing parent of this child, no action is taken. Otherwise, this
- # child is removed from the current parent (if one exists), and is added
- # to the new parent.
- # Returns:: The parent added
- def parent=( other )
- return @parent if @parent == other
- @parent.delete self if defined? @parent and @parent
- @parent = other
- end
+ # Sets the parent of this child to the supplied argument.
+ #
+ # other::
+ # Must be a Parent object. If this object is the same object as the
+ # existing parent of this child, no action is taken. Otherwise, this
+ # child is removed from the current parent (if one exists), and is added
+ # to the new parent.
+ # Returns:: The parent added
+ def parent=( other )
+ return @parent if @parent == other
+ @parent.delete self if defined? @parent and @parent
+ @parent = other
+ end
- alias :next_sibling :next_sibling_node
- alias :previous_sibling :previous_sibling_node
+ alias :next_sibling :next_sibling_node
+ alias :previous_sibling :previous_sibling_node
- # Sets the next sibling of this child. This can be used to insert a child
- # after some other child.
- # a = Element.new("a")
- # b = a.add_element("b")
- # c = Element.new("c")
- # b.next_sibling = c
- # # => <a><b/><c/></a>
- def next_sibling=( other )
- parent.insert_after self, other
- end
+ # Sets the next sibling of this child. This can be used to insert a child
+ # after some other child.
+ # a = Element.new("a")
+ # b = a.add_element("b")
+ # c = Element.new("c")
+ # b.next_sibling = c
+ # # => <a><b/><c/></a>
+ def next_sibling=( other )
+ parent.insert_after self, other
+ end
- # Sets the previous sibling of this child. This can be used to insert a
- # child before some other child.
- # a = Element.new("a")
- # b = a.add_element("b")
- # c = Element.new("c")
- # b.previous_sibling = c
- # # => <a><b/><c/></a>
- def previous_sibling=(other)
- parent.insert_before self, other
- end
+ # Sets the previous sibling of this child. This can be used to insert a
+ # child before some other child.
+ # a = Element.new("a")
+ # b = a.add_element("b")
+ # c = Element.new("c")
+ # b.previous_sibling = c
+ # # => <a><b/><c/></a>
+ def previous_sibling=(other)
+ parent.insert_before self, other
+ end
- # Returns:: the document this child belongs to, or nil if this child
- # belongs to no document
- def document
- return parent.document unless parent.nil?
- nil
- end
+ # Returns:: the document this child belongs to, or nil if this child
+ # belongs to no document
+ def document
+ return parent.document unless parent.nil?
+ nil
+ end
- # This doesn't yet handle encodings
- def bytes
- encoding = document.encoding
+ # This doesn't yet handle encodings
+ def bytes
+ encoding = document.encoding
- to_s
- end
- end
+ to_s
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/comment.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/comment.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/comment.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,80 +1,80 @@
require "rexml/child"
module REXML
- ##
- # Represents an XML comment; that is, text between \<!-- ... -->
- class Comment < Child
- include Comparable
- START = "<!--"
- STOP = "-->"
+ ##
+ # Represents an XML comment; that is, text between \<!-- ... -->
+ class Comment < Child
+ include Comparable
+ START = "<!--"
+ STOP = "-->"
- # The content text
+ # The content text
- attr_accessor :string
+ attr_accessor :string
- ##
- # Constructor. The first argument can be one of three types:
- # @param first If String, the contents of this comment are set to the
- # argument. If Comment, the argument is duplicated. If
- # Source, the argument is scanned for a comment.
- # @param second If the first argument is a Source, this argument
- # should be nil, not supplied, or a Parent to be set as the parent
- # of this object
- def initialize( first, second = nil )
- #puts "IN COMMENT CONSTRUCTOR; SECOND IS #{second.type}"
- super(second)
- if first.kind_of? String
- @string = first
- elsif first.kind_of? Comment
- @string = first.string
- end
- end
+ ##
+ # Constructor. The first argument can be one of three types:
+ # @param first If String, the contents of this comment are set to the
+ # argument. If Comment, the argument is duplicated. If
+ # Source, the argument is scanned for a comment.
+ # @param second If the first argument is a Source, this argument
+ # should be nil, not supplied, or a Parent to be set as the parent
+ # of this object
+ def initialize( first, second = nil )
+ #puts "IN COMMENT CONSTRUCTOR; SECOND IS #{second.type}"
+ super(second)
+ if first.kind_of? String
+ @string = first
+ elsif first.kind_of? Comment
+ @string = first.string
+ end
+ end
- def clone
- Comment.new self
- end
+ def clone
+ Comment.new self
+ end
# == DEPRECATED
# See REXML::Formatters
#
- # output::
- # Where to write the string
- # indent::
- # An integer. If -1, no indenting will be used; otherwise, the
- # indentation will be this number of spaces, and children will be
- # indented an additional amount.
- # transitive::
- # Ignored by this class. The contents of comments are never modified.
- # ie_hack::
- # Needed for conformity to the child API, but not used by this class.
- def write( output, indent=-1, transitive=false, ie_hack=false )
+ # output::
+ # Where to write the string
+ # indent::
+ # An integer. If -1, no indenting will be used; otherwise, the
+ # indentation will be this number of spaces, and children will be
+ # indented an additional amount.
+ # transitive::
+ # Ignored by this class. The contents of comments are never modified.
+ # ie_hack::
+ # Needed for conformity to the child API, but not used by this class.
+ def write( output, indent=-1, transitive=false, ie_hack=false )
Kernel.warn("Comment.write is deprecated. See REXML::Formatters")
- indent( output, indent )
- output << START
- output << @string
- output << STOP
- end
+ indent( output, indent )
+ output << START
+ output << @string
+ output << STOP
+ end
- alias :to_s :string
+ alias :to_s :string
- ##
- # Compares this Comment to another; the contents of the comment are used
- # in the comparison.
- def <=>(other)
- other.to_s <=> @string
- end
+ ##
+ # Compares this Comment to another; the contents of the comment are used
+ # in the comparison.
+ def <=>(other)
+ other.to_s <=> @string
+ end
- ##
- # Compares this Comment to another; the contents of the comment are used
- # in the comparison.
- def ==( other )
- other.kind_of? Comment and
- (other <=> self) == 0
- end
+ ##
+ # Compares this Comment to another; the contents of the comment are used
+ # in the comparison.
+ def ==( other )
+ other.kind_of? Comment and
+ (other <=> self) == 0
+ end
def node_type
:comment
end
- end
+ end
end
#vim:ts=2 sw=2 noexpandtab:
Modified: MacRuby/branches/experimental/lib/rexml/document.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/document.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/document.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -16,58 +16,59 @@
# Document has a single child that can be accessed by root().
# Note that if you want to have an XML declaration written for a document
# you create, you must add one; REXML documents do not write a default
- # declaration for you. See |DECLARATION| and |write|.
- class Document < Element
- # A convenient default XML declaration. If you want an XML declaration,
- # the easiest way to add one is mydoc << Document::DECLARATION
+ # declaration for you. See |DECLARATION| and |write|.
+ class Document < Element
+ # A convenient default XML declaration. If you want an XML declaration,
+ # the easiest way to add one is mydoc << Document::DECLARATION
# +DEPRECATED+
# Use: mydoc << XMLDecl.default
- DECLARATION = XMLDecl.default
+ DECLARATION = XMLDecl.default
- # Constructor
- # @param source if supplied, must be a Document, String, or IO.
- # Documents have their context and Element attributes cloned.
- # Strings are expected to be valid XML documents. IOs are expected
- # to be sources of valid XML documents.
- # @param context if supplied, contains the context of the document;
- # this should be a Hash.
- def initialize( source = nil, context = {} )
- super()
- @context = context
- return if source.nil?
- if source.kind_of? Document
- @context = source.context
- super source
- else
- build( source )
- end
- end
+ # Constructor
+ # @param source if supplied, must be a Document, String, or IO.
+ # Documents have their context and Element attributes cloned.
+ # Strings are expected to be valid XML documents. IOs are expected
+ # to be sources of valid XML documents.
+ # @param context if supplied, contains the context of the document;
+ # this should be a Hash.
+ def initialize( source = nil, context = {} )
+ @entity_expansion_count = 0
+ super()
+ @context = context
+ return if source.nil?
+ if source.kind_of? Document
+ @context = source.context
+ super source
+ else
+ build( source )
+ end
+ end
def node_type
:document
end
- # Should be obvious
- def clone
- Document.new self
- end
+ # Should be obvious
+ def clone
+ Document.new self
+ end
- # According to the XML spec, a root node has no expanded name
- def expanded_name
- ''
- #d = doc_type
- #d ? d.name : "UNDEFINED"
- end
+ # According to the XML spec, a root node has no expanded name
+ def expanded_name
+ ''
+ #d = doc_type
+ #d ? d.name : "UNDEFINED"
+ end
- alias :name :expanded_name
+ alias :name :expanded_name
- # We override this, because XMLDecls and DocTypes must go at the start
- # of the document
- def add( child )
- if child.kind_of? XMLDecl
- @children.unshift child
+ # We override this, because XMLDecls and DocTypes must go at the start
+ # of the document
+ def add( child )
+ if child.kind_of? XMLDecl
+ @children.unshift child
child.parent = self
- elsif child.kind_of? DocType
+ elsif child.kind_of? DocType
# Find first Element or DocType node and insert the decl right
# before it. If there is no such node, just insert the child at the
# end. If there is a child and it is an DocType, then replace it.
@@ -85,60 +86,60 @@
else # Insert at end of list
@children[insert_before_index] = child
end
- child.parent = self
- else
- rv = super
- raise "attempted adding second root element to document" if @elements.size > 1
- rv
- end
- end
- alias :<< :add
+ child.parent = self
+ else
+ rv = super
+ raise "attempted adding second root element to document" if @elements.size > 1
+ rv
+ end
+ end
+ alias :<< :add
- def add_element(arg=nil, arg2=nil)
- rv = super
- raise "attempted adding second root element to document" if @elements.size > 1
- rv
- end
+ def add_element(arg=nil, arg2=nil)
+ rv = super
+ raise "attempted adding second root element to document" if @elements.size > 1
+ rv
+ end
- # @return the root Element of the document, or nil if this document
- # has no children.
- def root
+ # @return the root Element of the document, or nil if this document
+ # has no children.
+ def root
elements[1]
#self
#@children.find { |item| item.kind_of? Element }
- end
+ end
- # @return the DocType child of the document, if one exists,
- # and nil otherwise.
- def doctype
- @children.find { |item| item.kind_of? DocType }
- end
+ # @return the DocType child of the document, if one exists,
+ # and nil otherwise.
+ def doctype
+ @children.find { |item| item.kind_of? DocType }
+ end
- # @return the XMLDecl of this document; if no XMLDecl has been
- # set, the default declaration is returned.
- def xml_decl
- rv = @children[0]
+ # @return the XMLDecl of this document; if no XMLDecl has been
+ # set, the default declaration is returned.
+ def xml_decl
+ rv = @children[0]
return rv if rv.kind_of? XMLDecl
rv = @children.unshift(XMLDecl.default)[0]
- end
+ end
- # @return the XMLDecl version of this document as a String.
- # If no XMLDecl has been set, returns the default version.
- def version
- xml_decl().version
- end
+ # @return the XMLDecl version of this document as a String.
+ # If no XMLDecl has been set, returns the default version.
+ def version
+ xml_decl().version
+ end
- # @return the XMLDecl encoding of this document as a String.
- # If no XMLDecl has been set, returns the default encoding.
- def encoding
- xml_decl().encoding
- end
+ # @return the XMLDecl encoding of this document as a String.
+ # If no XMLDecl has been set, returns the default encoding.
+ def encoding
+ xml_decl().encoding
+ end
- # @return the XMLDecl standalone value of this document as a String.
- # If no XMLDecl has been set, returns the default setting.
- def stand_alone?
- xml_decl().stand_alone?
- end
+ # @return the XMLDecl standalone value of this document as a String.
+ # If no XMLDecl has been set, returns the default setting.
+ def stand_alone?
+ xml_decl().stand_alone?
+ end
# Write the XML tree out, optionally with indent. This writes out the
# entire XML document, including XML declarations, doctype declarations,
@@ -147,7 +148,7 @@
# A controversial point is whether Document should always write the XML
# declaration (<?xml version='1.0'?>) whether or not one is given by the
# user (or source document). REXML does not write one if one was not
- # specified, because it adds unneccessary bandwidth to applications such
+ # specified, because it adds unnecessary bandwidth to applications such
# as XML-RPC.
#
# See also the classes in the rexml/formatters package for the proper way
@@ -168,7 +169,7 @@
# indentation will be twice this number of spaces, and children will be
# indented an additional amount. For a value of 3, every item will be
# indented 3 more levels, or 6 more spaces (2 * 3). Defaults to -1
- # trans::
+ # transitive::
# If transitive is true and indent is >= 0, then the output will be
# pretty-printed in such a way that the added whitespace does not affect
# the absolute *value* of the document -- that is, it leaves the value
@@ -185,6 +186,7 @@
end
formatter = if indent > -1
if transitive
+ require "rexml/formatters/transitive"
REXML::Formatters::Transitive.new( indent, ie_hack )
else
REXML::Formatters::Pretty.new( indent, ie_hack )
@@ -193,16 +195,37 @@
REXML::Formatters::Default.new( ie_hack )
end
formatter.write( self, output )
- end
+ end
-
- def Document::parse_stream( source, listener )
- Parsers::StreamParser.new( source, listener ).parse
- end
+
+ def Document::parse_stream( source, listener )
+ Parsers::StreamParser.new( source, listener ).parse
+ end
- private
- def build( source )
+ @@entity_expansion_limit = 10_000
+
+ # Set the entity expansion limit. By default the limit is set to 10000.
+ def Document::entity_expansion_limit=( val )
+ @@entity_expansion_limit = val
+ end
+
+ # Get the entity expansion limit. By default the limit is set to 10000.
+ def Document::entity_expansion_limit
+ return @@entity_expansion_limit
+ end
+
+ attr_reader :entity_expansion_count
+
+ def record_entity_expansion
+ @entity_expansion_count += 1
+ if @entity_expansion_count > @@entity_expansion_limit
+ raise "number of entity expansions exceeded, processing aborted."
+ end
+ end
+
+ private
+ def build( source )
Parsers::TreeParser.new( source, self ).parse
- end
- end
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/dtd/attlistdecl.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/dtd/attlistdecl.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/dtd/attlistdecl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,10 +1,10 @@
require "rexml/child"
module REXML
- module DTD
- class AttlistDecl < Child
- START = "<!ATTLIST"
- START_RE = /^\s*#{START}/um
- PATTERN_RE = /\s*(#{START}.*?>)/um
- end
- end
+ module DTD
+ class AttlistDecl < Child
+ START = "<!ATTLIST"
+ START_RE = /^\s*#{START}/um
+ PATTERN_RE = /\s*(#{START}.*?>)/um
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/dtd/dtd.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/dtd/dtd.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/dtd/dtd.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,46 +6,46 @@
require "rexml/parent"
module REXML
- module DTD
- class Parser
- def Parser.parse( input )
- case input
- when String
- parse_helper input
- when File
- parse_helper input.read
- end
- end
+ module DTD
+ class Parser
+ def Parser.parse( input )
+ case input
+ when String
+ parse_helper input
+ when File
+ parse_helper input.read
+ end
+ end
- # Takes a String and parses it out
- def Parser.parse_helper( input )
- contents = Parent.new
- while input.size > 0
- case input
- when ElementDecl.PATTERN_RE
- match = $&
- source = $'
- contents << ElementDecl.new( match )
- when AttlistDecl.PATTERN_RE
- matchdata = $~
- source = $'
- contents << AttlistDecl.new( matchdata )
- when EntityDecl.PATTERN_RE
- matchdata = $~
- source = $'
- contents << EntityDecl.new( matchdata )
- when Comment.PATTERN_RE
- matchdata = $~
- source = $'
- contents << Comment.new( matchdata )
- when NotationDecl.PATTERN_RE
- matchdata = $~
- source = $'
- contents << NotationDecl.new( matchdata )
- end
- end
- contents
- end
- end
- end
+ # Takes a String and parses it out
+ def Parser.parse_helper( input )
+ contents = Parent.new
+ while input.size > 0
+ case input
+ when ElementDecl.PATTERN_RE
+ match = $&
+ source = $'
+ contents << ElementDecl.new( match )
+ when AttlistDecl.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << AttlistDecl.new( matchdata )
+ when EntityDecl.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << EntityDecl.new( matchdata )
+ when Comment.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << Comment.new( matchdata )
+ when NotationDecl.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << NotationDecl.new( matchdata )
+ end
+ end
+ contents
+ end
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/dtd/elementdecl.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/dtd/elementdecl.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/dtd/elementdecl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,17 +1,17 @@
require "rexml/child"
module REXML
- module DTD
- class ElementDecl < Child
- START = "<!ELEMENT"
- START_RE = /^\s*#{START}/um
- PATTERN_RE = /^\s*(#{START}.*?)>/um
- PATTERN_RE = /^\s*#{START}\s+((?:[:\w_][-\.\w_]*:)?[-!\*\.\w_]*)(.*?)>/
- #\s*((((["']).*?\5)|[^\/'">]*)*?)(\/)?>/um, true)
+ module DTD
+ class ElementDecl < Child
+ START = "<!ELEMENT"
+ START_RE = /^\s*#{START}/um
+ PATTERN_RE = /^\s*(#{START}.*?)>/um
+ PATTERN_RE = /^\s*#{START}\s+((?:[:\w_][-\.\w_]*:)?[-!\*\.\w_]*)(.*?)>/
+ #\s*((((["']).*?\5)|[^\/'">]*)*?)(\/)?>/um, true)
- def initialize match
- @name = match[1]
- @rest = match[2]
- end
- end
- end
+ def initialize match
+ @name = match[1]
+ @rest = match[2]
+ end
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/dtd/entitydecl.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/dtd/entitydecl.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/dtd/entitydecl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,56 +1,56 @@
require "rexml/child"
module REXML
- module DTD
- class EntityDecl < Child
- START = "<!ENTITY"
- START_RE = /^\s*#{START}/um
- PUBLIC = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+PUBLIC\s+((["']).*?\3)\s+((["']).*?\5)\s*>/um
- SYSTEM = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+SYSTEM\s+((["']).*?\3)(?:\s+NDATA\s+\w+)?\s*>/um
- PLAIN = /^\s*#{START}\s+(\w+)\s+((["']).*?\3)\s*>/um
- PERCENT = /^\s*#{START}\s+%\s+(\w+)\s+((["']).*?\3)\s*>/um
- # <!ENTITY name SYSTEM "...">
- # <!ENTITY name "...">
- def initialize src
- super()
- md = nil
- if src.match( PUBLIC )
- md = src.match( PUBLIC, true )
- @middle = "PUBLIC"
- @content = "#{md[2]} #{md[4]}"
- elsif src.match( SYSTEM )
- md = src.match( SYSTEM, true )
- @middle = "SYSTEM"
- @content = md[2]
- elsif src.match( PLAIN )
- md = src.match( PLAIN, true )
- @middle = ""
- @content = md[2]
- elsif src.match( PERCENT )
- md = src.match( PERCENT, true )
- @middle = ""
- @content = md[2]
- end
- raise ParseException.new("failed Entity match", src) if md.nil?
- @name = md[1]
- end
+ module DTD
+ class EntityDecl < Child
+ START = "<!ENTITY"
+ START_RE = /^\s*#{START}/um
+ PUBLIC = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+PUBLIC\s+((["']).*?\3)\s+((["']).*?\5)\s*>/um
+ SYSTEM = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+SYSTEM\s+((["']).*?\3)(?:\s+NDATA\s+\w+)?\s*>/um
+ PLAIN = /^\s*#{START}\s+(\w+)\s+((["']).*?\3)\s*>/um
+ PERCENT = /^\s*#{START}\s+%\s+(\w+)\s+((["']).*?\3)\s*>/um
+ # <!ENTITY name SYSTEM "...">
+ # <!ENTITY name "...">
+ def initialize src
+ super()
+ md = nil
+ if src.match( PUBLIC )
+ md = src.match( PUBLIC, true )
+ @middle = "PUBLIC"
+ @content = "#{md[2]} #{md[4]}"
+ elsif src.match( SYSTEM )
+ md = src.match( SYSTEM, true )
+ @middle = "SYSTEM"
+ @content = md[2]
+ elsif src.match( PLAIN )
+ md = src.match( PLAIN, true )
+ @middle = ""
+ @content = md[2]
+ elsif src.match( PERCENT )
+ md = src.match( PERCENT, true )
+ @middle = ""
+ @content = md[2]
+ end
+ raise ParseException.new("failed Entity match", src) if md.nil?
+ @name = md[1]
+ end
- def to_s
- rv = "<!ENTITY #@name "
- rv << "#@middle " if @middle.size > 0
- rv << @content
- rv
- end
+ def to_s
+ rv = "<!ENTITY #@name "
+ rv << "#@middle " if @middle.size > 0
+ rv << @content
+ rv
+ end
- def write( output, indent )
+ def write( output, indent )
indent( output, indent )
- output << to_s
- end
+ output << to_s
+ end
- def EntityDecl.parse_source source, listener
- md = source.match( PATTERN_RE, true )
- thing = md[0].squeeze(" \t\n\r")
- listener.send inspect.downcase, thing
- end
- end
- end
+ def EntityDecl.parse_source source, listener
+ md = source.match( PATTERN_RE, true )
+ thing = md[0].squeeze(" \t\n\r")
+ listener.send inspect.downcase, thing
+ end
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/dtd/notationdecl.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/dtd/notationdecl.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/dtd/notationdecl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,39 +1,39 @@
require "rexml/child"
module REXML
- module DTD
- class NotationDecl < Child
- START = "<!NOTATION"
- START_RE = /^\s*#{START}/um
- PUBLIC = /^\s*#{START}\s+(\w[\w-]*)\s+(PUBLIC)\s+((["']).*?\4)\s*>/um
- SYSTEM = /^\s*#{START}\s+(\w[\w-]*)\s+(SYSTEM)\s+((["']).*?\4)\s*>/um
- def initialize src
- super()
- if src.match( PUBLIC )
- md = src.match( PUBLIC, true )
- elsif src.match( SYSTEM )
- md = src.match( SYSTEM, true )
- else
- raise ParseException.new( "error parsing notation: no matching pattern", src )
- end
- @name = md[1]
- @middle = md[2]
- @rest = md[3]
- end
+ module DTD
+ class NotationDecl < Child
+ START = "<!NOTATION"
+ START_RE = /^\s*#{START}/um
+ PUBLIC = /^\s*#{START}\s+(\w[\w-]*)\s+(PUBLIC)\s+((["']).*?\4)\s*>/um
+ SYSTEM = /^\s*#{START}\s+(\w[\w-]*)\s+(SYSTEM)\s+((["']).*?\4)\s*>/um
+ def initialize src
+ super()
+ if src.match( PUBLIC )
+ md = src.match( PUBLIC, true )
+ elsif src.match( SYSTEM )
+ md = src.match( SYSTEM, true )
+ else
+ raise ParseException.new( "error parsing notation: no matching pattern", src )
+ end
+ @name = md[1]
+ @middle = md[2]
+ @rest = md[3]
+ end
- def to_s
- "<!NOTATION #@name #@middle #@rest>"
- end
+ def to_s
+ "<!NOTATION #@name #@middle #@rest>"
+ end
- def write( output, indent )
+ def write( output, indent )
indent( output, indent )
- output << to_s
- end
+ output << to_s
+ end
- def NotationDecl.parse_source source, listener
- md = source.match( PATTERN_RE, true )
- thing = md[0].squeeze(" \t\n\r")
- listener.send inspect.downcase, thing
- end
- end
- end
+ def NotationDecl.parse_source source, listener
+ md = source.match( PATTERN_RE, true )
+ thing = md[0].squeeze(" \t\n\r")
+ listener.send inspect.downcase, thing
+ end
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/element.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/element.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/element.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -360,7 +360,7 @@
# Iterates through the children, yielding for each Element that
# has a particular text set.
# text::
- # the text to search for. If nil, or not supplied, will itterate
+ # the text to search for. If nil, or not supplied, will iterate
# over all +Element+ children that contain at least one +Text+ node.
# max::
# (optional) causes this method to return after yielding
@@ -691,6 +691,7 @@
Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters")
formatter = if indent > -1
if transitive
+ require "rexml/formatters/transitive"
REXML::Formatters::Transitive.new( indent, ie_hack )
else
REXML::Formatters::Pretty.new( indent, ie_hack )
@@ -1003,7 +1004,7 @@
end
alias :size :length
- # Itterates over the attributes of an Element. Yields actual Attribute
+ # Iterates over the attributes of an Element. Yields actual Attribute
# nodes, not String values.
#
# doc = Document.new '<a x="1" y="2"/>'
@@ -1020,7 +1021,7 @@
end
end
- # Itterates over each attribute of an Element, yielding the expanded name
+ # Iterates over each attribute of an Element, yielding the expanded name
# and value as a pair of Strings.
#
# doc = Document.new '<a x="1" y="2"/>'
Modified: MacRuby/branches/experimental/lib/rexml/encodings/CP-1252.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/encodings/CP-1252.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/encodings/CP-1252.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,12 +3,12 @@
#
module REXML
module Encoding
- register( "CP-1252" ) do |o|
- class << o
- alias encode encode_cp1252
- alias decode decode_cp1252
- end
- end
+ register( "CP-1252" ) do |o|
+ class << o
+ alias encode encode_cp1252
+ alias decode decode_cp1252
+ end
+ end
# Convert from UTF-8
def encode_cp1252(content)
Modified: MacRuby/branches/experimental/lib/rexml/encodings/ISO-8859-15.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/encodings/ISO-8859-15.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/encodings/ISO-8859-15.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,10 +3,10 @@
#
module REXML
module Encoding
- register("ISO-8859-15") do |o|
- alias encode to_iso_8859_15
+ register("ISO-8859-15") do |o|
+ alias encode to_iso_8859_15
alias decode from_iso_8859_15
- end
+ end
# Convert from UTF-8
def to_iso_8859_15(content)
Modified: MacRuby/branches/experimental/lib/rexml/entity.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/entity.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/entity.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,163 +3,164 @@
require 'rexml/xmltokens'
module REXML
- # God, I hate DTDs. I really do. Why this idiot standard still
- # plagues us is beyond me.
- class Entity < Child
- include XMLTokens
- PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
- SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))}
- PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')}
- EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))"
- NDATADECL = "\\s+NDATA\\s+#{NAME}"
- PEREFERENCE = "%#{NAME};"
- ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))}
- PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})"
- ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
- PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
- GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
- ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
+ # God, I hate DTDs. I really do. Why this idiot standard still
+ # plagues us is beyond me.
+ class Entity < Child
+ include XMLTokens
+ PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
+ SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))}
+ PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')}
+ EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))"
+ NDATADECL = "\\s+NDATA\\s+#{NAME}"
+ PEREFERENCE = "%#{NAME};"
+ ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))}
+ PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})"
+ ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
+ PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
+ GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
+ ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
- attr_reader :name, :external, :ref, :ndata, :pubid
+ attr_reader :name, :external, :ref, :ndata, :pubid
- # Create a new entity. Simple entities can be constructed by passing a
- # name, value to the constructor; this creates a generic, plain entity
- # reference. For anything more complicated, you have to pass a Source to
- # the constructor with the entity definiton, or use the accessor methods.
- # +WARNING+: There is no validation of entity state except when the entity
- # is read from a stream. If you start poking around with the accessors,
- # you can easily create a non-conformant Entity. The best thing to do is
- # dump the stupid DTDs and use XMLSchema instead.
- #
- # e = Entity.new( 'amp', '&' )
- def initialize stream, value=nil, parent=nil, reference=false
- super(parent)
- @ndata = @pubid = @value = @external = nil
- if stream.kind_of? Array
- @name = stream[1]
- if stream[-1] == '%'
- @reference = true
- stream.pop
- else
- @reference = false
- end
- if stream[2] =~ /SYSTEM|PUBLIC/
- @external = stream[2]
- if @external == 'SYSTEM'
- @ref = stream[3]
- @ndata = stream[4] if stream.size == 5
- else
- @pubid = stream[3]
- @ref = stream[4]
- end
- else
- @value = stream[2]
- end
- else
- @reference = reference
- @external = nil
- @name = stream
- @value = value
- end
- end
+ # Create a new entity. Simple entities can be constructed by passing a
+ # name, value to the constructor; this creates a generic, plain entity
+ # reference. For anything more complicated, you have to pass a Source to
+ # the constructor with the entity definiton, or use the accessor methods.
+ # +WARNING+: There is no validation of entity state except when the entity
+ # is read from a stream. If you start poking around with the accessors,
+ # you can easily create a non-conformant Entity. The best thing to do is
+ # dump the stupid DTDs and use XMLSchema instead.
+ #
+ # e = Entity.new( 'amp', '&' )
+ def initialize stream, value=nil, parent=nil, reference=false
+ super(parent)
+ @ndata = @pubid = @value = @external = nil
+ if stream.kind_of? Array
+ @name = stream[1]
+ if stream[-1] == '%'
+ @reference = true
+ stream.pop
+ else
+ @reference = false
+ end
+ if stream[2] =~ /SYSTEM|PUBLIC/
+ @external = stream[2]
+ if @external == 'SYSTEM'
+ @ref = stream[3]
+ @ndata = stream[4] if stream.size == 5
+ else
+ @pubid = stream[3]
+ @ref = stream[4]
+ end
+ else
+ @value = stream[2]
+ end
+ else
+ @reference = reference
+ @external = nil
+ @name = stream
+ @value = value
+ end
+ end
- # Evaluates whether the given string matchs an entity definition,
- # returning true if so, and false otherwise.
- def Entity::matches? string
- (ENTITYDECL =~ string) == 0
- end
+ # Evaluates whether the given string matchs an entity definition,
+ # returning true if so, and false otherwise.
+ def Entity::matches? string
+ (ENTITYDECL =~ string) == 0
+ end
- # Evaluates to the unnormalized value of this entity; that is, replacing
- # all entities -- both %ent; and &ent; entities. This differs from
- # +value()+ in that +value+ only replaces %ent; entities.
- def unnormalized
- v = value()
- return nil if v.nil?
- @unnormalized = Text::unnormalize(v, parent)
- @unnormalized
- end
+ # Evaluates to the unnormalized value of this entity; that is, replacing
+ # all entities -- both %ent; and &ent; entities. This differs from
+ # +value()+ in that +value+ only replaces %ent; entities.
+ def unnormalized
+ document.record_entity_expansion unless document.nil?
+ v = value()
+ return nil if v.nil?
+ @unnormalized = Text::unnormalize(v, parent)
+ @unnormalized
+ end
- #once :unnormalized
+ #once :unnormalized
- # Returns the value of this entity unprocessed -- raw. This is the
- # normalized value; that is, with all %ent; and &ent; entities intact
- def normalized
- @value
- end
+ # Returns the value of this entity unprocessed -- raw. This is the
+ # normalized value; that is, with all %ent; and &ent; entities intact
+ def normalized
+ @value
+ end
- # Write out a fully formed, correct entity definition (assuming the Entity
- # object itself is valid.)
+ # Write out a fully formed, correct entity definition (assuming the Entity
+ # object itself is valid.)
#
# out::
# An object implementing <TT><<<TT> to which the entity will be
# output
# indent::
# *DEPRECATED* and ignored
- def write out, indent=-1
- out << '<!ENTITY '
- out << '% ' if @reference
- out << @name
- out << ' '
- if @external
- out << @external << ' '
- if @pubid
- q = @pubid.include?('"')?"'":'"'
- out << q << @pubid << q << ' '
- end
- q = @ref.include?('"')?"'":'"'
- out << q << @ref << q
- out << ' NDATA ' << @ndata if @ndata
- else
- q = @value.include?('"')?"'":'"'
- out << q << @value << q
- end
- out << '>'
- end
+ def write out, indent=-1
+ out << '<!ENTITY '
+ out << '% ' if @reference
+ out << @name
+ out << ' '
+ if @external
+ out << @external << ' '
+ if @pubid
+ q = @pubid.include?('"')?"'":'"'
+ out << q << @pubid << q << ' '
+ end
+ q = @ref.include?('"')?"'":'"'
+ out << q << @ref << q
+ out << ' NDATA ' << @ndata if @ndata
+ else
+ q = @value.include?('"')?"'":'"'
+ out << q << @value << q
+ end
+ out << '>'
+ end
- # Returns this entity as a string. See write().
- def to_s
- rv = ''
- write rv
- rv
- end
+ # Returns this entity as a string. See write().
+ def to_s
+ rv = ''
+ write rv
+ rv
+ end
- PEREFERENCE_RE = /#{PEREFERENCE}/um
- # Returns the value of this entity. At the moment, only internal entities
- # are processed. If the value contains internal references (IE,
- # %blah;), those are replaced with their values. IE, if the doctype
- # contains:
- # <!ENTITY % foo "bar">
- # <!ENTITY yada "nanoo %foo; nanoo>
- # then:
- # doctype.entity('yada').value #-> "nanoo bar nanoo"
- def value
- if @value
- matches = @value.scan(PEREFERENCE_RE)
- rv = @value.clone
- if @parent
- matches.each do |entity_reference|
- entity_value = @parent.entity( entity_reference[0] )
- rv.gsub!( /%#{entity_reference.join};/um, entity_value )
- end
- end
- return rv
- end
- nil
- end
- end
+ PEREFERENCE_RE = /#{PEREFERENCE}/um
+ # Returns the value of this entity. At the moment, only internal entities
+ # are processed. If the value contains internal references (IE,
+ # %blah;), those are replaced with their values. IE, if the doctype
+ # contains:
+ # <!ENTITY % foo "bar">
+ # <!ENTITY yada "nanoo %foo; nanoo>
+ # then:
+ # doctype.entity('yada').value #-> "nanoo bar nanoo"
+ def value
+ if @value
+ matches = @value.scan(PEREFERENCE_RE)
+ rv = @value.clone
+ if @parent
+ matches.each do |entity_reference|
+ entity_value = @parent.entity( entity_reference[0] )
+ rv.gsub!( /%#{entity_reference.join};/um, entity_value )
+ end
+ end
+ return rv
+ end
+ nil
+ end
+ end
- # This is a set of entity constants -- the ones defined in the XML
- # specification. These are +gt+, +lt+, +amp+, +quot+ and +apos+.
- module EntityConst
- # +>+
- GT = Entity.new( 'gt', '>' )
- # +<+
- LT = Entity.new( 'lt', '<' )
- # +&+
- AMP = Entity.new( 'amp', '&' )
- # +"+
- QUOT = Entity.new( 'quot', '"' )
- # +'+
- APOS = Entity.new( 'apos', "'" )
- end
+ # This is a set of entity constants -- the ones defined in the XML
+ # specification. These are +gt+, +lt+, +amp+, +quot+ and +apos+.
+ module EntityConst
+ # +>+
+ GT = Entity.new( 'gt', '>' )
+ # +<+
+ LT = Entity.new( 'lt', '<' )
+ # +&+
+ AMP = Entity.new( 'amp', '&' )
+ # +"+
+ QUOT = Entity.new( 'quot', '"' )
+ # +'+
+ APOS = Entity.new( 'apos', "'" )
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/formatters/pretty.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/formatters/pretty.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/formatters/pretty.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -126,9 +126,10 @@
end
def wrap(string, width)
- # Recursivly wrap string at width.
+ # Recursively wrap string at width.
return string if string.length <= width
place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
+ return string if place.nil?
return string[0,place] + "\n" + wrap(string[place+1..-1], width)
end
Modified: MacRuby/branches/experimental/lib/rexml/formatters/transitive.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/formatters/transitive.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/formatters/transitive.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -12,9 +12,10 @@
# formatted. Since this formatter does not alter whitespace nodes, the
# results of formatting already formatted XML will be odd.
class Transitive < Default
- def initialize( indentation=2 )
+ def initialize( indentation=2, ie_hack=false )
@indentation = indentation
@level = 0
+ @ie_hack = ie_hack
end
protected
@@ -29,6 +30,7 @@
output << "\n"
output << ' '*@level
if node.children.empty?
+ output << " " if @ie_hack
output << "/"
else
output << ">"
Modified: MacRuby/branches/experimental/lib/rexml/functions.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/functions.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/functions.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -235,7 +235,7 @@
# from string then we ignore the second &
# subsequent mappings
#
- # if a charactcer maps to nil then we delete it
+ # if a character maps to nil then we delete it
# in the output. This happens if the from
# string is longer than the to string
#
Modified: MacRuby/branches/experimental/lib/rexml/instruction.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/instruction.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/instruction.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,62 +2,62 @@
require "rexml/source"
module REXML
- # Represents an XML Instruction; IE, <? ... ?>
- # TODO: Add parent arg (3rd arg) to constructor
- class Instruction < Child
- START = '<\?'
- STOP = '\?>'
+ # Represents an XML Instruction; IE, <? ... ?>
+ # TODO: Add parent arg (3rd arg) to constructor
+ class Instruction < Child
+ START = '<\?'
+ STOP = '\?>'
- # target is the "name" of the Instruction; IE, the "tag" in <?tag ...?>
- # content is everything else.
- attr_accessor :target, :content
+ # target is the "name" of the Instruction; IE, the "tag" in <?tag ...?>
+ # content is everything else.
+ attr_accessor :target, :content
- # Constructs a new Instruction
- # @param target can be one of a number of things. If String, then
- # the target of this instruction is set to this. If an Instruction,
- # then the Instruction is shallowly cloned (target and content are
- # copied). If a Source, then the source is scanned and parsed for
- # an Instruction declaration.
- # @param content Must be either a String, or a Parent. Can only
- # be a Parent if the target argument is a Source. Otherwise, this
- # String is set as the content of this instruction.
- def initialize(target, content=nil)
- if target.kind_of? String
- super()
- @target = target
- @content = content
- elsif target.kind_of? Instruction
- super(content)
- @target = target.target
- @content = target.content
- end
- @content.strip! if @content
- end
+ # Constructs a new Instruction
+ # @param target can be one of a number of things. If String, then
+ # the target of this instruction is set to this. If an Instruction,
+ # then the Instruction is shallowly cloned (target and content are
+ # copied). If a Source, then the source is scanned and parsed for
+ # an Instruction declaration.
+ # @param content Must be either a String, or a Parent. Can only
+ # be a Parent if the target argument is a Source. Otherwise, this
+ # String is set as the content of this instruction.
+ def initialize(target, content=nil)
+ if target.kind_of? String
+ super()
+ @target = target
+ @content = content
+ elsif target.kind_of? Instruction
+ super(content)
+ @target = target.target
+ @content = target.content
+ end
+ @content.strip! if @content
+ end
- def clone
- Instruction.new self
- end
-
+ def clone
+ Instruction.new self
+ end
+
# == DEPRECATED
# See the rexml/formatters package
#
- def write writer, indent=-1, transitive=false, ie_hack=false
+ def write writer, indent=-1, transitive=false, ie_hack=false
Kernel.warn( "#{self.class.name}.write is deprecated" )
- indent(writer, indent)
- writer << START.sub(/\\/u, '')
- writer << @target
- writer << ' '
- writer << @content
- writer << STOP.sub(/\\/u, '')
- end
+ indent(writer, indent)
+ writer << START.sub(/\\/u, '')
+ writer << @target
+ writer << ' '
+ writer << @content
+ writer << STOP.sub(/\\/u, '')
+ end
- # @return true if other is an Instruction, and the content and target
- # of the other matches the target and content of this object.
- def ==( other )
- other.kind_of? Instruction and
- other.target == @target and
- other.content == @content
- end
+ # @return true if other is an Instruction, and the content and target
+ # of the other matches the target and content of this object.
+ def ==( other )
+ other.kind_of? Instruction and
+ other.target == @target and
+ other.content == @content
+ end
def node_type
:processing_instruction
@@ -66,5 +66,5 @@
def inspect
"<?p-i #{target} ...?>"
end
- end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/light/node.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/light/node.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/light/node.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,195 +2,195 @@
require 'rexml/light/node'
# [ :element, parent, name, attributes, children* ]
- # a = Node.new
- # a << "B" # => <a>B</a>
- # a.b # => <a>B<b/></a>
- # a.b[1] # => <a>B<b/><b/><a>
- # a.b[1]["x"] = "y" # => <a>B<b/><b x="y"/></a>
- # a.b[0].c # => <a>B<b><c/></b><b x="y"/></a>
- # a.b.c << "D" # => <a>B<b><c>D</c></b><b x="y"/></a>
+ # a = Node.new
+ # a << "B" # => <a>B</a>
+ # a.b # => <a>B<b/></a>
+ # a.b[1] # => <a>B<b/><b/><a>
+ # a.b[1]["x"] = "y" # => <a>B<b/><b x="y"/></a>
+ # a.b[0].c # => <a>B<b><c/></b><b x="y"/></a>
+ # a.b.c << "D" # => <a>B<b><c>D</c></b><b x="y"/></a>
module REXML
- module Light
- # Represents a tagged XML element. Elements are characterized by
- # having children, attributes, and names, and can themselves be
- # children.
- class Node
- NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u
- PARENTS = [ :element, :document, :doctype ]
- # Create a new element.
- def initialize node=nil
- @node = node
- if node.kind_of? String
- node = [ :text, node ]
- elsif node.nil?
- node = [ :document, nil, nil ]
- elsif node[0] == :start_element
- node[0] = :element
- elsif node[0] == :start_doctype
- node[0] = :doctype
- elsif node[0] == :start_document
- node[0] = :document
- end
- end
+ module Light
+ # Represents a tagged XML element. Elements are characterized by
+ # having children, attributes, and names, and can themselves be
+ # children.
+ class Node
+ NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u
+ PARENTS = [ :element, :document, :doctype ]
+ # Create a new element.
+ def initialize node=nil
+ @node = node
+ if node.kind_of? String
+ node = [ :text, node ]
+ elsif node.nil?
+ node = [ :document, nil, nil ]
+ elsif node[0] == :start_element
+ node[0] = :element
+ elsif node[0] == :start_doctype
+ node[0] = :doctype
+ elsif node[0] == :start_document
+ node[0] = :document
+ end
+ end
- def size
- if PARENTS.include? @node[0]
- @node[-1].size
- else
- 0
- end
- end
+ def size
+ if PARENTS.include? @node[0]
+ @node[-1].size
+ else
+ 0
+ end
+ end
- def each( &block )
- size.times { |x| yield( at(x+4) ) }
- end
+ def each( &block )
+ size.times { |x| yield( at(x+4) ) }
+ end
- def name
- at(2)
- end
+ def name
+ at(2)
+ end
- def name=( name_str, ns=nil )
- pfx = ''
- pfx = "#{prefix(ns)}:" if ns
- _old_put(2, "#{pfx}#{name_str}")
- end
+ def name=( name_str, ns=nil )
+ pfx = ''
+ pfx = "#{prefix(ns)}:" if ns
+ _old_put(2, "#{pfx}#{name_str}")
+ end
- def parent=( node )
- _old_put(1,node)
- end
+ def parent=( node )
+ _old_put(1,node)
+ end
- def local_name
- namesplit
- @name
- end
+ def local_name
+ namesplit
+ @name
+ end
- def local_name=( name_str )
- _old_put( 1, "#@prefix:#{name_str}" )
- end
+ def local_name=( name_str )
+ _old_put( 1, "#@prefix:#{name_str}" )
+ end
- def prefix( namespace=nil )
- prefix_of( self, namespace )
- end
+ def prefix( namespace=nil )
+ prefix_of( self, namespace )
+ end
- def namespace( prefix=prefix() )
- namespace_of( self, prefix )
- end
+ def namespace( prefix=prefix() )
+ namespace_of( self, prefix )
+ end
- def namespace=( namespace )
- @prefix = prefix( namespace )
- pfx = ''
- pfx = "#@prefix:" if @prefix.size > 0
- _old_put(1, "#{pfx}#@name")
- end
+ def namespace=( namespace )
+ @prefix = prefix( namespace )
+ pfx = ''
+ pfx = "#@prefix:" if @prefix.size > 0
+ _old_put(1, "#{pfx}#@name")
+ end
- def []( reference, ns=nil )
- if reference.kind_of? String
- pfx = ''
- pfx = "#{prefix(ns)}:" if ns
- at(3)["#{pfx}#{reference}"]
- elsif reference.kind_of? Range
- _old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) )
- else
- _old_get( 4+reference )
- end
- end
+ def []( reference, ns=nil )
+ if reference.kind_of? String
+ pfx = ''
+ pfx = "#{prefix(ns)}:" if ns
+ at(3)["#{pfx}#{reference}"]
+ elsif reference.kind_of? Range
+ _old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) )
+ else
+ _old_get( 4+reference )
+ end
+ end
- def =~( path )
- XPath.match( self, path )
- end
+ def =~( path )
+ XPath.match( self, path )
+ end
- # Doesn't handle namespaces yet
- def []=( reference, ns, value=nil )
- if reference.kind_of? String
- value = ns unless value
- at( 3 )[reference] = value
- elsif reference.kind_of? Range
- _old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns )
- else
- if value
- _old_put( 4+reference, ns, value )
- else
- _old_put( 4+reference, ns )
- end
- end
- end
+ # Doesn't handle namespaces yet
+ def []=( reference, ns, value=nil )
+ if reference.kind_of? String
+ value = ns unless value
+ at( 3 )[reference] = value
+ elsif reference.kind_of? Range
+ _old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns )
+ else
+ if value
+ _old_put( 4+reference, ns, value )
+ else
+ _old_put( 4+reference, ns )
+ end
+ end
+ end
- # Append a child to this element, optionally under a provided namespace.
- # The namespace argument is ignored if the element argument is an Element
- # object. Otherwise, the element argument is a string, the namespace (if
- # provided) is the namespace the element is created in.
- def << element
- if node_type() == :text
- at(-1) << element
- else
- newnode = Node.new( element )
- newnode.parent = self
- self.push( newnode )
- end
- at(-1)
- end
+ # Append a child to this element, optionally under a provided namespace.
+ # The namespace argument is ignored if the element argument is an Element
+ # object. Otherwise, the element argument is a string, the namespace (if
+ # provided) is the namespace the element is created in.
+ def << element
+ if node_type() == :text
+ at(-1) << element
+ else
+ newnode = Node.new( element )
+ newnode.parent = self
+ self.push( newnode )
+ end
+ at(-1)
+ end
- def node_type
- _old_get(0)
- end
+ def node_type
+ _old_get(0)
+ end
- def text=( foo )
- replace = at(4).kind_of?(String)? 1 : 0
- self._old_put(4,replace, normalizefoo)
- end
+ def text=( foo )
+ replace = at(4).kind_of?(String)? 1 : 0
+ self._old_put(4,replace, normalizefoo)
+ end
- def root
- context = self
- context = context.at(1) while context.at(1)
- end
+ def root
+ context = self
+ context = context.at(1) while context.at(1)
+ end
- def has_name?( name, namespace = '' )
- at(3) == name and namespace() == namespace
- end
+ def has_name?( name, namespace = '' )
+ at(3) == name and namespace() == namespace
+ end
- def children
- self
- end
+ def children
+ self
+ end
- def parent
- at(1)
- end
+ def parent
+ at(1)
+ end
- def to_s
+ def to_s
- end
+ end
- private
+ private
- def namesplit
- return if @name.defined?
- at(2) =~ NAMESPLIT
- @prefix = '' || $1
- @name = $2
- end
+ def namesplit
+ return if @name.defined?
+ at(2) =~ NAMESPLIT
+ @prefix = '' || $1
+ @name = $2
+ end
- def namespace_of( node, prefix=nil )
- if not prefix
- name = at(2)
- name =~ NAMESPLIT
- prefix = $1
- end
- to_find = 'xmlns'
- to_find = "xmlns:#{prefix}" if not prefix.nil?
- ns = at(3)[ to_find ]
- ns ? ns : namespace_of( @node[0], prefix )
- end
+ def namespace_of( node, prefix=nil )
+ if not prefix
+ name = at(2)
+ name =~ NAMESPLIT
+ prefix = $1
+ end
+ to_find = 'xmlns'
+ to_find = "xmlns:#{prefix}" if not prefix.nil?
+ ns = at(3)[ to_find ]
+ ns ? ns : namespace_of( @node[0], prefix )
+ end
- def prefix_of( node, namespace=nil )
- if not namespace
- name = node.name
- name =~ NAMESPLIT
- $1
- else
- ns = at(3).find { |k,v| v == namespace }
- ns ? ns : prefix_of( node.parent, namespace )
- end
- end
- end
- end
+ def prefix_of( node, namespace=nil )
+ if not namespace
+ name = node.name
+ name =~ NAMESPLIT
+ $1
+ else
+ ns = at(3).find { |k,v| v == namespace }
+ ns ? ns : prefix_of( node.parent, namespace )
+ end
+ end
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/namespace.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/namespace.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/namespace.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,47 +1,47 @@
require 'rexml/xmltokens'
module REXML
- # Adds named attributes to an object.
- module Namespace
- # The name of the object, valid if set
- attr_reader :name, :expanded_name
- # The expanded name of the object, valid if name is set
- attr_accessor :prefix
- include XMLTokens
- NAMESPLIT = /^(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})/u
+ # Adds named attributes to an object.
+ module Namespace
+ # The name of the object, valid if set
+ attr_reader :name, :expanded_name
+ # The expanded name of the object, valid if name is set
+ attr_accessor :prefix
+ include XMLTokens
+ NAMESPLIT = /^(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})/u
- # Sets the name and the expanded name
- def name=( name )
- @expanded_name = name
- name =~ NAMESPLIT
- if $1
- @prefix = $1
- else
- @prefix = ""
- @namespace = ""
- end
- @name = $2
- end
+ # Sets the name and the expanded name
+ def name=( name )
+ @expanded_name = name
+ name =~ NAMESPLIT
+ if $1
+ @prefix = $1
+ else
+ @prefix = ""
+ @namespace = ""
+ end
+ @name = $2
+ end
- # Compares names optionally WITH namespaces
- def has_name?( other, ns=nil )
- if ns
- return (namespace() == ns and name() == other)
- elsif other.include? ":"
- return fully_expanded_name == other
- else
- return name == other
- end
- end
+ # Compares names optionally WITH namespaces
+ def has_name?( other, ns=nil )
+ if ns
+ return (namespace() == ns and name() == other)
+ elsif other.include? ":"
+ return fully_expanded_name == other
+ else
+ return name == other
+ end
+ end
- alias :local_name :name
+ alias :local_name :name
- # Fully expand the name, even if the prefix wasn't specified in the
- # source file.
- def fully_expanded_name
- ns = prefix
- return "#{ns}:#@name" if ns.size > 0
- return @name
- end
- end
+ # Fully expand the name, even if the prefix wasn't specified in the
+ # source file.
+ def fully_expanded_name
+ ns = prefix
+ return "#{ns}:#@name" if ns.size > 0
+ return @name
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/node.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/node.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/node.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,27 +3,27 @@
require "rexml/formatters/default"
module REXML
- # Represents a node in the tree. Nodes are never encountered except as
- # superclasses of other objects. Nodes have siblings.
- module Node
- # @return the next sibling (nil if unset)
- def next_sibling_node
- return nil if @parent.nil?
- @parent[ @parent.index(self) + 1 ]
- end
+ # Represents a node in the tree. Nodes are never encountered except as
+ # superclasses of other objects. Nodes have siblings.
+ module Node
+ # @return the next sibling (nil if unset)
+ def next_sibling_node
+ return nil if @parent.nil?
+ @parent[ @parent.index(self) + 1 ]
+ end
- # @return the previous sibling (nil if unset)
- def previous_sibling_node
- return nil if @parent.nil?
- ind = @parent.index(self)
- return nil if ind == 0
- @parent[ ind - 1 ]
- end
+ # @return the previous sibling (nil if unset)
+ def previous_sibling_node
+ return nil if @parent.nil?
+ ind = @parent.index(self)
+ return nil if ind == 0
+ @parent[ ind - 1 ]
+ end
# indent::
# *DEPRECATED* This parameter is now ignored. See the formatters in the
# REXML::Formatters package for changing the output style.
- def to_s indent=nil
+ def to_s indent=nil
unless indent.nil?
Kernel.warn( "#{self.class.name}.to_s(indent) parameter is deprecated" )
f = REXML::Formatters::Pretty.new( indent )
@@ -33,33 +33,33 @@
f.write( self, rv = "" )
end
return rv
- end
+ end
- def indent to, ind
+ def indent to, ind
if @parent and @parent.context and not @parent.context[:indentstyle].nil? then
indentstyle = @parent.context[:indentstyle]
else
indentstyle = ' '
end
to << indentstyle*ind unless ind<1
- end
+ end
- def parent?
- false;
- end
+ def parent?
+ false;
+ end
- # Visit all subnodes of +self+ recursively
- def each_recursive(&block) # :yields: node
- self.elements.each {|node|
- block.call(node)
- node.each_recursive(&block)
- }
- end
+ # Visit all subnodes of +self+ recursively
+ def each_recursive(&block) # :yields: node
+ self.elements.each {|node|
+ block.call(node)
+ node.each_recursive(&block)
+ }
+ end
- # Find (and return) first subnode (recursively) for which the block
+ # Find (and return) first subnode (recursively) for which the block
# evaluates to true. Returns +nil+ if none was found.
- def find_first_recursive(&block) # :yields: node
+ def find_first_recursive(&block) # :yields: node
each_recursive {|node|
return node if block.call(node)
}
@@ -71,5 +71,5 @@
def index_in_parent
parent.index(self)+1
end
- end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/output.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/output.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/output.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,24 +1,24 @@
require 'rexml/encoding'
module REXML
- class Output
- include Encoding
+ class Output
+ include Encoding
attr_reader :encoding
- def initialize real_IO, encd="iso-8859-1"
- @output = real_IO
- self.encoding = encd
+ def initialize real_IO, encd="iso-8859-1"
+ @output = real_IO
+ self.encoding = encd
- @to_utf = encd == UTF_8 ? false : true
- end
+ @to_utf = encd == UTF_8 ? false : true
+ end
- def <<( content )
- @output << (@to_utf ? self.encode(content) : content)
- end
+ def <<( content )
+ @output << (@to_utf ? self.encode(content) : content)
+ end
def to_s
"Output[#{encoding}]"
end
- end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/parsers/lightparser.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/parsers/lightparser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/parsers/lightparser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,12 +3,12 @@
require 'rexml/light/node'
module REXML
- module Parsers
- class LightParser
- def initialize stream
- @stream = stream
- @parser = REXML::Parsers::BaseParser.new( stream )
- end
+ module Parsers
+ class LightParser
+ def initialize stream
+ @stream = stream
+ @parser = REXML::Parsers::BaseParser.new( stream )
+ end
def add_listener( listener )
@parser.add_listener( listener )
@@ -19,42 +19,40 @@
@parser.stream = @stream
end
- def parse
- root = context = [ :document ]
- while true
- event = @parser.pull
- case event[0]
- when :end_document
- break
- when :end_doctype
- context = context[1]
- when :start_element, :start_doctype
- new_node = event
- context << new_node
- new_node[1,0] = [context]
- context = new_node
- when :end_element, :end_doctype
- context = context[1]
- else
- new_node = event
- context << new_node
- new_node[1,0] = [context]
- end
- end
- root
- end
- end
+ def parse
+ root = context = [ :document ]
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ break
+ when :start_element, :start_doctype
+ new_node = event
+ context << new_node
+ new_node[1,0] = [context]
+ context = new_node
+ when :end_element, :end_doctype
+ context = context[1]
+ else
+ new_node = event
+ context << new_node
+ new_node[1,0] = [context]
+ end
+ end
+ root
+ end
+ end
- # An element is an array. The array contains:
- # 0 The parent element
- # 1 The tag name
- # 2 A hash of attributes
- # 3..-1 The child elements
- # An element is an array of size > 3
- # Text is a String
- # PIs are [ :processing_instruction, target, data ]
- # Comments are [ :comment, data ]
- # DocTypes are DocType structs
- # The root is an array with XMLDecls, Text, DocType, Array, Text
- end
+ # An element is an array. The array contains:
+ # 0 The parent element
+ # 1 The tag name
+ # 2 A hash of attributes
+ # 3..-1 The child elements
+ # An element is an array of size > 3
+ # Text is a String
+ # PIs are [ :processing_instruction, target, data ]
+ # Comments are [ :comment, data ]
+ # DocTypes are DocType structs
+ # The root is an array with XMLDecls, Text, DocType, Array, Text
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/parsers/sax2parser.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/parsers/sax2parser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/parsers/sax2parser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -4,158 +4,158 @@
require 'rexml/text'
module REXML
- module Parsers
+ module Parsers
# SAX2Parser
- class SAX2Parser
- def initialize source
- @parser = BaseParser.new(source)
- @listeners = []
- @procs = []
- @namespace_stack = []
- @has_listeners = false
- @tag_stack = []
+ class SAX2Parser
+ def initialize source
+ @parser = BaseParser.new(source)
+ @listeners = []
+ @procs = []
+ @namespace_stack = []
+ @has_listeners = false
+ @tag_stack = []
@entities = {}
- end
+ end
def source
@parser.source
end
-
+
def add_listener( listener )
@parser.add_listener( listener )
end
- # Listen arguments:
- #
- # Symbol, Array, Block
- # Listen to Symbol events on Array elements
- # Symbol, Block
- # Listen to Symbol events
- # Array, Listener
- # Listen to all events on Array elements
- # Array, Block
- # Listen to :start_element events on Array elements
- # Listener
- # Listen to All events
- #
- # Symbol can be one of: :start_element, :end_element,
- # :start_prefix_mapping, :end_prefix_mapping, :characters,
- # :processing_instruction, :doctype, :attlistdecl, :elementdecl,
- # :entitydecl, :notationdecl, :cdata, :xmldecl, :comment
+ # Listen arguments:
#
+ # Symbol, Array, Block
+ # Listen to Symbol events on Array elements
+ # Symbol, Block
+ # Listen to Symbol events
+ # Array, Listener
+ # Listen to all events on Array elements
+ # Array, Block
+ # Listen to :start_element events on Array elements
+ # Listener
+ # Listen to All events
+ #
+ # Symbol can be one of: :start_element, :end_element,
+ # :start_prefix_mapping, :end_prefix_mapping, :characters,
+ # :processing_instruction, :doctype, :attlistdecl, :elementdecl,
+ # :entitydecl, :notationdecl, :cdata, :xmldecl, :comment
+ #
# There is an additional symbol that can be listened for: :progress.
# This will be called for every event generated, passing in the current
# stream position.
- #
- # Array contains regular expressions or strings which will be matched
- # against fully qualified element names.
- #
- # Listener must implement the methods in SAX2Listener
- #
- # Block will be passed the same arguments as a SAX2Listener method would
- # be, where the method name is the same as the matched Symbol.
- # See the SAX2Listener for more information.
- def listen( *args, &blok )
- if args[0].kind_of? Symbol
- if args.size == 2
- args[1].each { |match| @procs << [args[0], match, blok] }
- else
- add( [args[0], nil, blok] )
- end
- elsif args[0].kind_of? Array
- if args.size == 2
- args[0].each { |match| add( [nil, match, args[1]] ) }
- else
- args[0].each { |match| add( [ :start_element, match, blok ] ) }
- end
- else
- add([nil, nil, args[0]])
- end
- end
-
- def deafen( listener=nil, &blok )
- if listener
- @listeners.delete_if {|item| item[-1] == listener }
- @has_listeners = false if @listeners.size == 0
- else
- @procs.delete_if {|item| item[-1] == blok }
- end
- end
-
- def parse
- @procs.each { |sym,match,block| block.call if sym == :start_document }
- @listeners.each { |sym,match,block|
- block.start_document if sym == :start_document or sym.nil?
- }
- root = context = []
- while true
- event = @parser.pull
- case event[0]
- when :end_document
- handle( :end_document )
- break
+ #
+ # Array contains regular expressions or strings which will be matched
+ # against fully qualified element names.
+ #
+ # Listener must implement the methods in SAX2Listener
+ #
+ # Block will be passed the same arguments as a SAX2Listener method would
+ # be, where the method name is the same as the matched Symbol.
+ # See the SAX2Listener for more information.
+ def listen( *args, &blok )
+ if args[0].kind_of? Symbol
+ if args.size == 2
+ args[1].each { |match| @procs << [args[0], match, blok] }
+ else
+ add( [args[0], nil, blok] )
+ end
+ elsif args[0].kind_of? Array
+ if args.size == 2
+ args[0].each { |match| add( [nil, match, args[1]] ) }
+ else
+ args[0].each { |match| add( [ :start_element, match, blok ] ) }
+ end
+ else
+ add([nil, nil, args[0]])
+ end
+ end
+
+ def deafen( listener=nil, &blok )
+ if listener
+ @listeners.delete_if {|item| item[-1] == listener }
+ @has_listeners = false if @listeners.size == 0
+ else
+ @procs.delete_if {|item| item[-1] == blok }
+ end
+ end
+
+ def parse
+ @procs.each { |sym,match,block| block.call if sym == :start_document }
+ @listeners.each { |sym,match,block|
+ block.start_document if sym == :start_document or sym.nil?
+ }
+ root = context = []
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ handle( :end_document )
+ break
when :start_doctype
handle( :doctype, *event[1..-1])
- when :end_doctype
- context = context[1]
- when :start_element
- @tag_stack.push(event[1])
- # find the observers for namespaces
- procs = get_procs( :start_prefix_mapping, event[1] )
- listeners = get_listeners( :start_prefix_mapping, event[1] )
- if procs or listeners
- # break out the namespace declarations
- # The attributes live in event[2]
- event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
- nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
- nsdecl.collect! { |n, value| [ n[6..-1], value ] }
- @namespace_stack.push({})
- nsdecl.each do |n,v|
- @namespace_stack[-1][n] = v
- # notify observers of namespaces
- procs.each { |ob| ob.call( n, v ) } if procs
- listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
- end
- end
- event[1] =~ Namespace::NAMESPLIT
- prefix = $1
- local = $2
- uri = get_namespace(prefix)
- # find the observers for start_element
- procs = get_procs( :start_element, event[1] )
- listeners = get_listeners( :start_element, event[1] )
- # notify observers
- procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
- listeners.each { |ob|
- ob.start_element( uri, local, event[1], event[2] )
- } if listeners
- when :end_element
- @tag_stack.pop
- event[1] =~ Namespace::NAMESPLIT
- prefix = $1
- local = $2
- uri = get_namespace(prefix)
- # find the observers for start_element
- procs = get_procs( :end_element, event[1] )
- listeners = get_listeners( :end_element, event[1] )
- # notify observers
- procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
- listeners.each { |ob|
- ob.end_element( uri, local, event[1] )
- } if listeners
+ when :end_doctype
+ context = context[1]
+ when :start_element
+ @tag_stack.push(event[1])
+ # find the observers for namespaces
+ procs = get_procs( :start_prefix_mapping, event[1] )
+ listeners = get_listeners( :start_prefix_mapping, event[1] )
+ if procs or listeners
+ # break out the namespace declarations
+ # The attributes live in event[2]
+ event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
+ nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
+ nsdecl.collect! { |n, value| [ n[6..-1], value ] }
+ @namespace_stack.push({})
+ nsdecl.each do |n,v|
+ @namespace_stack[-1][n] = v
+ # notify observers of namespaces
+ procs.each { |ob| ob.call( n, v ) } if procs
+ listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
+ end
+ end
+ event[1] =~ Namespace::NAMESPLIT
+ prefix = $1
+ local = $2
+ uri = get_namespace(prefix)
+ # find the observers for start_element
+ procs = get_procs( :start_element, event[1] )
+ listeners = get_listeners( :start_element, event[1] )
+ # notify observers
+ procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
+ listeners.each { |ob|
+ ob.start_element( uri, local, event[1], event[2] )
+ } if listeners
+ when :end_element
+ @tag_stack.pop
+ event[1] =~ Namespace::NAMESPLIT
+ prefix = $1
+ local = $2
+ uri = get_namespace(prefix)
+ # find the observers for start_element
+ procs = get_procs( :end_element, event[1] )
+ listeners = get_listeners( :end_element, event[1] )
+ # notify observers
+ procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
+ listeners.each { |ob|
+ ob.end_element( uri, local, event[1] )
+ } if listeners
- namespace_mapping = @namespace_stack.pop
- # find the observers for namespaces
- procs = get_procs( :end_prefix_mapping, event[1] )
- listeners = get_listeners( :end_prefix_mapping, event[1] )
- if procs or listeners
- namespace_mapping.each do |ns_prefix, ns_uri|
- # notify observers of namespaces
- procs.each { |ob| ob.call( ns_prefix ) } if procs
- listeners.each { |ob| ob.end_prefix_mapping(ns_prefix) } if listeners
- end
- end
- when :text
+ namespace_mapping = @namespace_stack.pop
+ # find the observers for namespaces
+ procs = get_procs( :end_prefix_mapping, event[1] )
+ listeners = get_listeners( :end_prefix_mapping, event[1] )
+ if procs or listeners
+ namespace_mapping.each do |ns_prefix, ns_uri|
+ # notify observers of namespaces
+ procs.each { |ob| ob.call( ns_prefix ) } if procs
+ listeners.each { |ob| ob.end_prefix_mapping(ns_prefix) } if listeners
+ end
+ end
+ when :text
#normalized = @parser.normalize( event[1] )
#handle( :characters, normalized )
copy = event[1].clone
@@ -177,71 +177,71 @@
handle( :characters, copy )
when :entitydecl
@entities[ event[1] ] = event[2] if event.size == 3
- handle( *event )
- when :processing_instruction, :comment, :attlistdecl,
- :elementdecl, :cdata, :notationdecl, :xmldecl
- handle( *event )
- end
+ handle( *event )
+ when :processing_instruction, :comment, :attlistdecl,
+ :elementdecl, :cdata, :notationdecl, :xmldecl
+ handle( *event )
+ end
handle( :progress, @parser.position )
- end
- end
+ end
+ end
- private
- def handle( symbol, *arguments )
- tag = @tag_stack[-1]
- procs = get_procs( symbol, tag )
- listeners = get_listeners( symbol, tag )
- # notify observers
- procs.each { |ob| ob.call( *arguments ) } if procs
- listeners.each { |l|
- l.send( symbol.to_s, *arguments )
- } if listeners
- end
+ private
+ def handle( symbol, *arguments )
+ tag = @tag_stack[-1]
+ procs = get_procs( symbol, tag )
+ listeners = get_listeners( symbol, tag )
+ # notify observers
+ procs.each { |ob| ob.call( *arguments ) } if procs
+ listeners.each { |l|
+ l.send( symbol.to_s, *arguments )
+ } if listeners
+ end
- # The following methods are duplicates, but it is faster than using
- # a helper
- def get_procs( symbol, name )
- return nil if @procs.size == 0
- @procs.find_all do |sym, match, block|
+ # The following methods are duplicates, but it is faster than using
+ # a helper
+ def get_procs( symbol, name )
+ return nil if @procs.size == 0
+ @procs.find_all do |sym, match, block|
#puts sym.inspect+"=="+symbol.inspect+ "\t"+match.inspect+"=="+name.inspect+ "\t"+( (sym.nil? or symbol == sym) and ((name.nil? and match.nil?) or match.nil? or ( (name == match) or (match.kind_of? Regexp and name =~ match)))).to_s
- (
- (sym.nil? or symbol == sym) and
- ((name.nil? and match.nil?) or match.nil? or (
- (name == match) or
- (match.kind_of? Regexp and name =~ match)
- )
- )
- )
- end.collect{|x| x[-1]}
- end
- def get_listeners( symbol, name )
- return nil if @listeners.size == 0
- @listeners.find_all do |sym, match, block|
- (
- (sym.nil? or symbol == sym) and
- ((name.nil? and match.nil?) or match.nil? or (
- (name == match) or
- (match.kind_of? Regexp and name =~ match)
- )
- )
- )
- end.collect{|x| x[-1]}
- end
+ (
+ (sym.nil? or symbol == sym) and
+ ((name.nil? and match.nil?) or match.nil? or (
+ (name == match) or
+ (match.kind_of? Regexp and name =~ match)
+ )
+ )
+ )
+ end.collect{|x| x[-1]}
+ end
+ def get_listeners( symbol, name )
+ return nil if @listeners.size == 0
+ @listeners.find_all do |sym, match, block|
+ (
+ (sym.nil? or symbol == sym) and
+ ((name.nil? and match.nil?) or match.nil? or (
+ (name == match) or
+ (match.kind_of? Regexp and name =~ match)
+ )
+ )
+ )
+ end.collect{|x| x[-1]}
+ end
- def add( pair )
- if pair[-1].respond_to? :call
- @procs << pair unless @procs.include? pair
- else
- @listeners << pair unless @listeners.include? pair
- @has_listeners = true
- end
- end
+ def add( pair )
+ if pair[-1].respond_to? :call
+ @procs << pair unless @procs.include? pair
+ else
+ @listeners << pair unless @listeners.include? pair
+ @has_listeners = true
+ end
+ end
- def get_namespace( prefix )
+ def get_namespace( prefix )
uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) ||
- (@namespace_stack.find { |ns| not ns[nil].nil? })
- uris[-1][prefix] unless uris.nil? or 0 == uris.size
- end
- end
- end
+ (@namespace_stack.find { |ns| not ns[nil].nil? })
+ uris[-1][prefix] unless uris.nil? or 0 == uris.size
+ end
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/parsers/ultralightparser.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/parsers/ultralightparser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/parsers/ultralightparser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,12 +2,12 @@
require 'rexml/parsers/baseparser'
module REXML
- module Parsers
- class UltraLightParser
- def initialize stream
- @stream = stream
- @parser = REXML::Parsers::BaseParser.new( stream )
- end
+ module Parsers
+ class UltraLightParser
+ def initialize stream
+ @stream = stream
+ @parser = REXML::Parsers::BaseParser.new( stream )
+ end
def add_listener( listener )
@parser.add_listener( listener )
@@ -18,39 +18,39 @@
@parser.stream = @stream
end
- def parse
- root = context = []
- while true
- event = @parser.pull
- case event[0]
- when :end_document
- break
- when :end_doctype
- context = context[1]
- when :start_element, :doctype
- context << event
- event[1,0] = [context]
- context = event
- when :end_element
- context = context[1]
- else
- context << event
- end
- end
- root
- end
- end
+ def parse
+ root = context = []
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ break
+ when :end_doctype
+ context = context[1]
+ when :start_element, :doctype
+ context << event
+ event[1,0] = [context]
+ context = event
+ when :end_element
+ context = context[1]
+ else
+ context << event
+ end
+ end
+ root
+ end
+ end
- # An element is an array. The array contains:
- # 0 The parent element
- # 1 The tag name
- # 2 A hash of attributes
- # 3..-1 The child elements
- # An element is an array of size > 3
- # Text is a String
- # PIs are [ :processing_instruction, target, data ]
- # Comments are [ :comment, data ]
- # DocTypes are DocType structs
- # The root is an array with XMLDecls, Text, DocType, Array, Text
- end
+ # An element is an array. The array contains:
+ # 0 The parent element
+ # 1 The tag name
+ # 2 A hash of attributes
+ # 3..-1 The child elements
+ # An element is an array of size > 3
+ # Text is a String
+ # PIs are [ :processing_instruction, target, data ]
+ # Comments are [ :comment, data ]
+ # DocTypes are DocType structs
+ # The root is an array with XMLDecls, Text, DocType, Array, Text
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/parsers/xpathparser.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/parsers/xpathparser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/parsers/xpathparser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -39,10 +39,10 @@
case op
when :node
when :attribute
- string << "/" if string.size > 0
- string << "@"
+ string << "/" if string.size > 0
+ string << "@"
when :child
- string << "/" if string.size > 0
+ string << "/" if string.size > 0
when :descendant_or_self
string << "/"
when :self
@@ -51,8 +51,8 @@
string << ".."
when :any
string << "*"
- when :text
- string << "text()"
+ when :text
+ string << "text()"
when :following, :following_sibling,
:ancestor, :ancestor_or_self, :descendant,
:namespace, :preceding, :preceding_sibling
@@ -70,13 +70,13 @@
string << ']'
when :document
document = true
- when :function
- string << path.shift
- string << "( "
- string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
- string << " )"
- when :literal
- string << %Q{ "#{path.shift}" }
+ when :function
+ string << path.shift
+ string << "( "
+ string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
+ string << " )"
+ when :literal
+ string << %Q{ "#{path.shift}" }
else
string << "/" unless string.size == 0
string << "UNKNOWN("
@@ -84,7 +84,7 @@
string << ")"
end
end
- string = "/"+string if document
+ string = "/"+string if document
return string
end
@@ -653,39 +653,39 @@
def parse_args( string )
arguments = []
ind = 0
- inquot = false
- inapos = false
+ inquot = false
+ inapos = false
depth = 1
begin
case string[ind]
when ?"
- inquot = !inquot unless inapos
+ inquot = !inquot unless inapos
when ?'
- inapos = !inapos unless inquot
+ inapos = !inapos unless inquot
else
- unless inquot or inapos
- case string[ind]
- when ?(
- depth += 1
+ unless inquot or inapos
+ case string[ind]
+ when ?(
+ depth += 1
if depth == 1
- string = string[1..-1]
- ind -= 1
+ string = string[1..-1]
+ ind -= 1
end
- when ?)
- depth -= 1
- if depth == 0
- s = string[0,ind].strip
- arguments << s unless s == ""
- string = string[ind+1..-1]
- end
- when ?,
- if depth == 1
- s = string[0,ind].strip
- arguments << s unless s == ""
- string = string[ind+1..-1]
- ind = -1
- end
- end
+ when ?)
+ depth -= 1
+ if depth == 0
+ s = string[0,ind].strip
+ arguments << s unless s == ""
+ string = string[ind+1..-1]
+ end
+ when ?,
+ if depth == 1
+ s = string[0,ind].strip
+ arguments << s unless s == ""
+ string = string[ind+1..-1]
+ ind = -1
+ end
+ end
end
end
ind += 1
Modified: MacRuby/branches/experimental/lib/rexml/quickpath.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/quickpath.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/quickpath.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,262 +2,262 @@
require 'rexml/xmltokens'
module REXML
- class QuickPath
- include Functions
- include XMLTokens
+ class QuickPath
+ include Functions
+ include XMLTokens
- EMPTY_HASH = {}
+ EMPTY_HASH = {}
- def QuickPath::first element, path, namespaces=EMPTY_HASH
- match(element, path, namespaces)[0]
- end
+ def QuickPath::first element, path, namespaces=EMPTY_HASH
+ match(element, path, namespaces)[0]
+ end
- def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
- path = "*" unless path
- match(element, path, namespaces).each( &block )
- end
+ def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
+ path = "*" unless path
+ match(element, path, namespaces).each( &block )
+ end
- def QuickPath::match element, path, namespaces=EMPTY_HASH
- raise "nil is not a valid xpath" unless path
- results = nil
- Functions::namespace_context = namespaces
- case path
- when /^\/([^\/]|$)/u
- # match on root
- path = path[1..-1]
- return [element.root.parent] if path == ''
- results = filter([element.root], path)
- when /^[-\w]*::/u
- results = filter([element], path)
- when /^\*/u
- results = filter(element.to_a, path)
- when /^[\[!\w:]/u
- # match on child
- matches = []
- children = element.to_a
- results = filter(children, path)
- else
- results = filter([element], path)
- end
- return results
- end
+ def QuickPath::match element, path, namespaces=EMPTY_HASH
+ raise "nil is not a valid xpath" unless path
+ results = nil
+ Functions::namespace_context = namespaces
+ case path
+ when /^\/([^\/]|$)/u
+ # match on root
+ path = path[1..-1]
+ return [element.root.parent] if path == ''
+ results = filter([element.root], path)
+ when /^[-\w]*::/u
+ results = filter([element], path)
+ when /^\*/u
+ results = filter(element.to_a, path)
+ when /^[\[!\w:]/u
+ # match on child
+ matches = []
+ children = element.to_a
+ results = filter(children, path)
+ else
+ results = filter([element], path)
+ end
+ return results
+ end
- # Given an array of nodes it filters the array based on the path. The
- # result is that when this method returns, the array will contain elements
- # which match the path
- def QuickPath::filter elements, path
- return elements if path.nil? or path == '' or elements.size == 0
- case path
- when /^\/\//u # Descendant
- return axe( elements, "descendant-or-self", $' )
- when /^\/?\b(\w[-\w]*)\b::/u # Axe
- axe_name = $1
- rest = $'
- return axe( elements, $1, $' )
- when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
- rest = $'
- results = []
- elements.each do |element|
- results |= filter( element.to_a, rest )
- end
- return results
- when /^\/?(\w[-\w]*)\(/u # / Function
- return function( elements, $1, $' )
- when Namespace::NAMESPLIT # Element name
- name = $2
- ns = $1
- rest = $'
- elements.delete_if do |element|
- !(element.kind_of? Element and
- (element.expanded_name == name or
- (element.name == name and
- element.namespace == Functions.namespace_context[ns])))
- end
- return filter( elements, rest )
- when /^\/\[/u
- matches = []
- elements.each do |element|
- matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
- end
- return matches
- when /^\[/u # Predicate
- return predicate( elements, path )
- when /^\/?\.\.\./u # Ancestor
- return axe( elements, "ancestor", $' )
- when /^\/?\.\./u # Parent
- return filter( elements.collect{|e|e.parent}, $' )
- when /^\/?\./u # Self
- return filter( elements, $' )
- when /^\*/u # Any
- results = []
- elements.each do |element|
- results |= filter( [element], $' ) if element.kind_of? Element
- #if element.kind_of? Element
- # children = element.to_a
- # children.delete_if { |child| !child.kind_of?(Element) }
- # results |= filter( children, $' )
- #end
- end
- return results
- end
- return []
- end
+ # Given an array of nodes it filters the array based on the path. The
+ # result is that when this method returns, the array will contain elements
+ # which match the path
+ def QuickPath::filter elements, path
+ return elements if path.nil? or path == '' or elements.size == 0
+ case path
+ when /^\/\//u # Descendant
+ return axe( elements, "descendant-or-self", $' )
+ when /^\/?\b(\w[-\w]*)\b::/u # Axe
+ axe_name = $1
+ rest = $'
+ return axe( elements, $1, $' )
+ when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
+ rest = $'
+ results = []
+ elements.each do |element|
+ results |= filter( element.to_a, rest )
+ end
+ return results
+ when /^\/?(\w[-\w]*)\(/u # / Function
+ return function( elements, $1, $' )
+ when Namespace::NAMESPLIT # Element name
+ name = $2
+ ns = $1
+ rest = $'
+ elements.delete_if do |element|
+ !(element.kind_of? Element and
+ (element.expanded_name == name or
+ (element.name == name and
+ element.namespace == Functions.namespace_context[ns])))
+ end
+ return filter( elements, rest )
+ when /^\/\[/u
+ matches = []
+ elements.each do |element|
+ matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
+ end
+ return matches
+ when /^\[/u # Predicate
+ return predicate( elements, path )
+ when /^\/?\.\.\./u # Ancestor
+ return axe( elements, "ancestor", $' )
+ when /^\/?\.\./u # Parent
+ return filter( elements.collect{|e|e.parent}, $' )
+ when /^\/?\./u # Self
+ return filter( elements, $' )
+ when /^\*/u # Any
+ results = []
+ elements.each do |element|
+ results |= filter( [element], $' ) if element.kind_of? Element
+ #if element.kind_of? Element
+ # children = element.to_a
+ # children.delete_if { |child| !child.kind_of?(Element) }
+ # results |= filter( children, $' )
+ #end
+ end
+ return results
+ end
+ return []
+ end
- def QuickPath::axe( elements, axe_name, rest )
- matches = []
- matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
- case axe_name
- when /^descendant/u
- elements.each do |element|
- matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
- end
- when /^ancestor/u
- elements.each do |element|
- while element.parent
- matches << element.parent
- element = element.parent
- end
- end
- matches = filter( matches, rest )
- when "self"
- matches = filter( elements, rest )
- when "child"
- elements.each do |element|
- matches |= filter( element.to_a, rest ) if element.kind_of? Element
- end
- when "attribute"
- elements.each do |element|
- matches << element.attributes[ rest ] if element.kind_of? Element
- end
- when "parent"
- matches = filter(elements.collect{|element| element.parent}.uniq, rest)
- when "following-sibling"
- matches = filter(elements.collect{|element| element.next_sibling}.uniq,
- rest)
- when "previous-sibling"
- matches = filter(elements.collect{|element|
- element.previous_sibling}.uniq, rest )
- end
- return matches.uniq
- end
+ def QuickPath::axe( elements, axe_name, rest )
+ matches = []
+ matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
+ case axe_name
+ when /^descendant/u
+ elements.each do |element|
+ matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
+ end
+ when /^ancestor/u
+ elements.each do |element|
+ while element.parent
+ matches << element.parent
+ element = element.parent
+ end
+ end
+ matches = filter( matches, rest )
+ when "self"
+ matches = filter( elements, rest )
+ when "child"
+ elements.each do |element|
+ matches |= filter( element.to_a, rest ) if element.kind_of? Element
+ end
+ when "attribute"
+ elements.each do |element|
+ matches << element.attributes[ rest ] if element.kind_of? Element
+ end
+ when "parent"
+ matches = filter(elements.collect{|element| element.parent}.uniq, rest)
+ when "following-sibling"
+ matches = filter(elements.collect{|element| element.next_sibling}.uniq,
+ rest)
+ when "previous-sibling"
+ matches = filter(elements.collect{|element|
+ element.previous_sibling}.uniq, rest )
+ end
+ return matches.uniq
+ end
- # A predicate filters a node-set with respect to an axis to produce a
- # new node-set. For each node in the node-set to be filtered, the
- # PredicateExpr is evaluated with that node as the context node, with
- # the number of nodes in the node-set as the context size, and with the
- # proximity position of the node in the node-set with respect to the
- # axis as the context position; if PredicateExpr evaluates to true for
- # that node, the node is included in the new node-set; otherwise, it is
- # not included.
- #
- # A PredicateExpr is evaluated by evaluating the Expr and converting
- # the result to a boolean. If the result is a number, the result will
- # be converted to true if the number is equal to the context position
- # and will be converted to false otherwise; if the result is not a
- # number, then the result will be converted as if by a call to the
- # boolean function. Thus a location path para[3] is equivalent to
- # para[position()=3].
- def QuickPath::predicate( elements, path )
- ind = 1
- bcount = 1
- while bcount > 0
- bcount += 1 if path[ind] == ?[
- bcount -= 1 if path[ind] == ?]
- ind += 1
- end
- ind -= 1
- predicate = path[1..ind-1]
- rest = path[ind+1..-1]
+ # A predicate filters a node-set with respect to an axis to produce a
+ # new node-set. For each node in the node-set to be filtered, the
+ # PredicateExpr is evaluated with that node as the context node, with
+ # the number of nodes in the node-set as the context size, and with the
+ # proximity position of the node in the node-set with respect to the
+ # axis as the context position; if PredicateExpr evaluates to true for
+ # that node, the node is included in the new node-set; otherwise, it is
+ # not included.
+ #
+ # A PredicateExpr is evaluated by evaluating the Expr and converting
+ # the result to a boolean. If the result is a number, the result will
+ # be converted to true if the number is equal to the context position
+ # and will be converted to false otherwise; if the result is not a
+ # number, then the result will be converted as if by a call to the
+ # boolean function. Thus a location path para[3] is equivalent to
+ # para[position()=3].
+ def QuickPath::predicate( elements, path )
+ ind = 1
+ bcount = 1
+ while bcount > 0
+ bcount += 1 if path[ind] == ?[
+ bcount -= 1 if path[ind] == ?]
+ ind += 1
+ end
+ ind -= 1
+ predicate = path[1..ind-1]
+ rest = path[ind+1..-1]
- # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
- predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u,
- '\1 \2 \3 and \3 \4 \5' )
- # Let's do some Ruby trickery to avoid some work:
- predicate.gsub!( /&/u, "&&" )
- predicate.gsub!( /=/u, "==" )
- predicate.gsub!( /@(\w[-\w.]*)/u, 'attribute("\1")' )
- predicate.gsub!( /\bmod\b/u, "%" )
- predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
- fname = $1
- fname.gsub( /-/u, "_" )
- }
-
- Functions.pair = [ 0, elements.size ]
- results = []
- elements.each do |element|
- Functions.pair[0] += 1
- Functions.node = element
- res = eval( predicate )
- case res
- when true
- results << element
- when Fixnum
- results << element if Functions.pair[0] == res
- when String
- results << element
- end
- end
- return filter( results, rest )
- end
+ # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
+ predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u,
+ '\1 \2 \3 and \3 \4 \5' )
+ # Let's do some Ruby trickery to avoid some work:
+ predicate.gsub!( /&/u, "&&" )
+ predicate.gsub!( /=/u, "==" )
+ predicate.gsub!( /@(\w[-\w.]*)/u, 'attribute("\1")' )
+ predicate.gsub!( /\bmod\b/u, "%" )
+ predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
+ fname = $1
+ fname.gsub( /-/u, "_" )
+ }
+
+ Functions.pair = [ 0, elements.size ]
+ results = []
+ elements.each do |element|
+ Functions.pair[0] += 1
+ Functions.node = element
+ res = eval( predicate )
+ case res
+ when true
+ results << element
+ when Fixnum
+ results << element if Functions.pair[0] == res
+ when String
+ results << element
+ end
+ end
+ return filter( results, rest )
+ end
- def QuickPath::attribute( name )
- return Functions.node.attributes[name] if Functions.node.kind_of? Element
- end
+ def QuickPath::attribute( name )
+ return Functions.node.attributes[name] if Functions.node.kind_of? Element
+ end
- def QuickPath::name()
- return Functions.node.name if Functions.node.kind_of? Element
- end
+ def QuickPath::name()
+ return Functions.node.name if Functions.node.kind_of? Element
+ end
- def QuickPath::method_missing( id, *args )
- begin
- Functions.send( id.id2name, *args )
- rescue Exception
- raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
- end
- end
+ def QuickPath::method_missing( id, *args )
+ begin
+ Functions.send( id.id2name, *args )
+ rescue Exception
+ raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
+ end
+ end
- def QuickPath::function( elements, fname, rest )
- args = parse_args( elements, rest )
- Functions.pair = [0, elements.size]
- results = []
- elements.each do |element|
- Functions.pair[0] += 1
- Functions.node = element
- res = Functions.send( fname, *args )
- case res
- when true
- results << element
- when Fixnum
- results << element if Functions.pair[0] == res
- end
- end
- return results
- end
+ def QuickPath::function( elements, fname, rest )
+ args = parse_args( elements, rest )
+ Functions.pair = [0, elements.size]
+ results = []
+ elements.each do |element|
+ Functions.pair[0] += 1
+ Functions.node = element
+ res = Functions.send( fname, *args )
+ case res
+ when true
+ results << element
+ when Fixnum
+ results << element if Functions.pair[0] == res
+ end
+ end
+ return results
+ end
- def QuickPath::parse_args( element, string )
- # /.*?(?:\)|,)/
- arguments = []
- buffer = ""
- while string and string != ""
- c = string[0]
- string.sub!(/^./u, "")
- case c
- when ?,
- # if depth = 1, then we start a new argument
- arguments << evaluate( buffer )
- #arguments << evaluate( string[0..count] )
- when ?(
- # start a new method call
- function( element, buffer, string )
- buffer = ""
- when ?)
- # close the method call and return arguments
- return arguments
- else
- buffer << c
- end
- end
- ""
- end
- end
+ def QuickPath::parse_args( element, string )
+ # /.*?(?:\)|,)/
+ arguments = []
+ buffer = ""
+ while string and string != ""
+ c = string[0]
+ string.sub!(/^./u, "")
+ case c
+ when ?,
+ # if depth = 1, then we start a new argument
+ arguments << evaluate( buffer )
+ #arguments << evaluate( string[0..count] )
+ when ?(
+ # start a new method call
+ function( element, buffer, string )
+ buffer = ""
+ when ?)
+ # close the method call and return arguments
+ return arguments
+ else
+ buffer << c
+ end
+ end
+ ""
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/sax2listener.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/sax2listener.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/sax2listener.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,97 +1,97 @@
module REXML
- # A template for stream parser listeners.
- # Note that the declarations (attlistdecl, elementdecl, etc) are trivially
- # processed; REXML doesn't yet handle doctype entity declarations, so you
- # have to parse them out yourself.
- # === Missing methods from SAX2
- # ignorable_whitespace
- # === Methods extending SAX2
- # +WARNING+
- # These methods are certainly going to change, until DTDs are fully
- # supported. Be aware of this.
- # start_document
- # end_document
- # doctype
- # elementdecl
- # attlistdecl
- # entitydecl
- # notationdecl
- # cdata
- # xmldecl
- # comment
- module SAX2Listener
- def start_document
- end
- def end_document
- end
- def start_prefix_mapping prefix, uri
- end
- def end_prefix_mapping prefix
- end
- def start_element uri, localname, qname, attributes
- end
- def end_element uri, localname, qname
- end
- def characters text
- end
- def processing_instruction target, data
- end
- # Handles a doctype declaration. Any attributes of the doctype which are
- # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
- # @p name the name of the doctype; EG, "me"
- # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
- # @p long_name the supplied long name, or nil. EG, "foo"
- # @p uri the uri of the doctype, or nil. EG, "bar"
- def doctype name, pub_sys, long_name, uri
- end
- # If a doctype includes an ATTLIST declaration, it will cause this
- # method to be called. The content is the declaration itself, unparsed.
- # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
- # attr CDATA #REQUIRED". This is the same for all of the .*decl
- # methods.
- def attlistdecl(element, pairs, contents)
- end
- # <!ELEMENT ...>
- def elementdecl content
- end
- # <!ENTITY ...>
- # The argument passed to this method is an array of the entity
- # declaration. It can be in a number of formats, but in general it
- # returns (example, result):
- # <!ENTITY % YN '"Yes"'>
- # ["%", "YN", "'\"Yes\"'", "\""]
- # <!ENTITY % YN 'Yes'>
- # ["%", "YN", "'Yes'", "s"]
- # <!ENTITY WhatHeSaid "He said %YN;">
- # ["WhatHeSaid", "\"He said %YN;\"", "YN"]
- # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
- # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
- # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
- # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
- # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
- # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"]
- def entitydecl name, decl
- end
- # <!NOTATION ...>
- def notationdecl content
- end
- # Called when <![CDATA[ ... ]]> is encountered in a document.
- # @p content "..."
- def cdata content
- end
- # Called when an XML PI is encountered in the document.
- # EG: <?xml version="1.0" encoding="utf"?>
- # @p version the version attribute value. EG, "1.0"
- # @p encoding the encoding attribute value, or nil. EG, "utf"
- # @p standalone the standalone attribute value, or nil. EG, nil
+ # A template for stream parser listeners.
+ # Note that the declarations (attlistdecl, elementdecl, etc) are trivially
+ # processed; REXML doesn't yet handle doctype entity declarations, so you
+ # have to parse them out yourself.
+ # === Missing methods from SAX2
+ # ignorable_whitespace
+ # === Methods extending SAX2
+ # +WARNING+
+ # These methods are certainly going to change, until DTDs are fully
+ # supported. Be aware of this.
+ # start_document
+ # end_document
+ # doctype
+ # elementdecl
+ # attlistdecl
+ # entitydecl
+ # notationdecl
+ # cdata
+ # xmldecl
+ # comment
+ module SAX2Listener
+ def start_document
+ end
+ def end_document
+ end
+ def start_prefix_mapping prefix, uri
+ end
+ def end_prefix_mapping prefix
+ end
+ def start_element uri, localname, qname, attributes
+ end
+ def end_element uri, localname, qname
+ end
+ def characters text
+ end
+ def processing_instruction target, data
+ end
+ # Handles a doctype declaration. Any attributes of the doctype which are
+ # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
+ # @p name the name of the doctype; EG, "me"
+ # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
+ # @p long_name the supplied long name, or nil. EG, "foo"
+ # @p uri the uri of the doctype, or nil. EG, "bar"
+ def doctype name, pub_sys, long_name, uri
+ end
+ # If a doctype includes an ATTLIST declaration, it will cause this
+ # method to be called. The content is the declaration itself, unparsed.
+ # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
+ # attr CDATA #REQUIRED". This is the same for all of the .*decl
+ # methods.
+ def attlistdecl(element, pairs, contents)
+ end
+ # <!ELEMENT ...>
+ def elementdecl content
+ end
+ # <!ENTITY ...>
+ # The argument passed to this method is an array of the entity
+ # declaration. It can be in a number of formats, but in general it
+ # returns (example, result):
+ # <!ENTITY % YN '"Yes"'>
+ # ["%", "YN", "'\"Yes\"'", "\""]
+ # <!ENTITY % YN 'Yes'>
+ # ["%", "YN", "'Yes'", "s"]
+ # <!ENTITY WhatHeSaid "He said %YN;">
+ # ["WhatHeSaid", "\"He said %YN;\"", "YN"]
+ # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
+ # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"]
+ def entitydecl name, decl
+ end
+ # <!NOTATION ...>
+ def notationdecl content
+ end
+ # Called when <![CDATA[ ... ]]> is encountered in a document.
+ # @p content "..."
+ def cdata content
+ end
+ # Called when an XML PI is encountered in the document.
+ # EG: <?xml version="1.0" encoding="utf"?>
+ # @p version the version attribute value. EG, "1.0"
+ # @p encoding the encoding attribute value, or nil. EG, "utf"
+ # @p standalone the standalone attribute value, or nil. EG, nil
# @p spaced the declaration is followed by a line break
- def xmldecl version, encoding, standalone
- end
- # Called when a comment is encountered.
- # @p comment The content of the comment
- def comment comment
- end
+ def xmldecl version, encoding, standalone
+ end
+ # Called when a comment is encountered.
+ # @p comment The content of the comment
+ def comment comment
+ end
def progress position
end
- end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/streamlistener.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/streamlistener.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/streamlistener.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,92 +1,92 @@
module REXML
- # A template for stream parser listeners.
- # Note that the declarations (attlistdecl, elementdecl, etc) are trivially
- # processed; REXML doesn't yet handle doctype entity declarations, so you
- # have to parse them out yourself.
- module StreamListener
- # Called when a tag is encountered.
- # @p name the tag name
- # @p attrs an array of arrays of attribute/value pairs, suitable for
- # use with assoc or rassoc. IE, <tag attr1="value1" attr2="value2">
- # will result in
- # tag_start( "tag", # [["attr1","value1"],["attr2","value2"]])
- def tag_start name, attrs
- end
- # Called when the end tag is reached. In the case of <tag/>, tag_end
- # will be called immidiately after tag_start
- # @p the name of the tag
- def tag_end name
- end
- # Called when text is encountered in the document
- # @p text the text content.
- def text text
- end
- # Called when an instruction is encountered. EG: <?xsl sheet='foo'?>
- # @p name the instruction name; in the example, "xsl"
- # @p instruction the rest of the instruction. In the example,
- # "sheet='foo'"
- def instruction name, instruction
- end
- # Called when a comment is encountered.
- # @p comment The content of the comment
- def comment comment
- end
- # Handles a doctype declaration. Any attributes of the doctype which are
- # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
- # @p name the name of the doctype; EG, "me"
- # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
- # @p long_name the supplied long name, or nil. EG, "foo"
- # @p uri the uri of the doctype, or nil. EG, "bar"
- def doctype name, pub_sys, long_name, uri
- end
- # Called when the doctype is done
- def doctype_end
- end
- # If a doctype includes an ATTLIST declaration, it will cause this
- # method to be called. The content is the declaration itself, unparsed.
- # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
- # attr CDATA #REQUIRED". This is the same for all of the .*decl
- # methods.
- def attlistdecl element_name, attributes, raw_content
- end
- # <!ELEMENT ...>
- def elementdecl content
- end
- # <!ENTITY ...>
- # The argument passed to this method is an array of the entity
- # declaration. It can be in a number of formats, but in general it
- # returns (example, result):
- # <!ENTITY % YN '"Yes"'>
- # ["%", "YN", "'\"Yes\"'", "\""]
- # <!ENTITY % YN 'Yes'>
- # ["%", "YN", "'Yes'", "s"]
- # <!ENTITY WhatHeSaid "He said %YN;">
- # ["WhatHeSaid", "\"He said %YN;\"", "YN"]
- # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
- # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
- # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
- # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
- # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
- # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"]
- def entitydecl content
- end
- # <!NOTATION ...>
- def notationdecl content
- end
- # Called when %foo; is encountered in a doctype declaration.
- # @p content "foo"
- def entity content
- end
- # Called when <![CDATA[ ... ]]> is encountered in a document.
- # @p content "..."
- def cdata content
- end
- # Called when an XML PI is encountered in the document.
- # EG: <?xml version="1.0" encoding="utf"?>
- # @p version the version attribute value. EG, "1.0"
- # @p encoding the encoding attribute value, or nil. EG, "utf"
- # @p standalone the standalone attribute value, or nil. EG, nil
- def xmldecl version, encoding, standalone
- end
- end
+ # A template for stream parser listeners.
+ # Note that the declarations (attlistdecl, elementdecl, etc) are trivially
+ # processed; REXML doesn't yet handle doctype entity declarations, so you
+ # have to parse them out yourself.
+ module StreamListener
+ # Called when a tag is encountered.
+ # @p name the tag name
+ # @p attrs an array of arrays of attribute/value pairs, suitable for
+ # use with assoc or rassoc. IE, <tag attr1="value1" attr2="value2">
+ # will result in
+ # tag_start( "tag", # [["attr1","value1"],["attr2","value2"]])
+ def tag_start name, attrs
+ end
+ # Called when the end tag is reached. In the case of <tag/>, tag_end
+ # will be called immidiately after tag_start
+ # @p the name of the tag
+ def tag_end name
+ end
+ # Called when text is encountered in the document
+ # @p text the text content.
+ def text text
+ end
+ # Called when an instruction is encountered. EG: <?xsl sheet='foo'?>
+ # @p name the instruction name; in the example, "xsl"
+ # @p instruction the rest of the instruction. In the example,
+ # "sheet='foo'"
+ def instruction name, instruction
+ end
+ # Called when a comment is encountered.
+ # @p comment The content of the comment
+ def comment comment
+ end
+ # Handles a doctype declaration. Any attributes of the doctype which are
+ # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
+ # @p name the name of the doctype; EG, "me"
+ # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
+ # @p long_name the supplied long name, or nil. EG, "foo"
+ # @p uri the uri of the doctype, or nil. EG, "bar"
+ def doctype name, pub_sys, long_name, uri
+ end
+ # Called when the doctype is done
+ def doctype_end
+ end
+ # If a doctype includes an ATTLIST declaration, it will cause this
+ # method to be called. The content is the declaration itself, unparsed.
+ # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
+ # attr CDATA #REQUIRED". This is the same for all of the .*decl
+ # methods.
+ def attlistdecl element_name, attributes, raw_content
+ end
+ # <!ELEMENT ...>
+ def elementdecl content
+ end
+ # <!ENTITY ...>
+ # The argument passed to this method is an array of the entity
+ # declaration. It can be in a number of formats, but in general it
+ # returns (example, result):
+ # <!ENTITY % YN '"Yes"'>
+ # ["%", "YN", "'\"Yes\"'", "\""]
+ # <!ENTITY % YN 'Yes'>
+ # ["%", "YN", "'Yes'", "s"]
+ # <!ENTITY WhatHeSaid "He said %YN;">
+ # ["WhatHeSaid", "\"He said %YN;\"", "YN"]
+ # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
+ # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"]
+ def entitydecl content
+ end
+ # <!NOTATION ...>
+ def notationdecl content
+ end
+ # Called when %foo; is encountered in a doctype declaration.
+ # @p content "foo"
+ def entity content
+ end
+ # Called when <![CDATA[ ... ]]> is encountered in a document.
+ # @p content "..."
+ def cdata content
+ end
+ # Called when an XML PI is encountered in the document.
+ # EG: <?xml version="1.0" encoding="utf"?>
+ # @p version the version attribute value. EG, "1.0"
+ # @p encoding the encoding attribute value, or nil. EG, "utf"
+ # @p standalone the standalone attribute value, or nil. EG, nil
+ def xmldecl version, encoding, standalone
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/text.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/text.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/text.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -150,7 +150,7 @@
end
# context sensitive
- string.scan(pattern).each do
+ string.scan(pattern) do
if $1[-1] != ?;
raise "Illegal character '#{$1}' in raw string \"#{string}\""
elsif $1[0] == ?&
@@ -261,7 +261,7 @@
end
def wrap(string, width, addnewline=false)
- # Recursivly wrap string at width.
+ # Recursively wrap string at width.
return string if string.length <= width
place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
if addnewline then
Modified: MacRuby/branches/experimental/lib/rexml/validation/relaxng.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/validation/relaxng.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/validation/relaxng.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -436,7 +436,7 @@
arry[-1][-1].event_arg = evt[1]
@value = false
- end
+ end
else
arry << [] if evt[0] == :start_element
arry[-1] << generate_event( evt )
Modified: MacRuby/branches/experimental/lib/rexml/xmldecl.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/xmldecl.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/xmldecl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,40 +2,40 @@
require 'rexml/source'
module REXML
- # NEEDS DOCUMENTATION
- class XMLDecl < Child
- include Encoding
+ # NEEDS DOCUMENTATION
+ class XMLDecl < Child
+ include Encoding
- DEFAULT_VERSION = "1.0";
- DEFAULT_ENCODING = "UTF-8";
- DEFAULT_STANDALONE = "no";
- START = '<\?xml';
- STOP = '\?>';
+ DEFAULT_VERSION = "1.0";
+ DEFAULT_ENCODING = "UTF-8";
+ DEFAULT_STANDALONE = "no";
+ START = '<\?xml';
+ STOP = '\?>';
- attr_accessor :version, :standalone
+ attr_accessor :version, :standalone
attr_reader :writeencoding, :writethis
- def initialize(version=DEFAULT_VERSION, encoding=nil, standalone=nil)
+ def initialize(version=DEFAULT_VERSION, encoding=nil, standalone=nil)
@writethis = true
@writeencoding = !encoding.nil?
- if version.kind_of? XMLDecl
- super()
- @version = version.version
- self.encoding = version.encoding
+ if version.kind_of? XMLDecl
+ super()
+ @version = version.version
+ self.encoding = version.encoding
@writeencoding = version.writeencoding
- @standalone = version.standalone
- else
- super()
- @version = version
- self.encoding = encoding
- @standalone = standalone
- end
- @version = DEFAULT_VERSION if @version.nil?
- end
+ @standalone = version.standalone
+ else
+ super()
+ @version = version
+ self.encoding = encoding
+ @standalone = standalone
+ end
+ @version = DEFAULT_VERSION if @version.nil?
+ end
- def clone
- XMLDecl.new(self)
- end
+ def clone
+ XMLDecl.new(self)
+ end
# indent::
# Ignored. There must be no whitespace before an XML declaration
@@ -43,35 +43,35 @@
# Ignored
# ie_hack::
# Ignored
- def write(writer, indent=-1, transitive=false, ie_hack=false)
+ def write(writer, indent=-1, transitive=false, ie_hack=false)
return nil unless @writethis or writer.kind_of? Output
- writer << START.sub(/\\/u, '')
+ writer << START.sub(/\\/u, '')
if writer.kind_of? Output
writer << " #{content writer.encoding}"
else
writer << " #{content encoding}"
end
- writer << STOP.sub(/\\/u, '')
- end
+ writer << STOP.sub(/\\/u, '')
+ end
- def ==( other )
- other.kind_of?(XMLDecl) and
- other.version == @version and
- other.encoding == self.encoding and
- other.standalone == @standalone
- end
+ def ==( other )
+ other.kind_of?(XMLDecl) and
+ other.version == @version and
+ other.encoding == self.encoding and
+ other.standalone == @standalone
+ end
- def xmldecl version, encoding, standalone
- @version = version
- self.encoding = encoding
- @standalone = standalone
- end
+ def xmldecl version, encoding, standalone
+ @version = version
+ self.encoding = encoding
+ @standalone = standalone
+ end
- def node_type
- :xmldecl
- end
+ def node_type
+ :xmldecl
+ end
- alias :stand_alone? :standalone
+ alias :stand_alone? :standalone
alias :old_enc= :encoding=
def encoding=( enc )
@@ -108,12 +108,12 @@
START.sub(/\\/u, '') + " ... " + STOP.sub(/\\/u, '')
end
- private
- def content(enc)
- rv = "version='#@version'"
- rv << " encoding='#{enc}'" if @writeencoding || enc !~ /utf-8/i
- rv << " standalone='#@standalone'" if @standalone
- rv
- end
- end
+ private
+ def content(enc)
+ rv = "version='#@version'"
+ rv << " encoding='#{enc}'" if @writeencoding || enc !~ /utf-8/i
+ rv << " standalone='#@standalone'" if @standalone
+ rv
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/xmltokens.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/xmltokens.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/xmltokens.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,18 +1,18 @@
module REXML
- # Defines a number of tokens used for parsing XML. Not for general
- # consumption.
- module XMLTokens
- NCNAME_STR= '[\w:][\-\w\d.]*'
- NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
+ # Defines a number of tokens used for parsing XML. Not for general
+ # consumption.
+ module XMLTokens
+ NCNAME_STR= '[\w:][\-\w\d.]*'
+ NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
- NAMECHAR = '[\-\w\d\.:]'
- NAME = "([\\w:]#{NAMECHAR}*)"
- NMTOKEN = "(?:#{NAMECHAR})+"
- NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*"
- REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)"
+ NAMECHAR = '[\-\w\d\.:]'
+ NAME = "([\\w:]#{NAMECHAR}*)"
+ NMTOKEN = "(?:#{NAMECHAR})+"
+ NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*"
+ REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)"
- #REFERENCE = "(?:#{ENTITYREF}|#{CHARREF})"
- #ENTITYREF = "&#{NAME};"
- #CHARREF = "&#\\d+;|&#x[0-9a-fA-F]+;"
- end
+ #REFERENCE = "(?:#{ENTITYREF}|#{CHARREF})"
+ #ENTITYREF = "&#{NAME};"
+ #CHARREF = "&#\\d+;|&#x[0-9a-fA-F]+;"
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/xpath.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/xpath.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/xpath.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,65 +2,76 @@
require 'rexml/xpath_parser'
module REXML
- # Wrapper class. Use this class to access the XPath functions.
- class XPath
- include Functions
- EMPTY_HASH = {}
+ # Wrapper class. Use this class to access the XPath functions.
+ class XPath
+ include Functions
+ EMPTY_HASH = {}
- # Finds and returns the first node that matches the supplied xpath.
- # element::
- # The context element
- # path::
- # The xpath to search for. If not supplied or nil, returns the first
- # node matching '*'.
- # namespaces::
- # If supplied, a Hash which defines a namespace mapping.
- #
- # XPath.first( node )
- # XPath.first( doc, "//b"} )
- # XPath.first( node, "a/x:b", { "x"=>"http://doofus" } )
+ # Finds and returns the first node that matches the supplied xpath.
+ # element::
+ # The context element
+ # path::
+ # The xpath to search for. If not supplied or nil, returns the first
+ # node matching '*'.
+ # namespaces::
+ # If supplied, a Hash which defines a namespace mapping.
+ # variables::
+ # If supplied, a Hash which maps $variables in the query
+ # to values. This can be used to avoid XPath injection attacks
+ # or to automatically handle escaping string values.
+ #
+ # XPath.first( node )
+ # XPath.first( doc, "//b"} )
+ # XPath.first( node, "a/x:b", { "x"=>"http://doofus" } )
+ # XPath.first( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"})
def XPath::first element, path=nil, namespaces=nil, variables={}
raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash)
raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash)
- parser = XPathParser.new
- parser.namespaces = namespaces
- parser.variables = variables
- path = "*" unless path
- element = [element] unless element.kind_of? Array
- parser.parse(path, element).flatten[0]
- end
+ parser = XPathParser.new
+ parser.namespaces = namespaces
+ parser.variables = variables
+ path = "*" unless path
+ element = [element] unless element.kind_of? Array
+ parser.parse(path, element).flatten[0]
+ end
- # Itterates over nodes that match the given path, calling the supplied
- # block with the match.
- # element::
- # The context element
- # path::
- # The xpath to search for. If not supplied or nil, defaults to '*'
- # namespaces::
- # If supplied, a Hash which defines a namespace mapping
- #
- # XPath.each( node ) { |el| ... }
- # XPath.each( node, '/*[@attr='v']' ) { |el| ... }
- # XPath.each( node, 'ancestor::x' ) { |el| ... }
- def XPath::each element, path=nil, namespaces=nil, variables={}, &block
+ # Iterates over nodes that match the given path, calling the supplied
+ # block with the match.
+ # element::
+ # The context element
+ # path::
+ # The xpath to search for. If not supplied or nil, defaults to '*'
+ # namespaces::
+ # If supplied, a Hash which defines a namespace mapping
+ # variables::
+ # If supplied, a Hash which maps $variables in the query
+ # to values. This can be used to avoid XPath injection attacks
+ # or to automatically handle escaping string values.
+ #
+ # XPath.each( node ) { |el| ... }
+ # XPath.each( node, '/*[@attr='v']' ) { |el| ... }
+ # XPath.each( node, 'ancestor::x' ) { |el| ... }
+ # XPath.each( node, '/book/publisher/text()=$publisher', {}, {"publisher"=>"O'Reilly"}) \
+ # {|el| ... }
+ def XPath::each element, path=nil, namespaces=nil, variables={}, &block
raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.nil? or namespaces.kind_of?(Hash)
raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of?(Hash)
- parser = XPathParser.new
- parser.namespaces = namespaces
- parser.variables = variables
- path = "*" unless path
- element = [element] unless element.kind_of? Array
- parser.parse(path, element).each( &block )
- end
+ parser = XPathParser.new
+ parser.namespaces = namespaces
+ parser.variables = variables
+ path = "*" unless path
+ element = [element] unless element.kind_of? Array
+ parser.parse(path, element).each( &block )
+ end
- # Returns an array of nodes matching a given XPath.
- def XPath::match element, path=nil, namespaces=nil, variables={}
- parser = XPathParser.new
- parser.namespaces = namespaces
- parser.variables = variables
- path = "*" unless path
- element = [element] unless element.kind_of? Array
- parser.parse(path,element)
- end
- end
+ # Returns an array of nodes matching a given XPath.
+ def XPath::match element, path=nil, namespaces=nil, variables={}
+ parser = XPathParser.new
+ parser.namespaces = namespaces
+ parser.variables = variables
+ path = "*" unless path
+ element = [element] unless element.kind_of? Array
+ parser.parse(path,element)
+ end
+ end
end
Modified: MacRuby/branches/experimental/lib/rexml/xpath_parser.rb
===================================================================
--- MacRuby/branches/experimental/lib/rexml/xpath_parser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rexml/xpath_parser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -419,10 +419,10 @@
return @variables[ var_name ]
# :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
- # TODO: Special case for :or and :and -- not evaluate the right
- # operand if the left alone determines result (i.e. is true for
- # :or and false for :and).
- when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
+ # TODO: Special case for :or and :and -- not evaluate the right
+ # operand if the left alone determines result (i.e. is true for
+ # :or and false for :and).
+ when :eq, :neq, :lt, :lteq, :gt, :gteq, :or
left = expr( path_stack.shift, nodeset.dup, context )
#puts "LEFT => #{left.inspect} (#{left.class.name})"
right = expr( path_stack.shift, nodeset.dup, context )
@@ -675,7 +675,7 @@
def equality_relational_compare( set1, op, set2 )
#puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
if set1.kind_of? Array and set2.kind_of? Array
- #puts "#{set1.size} & #{set2.size}"
+ #puts "#{set1.size} & #{set2.size}"
if set1.size == 1 and set2.size == 1
set1 = set1[0]
set2 = set2[0]
@@ -696,7 +696,7 @@
return res
end
end
- #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
+ #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
#puts "COMPARING VALUES"
# If one is nodeset and other is number, compare number to each item
# in nodeset s.t. number op number(string(item))
@@ -705,7 +705,7 @@
# If one is nodeset and other is boolean, compare boolean to each item
# in nodeset s.t. boolean op boolean(item)
if set1.kind_of? Array or set2.kind_of? Array
- #puts "ISA ARRAY"
+ #puts "ISA ARRAY"
if set1.kind_of? Array
a = set1
b = set2
@@ -724,7 +724,7 @@
#puts "B = #{b.inspect}"
return a.collect {|v| compare( Functions::number(v), op, b )}
else
- #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
+ #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
b = Functions::string( b )
return a.collect { |v| compare( Functions::string(v), op, b ) }
end
Modified: MacRuby/branches/experimental/lib/rinda/ring.rb
===================================================================
--- MacRuby/branches/experimental/lib/rinda/ring.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rinda/ring.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -19,7 +19,7 @@
# 1. A RingServer begins listening on the broadcast UDP address.
# 2. A RingFinger sends a UDP packet containing the DRb URI where it will
# listen for a reply.
- # 3. The RingServer recieves the UDP packet and connects back to the
+ # 3. The RingServer receives the UDP packet and connects back to the
# provided DRb URI with the DRb service.
class RingServer
@@ -119,7 +119,7 @@
end
##
- # Contains all discoverd TupleSpaces except for the primary.
+ # Contains all discovered TupleSpaces except for the primary.
def self.to_a
finger.to_a
@@ -256,15 +256,15 @@
$stdin.gets
when 'w'
finger = Rinda::RingFinger.new(nil)
- finger.lookup_ring do |ts|
- p ts
- ts.write([:hello, :world])
+ finger.lookup_ring do |ts2|
+ p ts2
+ ts2.write([:hello, :world])
end
when 'r'
finger = Rinda::RingFinger.new(nil)
- finger.lookup_ring do |ts|
- p ts
- p ts.take([nil, nil])
+ finger.lookup_ring do |ts2|
+ p ts2
+ p ts2.take([nil, nil])
end
end
end
Modified: MacRuby/branches/experimental/lib/rinda/tuplespace.rb
===================================================================
--- MacRuby/branches/experimental/lib/rinda/tuplespace.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rinda/tuplespace.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,6 +2,8 @@
require 'thread'
require 'drb/drb'
require 'rinda/rinda'
+require 'enumerator'
+require 'forwardable'
module Rinda
@@ -286,45 +288,70 @@
# of Tuplespace.
class TupleBag
+ class TupleBin
+ extend Forwardable
+ def_delegators '@bin', :find_all, :delete_if, :each, :empty?
+ def initialize
+ @bin = []
+ end
+
+ def add(tuple)
+ @bin.push(tuple)
+ end
+
+ def delete(tuple)
+ idx = @bin.rindex(tuple)
+ @bin.delete_at(idx) if idx
+ end
+
+ def find(&blk)
+ @bin.reverse_each do |x|
+ return x if yield(x)
+ end
+ nil
+ end
+ end
+
def initialize # :nodoc:
@hash = {}
+ @enum = enum_for(:each_entry)
end
##
# +true+ if the TupleBag to see if it has any expired entries.
def has_expires?
- @hash.each do |k, v|
- v.each do |tuple|
- return true if tuple.expires
- end
+ @enum.find do |tuple|
+ tuple.expires
end
- false
end
##
- # Add +ary+ to the TupleBag.
+ # Add +tuple+ to the TupleBag.
- def push(ary)
- size = ary.size
- @hash[size] ||= []
- @hash[size].push(ary)
+ def push(tuple)
+ key = bin_key(tuple)
+ @hash[key] ||= TupleBin.new
+ @hash[key].add(tuple)
end
##
- # Removes +ary+ from the TupleBag.
+ # Removes +tuple+ from the TupleBag.
- def delete(ary)
- size = ary.size
- @hash.fetch(size, []).delete(ary)
+ def delete(tuple)
+ key = bin_key(tuple)
+ bin = @hash[key]
+ return nil unless bin
+ bin.delete(tuple)
+ @hash.delete(key) if bin.empty?
+ tuple
end
##
# Finds all live tuples that match +template+.
-
def find_all(template)
- @hash.fetch(template.size, []).find_all do |tuple|
+ bin_for_find(template).find_all do |tuple|
tuple.alive? && template.match(tuple)
end
end
@@ -333,7 +360,7 @@
# Finds a live tuple that matches +template+.
def find(template)
- @hash.fetch(template.size, []).find do |tuple|
+ bin_for_find(template).find do |tuple|
tuple.alive? && template.match(tuple)
end
end
@@ -343,7 +370,7 @@
# +tuple+ and are alive.
def find_all_template(tuple)
- @hash.fetch(tuple.size, []).find_all do |template|
+ @enum.find_all do |template|
template.alive? && template.match(tuple)
end
end
@@ -354,20 +381,39 @@
def delete_unless_alive
deleted = []
- @hash.keys.each do |size|
- ary = []
- @hash[size].each do |tuple|
+ @hash.each do |key, bin|
+ bin.delete_if do |tuple|
if tuple.alive?
- ary.push(tuple)
+ false
else
deleted.push(tuple)
+ true
end
end
- @hash[size] = ary
end
deleted
end
+ private
+ def each_entry(&blk)
+ @hash.each do |k, v|
+ v.each(&blk)
+ end
+ end
+
+ def bin_key(tuple)
+ head = tuple[0]
+ if head.class == Symbol
+ return head
+ else
+ false
+ end
+ end
+
+ def bin_for_find(template)
+ key = bin_key(template)
+ key ? @hash.fetch(key, []) : @enum
+ end
end
##
@@ -403,8 +449,7 @@
# Adds +tuple+
def write(tuple, sec=nil)
- entry = TupleEntry.new(tuple, sec)
- start_keeper
+ entry = create_entry(tuple, sec)
synchronize do
if entry.expired?
@read_waiter.find_all_template(entry).each do |template|
@@ -414,6 +459,7 @@
notify_event('delete', entry.value)
else
@bag.push(entry)
+ start_keeper if entry.expires
@read_waiter.find_all_template(entry).each do |template|
template.read(tuple)
end
@@ -439,7 +485,6 @@
def move(port, tuple, sec=nil)
template = WaitTemplateEntry.new(self, tuple, sec)
yield(template) if block_given?
- start_keeper
synchronize do
entry = @bag.find(template)
if entry
@@ -452,6 +497,7 @@
begin
@take_waiter.push(template)
+ start_keeper if template.expires
while true
raise RequestCanceledError if template.canceled?
raise RequestExpiredError if template.expired?
@@ -476,7 +522,6 @@
def read(tuple, sec=nil)
template = WaitTemplateEntry.new(self, tuple, sec)
yield(template) if block_given?
- start_keeper
synchronize do
entry = @bag.find(template)
return entry.value if entry
@@ -484,6 +529,7 @@
begin
@read_waiter.push(template)
+ start_keeper if template.expires
template.wait
raise RequestCanceledError if template.canceled?
raise RequestExpiredError if template.expired?
@@ -529,6 +575,10 @@
private
+ def create_entry(tuple, sec)
+ TupleEntry.new(tuple, sec)
+ end
+
##
# Removes dead tuples.
@@ -566,9 +616,12 @@
def start_keeper
return if @keeper && @keeper.alive?
@keeper = Thread.new do
- while need_keeper?
- keep_clean
+ while true
sleep(@period)
+ synchronize do
+ break unless need_keeper?
+ keep_clean
+ end
end
end
end
Modified: MacRuby/branches/experimental/lib/rss/converter.rb
===================================================================
--- MacRuby/branches/experimental/lib/rss/converter.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rss/converter.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -7,6 +7,10 @@
include Utils
def initialize(to_enc, from_enc=nil)
+ if "".respond_to?(:encode)
+ @to_encoding = to_enc
+ return
+ end
normalized_to_enc = to_enc.downcase.gsub(/-/, '_')
from_enc ||= 'utf-8'
normalized_from_enc = from_enc.downcase.gsub(/-/, '_')
@@ -23,7 +27,11 @@
end
def convert(value)
- value
+ if value.is_a?(String) and value.respond_to?(:encode)
+ value.encode(@to_encoding)
+ else
+ value
+ end
end
def def_convert(depth=0)
Modified: MacRuby/branches/experimental/lib/rss/maker/base.rb
===================================================================
--- MacRuby/branches/experimental/lib/rss/maker/base.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rss/maker/base.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -31,7 +31,9 @@
self::OTHER_ELEMENTS << variable_name
end
- def add_need_initialize_variable(variable_name, init_value="nil")
+ def add_need_initialize_variable(variable_name, init_value=nil,
+ &init_block)
+ init_value ||= init_block
self::NEED_INITIALIZE_VARIABLES << [variable_name, init_value]
end
@@ -45,7 +47,7 @@
def_delegators("@#{plural}", :push, :pop, :shift, :unshift)
def_delegators("@#{plural}", :each, :size, :empty?, :clear)
- add_need_initialize_variable(plural, "[]")
+ add_need_initialize_variable(plural) {[]}
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
def new_#{name}
@@ -74,7 +76,9 @@
def def_classed_element_without_accessor(name, class_name=nil)
class_name ||= Utils.to_class_name(name)
add_other_element(name)
- add_need_initialize_variable(name, "make_#{name}")
+ add_need_initialize_variable(name) do |object|
+ object.send("make_#{name}")
+ end
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
private
def setup_#{name}(feed, current)
@@ -185,7 +189,19 @@
private
def initialize_variables
self.class.need_initialize_variables.each do |variable_name, init_value|
- instance_eval("@#{variable_name} = #{init_value}", __FILE__, __LINE__)
+ if init_value.nil?
+ value = nil
+ else
+ if init_value.respond_to?(:call)
+ value = init_value.call(self)
+ elsif init_value.is_a?(String)
+ # just for backward compatibility
+ value = instance_eval(init_value, __FILE__, __LINE__)
+ else
+ value = init_value
+ end
+ end
+ instance_variable_set("@#{variable_name}", value)
end
end
@@ -238,7 +254,8 @@
def variables
self.class.need_initialize_variables.find_all do |name, init|
- "nil" == init
+ # init == "nil" is just for backward compatibility
+ init.nil? or init == "nil"
end.collect do |name, init|
name
end
@@ -364,7 +381,9 @@
%w(xml_stylesheets channel image items textinput).each do |element|
attr_reader element
- add_need_initialize_variable(element, "make_#{element}")
+ add_need_initialize_variable(element) do |object|
+ object.send("make_#{element}")
+ end
module_eval(<<-EOC, __FILE__, __LINE__)
private
def setup_#{element}(feed)
@@ -392,12 +411,8 @@
end
def make
- if block_given?
- yield(self)
- to_feed
- else
- nil
- end
+ yield(self)
+ to_feed
end
def to_feed
@@ -405,11 +420,8 @@
setup_xml_stylesheets(feed)
setup_elements(feed)
setup_other_elements(feed)
- if feed.valid?
- feed
- else
- nil
- end
+ feed.validate
+ feed
end
private
Modified: MacRuby/branches/experimental/lib/rss/maker/itunes.rb
===================================================================
--- MacRuby/branches/experimental/lib/rss/maker/itunes.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rss/maker/itunes.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -176,7 +176,7 @@
%w(hour minute second).each do |name|
attr_reader(name)
- add_need_initialize_variable(name, '0')
+ add_need_initialize_variable(name, 0)
end
def content=(content)
Modified: MacRuby/branches/experimental/lib/rss/parser.rb
===================================================================
--- MacRuby/branches/experimental/lib/rss/parser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rss/parser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -8,6 +8,10 @@
class NotWellFormedError < Error
attr_reader :line, :element
+
+ # Create a new NotWellFormedError for an error at +line+
+ # in +element+. If a block is given the return value of
+ # the block ends up in the error message.
def initialize(line=nil, element=nil)
message = "This is not well formed XML"
if element or line
@@ -22,15 +26,16 @@
class XMLParserNotFound < Error
def initialize
- super("available XML parser does not found in " <<
+ super("available XML parser was not found in " <<
"#{AVAILABLE_PARSER_LIBRARIES.inspect}.")
end
end
class NotValidXMLParser < Error
def initialize(parser)
- super("#{parser} is not available XML parser. " <<
- "available XML parser is " <<
+ super("#{parser} is not an available XML parser. " <<
+ "Available XML parser" <<
+ (AVAILABLE_PARSERS.size > 1 ? "s are " : " is ") <<
"#{AVAILABLE_PARSERS.inspect}.")
end
end
@@ -56,6 +61,8 @@
@@default_parser || AVAILABLE_PARSERS.first
end
+ # Set @@default_parser to new_value if it is one of the
+ # available parsers. Else raise NotValidXMLParser error.
def default_parser=(new_value)
if AVAILABLE_PARSERS.include?(new_value)
@@default_parser = new_value
@@ -83,6 +90,10 @@
end
private
+
+ # Try to get the XML associated with +rss+.
+ # Return +rss+ if it already looks like XML, or treat it as a URI,
+ # or a file to get the XML,
def normalize_rss(rss)
return rss if maybe_xml?(rss)
@@ -97,10 +108,13 @@
end
end
+ # maybe_xml? tests if source is a string that looks like XML.
def maybe_xml?(source)
source.is_a?(String) and /</ =~ source
end
+ # Attempt to convert rss to a URI, but just return it if
+ # there's a ::URI::Error
def to_uri(rss)
return rss if rss.is_a?(::URI::Generic)
@@ -164,6 +178,7 @@
@@registered_uris = {}
@@class_names = {}
+ # return the setter for the uri, tag_name pair, or nil.
def setter(uri, tag_name)
_getter = getter(uri, tag_name)
if _getter
@@ -177,29 +192,35 @@
(@@accessor_bases[uri] || {})[tag_name]
end
+ # return the tag_names for setters associated with uri
def available_tags(uri)
(@@accessor_bases[uri] || {}).keys
end
+ # register uri against this name.
def register_uri(uri, name)
@@registered_uris[name] ||= {}
@@registered_uris[name][uri] = nil
end
+ # test if this uri is registered against this name
def uri_registered?(uri, name)
@@registered_uris[name].has_key?(uri)
end
+ # record class_name for the supplied uri and tag_name
def install_class_name(uri, tag_name, class_name)
@@class_names[uri] ||= {}
@@class_names[uri][tag_name] = class_name
end
+ # retrieve class_name for the supplied uri and tag_name
+ # If it doesn't exist, capitalize the tag_name
def class_name(uri, tag_name)
name = (@@class_names[uri] || {})[tag_name]
return name if name
- tag_name.gsub!(/[_\-]([a-z]?)/){$1.upcase}
+ tag_name = tag_name.gsub(/[_\-]([a-z]?)/) {$1.upcase}
tag_name[0, 1].upcase + tag_name[1..-1]
end
@@ -213,6 +234,7 @@
end
private
+ # set the accessor for the uri, tag_name pair
def install_accessor_base(uri, tag_name, accessor_base)
@@accessor_bases[uri] ||= {}
@@accessor_bases[uri][tag_name] = accessor_base.chomp("=")
@@ -258,6 +280,7 @@
@last_xml_element = nil
end
+ # set instance vars for version, encoding, standalone
def xmldecl(version, encoding, standalone)
@version, @encoding, @standalone = version, encoding, standalone
end
@@ -350,6 +373,9 @@
end
CONTENT_PATTERN = /\s*([^=]+)=(["'])([^\2]+?)\2/
+ # Extract the first name="value" pair from content.
+ # Works with single quotes according to the constant
+ # CONTENT_PATTERN. Return a Hash.
def parse_pi_content(content)
params = {}
content.scan(CONTENT_PATTERN) do |name, quote, value|
@@ -361,9 +387,7 @@
def start_else_element(local, prefix, attrs, ns)
class_name = self.class.class_name(_ns(ns, prefix), local)
current_class = @last_element.class
- if class_name and
- (current_class.const_defined?(class_name, false) or
- current_class.constants.include?(class_name.to_sym))
+ if known_class?(current_class, class_name)
next_class = current_class.const_get(class_name)
start_have_something_element(local, prefix, attrs, ns, next_class)
else
@@ -379,6 +403,20 @@
end
end
+ if Module.method(:const_defined?).arity == -1
+ def known_class?(target_class, class_name)
+ class_name and
+ (target_class.const_defined?(class_name, false) or
+ target_class.constants.include?(class_name.to_sym))
+ end
+ else
+ def known_class?(target_class, class_name)
+ class_name and
+ (target_class.const_defined?(class_name) or
+ target_class.constants.include?(class_name))
+ end
+ end
+
NAMESPLIT = /^(?:([\w:][-\w\d.]*):)?([\w:][-\w\d.]*)/
def split_name(name)
name =~ NAMESPLIT
Modified: MacRuby/branches/experimental/lib/rss/rss.rb
===================================================================
--- MacRuby/branches/experimental/lib/rss/rss.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rss/rss.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -53,7 +53,7 @@
module RSS
- VERSION = "0.2.4"
+ VERSION = "0.2.5"
URI = "http://purl.org/rss/1.0/"
Modified: MacRuby/branches/experimental/lib/rss/utils.rb
===================================================================
--- MacRuby/branches/experimental/lib/rss/utils.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rss/utils.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,6 +1,8 @@
module RSS
module Utils
module_function
+
+ # Convert a name_with_underscores to CamelCase.
def to_class_name(name)
name.split(/[_\-]/).collect do |part|
"#{part[0, 1].upcase}#{part[1..-1]}"
@@ -14,11 +16,14 @@
[file, line]
end
+ # escape '&', '"', '<' and '>' for use in HTML.
def html_escape(s)
s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<")
end
alias h html_escape
+ # If +value+ is an instance of class +klass+, return it, else
+ # create a new instance of +klass+ with value +value+.
def new_with_value_if_need(klass, value)
if value.is_a?(klass)
value
Modified: MacRuby/branches/experimental/lib/rubygems/command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -342,7 +342,7 @@
add_common_option('-V', '--[no-]verbose',
'Set the verbose level of output') do |value, options|
- # Set us to "really verbose" so the progess meter works
+ # Set us to "really verbose" so the progress meter works
if Gem.configuration.verbose and value then
Gem.configuration.verbose = 1
else
Modified: MacRuby/branches/experimental/lib/rubygems/command_manager.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/command_manager.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/command_manager.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -16,7 +16,7 @@
class CommandManager
include UserInteraction
- # Return the authoratative instance of the command manager.
+ # Return the authoritative instance of the command manager.
def self.instance
@command_manager ||= CommandManager.new
end
@@ -46,6 +46,7 @@
register_command :server
register_command :sources
register_command :specification
+ register_command :stale
register_command :uninstall
register_command :unpack
register_command :update
@@ -69,7 +70,7 @@
@commands.keys.collect {|key| key.to_s}.sort
end
- # Run the config specificed by +args+.
+ # Run the config specified by +args+.
def run(args)
process_args(args)
rescue StandardError, Timeout::Error => ex
Modified: MacRuby/branches/experimental/lib/rubygems/commands/cert_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/cert_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/cert_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -19,7 +19,7 @@
Dir::glob(glob_str) do |path|
begin
cert = OpenSSL::X509::Certificate.new(File.read(path))
- # this could proably be formatted more gracefully
+ # this could probably be formatted more gracefully
say cert.subject.to_s
rescue OpenSSL::X509::CertificateError
next
Modified: MacRuby/branches/experimental/lib/rubygems/commands/check_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/check_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/check_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -31,7 +31,8 @@
def execute
if options[:test]
version = options[:version] || Gem::Requirement.default
- gem_spec = Gem::SourceIndex.from_installed_gems.search(get_one_gem_name, version).first
+ dep = Gem::Dependency.new get_one_gem_name, version
+ gem_spec = Gem::SourceIndex.from_installed_gems.search(dep).first
Gem::Validator.new.unit_test(gem_spec)
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/contents_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/contents_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/contents_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -51,7 +51,7 @@
si = Gem::SourceIndex.from_gems_in(*s)
- gem_spec = si.search(/\A#{gem}\z/, version).last
+ gem_spec = si.find_name(gem, version).last
unless gem_spec then
say "Unable to find gem '#{gem}' in #{path_kind}"
Modified: MacRuby/branches/experimental/lib/rubygems/commands/dependency_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/dependency_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/dependency_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -43,40 +43,77 @@
end
def execute
- options[:args] << '.' if options[:args].empty?
+ options[:args] << '' if options[:args].empty?
specs = {}
- source_indexes = []
+ source_indexes = Hash.new do |h, source_uri|
+ h[source_uri] = Gem::SourceIndex.new
+ end
- if local? then
- source_indexes << Gem::SourceIndex.from_installed_gems
+ pattern = if options[:args].length == 1 and
+ options[:args].first =~ /\A\/(.*)\/(i)?\z/m then
+ flags = $2 ? Regexp::IGNORECASE : nil
+ Regexp.new $1, flags
+ else
+ /\A#{Regexp.union(*options[:args])}/
+ end
+
+ dependency = Gem::Dependency.new pattern, options[:version]
+
+ if options[:reverse_dependencies] and remote? and not local? then
+ alert_error 'Only reverse dependencies for local gems are supported.'
+ terminate_interaction 1
end
- if remote? then
- Gem::SourceInfoCache.cache_data.map do |_, sice|
- source_indexes << sice.source_index
+ if local? then
+ Gem.source_index.search(dependency).each do |spec|
+ source_indexes[:local].add_spec spec
end
end
- options[:args].each do |name|
- new_specs = nil
- source_indexes.each do |source_index|
- new_specs = find_gems(name, source_index)
+ if remote? and not options[:reverse_dependencies] then
+ fetcher = Gem::SpecFetcher.fetcher
+
+ begin
+ fetcher.find_matching(dependency).each do |spec_tuple, source_uri|
+ spec = fetcher.fetch_spec spec_tuple, URI.parse(source_uri)
+
+ source_indexes[source_uri].add_spec spec
+ end
+ rescue Gem::RemoteFetcher::FetchError => e
+ raise unless fetcher.warn_legacy e do
+ require 'rubygems/source_info_cache'
+
+ specs = Gem::SourceInfoCache.search_with_source dependency, false
+
+ specs.each do |spec, source_uri|
+ source_indexes[source_uri].add_spec spec
+ end
+ end
end
+ end
- say "No match found for #{name} (#{options[:version]})" if
- new_specs.empty?
+ if source_indexes.empty? then
+ patterns = options[:args].join ','
+ say "No gems found matching #{patterns} (#{options[:version]})" if
+ Gem.configuration.verbose
- specs = specs.merge new_specs
+ terminate_interaction 1
end
- terminate_interaction 1 if specs.empty?
+ specs = {}
+ source_indexes.values.each do |source_index|
+ source_index.gems.each do |name, spec|
+ specs[spec.full_name] = [source_index, spec]
+ end
+ end
+
reverse = Hash.new { |h, k| h[k] = [] }
if options[:reverse_dependencies] then
- specs.values.each do |source_index, spec|
- reverse[spec.full_name] = find_reverse_dependencies spec, source_index
+ specs.values.each do |_, spec|
+ reverse[spec.full_name] = find_reverse_dependencies spec
end
end
@@ -118,10 +155,10 @@
end
# Retuns list of [specification, dep] that are satisfied by spec.
- def find_reverse_dependencies(spec, source_index)
+ def find_reverse_dependencies(spec)
result = []
- source_index.each do |name, sp|
+ Gem.source_index.each do |name, sp|
sp.dependencies.each do |dep|
dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
@@ -146,5 +183,6 @@
specs
end
+
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/environment_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/environment_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/environment_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -18,6 +18,46 @@
return args.gsub(/^\s+/, '')
end
+ def description # :nodoc:
+ <<-EOF
+The RubyGems environment can be controlled through command line arguments,
+gemrc files, environment variables and built-in defaults.
+
+Command line argument defaults and some RubyGems defaults can be set in
+~/.gemrc file for individual users and a /etc/gemrc for all users. A gemrc
+is a YAML file with the following YAML keys:
+
+ :sources: A YAML array of remote gem repositories to install gems from
+ :verbose: Verbosity of the gem command. false, true, and :really are the
+ levels
+ :update_sources: Enable/disable automatic updating of repository metadata
+ :backtrace: Print backtrace when RubyGems encounters an error
+ :bulk_threshold: Switch to a bulk update when this many sources are out of
+ date (legacy setting)
+ :gempath: The paths in which to look for gems
+ gem_command: A string containing arguments for the specified gem command
+
+Example:
+
+ :verbose: false
+ install: --no-wrappers
+ update: --no-wrappers
+
+RubyGems' default local repository can be overriden with the GEM_PATH and
+GEM_HOME environment variables. GEM_HOME sets the default repository to
+install into. GEM_PATH allows multiple local repositories to be searched for
+gems.
+
+If you are behind a proxy server, RubyGems uses the HTTP_PROXY,
+HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the
+proxy server.
+
+If you are packaging RubyGems all of RubyGems' defaults are in
+lib/rubygems/defaults.rb. You may override these in
+lib/rubygems/defaults/operating_system.rb
+ EOF
+ end
+
def usage # :nodoc:
"#{program_name} [arg]"
end
@@ -51,6 +91,8 @@
out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
+ out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
+
out << " - RUBYGEMS PLATFORMS:\n"
Gem.platforms.each do |platform|
out << " - #{platform}\n"
Modified: MacRuby/branches/experimental/lib/rubygems/commands/fetch_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/fetch_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/fetch_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -33,13 +33,15 @@
def execute
version = options[:version] || Gem::Requirement.default
+ all = Gem::Requirement.default
gem_names = get_all_gem_names
gem_names.each do |gem_name|
dep = Gem::Dependency.new gem_name, version
- specs_and_sources = Gem::SourceInfoCache.search_with_source dep, true
+ specs_and_sources = Gem::SpecFetcher.fetcher.fetch dep, all
+
specs_and_sources.sort_by { |spec,| spec.version }
spec, source_uri = specs_and_sources.last
Modified: MacRuby/branches/experimental/lib/rubygems/commands/help_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/help_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/help_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -20,9 +20,9 @@
gem install --remote rake --test --rdoc --ri
* Install 'rake', but only version 0.3.1, even if dependencies
- are not met, and into a specific directory:
+ are not met, and into a user-specific directory:
- gem install rake --version 0.3.1 --force --install-dir $HOME/.gems
+ gem install rake --version 0.3.1 --force --user-install
* List local gems whose name begins with 'D':
Modified: MacRuby/branches/experimental/lib/rubygems/commands/install_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/install_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/install_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -16,7 +16,6 @@
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:generate_rdoc => true,
:generate_ri => true,
- :install_dir => Gem.dir,
:format_executable => false,
:test => false,
:version => Gem::Requirement.default,
@@ -39,6 +38,19 @@
"--no-test --install-dir #{Gem.dir}"
end
+ def description # :nodoc:
+ <<-EOF
+The install command installs local or remote gem into a gem repository.
+
+For gems with executables ruby installs a wrapper file into the executable
+directory by deault. This can be overridden with the --no-wrappers option.
+The wrapper allows you to choose among alternate gem versions using _version_.
+
+For example `rake _0.7.3_ --version` will run rake version 0.7.3 if a newer
+version is also installed.
+ EOF
+ end
+
def usage # :nodoc:
"#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags"
end
@@ -51,7 +63,7 @@
installed_gems = []
- ENV['GEM_PATH'] = options[:install_dir] # HACK what does this do?
+ ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9'
install_options = {
:env_shebang => options[:env_shebang],
@@ -62,7 +74,8 @@
:install_dir => options[:install_dir],
:security_policy => options[:security_policy],
:wrappers => options[:wrappers],
- :bin_dir => options[:bin_dir]
+ :bin_dir => options[:bin_dir],
+ :development => options[:development],
}
exit_code = 0
@@ -106,6 +119,8 @@
installed_gems.each do |gem|
Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri
end
+
+ Gem::DocManager.update_ri_cache
end
if options[:generate_rdoc] then
Modified: MacRuby/branches/experimental/lib/rubygems/commands/list_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/list_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/list_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,33 +1,35 @@
require 'rubygems/command'
require 'rubygems/commands/query_command'
-module Gem
- module Commands
- class ListCommand < QueryCommand
+##
+# An alternate to Gem::Commands::QueryCommand that searches for gems starting
+# with the the supplied argument.
- def initialize
- super 'list', 'Display gems whose name starts with STRING'
+class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
- remove_option('--name-matches')
- end
+ def initialize
+ super 'list', 'Display gems whose name starts with STRING'
- def arguments # :nodoc:
- "STRING start of gem name to look for"
- end
+ remove_option('--name-matches')
+ end
- def defaults_str # :nodoc:
- "--local --no-details"
- end
+ def arguments # :nodoc:
+ "STRING start of gem name to look for"
+ end
- def usage # :nodoc:
- "#{program_name} [STRING]"
- end
+ def defaults_str # :nodoc:
+ "--local --no-details"
+ end
- def execute
- string = get_one_optional_argument || ''
- options[:name] = /^#{string}/i
- super
- end
- end
+ def usage # :nodoc:
+ "#{program_name} [STRING]"
end
+
+ def execute
+ string = get_one_optional_argument || ''
+ options[:name] = /^#{string}/i
+ super
+ end
+
end
+
Modified: MacRuby/branches/experimental/lib/rubygems/commands/lock_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/lock_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/lock_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -58,15 +58,15 @@
end
def complain(message)
- if options.strict then
- raise message
+ if options[:strict] then
+ raise Gem::Exception, message
else
say "# #{message}"
end
end
def execute
- say 'require "rubygems"'
+ say "require 'rubygems'"
locked = {}
@@ -77,15 +77,20 @@
spec = Gem::SourceIndex.load_specification spec_path(full_name)
+ if spec.nil? then
+ complain "Could not find gem #{full_name}, try using the full name"
+ next
+ end
+
say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name]
locked[spec.name] = true
- spec.dependencies.each do |dep|
+ spec.runtime_dependencies.each do |dep|
next if locked[dep.name]
- candidates = Gem.source_index.search dep.name, dep.requirement_list
+ candidates = Gem.source_index.search dep
if candidates.empty? then
- complain "Unable to satisfy '#{dep}' from currently installed gems."
+ complain "Unable to satisfy '#{dep}' from currently installed gems"
else
pending << candidates.last.full_name
end
@@ -94,7 +99,11 @@
end
def spec_path(gem_full_name)
- File.join Gem.path, "specifications", "#{gem_full_name }.gemspec"
+ gemspecs = Gem.path.map do |path|
+ File.join path, "specifications", "#{gem_full_name}.gemspec"
+ end
+
+ gemspecs.find { |gemspec| File.exist? gemspec }
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/outdated_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/outdated_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/outdated_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,6 +1,6 @@
require 'rubygems/command'
require 'rubygems/local_remote_options'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/version_option'
class Gem::Commands::OutdatedCommand < Gem::Command
@@ -19,9 +19,12 @@
locals = Gem::SourceIndex.from_installed_gems
locals.outdated.sort.each do |name|
- local = locals.search(/^#{name}$/).last
- remotes = Gem::SourceInfoCache.search_with_source(/^#{name}$/, true)
+ local = locals.find_name(name).last
+
+ dep = Gem::Dependency.new local.name, ">= #{local.version}"
+ remotes = Gem::SpecFetcher.fetcher.fetch dep
remote = remotes.last.first
+
say "#{local.name} (#{local.version} < #{remote.version})"
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/pristine_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/pristine_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/pristine_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -57,7 +57,7 @@
end
else
gem_name = get_one_gem_name
- Gem::SourceIndex.from_installed_gems.search(gem_name,
+ Gem::SourceIndex.from_installed_gems.find_name(gem_name,
options[:version])
end
@@ -82,51 +82,10 @@
end
# TODO use installer options
- installer = Gem::Installer.new gem, :wrappers => true
+ installer = Gem::Installer.new gem, :wrappers => true, :force => true
+ installer.install
- gem_file = File.join install_dir, "cache", "#{spec.full_name}.gem"
-
- security_policy = nil # TODO use installer option
-
- format = Gem::Format.from_file_by_path gem_file, security_policy
-
- target_directory = File.join(install_dir, "gems", format.spec.full_name)
- target_directory.untaint
-
- pristine_files = format.file_entries.collect { |data| data[0]["path"] }
- file_map = {}
-
- format.file_entries.each do |entry, file_data|
- file_map[entry["path"]] = file_data
- end
-
- Dir.chdir target_directory do
- deployed_files = Dir.glob(File.join("**", "*")) +
- Dir.glob(File.join("**", ".*"))
-
- pristine_files = pristine_files.map { |f| File.expand_path f }
- deployed_files = deployed_files.map { |f| File.expand_path f }
-
- to_redeploy = (pristine_files - deployed_files)
- to_redeploy = to_redeploy.map { |path| path.untaint}
-
- if to_redeploy.length > 0 then
- say "Restoring #{to_redeploy.length} file#{to_redeploy.length == 1 ? "" : "s"} to #{spec.full_name}..."
-
- to_redeploy.each do |path|
- say " #{path}"
- FileUtils.mkdir_p File.dirname(path)
- File.open(path, "wb") do |out|
- out.write file_map[path]
- end
- end
- else
- say "#{spec.full_name} is in pristine condition"
- end
- end
-
- installer.generate_bin
- installer.build_extensions
+ say "Restored #{spec.full_name}"
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/query_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/query_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/query_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,6 +1,6 @@
require 'rubygems/command'
require 'rubygems/local_remote_options'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/version_option'
class Gem::Commands::QueryCommand < Gem::Command
@@ -59,7 +59,7 @@
if name.source.empty? then
alert_error "You must specify a gem name"
exit_code |= 4
- elsif installed? name.source, options[:version] then
+ elsif installed? name, options[:version] then
say "true"
else
say "false"
@@ -69,28 +69,52 @@
raise Gem::SystemExitException, exit_code
end
+ dep = Gem::Dependency.new name, Gem::Requirement.default
+
if local? then
- say
- say "*** LOCAL GEMS ***"
- say
+ if ui.outs.tty? or both? then
+ say
+ say "*** LOCAL GEMS ***"
+ say
+ end
- output_query_results Gem.source_index.search(name)
+ specs = Gem.source_index.search dep
+
+ spec_tuples = specs.map do |spec|
+ [[spec.name, spec.version, spec.original_platform, spec], :local]
+ end
+
+ output_query_results spec_tuples
end
if remote? then
- say
- say "*** REMOTE GEMS ***"
- say
+ if ui.outs.tty? or both? then
+ say
+ say "*** REMOTE GEMS ***"
+ say
+ end
all = options[:all]
begin
- Gem::SourceInfoCache.cache all
- rescue Gem::RemoteFetcher::FetchError
- # no network
+ fetcher = Gem::SpecFetcher.fetcher
+ spec_tuples = fetcher.find_matching dep, all, false
+ rescue Gem::RemoteFetcher::FetchError => e
+ raise unless fetcher.warn_legacy e do
+ require 'rubygems/source_info_cache'
+
+ dep.name = '' if dep.name == //
+
+ specs = Gem::SourceInfoCache.search_with_source dep, false, all
+
+ spec_tuples = specs.map do |spec, source_uri|
+ [[spec.name, spec.version, spec.original_platform, spec],
+ source_uri]
+ end
+ end
end
- output_query_results Gem::SourceInfoCache.search(name, false, all)
+ output_query_results spec_tuples
end
end
@@ -104,28 +128,30 @@
!Gem.source_index.search(dep).empty?
end
- def output_query_results(gemspecs)
+ def output_query_results(spec_tuples)
output = []
- gem_list_with_version = {}
+ versions = Hash.new { |h,name| h[name] = [] }
- gemspecs.flatten.each do |gemspec|
- gem_list_with_version[gemspec.name] ||= []
- gem_list_with_version[gemspec.name] << gemspec
+ spec_tuples.each do |spec_tuple, source_uri|
+ versions[spec_tuple.first] << [spec_tuple, source_uri]
end
- gem_list_with_version = gem_list_with_version.sort_by do |name, spec|
+ versions = versions.sort_by do |(name,_),_|
name.downcase
end
- gem_list_with_version.each do |gem_name, list_of_matching|
- list_of_matching = list_of_matching.sort_by { |x| x.version.to_ints }.reverse
- seen_versions = {}
+ versions.each do |gem_name, matching_tuples|
+ matching_tuples = matching_tuples.sort_by do |(name, version,_),_|
+ version
+ end.reverse
- list_of_matching.delete_if do |item|
- if seen_versions[item.version] then
+ seen = {}
+
+ matching_tuples.delete_if do |(name, version,_),_|
+ if seen[version] then
true
else
- seen_versions[item.version] = true
+ seen[version] = true
false
end
end
@@ -133,12 +159,50 @@
entry = gem_name.dup
if options[:versions] then
- versions = list_of_matching.map { |s| s.version }.uniq
+ versions = matching_tuples.map { |(name, version,_),_| version }.uniq
entry << " (#{versions.join ', '})"
end
- entry << "\n" << format_text(list_of_matching[0].summary, 68, 4) if
- options[:details]
+ if options[:details] then
+ detail_tuple = matching_tuples.first
+
+ spec = if detail_tuple.first.length == 4 then
+ detail_tuple.first.last
+ else
+ uri = URI.parse detail_tuple.last
+ Gem::SpecFetcher.fetcher.fetch_spec detail_tuple.first, uri
+ end
+
+ entry << "\n"
+ authors = "Author#{spec.authors.length > 1 ? 's' : ''}: "
+ authors << spec.authors.join(', ')
+ entry << format_text(authors, 68, 4)
+
+ if spec.rubyforge_project and not spec.rubyforge_project.empty? then
+ rubyforge = "Rubyforge: http://rubyforge.org/projects/#{spec.rubyforge_project}"
+ entry << "\n" << format_text(rubyforge, 68, 4)
+ end
+
+ if spec.homepage and not spec.homepage.empty? then
+ entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
+ end
+
+ if spec.loaded_from then
+ if matching_tuples.length == 1 then
+ loaded_from = File.dirname File.dirname(spec.loaded_from)
+ entry << "\n" << " Installed at: #{loaded_from}"
+ else
+ label = 'Installed at'
+ matching_tuples.each do |(_,version,_,s),|
+ loaded_from = File.dirname File.dirname(s.loaded_from)
+ entry << "\n" << " #{label} (#{version}): #{loaded_from}"
+ label = ' ' * label.length
+ end
+ end
+ end
+
+ entry << "\n\n" << format_text(spec.summary, 68, 4)
+ end
output << entry
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/rdoc_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/rdoc_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/rdoc_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -59,11 +59,15 @@
if specs.empty?
fail "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}"
end
+
if options[:include_ri]
specs.each do |spec|
Gem::DocManager.new(spec).generate_ri
end
+
+ Gem::DocManager.update_ri_cache
end
+
if options[:include_rdoc]
specs.each do |spec|
Gem::DocManager.new(spec).generate_rdoc
@@ -73,6 +77,6 @@
true
end
end
-
+
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/sources_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/sources_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/sources_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,8 @@
+require 'fileutils'
require 'rubygems/command'
require 'rubygems/remote_fetcher'
require 'rubygems/source_info_cache'
-require 'rubygems/source_info_cache_entry'
+require 'rubygems/spec_fetcher'
class Gem::Commands::SourcesCommand < Gem::Command
@@ -21,14 +22,14 @@
options[:remove] = value
end
- add_option '-u', '--update', 'Update source cache' do |value, options|
- options[:update] = value
- end
-
add_option '-c', '--clear-all',
'Remove all sources (clear the cache)' do |value, options|
options[:clear_all] = value
end
+
+ add_option '-u', '--update', 'Update source cache' do |value, options|
+ options[:update] = value
+ end
end
def defaults_str
@@ -36,9 +37,23 @@
end
def execute
- options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update])
+ options[:list] = !(options[:add] ||
+ options[:clear_all] ||
+ options[:remove] ||
+ options[:update])
if options[:clear_all] then
+ path = Gem::SpecFetcher.fetcher.dir
+ FileUtils.rm_rf path
+
+ if not File.exist?(path) then
+ say "*** Removed specs cache ***"
+ elsif not File.writable?(path) then
+ say "*** Unable to remove source cache (write protected) ***"
+ else
+ say "*** Unable to remove source cache ***"
+ end
+
sic = Gem::SourceInfoCache
remove_cache_file 'user', sic.user_cache_file
remove_cache_file 'latest user', sic.latest_user_cache_file
@@ -48,15 +63,10 @@
if options[:add] then
source_uri = options[:add]
+ uri = URI.parse source_uri
- sice = Gem::SourceInfoCacheEntry.new nil, nil
begin
- sice.refresh source_uri, true
-
- Gem::SourceInfoCache.cache_data[source_uri] = sice
- Gem::SourceInfoCache.cache.update
- Gem::SourceInfoCache.cache.flush
-
+ Gem::SpecFetcher.fetcher.load_specs uri, 'specs'
Gem.sources << source_uri
Gem.configuration.write
@@ -64,15 +74,24 @@
rescue URI::Error, ArgumentError
say "#{source_uri} is not a URI"
rescue Gem::RemoteFetcher::FetchError => e
- say "Error fetching #{source_uri}:\n\t#{e.message}"
- end
- end
+ yaml_uri = uri + 'yaml'
+ gem_repo = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri rescue false
- if options[:update] then
- Gem::SourceInfoCache.cache true
- Gem::SourceInfoCache.cache.flush
+ if e.uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ and
+ gem_repo then
- say "source cache successfully updated"
+ alert_warning <<-EOF
+RubyGems 1.2+ index not found for:
+\t#{source_uri}
+
+Will cause RubyGems to revert to legacy indexes, degrading performance.
+ EOF
+
+ say "#{source_uri} added to sources"
+ else
+ say "Error fetching #{source_uri}:\n\t#{e.message}"
+ end
+ end
end
if options[:remove] then
@@ -81,14 +100,6 @@
unless Gem.sources.include? source_uri then
say "source #{source_uri} not present in cache"
else
- begin # HACK figure out how to get the cache w/o update
- Gem::SourceInfoCache.cache
- rescue Gem::RemoteFetcher::FetchError
- end
-
- Gem::SourceInfoCache.cache_data.delete source_uri
- Gem::SourceInfoCache.cache.update
- Gem::SourceInfoCache.cache.flush
Gem.sources.delete source_uri
Gem.configuration.write
@@ -96,6 +107,23 @@
end
end
+ if options[:update] then
+ fetcher = Gem::SpecFetcher.fetcher
+
+ if fetcher.legacy_repos.empty? then
+ Gem.sources.each do |update_uri|
+ update_uri = URI.parse update_uri
+ fetcher.load_specs update_uri, 'specs'
+ fetcher.load_specs update_uri, 'latest_specs'
+ end
+ else
+ Gem::SourceInfoCache.cache true
+ Gem::SourceInfoCache.cache.flush
+ end
+
+ say "source cache successfully updated"
+ end
+
if options[:list] then
say "*** CURRENT SOURCES ***"
say
Modified: MacRuby/branches/experimental/lib/rubygems/commands/specification_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/specification_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/specification_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -40,6 +40,7 @@
def execute
specs = []
gem = get_one_gem_name
+ dep = Gem::Dependency.new gem, options[:version]
if local? then
if File.exist? gem then
@@ -47,14 +48,14 @@
end
if specs.empty? then
- specs.push(*Gem.source_index.search(/\A#{gem}\z/, options[:version]))
+ specs.push(*Gem.source_index.search(dep))
end
end
if remote? then
- Gem::SourceInfoCache.cache_data.each do |_,sice|
- specs.push(*sice.source_index.search(gem, options[:version]))
- end
+ found = Gem::SpecFetcher.fetcher.fetch dep
+
+ specs.push(*found.map { |spec,| spec })
end
if specs.empty? then
Copied: MacRuby/branches/experimental/lib/rubygems/commands/stale_command.rb (from rev 1886, MacRuby/trunk/lib/rubygems/commands/stale_command.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/stale_command.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rubygems/commands/stale_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,27 @@
+require 'rubygems/command'
+
+class Gem::Commands::StaleCommand < Gem::Command
+ def initialize
+ super('stale', 'List gems along with access times')
+ end
+
+ def usage # :nodoc:
+ "#{program_name}"
+ end
+
+ def execute
+ gem_to_atime = {}
+ Gem.source_index.each do |name, spec|
+ Dir["#{spec.full_gem_path}/**/*.*"].each do |file|
+ next if File.directory?(file)
+ stat = File.stat(file)
+ gem_to_atime[name] ||= stat.atime
+ gem_to_atime[name] = stat.atime if gem_to_atime[name] < stat.atime
+ end
+ end
+
+ gem_to_atime.sort_by { |_, atime| atime }.each do |name, atime|
+ say "#{name} at #{atime.strftime '%c'}"
+ end
+ end
+end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/unpack_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/unpack_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/unpack_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -43,7 +43,7 @@
basename = File.basename(path).sub(/\.gem$/, '')
target_dir = File.expand_path File.join(options[:target], basename)
FileUtils.mkdir_p target_dir
- Gem::Installer.new(path).unpack target_dir
+ Gem::Installer.new(path, :unpack => true).unpack target_dir
say "Unpacked gem: '#{target_dir}'"
else
alert_error "Gem '#{gemname}' not installed."
@@ -68,7 +68,7 @@
def get_path(gemname, version_req)
return gemname if gemname =~ /\.gem$/i
- specs = Gem::source_index.search(/\A#{gemname}\z/, version_req)
+ specs = Gem::source_index.find_name gemname, version_req
selected = specs.sort_by { |s| s.version }.last
Modified: MacRuby/branches/experimental/lib/rubygems/commands/update_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/update_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/update_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,7 +2,7 @@
require 'rubygems/command_manager'
require 'rubygems/install_update_options'
require 'rubygems/local_remote_options'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/version_option'
require 'rubygems/commands/install_command'
@@ -15,11 +15,10 @@
def initialize
super 'update',
'Update the named gems (or all installed gems) in the local repository',
- :generate_rdoc => true,
- :generate_ri => true,
- :force => false,
- :test => false,
- :install_dir => Gem.dir
+ :generate_rdoc => true,
+ :generate_ri => true,
+ :force => false,
+ :test => false
add_install_update_options
@@ -46,6 +45,8 @@
end
def execute
+ hig = {}
+
if options[:system] then
say "Updating RubyGems"
@@ -53,29 +54,26 @@
fail "No gem names are allowed with the --system option"
end
- options[:args] = ["rubygems-update"]
+ rubygems_update = Gem::Specification.new
+ rubygems_update.name = 'rubygems-update'
+ rubygems_update.version = Gem::Version.new Gem::RubyGemsVersion
+ hig['rubygems-update'] = rubygems_update
+
+ options[:user_install] = false
else
say "Updating installed gems"
- end
- hig = {} # highest installed gems
+ hig = {} # highest installed gems
- Gem::SourceIndex.from_installed_gems.each do |name, spec|
- if hig[spec.name].nil? or hig[spec.name].version < spec.version then
- hig[spec.name] = spec
+ Gem.source_index.each do |name, spec|
+ if hig[spec.name].nil? or hig[spec.name].version < spec.version then
+ hig[spec.name] = spec
+ end
end
end
- pattern = if options[:args].empty? then
- //
- else
- Regexp.union(*options[:args])
- end
+ gems_to_update = which_to_update hig, options[:args]
- remote_gemspecs = Gem::SourceInfoCache.search pattern
-
- gems_to_update = which_to_update hig, remote_gemspecs
-
updated = []
installer = Gem::DependencyInstaller.new options
@@ -93,15 +91,15 @@
end
if gems_to_update.include? "rubygems-update" then
- latest_ruby_gem = remote_gemspecs.select do |s|
- s.name == 'rubygems-update'
- end
+ Gem.source_index.refresh!
- latest_ruby_gem = latest_ruby_gem.sort_by { |s| s.version }.last
+ update_gems = Gem.source_index.search 'rubygems-update'
- say "Updating version of RubyGems to #{latest_ruby_gem.version}"
- installed = do_rubygems_update latest_ruby_gem.version
+ latest_update_gem = update_gems.sort_by { |s| s.version }.last
+ say "Updating RubyGems to #{latest_update_gem.version}"
+ installed = do_rubygems_update latest_update_gem.version
+
say "RubyGems system software updated" if installed
else
if updated.empty? then
@@ -112,6 +110,9 @@
end
end
+ ##
+ # Update the RubyGems software to +version+.
+
def do_rubygems_update(version)
args = []
args.push '--prefix', Gem.prefix unless Gem.prefix.nil?
@@ -121,8 +122,6 @@
update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}"
- success = false
-
Dir.chdir update_dir do
say "Installing RubyGems #{version}"
setup_cmd = "#{Gem.ruby} setup.rb #{args.join ' '}"
@@ -135,20 +134,42 @@
end
end
- def which_to_update(highest_installed_gems, remote_gemspecs)
+ def which_to_update(highest_installed_gems, gem_names)
result = []
highest_installed_gems.each do |l_name, l_spec|
- matching_gems = remote_gemspecs.select do |spec|
- spec.name == l_name and Gem.platforms.any? do |platform|
- platform == spec.platform
+ next if not gem_names.empty? and
+ gem_names.all? { |name| /#{name}/ !~ l_spec.name }
+
+ dependency = Gem::Dependency.new l_spec.name, "> #{l_spec.version}"
+
+ begin
+ fetcher = Gem::SpecFetcher.fetcher
+ spec_tuples = fetcher.find_matching dependency
+ rescue Gem::RemoteFetcher::FetchError => e
+ raise unless fetcher.warn_legacy e do
+ require 'rubygems/source_info_cache'
+
+ dependency.name = '' if dependency.name == //
+
+ specs = Gem::SourceInfoCache.search_with_source dependency
+
+ spec_tuples = specs.map do |spec, source_uri|
+ [[spec.name, spec.version, spec.original_platform], source_uri]
+ end
end
end
- highest_remote_gem = matching_gems.sort_by { |spec| spec.version }.last
+ matching_gems = spec_tuples.select do |(name, version, platform),|
+ name == l_name and Gem::Platform.match platform
+ end
+ highest_remote_gem = matching_gems.sort_by do |(name, version),|
+ version
+ end.last
+
if highest_remote_gem and
- l_spec.version < highest_remote_gem.version then
+ l_spec.version < highest_remote_gem.first[1] then
result << l_name
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/commands/which_command.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/commands/which_command.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/commands/which_command.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,10 +3,10 @@
class Gem::Commands::WhichCommand < Gem::Command
- EXT = %w[.rb .rbw .so .dll] # HACK
+ EXT = %w[.rb .rbw .so .dll .bundle] # HACK
def initialize
- super 'which', 'Find the location of a library',
+ super 'which', 'Find the location of a library file you can require',
:search_gems_first => false, :show_all => false
add_option '-a', '--[no-]all', 'show all matching files' do |show_all, options|
@@ -52,7 +52,7 @@
paths = find_paths arg, dirs
if paths.empty? then
- say "Can't find #{arg}"
+ say "Can't find ruby library file or shared library #{arg}"
else
say paths
end
@@ -84,3 +84,4 @@
end
end
+
Modified: MacRuby/branches/experimental/lib/rubygems/config_file.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/config_file.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/config_file.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -18,9 +18,42 @@
DEFAULT_VERBOSITY = true
DEFAULT_UPDATE_SOURCES = true
+ ##
+ # For Ruby packagers to set configuration defaults. Set in
+ # rubygems/defaults/operating_system.rb
+
+ OPERATING_SYSTEM_DEFAULTS = {}
+
+ ##
+ # For Ruby implementers to set configuration defaults. Set in
+ # rubygems/defaults/#{RUBY_ENGINE}.rb
+
+ PLATFORM_DEFAULTS = {}
+
+ system_config_path =
+ begin
+ require 'Win32API'
+
+ CSIDL_COMMON_APPDATA = 0x0023
+ path = 0.chr * 260
+ SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'LLLLP', 'L'
+ SHGetFolderPath.call 0, CSIDL_COMMON_APPDATA, 0, 1, path
+
+ path.strip
+ rescue LoadError
+ '/etc'
+ end
+
+ SYSTEM_WIDE_CONFIG_FILE = File.join system_config_path, 'gemrc'
+
# List of arguments supplied to the config file object.
attr_reader :args
+ # Where to look for gems
+ attr_accessor :path
+
+ attr_accessor :home
+
# True if we print backtraces on errors.
attr_writer :backtrace
@@ -63,6 +96,7 @@
arg_list = arg_list.map do |arg|
if need_config_file_name then
@config_file_name = arg
+ need_config_file_name = false
nil
elsif arg =~ /^--config-file=(.*)/ then
@config_file_name = $1
@@ -81,30 +115,38 @@
@verbose = DEFAULT_VERBOSITY
@update_sources = DEFAULT_UPDATE_SOURCES
- begin
- # HACK $SAFE ok?
- @hash = open(config_file_name.dup.untaint) {|f| YAML.load(f) }
- rescue ArgumentError
- warn "Failed to load #{config_file_name}"
- rescue Errno::ENOENT
- # Ignore missing config file error.
- rescue Errno::EACCES
- warn "Failed to load #{config_file_name} due to permissions problem."
- end
+ operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
+ platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
+ system_config = load_file SYSTEM_WIDE_CONFIG_FILE
+ user_config = load_file config_file_name.dup.untaint
- @hash ||= {}
+ @hash = operating_system_config.merge platform_config
+ @hash = @hash.merge system_config
+ @hash = @hash.merge user_config
# HACK these override command-line args, which is bad
@backtrace = @hash[:backtrace] if @hash.key? :backtrace
@benchmark = @hash[:benchmark] if @hash.key? :benchmark
@bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
- Gem.sources.replace @hash[:sources] if @hash.key? :sources
+ Gem.sources = @hash[:sources] if @hash.key? :sources
@verbose = @hash[:verbose] if @hash.key? :verbose
@update_sources = @hash[:update_sources] if @hash.key? :update_sources
+ @path = @hash[:gempath] if @hash.key? :gempath
+ @home = @hash[:gemhome] if @hash.key? :gemhome
handle_arguments arg_list
end
+ def load_file(filename)
+ begin
+ YAML.load(File.read(filename)) if filename and File.exist?(filename)
+ rescue ArgumentError
+ warn "Failed to load #{config_file_name}"
+ rescue Errno::EACCES
+ warn "Failed to load #{config_file_name} due to permissions problem."
+ end or {}
+ end
+
# True if the backtrace option has been specified, or debug is on.
def backtrace
@backtrace or $DEBUG
Modified: MacRuby/branches/experimental/lib/rubygems/custom_require.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/custom_require.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/custom_require.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -7,12 +7,16 @@
require 'rubygems'
module Kernel
- alias gem_original_require require # :nodoc:
+ ##
+ # The Kernel#require from before RubyGems was loaded.
+
+ alias gem_original_require require
+
+ ##
+ # When RubyGems is required, Kernel#require is replaced with our own which
+ # is capable of loading gems on demand.
#
- # We replace Ruby's require with our own, which is capable of
- # loading gems on demand.
- #
# When you call <tt>require 'x'</tt>, this is what happens:
# * If the file can be loaded from the existing Ruby loadpath, it
# is.
@@ -22,11 +26,11 @@
#
# The normal <tt>require</tt> functionality of returning false if
# that file has already been loaded is preserved.
- #
- def require(path) # :nodoc:
+
+ def require(path) # :doc:
gem_original_require path
rescue LoadError => load_error
- if load_error.message =~ /\A[Nn]o such file to load -- #{Regexp.escape path}\z/ and
+ if load_error.message =~ /#{Regexp.escape path}\z/ and
spec = Gem.searcher.find(path) then
Gem.activate(spec.name, "= #{spec.version}")
gem_original_require path
@@ -34,5 +38,9 @@
raise load_error
end
end
-end # module Kernel
+ private :require
+ private :gem_original_require
+
+end
+
Modified: MacRuby/branches/experimental/lib/rubygems/defaults.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/defaults.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/defaults.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,36 +1,56 @@
module Gem
- # An Array of the default sources that come with RubyGems.
+ @post_install_hooks ||= []
+ @post_uninstall_hooks ||= []
+ @pre_uninstall_hooks ||= []
+ @pre_install_hooks ||= []
+
+ ##
+ # An Array of the default sources that come with RubyGems
+
def self.default_sources
- %w[http://gems.rubyforge.org]
+ %w[http://gems.rubyforge.org/]
end
+ ##
# Default home directory path to be used if an alternate value is not
- # specified in the environment.
+ # specified in the environment
+
def self.default_dir
if defined? RUBY_FRAMEWORK_VERSION then
File.join File.dirname(ConfigMap[:sitedir]), 'Gems',
ConfigMap[:ruby_version]
- elsif defined? RUBY_ENGINE then
- File.join ConfigMap[:libdir], RUBY_ENGINE, 'gems',
- ConfigMap[:ruby_version]
else
- File.join ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version]
+ ConfigMap[:sitelibdir].sub(%r'/site_ruby/(?=[^/]+)', '/gems/')
end
end
- # Default gem path.
+ ##
+ # Path for gems in the user's home directory
+
+ def self.user_dir
+ File.join(Gem.user_home, '.gem', ruby_engine,
+ ConfigMap[:ruby_version])
+ end
+
+ ##
+ # Default gem load path
+
def self.default_path
- default_dir
+ [user_dir, default_dir]
end
- # Deduce Ruby's --program-prefix and --program-suffix from its install name.
+ ##
+ # Deduce Ruby's --program-prefix and --program-suffix from its install name
+
def self.default_exec_format
baseruby = ConfigMap[:BASERUBY] || 'ruby'
ConfigMap[:RUBY_INSTALL_NAME].sub(baseruby, '%s') rescue '%s'
end
+ ##
# The default directory for binaries
+
def self.default_bindir
if defined? RUBY_FRAMEWORK_VERSION then # mac framework support
'/usr/bin'
@@ -39,15 +59,30 @@
end
end
- # The default system-wide source info cache directory.
+ ##
+ # The default system-wide source info cache directory
+
def self.default_system_source_cache_dir
File.join Gem.dir, 'source_cache'
end
- # The default user-specific source info cache directory.
+ ##
+ # The default user-specific source info cache directory
+
def self.default_user_source_cache_dir
File.join Gem.user_home, '.gem', 'source_cache'
end
+ ##
+ # A wrapper around RUBY_ENGINE const that may not be defined
+
+ def self.ruby_engine
+ if defined? RUBY_ENGINE then
+ RUBY_ENGINE
+ else
+ 'ruby'
+ end
+ end
+
end
Modified: MacRuby/branches/experimental/lib/rubygems/dependency.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/dependency.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/dependency.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -8,24 +8,54 @@
##
# The Dependency class holds a Gem name and a Gem::Requirement
+
class Gem::Dependency
+ ##
+ # Valid dependency types.
+ #--
+ # When this list is updated, be sure to change
+ # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well.
+
+ TYPES = [
+ :development,
+ :runtime,
+ ]
+
+ ##
+ # Dependency name or regular expression.
+
attr_accessor :name
+ ##
+ # Dependency type.
+
+ attr_reader :type
+
+ ##
+ # Dependent versions.
+
attr_writer :version_requirements
+ ##
+ # Orders dependencies by name only.
+
def <=>(other)
[@name] <=> [other.name]
end
##
- # Constructs the dependency
- #
- # name:: [String] name of the Gem
- # version_requirements:: [String Array] version requirement (e.g. ["> 1.2"])
- #
- def initialize(name, version_requirements)
+ # Constructs a dependency with +name+ and +requirements+.
+
+ def initialize(name, version_requirements, type=:runtime)
@name = name
+
+ unless TYPES.include? type
+ raise ArgumentError, "Valid types are #{TYPES.inspect}, not #{@type.inspect}"
+ end
+
+ @type = type
+
@version_requirements = Gem::Requirement.create version_requirements
@version_requirement = nil # Avoid warnings.
end
@@ -48,18 +78,42 @@
end
def to_s # :nodoc:
- "#{name} (#{version_requirements})"
+ "#{name} (#{version_requirements}, #{@type || :runtime})"
end
def ==(other) # :nodoc:
self.class === other &&
self.name == other.name &&
+ self.type == other.type &&
self.version_requirements == other.version_requirements
end
- def hash
- name.hash + version_requirements.hash
+ ##
+ # Uses this dependency as a pattern to compare to the dependency +other+.
+ # This dependency will match if the name matches the other's name, and other
+ # has only an equal version requirement that satisfies this dependency.
+
+ def =~(other)
+ return false unless self.class === other
+
+ pattern = @name
+ pattern = /\A#{@name}\Z/ unless Regexp === pattern
+
+ return false unless pattern =~ other.name
+
+ reqs = other.version_requirements.requirements
+
+ return false unless reqs.length == 1
+ return false unless reqs.first.first == '='
+
+ version = reqs.first.last
+
+ version_requirements.satisfied_by? version
end
+ def hash # :nodoc:
+ name.hash + type.hash + version_requirements.hash
+ end
+
end
Modified: MacRuby/branches/experimental/lib/rubygems/dependency_installer.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/dependency_installer.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/dependency_installer.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,9 +1,12 @@
require 'rubygems'
require 'rubygems/dependency_list'
require 'rubygems/installer'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/user_interaction'
+##
+# Installs a gem along with all its dependencies from local and remote gems.
+
class Gem::DependencyInstaller
include Gem::UserInteraction
@@ -25,36 +28,52 @@
# Creates a new installer instance.
#
# Options are:
- # :env_shebang:: See Gem::Installer::new.
+ # :cache_dir:: Alternate repository path to store .gem files in.
# :domain:: :local, :remote, or :both. :local only searches gems in the
# current directory. :remote searches only gems in Gem::sources.
# :both searches both.
+ # :env_shebang:: See Gem::Installer::new.
# :force:: See Gem::Installer#install.
# :format_executable:: See Gem::Installer#initialize.
- # :ignore_dependencies: Don't install any dependencies.
- # :install_dir: See Gem::Installer#install.
- # :security_policy: See Gem::Installer::new and Gem::Security.
- # :wrappers: See Gem::Installer::new
+ # :ignore_dependencies:: Don't install any dependencies.
+ # :install_dir:: See Gem::Installer#install.
+ # :security_policy:: See Gem::Installer::new and Gem::Security.
+ # :user_install:: See Gem::Installer.new
+ # :wrappers:: See Gem::Installer::new
+
def initialize(options = {})
+ if options[:install_dir] then
+ spec_dir = options[:install_dir], 'specifications'
+ @source_index = Gem::SourceIndex.from_gems_in spec_dir
+ else
+ @source_index = Gem.source_index
+ end
+
options = DEFAULT_OPTIONS.merge options
+
+ @bin_dir = options[:bin_dir]
+ @development = options[:development]
+ @domain = options[:domain]
@env_shebang = options[:env_shebang]
- @domain = options[:domain]
@force = options[:force]
@format_executable = options[:format_executable]
@ignore_dependencies = options[:ignore_dependencies]
- @install_dir = options[:install_dir] || Gem.dir
@security_policy = options[:security_policy]
+ @user_install = options[:user_install]
@wrappers = options[:wrappers]
- @bin_dir = options[:bin_dir]
@installed_gems = []
+
+ @install_dir = options[:install_dir] || Gem.dir
+ @cache_dir = options[:cache_dir] || @install_dir
end
##
# Returns a list of pairs of gemspecs and source_uris that match
# Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
# sources. Gems are sorted with newer gems prefered over older gems, and
- # local gems prefered over remote gems.
+ # local gems preferred over remote gems.
+
def find_gems_with_sources(dep)
gems_and_sources = []
@@ -74,8 +93,7 @@
all = requirements.length > 1 ||
(requirements.first != ">=" and requirements.first != ">")
- found = Gem::SourceInfoCache.search_with_source dep, true, all
-
+ found = Gem::SpecFetcher.fetcher.fetch dep, all
gems_and_sources.push(*found)
rescue Gem::RemoteFetcher::FetchError => e
@@ -95,6 +113,7 @@
##
# Gathers all dependencies necessary for the installation from local and
# remote sources unless the ignore_dependencies was given.
+
def gather_dependencies
specs = @specs_and_sources.map { |spec,_| spec }
@@ -110,14 +129,25 @@
next if spec.nil? or seen[spec.name]
seen[spec.name] = true
- spec.dependencies.each do |dep|
- results = find_gems_with_sources(dep).reverse # local gems first
+ deps = spec.runtime_dependencies
+ deps |= spec.development_dependencies if @development
+ deps.each do |dep|
+ results = find_gems_with_sources(dep).reverse
+
+ results.reject! do |dep_spec,|
+ to_do.push dep_spec
+
+ @source_index.any? do |_, installed_spec|
+ dep.name == installed_spec.name and
+ dep.version_requirements.satisfied_by? installed_spec.version
+ end
+ end
+
results.each do |dep_spec, source_uri|
next if seen[dep_spec.name]
@specs_and_sources << [dep_spec, source_uri]
dependency_list.add dep_spec
- to_do.push dep_spec
end
end
end
@@ -126,6 +156,11 @@
@gems_to_install = dependency_list.dependency_order.reverse
end
+ ##
+ # Finds a spec and the source_uri it came from for gem +gem_name+ and
+ # +version+. Returns an Array of specs and sources required for
+ # installation of the gem.
+
def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default
spec_and_source = nil
@@ -160,14 +195,16 @@
if spec_and_source.nil? then
raise Gem::GemNotFoundException,
- "could not find #{gem_name} locally or in a repository"
+ "could not find gem #{gem_name} locally or in a repository"
end
@specs_and_sources = [spec_and_source]
end
##
- # Installs the gem and all its dependencies.
+ # Installs the gem and all its dependencies. Returns an Array of installed
+ # gems specifications.
+
def install dep_or_name, version = Gem::Requirement.default
if String === dep_or_name then
find_spec_by_name_and_version dep_or_name, version
@@ -175,15 +212,14 @@
@specs_and_sources = [find_gems_with_sources(dep_or_name).last]
end
+ @installed_gems = []
+
gather_dependencies
- spec_dir = File.join @install_dir, 'specifications'
- source_index = Gem::SourceIndex.from_gems_in spec_dir
-
@gems_to_install.each do |spec|
last = spec == @gems_to_install.last
# HACK is this test for full_name acceptable?
- next if source_index.any? { |n,_| n == spec.full_name } and not last
+ next if @source_index.any? { |n,_| n == spec.full_name } and not last
# TODO: make this sorta_verbose so other users can benefit from it
say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
@@ -191,26 +227,31 @@
_, source_uri = @specs_and_sources.assoc spec
begin
local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
- @install_dir
+ @cache_dir
rescue Gem::RemoteFetcher::FetchError
next if @force
raise
end
inst = Gem::Installer.new local_gem_path,
- :env_shebang => @env_shebang,
- :force => @force,
- :format_executable => @format_executable,
+ :bin_dir => @bin_dir,
+ :development => @development,
+ :env_shebang => @env_shebang,
+ :force => @force,
+ :format_executable => @format_executable,
:ignore_dependencies => @ignore_dependencies,
- :install_dir => @install_dir,
- :security_policy => @security_policy,
- :wrappers => @wrappers,
- :bin_dir => @bin_dir
+ :install_dir => @install_dir,
+ :security_policy => @security_policy,
+ :source_index => @source_index,
+ :user_install => @user_install,
+ :wrappers => @wrappers
spec = inst.install
@installed_gems << spec
end
+
+ @installed_gems
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/dependency_list.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/dependency_list.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/dependency_list.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -69,7 +69,7 @@
# Are all the dependencies in the list satisfied?
def ok?
@specs.all? do |spec|
- spec.dependencies.all? do |dep|
+ spec.runtime_dependencies.all? do |dep|
@specs.find { |s| s.satisfies_requirement? dep }
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/doc_manager.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/doc_manager.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/doc_manager.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -5,133 +5,195 @@
#++
require 'fileutils'
+require 'rubygems'
-module Gem
+##
+# The documentation manager generates RDoc and RI for RubyGems.
- class DocManager
-
- include UserInteraction
-
- # Create a document manager for the given gem spec.
- #
- # spec:: The Gem::Specification object representing the gem.
- # rdoc_args:: Optional arguments for RDoc (template etc.) as a String.
- #
- def initialize(spec, rdoc_args="")
- @spec = spec
- @doc_dir = File.join(spec.installation_path, "doc", spec.full_name)
- @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
+class Gem::DocManager
+
+ include Gem::UserInteraction
+
+ @configured_args = []
+
+ def self.configured_args
+ @configured_args ||= []
+ end
+
+ def self.configured_args=(args)
+ case args
+ when Array
+ @configured_args = args
+ when String
+ @configured_args = args.split
end
-
- # Is the RDoc documentation installed?
- def rdoc_installed?
- return File.exist?(File.join(@doc_dir, "rdoc"))
+ end
+
+ ##
+ # Load RDoc from a gem if it is available, otherwise from Ruby's stdlib
+
+ def self.load_rdoc
+ begin
+ gem 'rdoc'
+ rescue Gem::LoadError
+ # use built-in RDoc
end
-
- # Generate the RI documents for this gem spec.
- #
- # Note that if both RI and RDoc documents are generated from the
- # same process, the RI docs should be done first (a likely bug in
- # RDoc will cause RI docs generation to fail if run after RDoc).
- def generate_ri
- if @spec.has_rdoc then
- load_rdoc
- install_ri # RDoc bug, ri goes first
- end
- FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
+ begin
+ require 'rdoc/rdoc'
+ rescue LoadError => e
+ raise Gem::DocumentError,
+ "ERROR: RDoc documentation generator not installed!"
end
+ end
- # Generate the RDoc documents for this gem spec.
- #
- # Note that if both RI and RDoc documents are generated from the
- # same process, the RI docs should be done first (a likely bug in
- # RDoc will cause RI docs generation to fail if run after RDoc).
- def generate_rdoc
- if @spec.has_rdoc then
- load_rdoc
- install_rdoc
- end
+ ##
+ # Updates the RI cache for RDoc 2 if it is installed
- FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
- end
+ def self.update_ri_cache
+ load_rdoc rescue return
- # Load the RDoc documentation generator library.
- def load_rdoc
- if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then
- raise Gem::FilePermissionError.new(@doc_dir)
- end
+ return unless defined? RDoc::VERSION # RDoc 1 does not have VERSION
- FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
+ require 'rdoc/ri/driver'
- begin
- gem 'rdoc'
- rescue Gem::LoadError
- # use built-in RDoc
- end
+ options = {
+ :use_cache => true,
+ :use_system => true,
+ :use_site => true,
+ :use_home => true,
+ :use_gems => true,
+ :formatter => RDoc::RI::Formatter,
+ }
- begin
- require 'rdoc/rdoc'
- rescue LoadError => e
- raise Gem::DocumentError,
- "ERROR: RDoc documentation generator not installed!"
- end
- end
+ driver = RDoc::RI::Driver.new(options).class_cache
+ end
- def install_rdoc
- rdoc_dir = File.join @doc_dir, 'rdoc'
+ ##
+ # Create a document manager for +spec+. +rdoc_args+ contains arguments for
+ # RDoc (template etc.) as a String.
- FileUtils.rm_rf rdoc_dir
+ def initialize(spec, rdoc_args="")
+ @spec = spec
+ @doc_dir = File.join(spec.installation_path, "doc", spec.full_name)
+ @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
+ end
- say "Installing RDoc documentation for #{@spec.full_name}..."
- run_rdoc '--op', rdoc_dir
+ ##
+ # Is the RDoc documentation installed?
+
+ def rdoc_installed?
+ File.exist?(File.join(@doc_dir, "rdoc"))
+ end
+
+ ##
+ # Generate the RI documents for this gem spec.
+ #
+ # Note that if both RI and RDoc documents are generated from the same
+ # process, the RI docs should be done first (a likely bug in RDoc will cause
+ # RI docs generation to fail if run after RDoc).
+
+ def generate_ri
+ if @spec.has_rdoc then
+ setup_rdoc
+ install_ri # RDoc bug, ri goes first
end
- def install_ri
- ri_dir = File.join @doc_dir, 'ri'
+ FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
+ end
- FileUtils.rm_rf ri_dir
+ ##
+ # Generate the RDoc documents for this gem spec.
+ #
+ # Note that if both RI and RDoc documents are generated from the same
+ # process, the RI docs should be done first (a likely bug in RDoc will cause
+ # RI docs generation to fail if run after RDoc).
- say "Installing ri documentation for #{@spec.full_name}..."
- run_rdoc '--ri', '--op', ri_dir
+ def generate_rdoc
+ if @spec.has_rdoc then
+ setup_rdoc
+ install_rdoc
end
- def run_rdoc(*args)
- args << @spec.rdoc_options
- args << DocManager.configured_args
- args << '--quiet'
- args << @spec.require_paths.clone
- args << @spec.extra_rdoc_files
- args.flatten!
+ FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
+ end
- r = RDoc::RDoc.new
+ ##
+ # Generate and install RDoc into the documentation directory
- old_pwd = Dir.pwd
- Dir.chdir(@spec.full_gem_path)
- begin
- r.document args
- rescue Errno::EACCES => e
- dirname = File.dirname e.message.split("-")[1].strip
- raise Gem::FilePermissionError.new(dirname)
- rescue RuntimeError => ex
- alert_error "While generating documentation for #{@spec.full_name}"
- ui.errs.puts "... MESSAGE: #{ex}"
- ui.errs.puts "... RDOC args: #{args.join(' ')}"
- ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if
- Gem.configuration.backtrace
- ui.errs.puts "(continuing with the rest of the installation)"
- ensure
- Dir.chdir(old_pwd)
- end
+ def install_rdoc
+ rdoc_dir = File.join @doc_dir, 'rdoc'
+
+ FileUtils.rm_rf rdoc_dir
+
+ say "Installing RDoc documentation for #{@spec.full_name}..."
+ run_rdoc '--op', rdoc_dir
+ end
+
+ ##
+ # Generate and install RI into the documentation directory
+
+ def install_ri
+ ri_dir = File.join @doc_dir, 'ri'
+
+ FileUtils.rm_rf ri_dir
+
+ say "Installing ri documentation for #{@spec.full_name}..."
+ run_rdoc '--ri', '--op', ri_dir
+ end
+
+ ##
+ # Run RDoc with +args+, which is an ARGV style argument list
+
+ def run_rdoc(*args)
+ args << @spec.rdoc_options
+ args << self.class.configured_args
+ args << '--quiet'
+ args << @spec.require_paths.clone
+ args << @spec.extra_rdoc_files
+ args = args.flatten.map do |arg| arg.to_s end
+
+ r = RDoc::RDoc.new
+
+ old_pwd = Dir.pwd
+ Dir.chdir(@spec.full_gem_path)
+ begin
+ r.document args
+ rescue Errno::EACCES => e
+ dirname = File.dirname e.message.split("-")[1].strip
+ raise Gem::FilePermissionError.new(dirname)
+ rescue RuntimeError => ex
+ alert_error "While generating documentation for #{@spec.full_name}"
+ ui.errs.puts "... MESSAGE: #{ex}"
+ ui.errs.puts "... RDOC args: #{args.join(' ')}"
+ ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if
+ Gem.configuration.backtrace
+ ui.errs.puts "(continuing with the rest of the installation)"
+ ensure
+ Dir.chdir(old_pwd)
end
+ end
- def uninstall_doc
- raise Gem::FilePermissionError.new(@spec.installation_path) unless
- File.writable? @spec.installation_path
+ def setup_rdoc
+ if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then
+ raise Gem::FilePermissionError.new(@doc_dir)
+ end
- original_name = [
- @spec.name, @spec.version, @spec.original_platform].join '-'
+ FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
+ self.class.load_rdoc
+ end
+
+ ##
+ # Remove RDoc and RI documentation
+
+ def uninstall_doc
+ raise Gem::FilePermissionError.new(@spec.installation_path) unless
+ File.writable? @spec.installation_path
+
+ original_name = [
+ @spec.name, @spec.version, @spec.original_platform].join '-'
+
doc_dir = File.join @spec.installation_path, 'doc', @spec.full_name
unless File.directory? doc_dir then
doc_dir = File.join @spec.installation_path, 'doc', original_name
@@ -146,22 +208,7 @@
end
FileUtils.rm_rf ri_dir
- end
+ end
- class << self
- def configured_args
- @configured_args ||= []
- end
+end
- def configured_args=(args)
- case args
- when Array
- @configured_args = args
- when String
- @configured_args = args.split
- end
- end
- end
-
- end
-end
Modified: MacRuby/branches/experimental/lib/rubygems/ext/builder.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/ext/builder.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/ext/builder.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -35,7 +35,7 @@
results << `#{cmd} #{redirector}`
raise Gem::InstallError, "make#{target} failed:\n\n#{results}" unless
- $?.exitstatus.zero?
+ $?.success?
end
end
@@ -47,7 +47,7 @@
results << command
results << `#{command} #{redirector}`
- unless $?.exitstatus.zero? then
+ unless $?.success? then
raise Gem::InstallError, "#{class_name} failed:\n\n#{results.join "\n"}"
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/ext/rake_builder.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/ext/rake_builder.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/ext/rake_builder.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -16,7 +16,7 @@
end
cmd = ENV['rake'] || 'rake'
- cmd << " RUBYARCHDIR=#{dest_path} RUBYLIBDIR=#{dest_path}"
+ cmd += " RUBYARCHDIR=#{dest_path} RUBYLIBDIR=#{dest_path}" # ENV is frozen
run cmd, results
Modified: MacRuby/branches/experimental/lib/rubygems/gem_openssl.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/gem_openssl.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/gem_openssl.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -17,7 +17,7 @@
@ssl_available
end
- # Set the value of the ssl_avilable flag.
+ # Set the value of the ssl_available flag.
attr_writer :ssl_available
# Ensure that SSL is available. Throw an exception if it is not.
Modified: MacRuby/branches/experimental/lib/rubygems/gem_path_searcher.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/gem_path_searcher.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/gem_path_searcher.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,15 +6,15 @@
require 'rubygems'
-#
+##
# GemPathSearcher has the capability to find loadable files inside
# gems. It generates data up front to speed up searches later.
-#
+
class Gem::GemPathSearcher
- #
+ ##
# Initialise the data we need to make searches later.
- #
+
def initialize
# We want a record of all the installed gemspecs, in the order
# we wish to examine them.
@@ -27,7 +27,7 @@
end
end
- #
+ ##
# Look in all the installed gems until a matching _path_ is found.
# Return the _gemspec_ of the gem where it was found. If no match
# is found, return nil.
@@ -46,36 +46,52 @@
# others), which may or may not already be attached to _file_.
# This method doesn't care about the full filename that matches;
# only that there is a match.
- #
+
def find(path)
- @gemspecs.each do |spec|
- return spec if matching_file(spec, path)
+ @gemspecs.find do |spec| matching_file? spec, path end
+ end
+
+ ##
+ # Works like #find, but finds all gemspecs matching +path+.
+
+ def find_all(path)
+ @gemspecs.select do |spec|
+ matching_file? spec, path
end
- nil
end
- private
+ ##
+ # Attempts to find a matching path using the require_paths of the given
+ # +spec+.
- # Attempts to find a matching path using the require_paths of the
- # given _spec_.
- #
- # Some of the intermediate results are cached in @lib_dirs for
- # speed.
- def matching_file(spec, path) # :doc:
+ def matching_file?(spec, path)
+ !matching_files(spec, path).empty?
+ end
+
+ ##
+ # Returns files matching +path+ in +spec+.
+ #--
+ # Some of the intermediate results are cached in @lib_dirs for speed.
+
+ def matching_files(spec, path)
glob = File.join @lib_dirs[spec.object_id], "#{path}#{Gem.suffix_pattern}"
- return true unless Dir[glob].select { |f| File.file?(f.untaint) }.empty?
+ Dir[glob].select { |f| File.file? f.untaint }
end
- # Return a list of all installed gemspecs, sorted by alphabetical
- # order and in reverse version order.
+ ##
+ # Return a list of all installed gemspecs, sorted by alphabetical order and
+ # in reverse version order.
+
def init_gemspecs
Gem.source_index.map { |_, spec| spec }.sort { |a,b|
(a.name <=> b.name).nonzero? || (b.version <=> a.version)
}
end
+ ##
# Returns library directories glob for a gemspec. For example,
# '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}'
+
def lib_dirs_for(spec)
"#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}"
end
Modified: MacRuby/branches/experimental/lib/rubygems/indexer.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/indexer.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/indexer.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,5 +1,6 @@
require 'fileutils'
require 'tmpdir'
+require 'zlib'
require 'rubygems'
require 'rubygems/format'
@@ -40,125 +41,324 @@
marshal_name = "Marshal.#{Gem.marshal_version}"
- @master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory
- @marshal_index = Gem::Indexer::MarshalIndexBuilder.new marshal_name, @directory
- @quick_index = Gem::Indexer::QuickIndexBuilder.new 'index', @directory
+ @master_index = File.join @directory, 'yaml'
+ @marshal_index = File.join @directory, marshal_name
- quick_dir = File.join @directory, 'quick'
- @latest_index = Gem::Indexer::LatestIndexBuilder.new 'latest_index', quick_dir
+ @quick_dir = File.join @directory, 'quick'
+
+ @quick_marshal_dir = File.join @quick_dir, marshal_name
+
+ @quick_index = File.join @quick_dir, 'index'
+ @latest_index = File.join @quick_dir, 'latest_index'
+
+ @specs_index = File.join @directory, "specs.#{Gem.marshal_version}"
+ @latest_specs_index = File.join @directory,
+ "latest_specs.#{Gem.marshal_version}"
+
+ files = [
+ @specs_index,
+ "#{@specs_index}.gz",
+ @latest_specs_index,
+ "#{@latest_specs_index}.gz",
+ @quick_dir,
+ @master_index,
+ "#{@master_index}.Z",
+ @marshal_index,
+ "#{@marshal_index}.Z",
+ ]
+
+ @files = files.map do |path|
+ path.sub @directory, ''
+ end
end
##
- # Build the index.
+ # Abbreviate the spec for downloading. Abbreviated specs are only used for
+ # searching, downloading and related activities and do not need deployment
+ # specific information (e.g. list of files). So we abbreviate the spec,
+ # making it much smaller for quicker downloads.
- def build_index
- @master_index.build do
- @quick_index.build do
- @marshal_index.build do
- @latest_index.build do
- progress = ui.progress_reporter gem_file_list.size,
- "Generating index for #{gem_file_list.size} gems in #{@dest_directory}",
- "Loaded all gems"
+ def abbreviate(spec)
+ spec.files = []
+ spec.test_files = []
+ spec.rdoc_options = []
+ spec.extra_rdoc_files = []
+ spec.cert_chain = []
+ spec
+ end
- gem_file_list.each do |gemfile|
- if File.size(gemfile.to_s) == 0 then
- alert_warning "Skipping zero-length gem: #{gemfile}"
- next
- end
+ ##
+ # Build various indicies
- begin
- spec = Gem::Format.from_file_by_path(gemfile).spec
+ def build_indicies(index)
+ progress = ui.progress_reporter index.size,
+ "Generating quick index gemspecs for #{index.size} gems",
+ "Complete"
- unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
- alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
- next
- end
+ index.each do |original_name, spec|
+ spec_file_name = "#{original_name}.gemspec.rz"
+ yaml_name = File.join @quick_dir, spec_file_name
+ marshal_name = File.join @quick_marshal_dir, spec_file_name
- abbreviate spec
- sanitize spec
+ yaml_zipped = Gem.deflate spec.to_yaml
+ open yaml_name, 'wb' do |io| io.write yaml_zipped end
- @master_index.add spec
- @quick_index.add spec
- @marshal_index.add spec
- @latest_index.add spec
+ marshal_zipped = Gem.deflate Marshal.dump(spec)
+ open marshal_name, 'wb' do |io| io.write marshal_zipped end
- progress.updated spec.original_name
+ progress.updated original_name
+ end
- rescue SignalException => e
- alert_error "Received signal, exiting"
- raise
- rescue Exception => e
- alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
- end
- end
+ progress.done
- progress.done
+ say "Generating specs index"
- say "Generating master indexes (this may take a while)"
- end
+ open @specs_index, 'wb' do |io|
+ specs = index.sort.map do |_, spec|
+ platform = spec.original_platform
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
+ [spec.name, spec.version, platform]
+ end
+
+ specs = compact_specs specs
+
+ Marshal.dump specs, io
+ end
+
+ say "Generating latest specs index"
+
+ open @latest_specs_index, 'wb' do |io|
+ specs = index.latest_specs.sort.map do |spec|
+ platform = spec.original_platform
+ platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
+ [spec.name, spec.version, platform]
+ end
+
+ specs = compact_specs specs
+
+ Marshal.dump specs, io
+ end
+
+ say "Generating quick index"
+
+ quick_index = File.join @quick_dir, 'index'
+ open quick_index, 'wb' do |io|
+ io.puts index.sort.map { |_, spec| spec.original_name }
+ end
+
+ say "Generating latest index"
+
+ latest_index = File.join @quick_dir, 'latest_index'
+ open latest_index, 'wb' do |io|
+ io.puts index.latest_specs.sort.map { |spec| spec.original_name }
+ end
+
+ say "Generating Marshal master index"
+
+ open @marshal_index, 'wb' do |io|
+ io.write index.dump
+ end
+
+ progress = ui.progress_reporter index.size,
+ "Generating YAML master index for #{index.size} gems (this may take a while)",
+ "Complete"
+
+ open @master_index, 'wb' do |io|
+ io.puts "--- !ruby/object:#{index.class}"
+ io.puts "gems:"
+
+ gems = index.sort_by { |name, gemspec| gemspec.sort_obj }
+ gems.each do |original_name, gemspec|
+ yaml = gemspec.to_yaml.gsub(/^/, ' ')
+ yaml = yaml.sub(/\A ---/, '') # there's a needed extra ' ' here
+ io.print " #{original_name}:"
+ io.puts yaml
+
+ progress.updated original_name
+ end
+ end
+
+ progress.done
+
+ say "Compressing indicies"
+ # use gzip for future files.
+
+ compress quick_index, 'rz'
+ paranoid quick_index, 'rz'
+
+ compress latest_index, 'rz'
+ paranoid latest_index, 'rz'
+
+ compress @marshal_index, 'Z'
+ paranoid @marshal_index, 'Z'
+
+ compress @master_index, 'Z'
+ paranoid @master_index, 'Z'
+
+ gzip @specs_index
+ gzip @latest_specs_index
+ end
+
+ ##
+ # Collect specifications from .gem files from the gem directory.
+
+ def collect_specs
+ index = Gem::SourceIndex.new
+
+ progress = ui.progress_reporter gem_file_list.size,
+ "Loading #{gem_file_list.size} gems from #{@dest_directory}",
+ "Loaded all gems"
+
+ gem_file_list.each do |gemfile|
+ if File.size(gemfile.to_s) == 0 then
+ alert_warning "Skipping zero-length gem: #{gemfile}"
+ next
+ end
+
+ begin
+ spec = Gem::Format.from_file_by_path(gemfile).spec
+
+ unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
+ alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
+ next
end
+
+ abbreviate spec
+ sanitize spec
+
+ index.gems[spec.original_name] = spec
+
+ progress.updated spec.original_name
+
+ rescue SignalException => e
+ alert_error "Received signal, exiting"
+ raise
+ rescue Exception => e
+ alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
end
end
+
+ progress.done
+
+ index
end
- def install_index
+ ##
+ # Compacts Marshal output for the specs index data source by using identical
+ # objects as much as possible.
+
+ def compact_specs(specs)
+ names = {}
+ versions = {}
+ platforms = {}
+
+ specs.map do |(name, version, platform)|
+ names[name] = name unless names.include? name
+ versions[version] = version unless versions.include? version
+ platforms[platform] = platform unless platforms.include? platform
+
+ [names[name], versions[version], platforms[platform]]
+ end
+ end
+
+ ##
+ # Compress +filename+ with +extension+.
+
+ def compress(filename, extension)
+ data = Gem.read_binary filename
+
+ zipped = Gem.deflate data
+
+ open "#{filename}.#{extension}", 'wb' do |io|
+ io.write zipped
+ end
+ end
+
+ ##
+ # List of gem file names to index.
+
+ def gem_file_list
+ Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
+ end
+
+ ##
+ # Builds and installs indexicies.
+
+ def generate_index
+ make_temp_directories
+ index = collect_specs
+ build_indicies index
+ install_indicies
+ rescue SignalException
+ ensure
+ FileUtils.rm_rf @directory
+ end
+
+ ##
+ # Zlib::GzipWriter wrapper that gzips +filename+ on disk.
+
+ def gzip(filename)
+ Zlib::GzipWriter.open "#{filename}.gz" do |io|
+ io.write Gem.read_binary(filename)
+ end
+ end
+
+ ##
+ # Install generated indicies into the destination directory.
+
+ def install_indicies
verbose = Gem.configuration.really_verbose
say "Moving index into production dir #{@dest_directory}" if verbose
- files = @master_index.files + @quick_index.files + @marshal_index.files +
- @latest_index.files
-
- files.each do |file|
+ @files.each do |file|
src_name = File.join @directory, file
dst_name = File.join @dest_directory, file
FileUtils.rm_rf dst_name, :verbose => verbose
- FileUtils.mv src_name, @dest_directory, :verbose => verbose
+ FileUtils.mv src_name, @dest_directory, :verbose => verbose,
+ :force => true
end
end
- def generate_index
+ ##
+ # Make directories for index generation
+
+ def make_temp_directories
FileUtils.rm_rf @directory
FileUtils.mkdir_p @directory, :mode => 0700
-
- build_index
- install_index
- rescue SignalException
- ensure
- FileUtils.rm_rf @directory
+ FileUtils.mkdir_p @quick_marshal_dir
end
- # List of gem file names to index.
- def gem_file_list
- Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
- end
+ ##
+ # Ensure +path+ and path with +extension+ are identical.
- # Abbreviate the spec for downloading. Abbreviated specs are only
- # used for searching, downloading and related activities and do not
- # need deployment specific information (e.g. list of files). So we
- # abbreviate the spec, making it much smaller for quicker downloads.
- def abbreviate(spec)
- spec.files = []
- spec.test_files = []
- spec.rdoc_options = []
- spec.extra_rdoc_files = []
- spec.cert_chain = []
- spec
+ def paranoid(path, extension)
+ data = Gem.read_binary path
+ compressed_data = Gem.read_binary "#{path}.#{extension}"
+
+ unless data == Gem.inflate(compressed_data) then
+ raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
+ end
end
+ ##
# Sanitize the descriptive fields in the spec. Sometimes non-ASCII
# characters will garble the site index. Non-ASCII characters will
# be replaced by their XML entity equivalent.
+
def sanitize(spec)
spec.summary = sanitize_string(spec.summary)
spec.description = sanitize_string(spec.description)
spec.post_install_message = sanitize_string(spec.post_install_message)
spec.authors = spec.authors.collect { |a| sanitize_string(a) }
+
spec
end
+ ##
# Sanitize a single string.
+
def sanitize_string(string)
# HACK the #to_s is in here because RSpec has an Array of Arrays of
# Strings for authors. Need a way to disallow bad values on gempsec
@@ -168,9 +368,3 @@
end
-require 'rubygems/indexer/abstract_index_builder'
-require 'rubygems/indexer/master_index_builder'
-require 'rubygems/indexer/quick_index_builder'
-require 'rubygems/indexer/marshal_index_builder'
-require 'rubygems/indexer/latest_index_builder'
-
Modified: MacRuby/branches/experimental/lib/rubygems/install_update_options.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/install_update_options.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/install_update_options.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -89,6 +89,19 @@
'foo_exec18') do |value, options|
options[:format_executable] = value
end
+
+ add_option(:"Install/Update", '--[no-]user-install',
+ 'Install in user\'s home directory instead',
+ 'of GEM_HOME. Defaults to using home directory',
+ 'only if GEM_HOME is not writable.') do |value, options|
+ options[:user_install] = value
+ end
+
+ add_option(:"Install/Update", "--development",
+ "Install any additional development",
+ "dependencies") do |value, options|
+ options[:development] = true
+ end
end
# Default options for the gem install command.
Modified: MacRuby/branches/experimental/lib/rubygems/installer.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/installer.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/installer.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -20,6 +20,7 @@
# filesystem including unpacking the gem into its gem dir, installing the
# gemspec in the specifications dir, storing the cached gem in the cache dir,
# and installing either wrappers or symlinks for executables.
+
class Gem::Installer
##
@@ -31,8 +32,36 @@
include Gem::RequirePathsBuilder
+ ##
+ # The directory a gem's executables will be installed into
+
+ attr_reader :bin_dir
+
+ ##
+ # The gem repository the gem will be installed into
+
+ attr_reader :gem_home
+
+ ##
+ # The Gem::Specification for the gem being installed
+
+ attr_reader :spec
+
+ @home_install_warning = false
+ @path_warning = false
+
class << self
+ ##
+ # True if we've warned about ~/.gems install
+
+ attr_accessor :home_install_warning
+
+ ##
+ # True if we've warned about PATH not including Gem.bindir
+
+ attr_accessor :path_warning
+
attr_writer :exec_format
# Defaults to use Ruby's program prefix and suffix.
@@ -56,15 +85,17 @@
# foo_exec18.
# :security_policy:: Use the specified security policy. See Gem::Security
# :wrappers:: Install wrappers if true, symlinks if false.
+
def initialize(gem, options={})
@gem = gem
options = {
- :force => false,
- :install_dir => Gem.dir,
- :exec_format => false,
- :env_shebang => false,
- :bin_dir => nil
+ :bin_dir => nil,
+ :env_shebang => false,
+ :exec_format => false,
+ :force => false,
+ :install_dir => Gem.dir,
+ :source_index => Gem.source_index,
}.merge options
@env_shebang = options[:env_shebang]
@@ -76,6 +107,8 @@
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
@bin_dir = options[:bin_dir]
+ @development = options[:development]
+ @source_index = options[:source_index]
begin
@format = Gem::Format.from_file_by_path @gem, @security_policy
@@ -83,6 +116,43 @@
raise Gem::InstallError, "invalid gem format for #{@gem}"
end
+ begin
+ FileUtils.mkdir_p @gem_home
+ rescue Errno::EACCES, Errno::ENOTDIR
+ # We'll divert to ~/.gem below
+ end
+
+ if not File.writable? @gem_home or
+ # TODO: Shouldn't have to test for existence of bindir; tests need it.
+ (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and
+ not File.writable? Gem.bindir) then
+ if options[:user_install] == false then # You don't want to use ~
+ raise Gem::FilePermissionError, @gem_home
+ elsif options[:user_install].nil? then
+ unless self.class.home_install_warning then
+ alert_warning "Installing to ~/.gem since #{@gem_home} and\n\t #{Gem.bindir} aren't both writable."
+ self.class.home_install_warning = true
+ end
+ end
+ options[:user_install] = true
+ end
+
+ if options[:user_install] and not options[:unpack] then
+ @gem_home = Gem.user_dir
+
+ user_bin_dir = File.join(@gem_home, 'bin')
+ unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then
+ unless self.class.path_warning then
+ alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
+ self.class.path_warning = true
+ end
+ end
+
+ FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
+ # If it's still not writable, you've got issues.
+ raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
+ end
+
@spec = @format.spec
@gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
@@ -98,6 +168,7 @@
# cache/<gem-version>.gem #=> a cached copy of the installed gem
# gems/<gem-version>/... #=> extracted files
# specifications/<gem-version>.gemspec #=> the Gem::Specification
+
def install
# If we're forcing the install then disable security unless the security
# policy says that we only install singed gems.
@@ -119,14 +190,20 @@
end
unless @ignore_dependencies then
- @spec.dependencies.each do |dep_gem|
+ deps = @spec.runtime_dependencies
+ deps |= @spec.development_dependencies if @development
+
+ deps.each do |dep_gem|
ensure_dependency @spec, dep_gem
end
end
end
+ Gem.pre_install_hooks.each do |hook|
+ hook.call self
+ end
+
FileUtils.mkdir_p @gem_home unless File.directory? @gem_home
- raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home
Gem.ensure_gem_subdirectories @gem_home
@@ -150,6 +227,12 @@
@spec.loaded_from = File.join(@gem_home, 'specifications',
"#{@spec.full_name}.gemspec")
+ @source_index.add_spec @spec
+
+ Gem.post_install_hooks.each do |hook|
+ hook.call self
+ end
+
return @spec
rescue Zlib::GzipFile::Error
raise Gem::InstallError, "gzip error installing #{@gem}"
@@ -161,6 +244,7 @@
#
# spec :: Gem::Specification
# dependency :: Gem::Dependency
+
def ensure_dependency(spec, dependency)
unless installation_satisfies_dependency? dependency then
raise Gem::InstallError, "#{spec.name} requires #{dependency}"
@@ -170,17 +254,15 @@
end
##
- # True if the current installed gems satisfy the given dependency.
- #
- # dependency :: Gem::Dependency
+ # True if the gems in the source_index satisfy +dependency+.
+
def installation_satisfies_dependency?(dependency)
- current_index = Gem::SourceIndex.from_installed_gems
- current_index.find_name(dependency.name, dependency.version_requirements).size > 0
+ @source_index.find_name(dependency.name, dependency.version_requirements).size > 0
end
##
# Unpacks the gem into the given directory.
- #
+
def unpack(directory)
@gem_dir = directory
@format = Gem::Format.from_file_by_path @gem, @security_policy
@@ -188,17 +270,15 @@
end
##
- # Writes the .gemspec specification (in Ruby) to the supplied
- # spec_path.
- #
- # spec:: [Gem::Specification] The Gem specification to output
- # spec_path:: [String] The location (path) to write the gemspec to
- #
+ # Writes the .gemspec specification (in Ruby) to the gem home's
+ # specifications directory.
+
def write_spec
rubycode = @spec.to_ruby
file_name = File.join @gem_home, 'specifications',
"#{@spec.full_name}.gemspec"
+
file_name.untaint
File.open(file_name, "w") do |file|
@@ -208,7 +288,7 @@
##
# Creates windows .bat files for easy running of commands
- #
+
def generate_windows_script(bindir, filename)
if Gem.win_platform? then
script_name = filename + ".bat"
@@ -227,7 +307,7 @@
# If the user has asked for the gem to be installed in a directory that is
# the system gem directory, then use the system bin directory, else create
# (or use) a new bin dir under the gem_home.
- bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
+ bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)
Dir.mkdir bindir unless File.exist? bindir
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
@@ -252,7 +332,7 @@
# The Windows script is generated in addition to the regular one due to a
# bug or misfeature in the Windows shell's pipe. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
- #
+
def generate_bin_script(filename, bindir)
bin_script_path = File.join bindir, formatted_program_filename(filename)
@@ -260,6 +340,8 @@
# HACK some gems don't have #! in their executables, restore 2008/06
#if File.read(exec_path, 2) == '#!' then
+ FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
+
File.open bin_script_path, 'w', 0755 do |file|
file.print app_script_text(filename)
end
@@ -277,7 +359,7 @@
##
# Creates the symlinks to run the applications in the gem. Moves
# the symlink if the gem being installed has a newer version.
- #
+
def generate_bin_symlink(filename, bindir)
if Gem.win_platform? then
alert_warning "Unable to use symlinks on Windows, installing wrapper"
@@ -303,6 +385,7 @@
##
# Generates a #! line for +bin_file_name+'s wrapper copying arguments if
# necessary.
+
def shebang(bin_file_name)
if @env_shebang then
"#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
@@ -324,7 +407,9 @@
end
end
+ ##
# Return the text for an application file.
+
def app_script_text(bin_file_name)
<<-TEXT
#{shebang bin_file_name}
@@ -349,7 +434,9 @@
TEXT
end
+ ##
# return the stub script text used to launch the true ruby script
+
def windows_stub_script(bindir, bin_file_name)
<<-TEXT
@ECHO OFF
@@ -361,8 +448,10 @@
TEXT
end
+ ##
# Builds extensions. Valid types of extensions are extconf.rb files,
# configure scripts and rakefiles or mkrf_conf files.
+
def build_extensions
return if @spec.extensions.empty?
say "Building native extensions. This could take a while..."
@@ -418,6 +507,7 @@
# Reads the file index and extracts each file into the gem directory.
#
# Ensures that files can't be installed outside the gem directory.
+
def extract_files
expand_and_validate_gem_dir
@@ -445,11 +535,15 @@
out.write file_data
end
+ FileUtils.chmod entry['mode'], path
+
say path if Gem.configuration.really_verbose
end
end
+ ##
# Prefix and suffix the program filename the same as ruby.
+
def formatted_program_filename(filename)
if @format_executable then
self.class.exec_format % File.basename(filename)
@@ -460,7 +554,9 @@
private
+ ##
# HACK Pathname is broken on windows.
+
def absolute_path? pathname
pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i)
end
Modified: MacRuby/branches/experimental/lib/rubygems/local_remote_options.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/local_remote_options.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/local_remote_options.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -4,27 +4,34 @@
# See LICENSE.txt for permissions.
#++
+require 'uri'
require 'rubygems'
+##
# Mixin methods for local and remote Gem::Command options.
+
module Gem::LocalRemoteOptions
+ ##
# Allows OptionParser to handle HTTP URIs.
+
def accept_uri_http
OptionParser.accept URI::HTTP do |value|
begin
- value = URI.parse value
+ uri = URI.parse value
rescue URI::InvalidURIError
raise OptionParser::InvalidArgument, value
end
- raise OptionParser::InvalidArgument, value unless value.scheme == 'http'
+ raise OptionParser::InvalidArgument, value unless uri.scheme == 'http'
value
end
end
+ ##
# Add local/remote options to the command line parser.
+
def add_local_remote_options
add_option(:"Local/Remote", '-l', '--local',
'Restrict operations to the LOCAL domain') do |value, options|
@@ -47,7 +54,9 @@
add_update_sources_option
end
+ ##
# Add the --bulk-threshold option
+
def add_bulk_threshold_option
add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT',
"Threshold for switching to bulk",
@@ -57,7 +66,9 @@
end
end
+ ##
# Add the --http-proxy option
+
def add_proxy_option
accept_uri_http
@@ -68,22 +79,28 @@
end
end
+ ##
# Add the --source option
+
def add_source_option
accept_uri_http
add_option(:"Local/Remote", '--source URL', URI::HTTP,
- 'Use URL as the remote source for gems') do |value, options|
+ 'Use URL as the remote source for gems') do |source, options|
+ source << '/' if source !~ /\/\z/
+
if options[:added_source] then
- Gem.sources << value
+ Gem.sources << source
else
options[:added_source] = true
- Gem.sources.replace [value]
+ Gem.sources.replace [source]
end
end
end
- # Add the --source option
+ ##
+ # Add the --update-source option
+
def add_update_sources_option
add_option(:"Local/Remote", '-u', '--[no-]update-sources',
@@ -92,12 +109,23 @@
end
end
+ ##
+ # Is fetching of local and remote information enabled?
+
+ def both?
+ options[:domain] == :both
+ end
+
+ ##
# Is local fetching enabled?
+
def local?
options[:domain] == :local || options[:domain] == :both
end
+ ##
# Is remote fetching enabled?
+
def remote?
options[:domain] == :remote || options[:domain] == :both
end
Modified: MacRuby/branches/experimental/lib/rubygems/package/tar_reader.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/package/tar_reader.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/package/tar_reader.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -46,17 +46,17 @@
yield entry
skip = (512 - (size % 512)) % 512
+ pending = size - entry.bytes_read
- if @io.respond_to? :seek then
+ begin
# avoid reading...
- @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
- else
- pending = size - entry.bytes_read
-
+ @io.seek pending, IO::SEEK_CUR
+ pending = 0
+ rescue Errno::EINVAL, NameError
while pending > 0 do
- bread = @io.read([pending, 4096].min).size
+ bytes_read = @io.read([pending, 4096].min).size
raise UnexpectedEOF if @io.eof?
- pending -= bread
+ pending -= bytes_read
end
end
Modified: MacRuby/branches/experimental/lib/rubygems/platform.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/platform.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/platform.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,8 @@
require 'rubygems'
+##
# Available list of platforms for targeting Gem installations.
-#
+
class Gem::Platform
@local = nil
@@ -12,23 +13,6 @@
attr_accessor :version
- DEPRECATED_CONSTS = [
- :DARWIN,
- :LINUX_586,
- :MSWIN32,
- :PPC_DARWIN,
- :WIN32,
- :X86_LINUX
- ]
-
- def self.const_missing(name) # TODO remove six months from 2007/12
- if DEPRECATED_CONSTS.include? name then
- raise NameError, "#{name} has been removed, use CURRENT instead"
- else
- super
- end
- end
-
def self.local
arch = Gem::ConfigMap[:arch]
arch = "#{arch}_60" if arch =~ /mswin32$/
@@ -72,7 +56,7 @@
else cpu
end
- if arch.length == 2 and arch.last =~ /^\d+$/ then # for command-line
+ if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line
@os, @version = arch
return
end
@@ -122,11 +106,20 @@
to_a.compact.join '-'
end
+ ##
+ # Is +other+ equal to this platform? Two platforms are equal if they have
+ # the same CPU, OS and version.
+
def ==(other)
self.class === other and
@cpu == other.cpu and @os == other.os and @version == other.version
end
+ ##
+ # Does +other+ match this platform? Two platforms match if they have the
+ # same CPU, or either has a CPU of 'universal', they have the same OS, and
+ # they have the same version, or either has no version.
+
def ===(other)
return nil unless Gem::Platform === other
@@ -140,6 +133,10 @@
(@version.nil? or other.version.nil? or @version == other.version)
end
+ ##
+ # Does +other+ match this platform? If +other+ is a String it will be
+ # converted to a Gem::Platform first. See #=== for matching rules.
+
def =~(other)
case other
when Gem::Platform then # nop
Modified: MacRuby/branches/experimental/lib/rubygems/remote_fetcher.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/remote_fetcher.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/remote_fetcher.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,4 +1,6 @@
require 'net/http'
+require 'stringio'
+require 'time'
require 'uri'
require 'rubygems'
@@ -11,15 +13,38 @@
include Gem::UserInteraction
- class FetchError < Gem::Exception; end
+ ##
+ # A FetchError exception wraps up the various possible IO and HTTP failures
+ # that could happen while downloading from the internet.
+ class FetchError < Gem::Exception
+
+ ##
+ # The URI which was being accessed when the exception happened.
+
+ attr_accessor :uri
+
+ def initialize(message, uri)
+ super message
+ @uri = uri
+ end
+
+ def to_s # :nodoc:
+ "#{super} (#{uri})"
+ end
+
+ end
+
@fetcher = nil
+ ##
# Cached RemoteFetcher instance.
+
def self.fetcher
@fetcher ||= self.new Gem.configuration[:http_proxy]
end
+ ##
# Initialize a remote fetcher using the source URI and possible proxy
# information.
#
@@ -29,6 +54,7 @@
# * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
# HTTP_PROXY_PASS)
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
+
def initialize(proxy)
Socket.do_not_reverse_lookup = true
@@ -47,11 +73,18 @@
# Moves the gem +spec+ from +source_uri+ to the cache dir unless it is
# already there. If the source_uri is local the gem cache dir copy is
# always replaced.
+
def download(spec, source_uri, install_dir = Gem.dir)
+ if File.writable?(install_dir)
+ cache_dir = File.join install_dir, 'cache'
+ else
+ cache_dir = File.join(Gem.user_dir, 'cache')
+ end
+
gem_file_name = "#{spec.full_name}.gem"
- local_gem_path = File.join install_dir, 'cache', gem_file_name
+ local_gem_path = File.join cache_dir, gem_file_name
- Gem.ensure_gem_subdirectories install_dir
+ FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
source_uri = URI.parse source_uri unless URI::Generic === source_uri
scheme = source_uri.scheme
@@ -60,7 +93,7 @@
scheme = nil if scheme =~ /^[a-z]$/i
case scheme
- when 'http' then
+ when 'http', 'https' then
unless File.exist? local_gem_path then
begin
say "Downloading gem #{gem_file_name}" if
@@ -102,56 +135,30 @@
local_gem_path
end
- # Downloads +uri+.
- def fetch_path(uri)
- open_uri_or_path(uri) do |input|
- input.read
- end
+ ##
+ # Downloads +uri+ and returns it as a String.
+
+ def fetch_path(uri, mtime = nil, head = false)
+ data = open_uri_or_path uri, mtime, head
+ data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/
+ data
+ rescue FetchError
+ raise
rescue Timeout::Error
- raise FetchError, "timed out fetching #{uri}"
+ raise FetchError.new('timed out', uri)
rescue IOError, SocketError, SystemCallError => e
- raise FetchError, "#{e.class}: #{e} reading #{uri}"
- rescue => e
- message = "#{e.class}: #{e} reading #{uri}"
- raise FetchError, message
+ raise FetchError.new("#{e.class}: #{e}", uri)
end
+ ##
# Returns the size of +uri+ in bytes.
- def fetch_size(uri)
- return File.size(get_file_uri_path(uri)) if file_uri? uri
- uri = URI.parse uri unless URI::Generic === uri
+ def fetch_size(uri) # TODO: phase this out
+ response = fetch_path(uri, nil, true)
- raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
-
- http = connect_to uri.host, uri.port
-
- request = Net::HTTP::Head.new uri.request_uri
-
- request.basic_auth unescape(uri.user), unescape(uri.password) unless
- uri.user.nil? or uri.user.empty?
-
- resp = http.request request
-
- if resp.code !~ /^2/ then
- raise Gem::RemoteSourceException,
- "HTTP Response #{resp.code} fetching #{uri}"
- end
-
- if resp['content-length'] then
- return resp['content-length'].to_i
- else
- resp = http.get uri.request_uri
- return resp.body.size
- end
-
- rescue SocketError, SystemCallError, Timeout::Error => e
- raise Gem::RemoteFetcher::FetchError,
- "#{e.message} (#{e.class})\n\tgetting size of #{uri}"
+ response['content-length'].to_i
end
- private
-
def escape(str)
return unless str
URI.escape(str)
@@ -162,7 +169,9 @@
URI.unescape(str)
end
+ ##
# Returns an HTTP proxy URI if one is set in the environment variables.
+
def get_proxy_from_env
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
@@ -179,23 +188,49 @@
uri
end
+ ##
# Normalize the URI by adding "http://" if it is missing.
+
def normalize_uri(uri)
(uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}"
end
- # Connect to the source host/port, using a proxy if needed.
- def connect_to(host, port)
- if @proxy_uri
- Net::HTTP::Proxy(@proxy_uri.host, @proxy_uri.port, unescape(@proxy_uri.user), unescape(@proxy_uri.password)).new(host, port)
- else
- Net::HTTP.new(host, port)
+ ##
+ # Creates or an HTTP connection based on +uri+, or retrieves an existing
+ # connection, using a proxy if needed.
+
+ def connection_for(uri)
+ net_http_args = [uri.host, uri.port]
+
+ if @proxy_uri then
+ net_http_args += [
+ @proxy_uri.host,
+ @proxy_uri.port,
+ @proxy_uri.user,
+ @proxy_uri.password
+ ]
end
+
+ connection_id = net_http_args.join ':'
+ @connections[connection_id] ||= Net::HTTP.new(*net_http_args)
+ connection = @connections[connection_id]
+
+ if uri.scheme == 'https' and not connection.started? then
+ require 'net/https'
+ connection.use_ssl = true
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+
+ connection.start unless connection.started?
+
+ connection
end
+ ##
# Read the data from the (source based) URI, but if it is a file:// URI,
# read from the filesystem instead.
- def open_uri_or_path(uri, depth = 0, &block)
+
+ def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0)
if RUBY_ENGINE == 'macruby'
# XXX we are taking a _much faster_ code path here, this change should be
# removed once we re-implement the IO subsystem (and therefore Net::HTTP)
@@ -203,93 +238,117 @@
url = NSURL.URLWithString(uri.to_s)
data = NSMutableData.dataWithContentsOfURL(url)
if data.nil?
- raise Gem::RemoteFetcher::FetchError, "error when fetching data from #{uri}"
+ raise Gem::RemoteFetcher::FetchError, "error when fetching data from #{uri}"
end
string = String.__new_bytestring__(data)
#block.call(string) if block
return string
+ end
+ raise "block is dead" if block_given?
+
+ return open(get_file_uri_path(uri)) if file_uri? uri
+
+ uri = URI.parse uri unless URI::Generic === uri
+ raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
+
+ fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
+ response = request uri, fetch_type, last_modified
+
+ case response
+ when Net::HTTPOK, Net::HTTPNotModified then
+ head ? response : response.body
+ when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
+ Net::HTTPTemporaryRedirect then
+ raise FetchError.new('too many redirects', uri) if depth > 10
+
+ open_uri_or_path(response['Location'], last_modified, head, depth + 1)
+ else
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
end
- if file_uri?(uri)
- open(get_file_uri_path(uri), &block)
- else
- uri = URI.parse uri unless URI::Generic === uri
- net_http_args = [uri.host, uri.port]
+ end
- if @proxy_uri then
- net_http_args += [ @proxy_uri.host,
- @proxy_uri.port,
- @proxy_uri.user,
- @proxy_uri.password
- ]
- end
+ ##
+ # Performs a Net::HTTP request of type +request_class+ on +uri+ returning
+ # a Net::HTTP response object. request maintains a table of persistent
+ # connections to reduce connect overhead.
- connection_id = net_http_args.join ':'
- @connections[connection_id] ||= Net::HTTP.new(*net_http_args)
- connection = @connections[connection_id]
+ def request(uri, request_class, last_modified = nil)
+ request = request_class.new uri.request_uri
- if uri.scheme == 'https' && ! connection.started?
- http_obj.use_ssl = true
- http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
- end
+ unless uri.nil? || uri.user.nil? || uri.user.empty? then
+ request.basic_auth uri.user, uri.password
+ end
- connection.start unless connection.started?
+ ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
+ ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
+ ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
+ ua << ")"
- request = Net::HTTP::Get.new(uri.request_uri)
- unless uri.nil? || uri.user.nil? || uri.user.empty? then
- request.basic_auth(uri.user, uri.password)
- end
+ request.add_field 'User-Agent', ua
+ request.add_field 'Connection', 'keep-alive'
+ request.add_field 'Keep-Alive', '30'
- ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
- ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
- ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
- ua << ")"
+ if last_modified then
+ last_modified = last_modified.utc
+ request.add_field 'If-Modified-Since', last_modified.rfc2822
+ end
- request.add_field 'User-Agent', ua
- request.add_field 'Connection', 'keep-alive'
- request.add_field 'Keep-Alive', '30'
+ connection = connection_for uri
- # HACK work around EOFError bug in Net::HTTP
- # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
- # to install gems.
- retried = false
- begin
- @requests[connection_id] += 1
- response = connection.request(request)
- rescue EOFError, Errno::ECONNABORTED
- requests = @requests[connection_id]
- say "connection reset after #{requests} requests, retrying" if
- Gem.configuration.really_verbose
+ retried = false
+ bad_response = false
- raise Gem::RemoteFetcher::FetchError, 'too many connection resets' if
- retried
+ begin
+ @requests[connection.object_id] += 1
+ response = connection.request request
+ say "#{request.method} #{response.code} #{response.message}: #{uri}" if
+ Gem.configuration.really_verbose
+ rescue Net::HTTPBadResponse
+ reset connection
- @requests[connection_id] = 0
+ raise FetchError.new('too many bad responses', uri) if bad_response
- connection.finish
- connection.start
- retried = true
- retry
- end
+ bad_response = true
+ retry
+ # HACK work around EOFError bug in Net::HTTP
+ # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
+ # to install gems.
+ rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET
+ requests = @requests[connection.object_id]
+ say "connection reset after #{requests} requests, retrying" if
+ Gem.configuration.really_verbose
- case response
- when Net::HTTPOK then
- block.call(StringIO.new(response.body)) if block
- when Net::HTTPRedirection then
- raise Gem::RemoteFetcher::FetchError, "too many redirects" if depth > 10
- open_uri_or_path(response['Location'], depth + 1, &block)
- else
- raise Gem::RemoteFetcher::FetchError,
- "bad response #{response.message} #{response.code}"
- end
+ raise FetchError.new('too many connection resets', uri) if retried
+
+ reset connection
+
+ retried = true
+ retry
end
+
+ response
end
+ ##
+ # Resets HTTP connection +connection+.
+
+ def reset(connection)
+ @requests.delete connection.object_id
+
+ connection.finish
+ connection.start
+ end
+
+ ##
# Checks if the provided string is a file:// URI.
+
def file_uri?(uri)
uri =~ %r{\Afile://}
end
+ ##
# Given a file:// URI, returns its local path.
+
def get_file_uri_path(uri)
uri.sub(%r{\Afile://}, '')
end
Modified: MacRuby/branches/experimental/lib/rubygems/requirement.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/requirement.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/requirement.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -12,6 +12,7 @@
#
# A Requirement object can actually contain multiple, er,
# requirements, as in (> 1.2, < 2.0).
+
class Gem::Requirement
include Comparable
@@ -35,7 +36,7 @@
# Version, a String, or nil. Intended to simplify client code.
#
# If the input is "weird", the default version requirement is returned.
- #
+
def self.create(input)
case input
when Gem::Requirement then
@@ -57,6 +58,7 @@
# This comment once said:
#
# "A default "version requirement" can surely _only_ be '> 0'."
+
def self.default
self.new ['>= 0']
end
@@ -65,6 +67,7 @@
# Constructs a Requirement from +requirements+ which can be a String, a
# Gem::Version, or an Array of those. See parse for details on the
# formatting of requirement strings.
+
def initialize(requirements)
@requirements = case requirements
when Array then
@@ -81,14 +84,18 @@
@requirements = values['requirements']
@version = nil
end
+
+ ##
+ # Marshal raw requirements, rather than the full object
- # Marshal raw requirements, rather than the full object
- def marshal_dump
+ def marshal_dump # :nodoc:
[@requirements]
end
+ ##
# Load custom marshal format
- def marshal_load(array)
+
+ def marshal_load(array) # :nodoc:
@requirements = array[0]
@version = nil
end
@@ -113,20 +120,16 @@
end
##
- # Is the requirement satifised by +version+.
- #
- # version:: [Gem::Version] the version to compare against
- # return:: [Boolean] true if this requirement is satisfied by
- # the version, otherwise false
- #
+ # True if this requirement satisfied by the Gem::Version +version+.
+
def satisfied_by?(version)
normalize
@requirements.all? { |op, rv| satisfy?(op, version, rv) }
end
##
- # Is "version op required_version" satisfied?
- #
+ # Is "+version+ +op+ +required_version+" satisfied?
+
def satisfy?(op, version, required_version)
OPS[op].call(version, required_version)
end
@@ -137,6 +140,7 @@
# The requirement can be a String or a Gem::Version. A String can be an
# operator (<, <=, =, =>, >, !=, ~>), a version number, or both, operator
# first.
+
def parse(obj)
case obj
when /^\s*(#{OP_RE})\s*([0-9.]+)\s*$/o then
@@ -152,7 +156,7 @@
end
end
- def <=>(other)
+ def <=>(other) # :nodoc:
to_s <=> other.to_s
end
Modified: MacRuby/branches/experimental/lib/rubygems/rubygems_version.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/rubygems_version.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/rubygems_version.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -2,5 +2,5 @@
# This file is auto-generated by build scripts.
# See: rake update_version
module Gem
- RubyGemsVersion = '1.1.1'
+ RubyGemsVersion = '1.3.1'
end
Modified: MacRuby/branches/experimental/lib/rubygems/security.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/security.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/security.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -557,7 +557,7 @@
#
# Medium security policy: verify the signing certificate, verify the signing
# certificate chain all the way to the root certificate, and only trust root
- # certificates that we have explicity allowed trust for.
+ # certificates that we have explicitly allowed trust for.
#
# This security policy is reasonable, but it allows unsigned packages, so a
# malicious person could simply delete the package signature and pass the
@@ -576,7 +576,7 @@
# High security policy: only allow signed gems to be installed, verify the
# signing certificate, verify the signing certificate chain all the way to
# the root certificate, and only trust root certificates that we have
- # explicity allowed trust for.
+ # explicitly allowed trust for.
#
# This security policy is significantly more difficult to bypass, and offers
# a reasonable guarantee that the contents of the gem have not been altered.
Modified: MacRuby/branches/experimental/lib/rubygems/server.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/server.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/server.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -4,25 +4,32 @@
require 'erb'
require 'rubygems'
+require 'rubygems/doc_manager'
##
# Gem::Server and allows users to serve gems for consumption by
# `gem --remote-install`.
#
-# gem_server starts an HTTP server on the given port and serves the folowing:
+# gem_server starts an HTTP server on the given port and serves the following:
# * "/" - Browsing of gem spec files for installed gems
-# * "/Marshal" - Full SourceIndex dump of metadata for installed gems
-# * "/yaml" - YAML dump of metadata for installed gems - deprecated
+# * "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index
+# * "/latest_specs.#{Gem.marshal_version}.gz" - latest specs
+# name/version/platform index
+# * "/quick/" - Individual gemspecs
# * "/gems" - Direct access to download the installable gems
+# * legacy indexes:
+# * "/Marshal.#{Gem.marshal_version}" - Full SourceIndex dump of metadata
+# for installed gems
+# * "/yaml" - YAML dump of metadata for installed gems - deprecated
#
# == Usage
#
-# gem server [-p portnum] [-d gem_path]
+# gem_server = Gem::Server.new Gem.dir, 8089, false
+# gem_server.run
#
-# port_num:: The TCP port the HTTP server will bind to
-# gem_path::
-# Root gem directory containing both "cache" and "specifications"
-# subdirectories.
+#--
+# TODO Refactor into a real WEBrick servlet to remove code duplication.
+
class Gem::Server
include Gem::UserInteraction
@@ -36,7 +43,6 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>RubyGems Documentation Index</title>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
</head>
<body>
@@ -325,32 +331,99 @@
new(options[:gemdir], options[:port], options[:daemon]).run
end
- def initialize(gemdir, port, daemon)
+ def initialize(gem_dir, port, daemon)
Socket.do_not_reverse_lookup = true
- @gemdir = gemdir
+ @gem_dir = gem_dir
@port = port
@daemon = daemon
logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
@server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
- @spec_dir = File.join @gemdir, "specifications"
+ @spec_dir = File.join @gem_dir, 'specifications'
+
+ unless File.directory? @spec_dir then
+ raise ArgumentError, "#{@gem_dir} does not appear to be a gem repository"
+ end
+
@source_index = Gem::SourceIndex.from_gems_in @spec_dir
end
+ def Marshal(req, res)
+ @source_index.refresh!
+
+ res['date'] = File.stat(@spec_dir).mtime
+
+ index = Marshal.dump @source_index
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = index.length
+ return
+ end
+
+ if req.path =~ /Z$/ then
+ res['content-type'] = 'application/x-deflate'
+ index = Gem.deflate index
+ else
+ res['content-type'] = 'application/octet-stream'
+ end
+
+ res.body << index
+ end
+
+ def latest_specs(req, res)
+ @source_index.refresh!
+
+ res['content-type'] = 'application/x-gzip'
+
+ res['date'] = File.stat(@spec_dir).mtime
+
+ specs = @source_index.latest_specs.sort.map do |spec|
+ platform = spec.original_platform
+ platform = Gem::Platform::RUBY if platform.nil?
+ [spec.name, spec.version, platform]
+ end
+
+ specs = Marshal.dump specs
+
+ if req.path =~ /\.gz$/ then
+ specs = Gem.gzip specs
+ res['content-type'] = 'application/x-gzip'
+ else
+ res['content-type'] = 'application/octet-stream'
+ end
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = specs.length
+ else
+ res.body << specs
+ end
+ end
+
def quick(req, res)
+ @source_index.refresh!
+
res['content-type'] = 'text/plain'
res['date'] = File.stat(@spec_dir).mtime
- case req.request_uri.request_uri
+ case req.request_uri.path
when '/quick/index' then
- res.body << @source_index.map { |name,_| name }.join("\n")
+ res.body << @source_index.map { |name,| name }.sort.join("\n")
when '/quick/index.rz' then
- index = @source_index.map { |name,_| name }.join("\n")
- res.body << Zlib::Deflate.deflate(index)
+ index = @source_index.map { |name,| name }.sort.join("\n")
+ res['content-type'] = 'application/x-deflate'
+ res.body << Gem.deflate(index)
+ when '/quick/latest_index' then
+ index = @source_index.latest_specs.map { |spec| spec.full_name }
+ res.body << index.sort.join("\n")
+ when '/quick/latest_index.rz' then
+ index = @source_index.latest_specs.map { |spec| spec.full_name }
+ res['content-type'] = 'application/x-deflate'
+ res.body << Gem.deflate(index.sort.join("\n"))
when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then
dep = Gem::Dependency.new $2, $3
specs = @source_index.search dep
+ marshal_format = $1
selector = [$2, $3, $4].map { |s| s.inspect }.join ' '
@@ -368,126 +441,133 @@
elsif specs.length > 1 then
res.status = 500
res.body = "Multiple gems found matching #{selector}"
- elsif $1 then # marshal quickindex instead of YAML
- res.body << Zlib::Deflate.deflate(Marshal.dump(specs.first))
+ elsif marshal_format then
+ res['content-type'] = 'application/x-deflate'
+ res.body << Gem.deflate(Marshal.dump(specs.first))
else # deprecated YAML format
- res.body << Zlib::Deflate.deflate(specs.first.to_yaml)
+ res['content-type'] = 'application/x-deflate'
+ res.body << Gem.deflate(specs.first.to_yaml)
end
else
- res.status = 404
- res.body = "#{req.request_uri} not found"
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
end
end
- def run
- @server.listen nil, @port
+ def root(req, res)
+ @source_index.refresh!
+ res['date'] = File.stat(@spec_dir).mtime
- say "Starting gem server on http://localhost:#{@port}/"
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
+ req.path == '/'
- WEBrick::Daemon.start if @daemon
+ specs = []
+ total_file_count = 0
- @server.mount_proc("/yaml") do |req, res|
- res['content-type'] = 'text/plain'
- res['date'] = File.stat(@spec_dir).mtime
- if req.request_method == 'HEAD' then
- res['content-length'] = @source_index.to_yaml.length
- else
- res.body << @source_index.to_yaml
+ @source_index.each do |path, spec|
+ total_file_count += spec.files.size
+ deps = spec.dependencies.map do |dep|
+ { "name" => dep.name,
+ "type" => dep.type,
+ "version" => dep.version_requirements.to_s, }
end
- end
- @server.mount_proc("/Marshal") do |req, res|
- res['content-type'] = 'text/plain'
- res['date'] = File.stat(@spec_dir).mtime
- if req.request_method == 'HEAD' then
- res['content-length'] = Marshal.dump(@source_index).length
- else
- res.body << Marshal.dump(@source_index)
- end
+ deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
+ deps.last["is_last"] = true unless deps.empty?
+
+ # executables
+ executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
+ executables = nil if executables.empty?
+ executables.last["is_last"] = true if executables
+
+ specs << {
+ "authors" => spec.authors.sort.join(", "),
+ "date" => spec.date.to_s,
+ "dependencies" => deps,
+ "doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html",
+ "executables" => executables,
+ "only_one_executable" => (executables && executables.size == 1),
+ "full_name" => spec.full_name,
+ "has_deps" => !deps.empty?,
+ "homepage" => spec.homepage,
+ "name" => spec.name,
+ "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?,
+ "summary" => spec.summary,
+ "version" => spec.version.to_s,
+ }
end
- @server.mount_proc("/quick/", &method(:quick))
+ specs << {
+ "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
+ "dependencies" => [],
+ "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html",
+ "executables" => [{"executable" => 'gem', "is_last" => true}],
+ "only_one_executable" => true,
+ "full_name" => "rubygems-#{Gem::RubyGemsVersion}",
+ "has_deps" => false,
+ "homepage" => "http://rubygems.org/",
+ "name" => 'rubygems',
+ "rdoc_installed" => true,
+ "summary" => "RubyGems itself",
+ "version" => Gem::RubyGemsVersion,
+ }
- @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
- res['content-type'] = 'text/css'
- res['date'] = File.stat(@spec_dir).mtime
- res.body << RDOC_CSS
+ specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
+ specs.last["is_last"] = true
+
+ # tag all specs with first_name_entry
+ last_spec = nil
+ specs.each do |spec|
+ is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
+ spec["first_name_entry"] = is_first
+ last_spec = spec
end
- @server.mount_proc("/") do |req, res|
- specs = []
- total_file_count = 0
+ # create page from template
+ template = ERB.new(DOC_TEMPLATE)
+ res['content-type'] = 'text/html'
- @source_index.each do |path, spec|
- total_file_count += spec.files.size
- deps = spec.dependencies.collect { |dep|
- { "name" => dep.name,
- "version" => dep.version_requirements.to_s, }
- }
- deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
- deps.last["is_last"] = true unless deps.empty?
+ values = { "gem_count" => specs.size.to_s, "specs" => specs,
+ "total_file_count" => total_file_count.to_s }
- # executables
- executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
- executables = nil if executables.empty?
- executables.last["is_last"] = true if executables
+ result = template.result binding
+ res.body = result
+ end
- specs << {
- "authors" => spec.authors.sort.join(", "),
- "date" => spec.date.to_s,
- "dependencies" => deps,
- "doc_path" => ('/doc_root/' + spec.full_name + '/rdoc/index.html'),
- "executables" => executables,
- "only_one_executable" => (executables && executables.size==1),
- "full_name" => spec.full_name,
- "has_deps" => !deps.empty?,
- "homepage" => spec.homepage,
- "name" => spec.name,
- "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?,
- "summary" => spec.summary,
- "version" => spec.version.to_s,
- }
- end
+ def run
+ @server.listen nil, @port
- specs << {
- "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
- "dependencies" => [],
- "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html",
- "executables" => [{"executable" => 'gem', "is_last" => true}],
- "only_one_executable" => true,
- "full_name" => "rubygems-#{Gem::RubyGemsVersion}",
- "has_deps" => false,
- "homepage" => "http://rubygems.org/",
- "name" => 'rubygems',
- "rdoc_installed" => true,
- "summary" => "RubyGems itself",
- "version" => Gem::RubyGemsVersion,
- }
+ say "Starting gem server on http://localhost:#{@port}/"
- specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
- specs.last["is_last"] = true
+ WEBrick::Daemon.start if @daemon
- # tag all specs with first_name_entry
- last_spec = nil
- specs.each do |spec|
- is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
- spec["first_name_entry"] = is_first
- last_spec = spec
- end
+ @server.mount_proc "/yaml", method(:yaml)
+ @server.mount_proc "/yaml.Z", method(:yaml)
- # create page from template
- template = ERB.new(DOC_TEMPLATE)
- res['content-type'] = 'text/html'
- values = { "gem_count" => specs.size.to_s, "specs" => specs,
- "total_file_count" => total_file_count.to_s }
- result = template.result binding
- res.body = result
+ @server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal)
+ @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal)
+
+ @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
+ @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
+
+ @server.mount_proc "/latest_specs.#{Gem.marshal_version}",
+ method(:latest_specs)
+ @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
+ method(:latest_specs)
+
+ @server.mount_proc "/quick/", method(:quick)
+
+ @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
+ res['content-type'] = 'text/css'
+ res['date'] = File.stat(@spec_dir).mtime
+ res.body << RDOC_CSS
end
+ @server.mount_proc "/", method(:root)
+
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
paths.each do |mount_point, mount_dir|
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
- File.join(@gemdir, mount_dir), true)
+ File.join(@gem_dir, mount_dir), true)
end
trap("INT") { @server.shutdown; exit! }
@@ -496,5 +576,54 @@
@server.start
end
+ def specs(req, res)
+ @source_index.refresh!
+
+ res['date'] = File.stat(@spec_dir).mtime
+
+ specs = @source_index.sort.map do |_, spec|
+ platform = spec.original_platform
+ platform = Gem::Platform::RUBY if platform.nil?
+ [spec.name, spec.version, platform]
+ end
+
+ specs = Marshal.dump specs
+
+ if req.path =~ /\.gz$/ then
+ specs = Gem.gzip specs
+ res['content-type'] = 'application/x-gzip'
+ else
+ res['content-type'] = 'application/octet-stream'
+ end
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = specs.length
+ else
+ res.body << specs
+ end
+ end
+
+ def yaml(req, res)
+ @source_index.refresh!
+
+ res['date'] = File.stat(@spec_dir).mtime
+
+ index = @source_index.to_yaml
+
+ if req.path =~ /Z$/ then
+ res['content-type'] = 'application/x-deflate'
+ index = Gem.deflate index
+ else
+ res['content-type'] = 'text/plain'
+ end
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = index.length
+ return
+ end
+
+ res.body << index
+ end
+
end
Modified: MacRuby/branches/experimental/lib/rubygems/source_index.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/source_index.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/source_index.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -7,6 +7,9 @@
require 'rubygems'
require 'rubygems/user_interaction'
require 'rubygems/specification'
+module Gem
+ autoload(:SpecFetcher, 'rubygems/spec_fetcher')
+end
##
# The SourceIndex object indexes all the gems available from a
@@ -27,6 +30,11 @@
attr_reader :gems # :nodoc:
+ ##
+ # Directories to use to refresh this SourceIndex when calling refresh!
+
+ attr_accessor :spec_dirs
+
class << self
include Gem::UserInteraction
@@ -39,7 +47,7 @@
# +from_gems_in+. This argument is deprecated and is provided
# just for backwards compatibility, and should not generally
# be used.
- #
+ #
# return::
# SourceIndex instance
@@ -63,7 +71,9 @@
# +spec_dirs+.
def from_gems_in(*spec_dirs)
- self.new.load_gems_in(*spec_dirs)
+ source_index = new
+ source_index.spec_dirs = spec_dirs
+ source_index.refresh!
end
##
@@ -72,18 +82,26 @@
def load_specification(file_name)
begin
- spec_code = File.read(file_name).untaint
+ spec_code = if RUBY_VERSION < '1.9' then
+ File.read file_name
+ else
+ File.read file_name, :encoding => 'UTF-8'
+ end.untaint
+
gemspec = eval spec_code, binding, file_name
+
if gemspec.is_a?(Gem::Specification)
gemspec.loaded_from = file_name
return gemspec
end
alert_warning "File '#{file_name}' does not evaluate to a gem specification"
+ rescue SignalException, SystemExit
+ raise
rescue SyntaxError => e
alert_warning e
alert_warning spec_code
rescue Exception => e
- alert_warning(e.inspect.to_s + "\n" + spec_code)
+ alert_warning "#{e.inspect}\n#{spec_code}"
alert_warning "Invalid .gemspec format in '#{file_name}'"
end
return nil
@@ -100,6 +118,7 @@
def initialize(specifications={})
@gems = specifications
+ @spec_dirs = nil
end
##
@@ -121,8 +140,8 @@
end
##
- # Returns a Hash of name => Specification of the latest versions of each
- # gem in this index.
+ # Returns an Array specifications for the latest versions of each gem in
+ # this index.
def latest_specs
result = Hash.new { |h,k| h[k] = [] }
@@ -219,7 +238,8 @@
# Find a gem by an exact match on the short name.
def find_name(gem_name, version_requirement = Gem::Requirement.default)
- search(/^#{gem_name}$/, version_requirement)
+ dep = Gem::Dependency.new(/^#{gem_name}$/, version_requirement)
+ search dep
end
##
@@ -235,13 +255,20 @@
version_requirement = nil
only_platform = false
- case gem_pattern # TODO warn after 2008/03, remove three months after
+ # TODO - Remove support and warning for legacy arguments after 2008/11
+ unless Gem::Dependency === gem_pattern
+ warn "#{Gem.location_of_caller.join ':'}:Warning: Gem::SourceIndex#search support for #{gem_pattern.class} patterns is deprecated"
+ end
+
+ case gem_pattern
when Regexp then
version_requirement = platform_only || Gem::Requirement.default
when Gem::Dependency then
only_platform = platform_only
version_requirement = gem_pattern.version_requirements
- gem_pattern = if gem_pattern.name.empty? then
+ gem_pattern = if Regexp === gem_pattern.name then
+ gem_pattern.name
+ elsif gem_pattern.name.empty? then
//
else
/^#{Regexp.escape gem_pattern.name}$/
@@ -257,7 +284,7 @@
specs = @gems.values.select do |spec|
spec.name =~ gem_pattern and
- version_requirement.satisfied_by? spec.version
+ version_requirement.satisfied_by? spec.version
end
if only_platform then
@@ -271,29 +298,41 @@
##
# Replaces the gems in the source index from specifications in the
- # installed_spec_directories,
+ # directories this source index was created from. Raises an exception if
+ # this source index wasn't created from a directory (via from_gems_in or
+ # from_installed_gems, or having spec_dirs set).
def refresh!
- load_gems_in(*self.class.installed_spec_directories)
+ raise 'source index not created from disk' if @spec_dirs.nil?
+ load_gems_in(*@spec_dirs)
end
##
# Returns an Array of Gem::Specifications that are not up to date.
def outdated
- dep = Gem::Dependency.new '', Gem::Requirement.default
-
- remotes = Gem::SourceInfoCache.search dep, true
-
outdateds = []
latest_specs.each do |local|
- name = local.name
- remote = remotes.select { |spec| spec.name == name }.
- sort_by { |spec| spec.version.to_ints }.
- last
+ dependency = Gem::Dependency.new local.name, ">= #{local.version}"
- outdateds << name if remote and local.version < remote.version
+ begin
+ fetcher = Gem::SpecFetcher.fetcher
+ remotes = fetcher.find_matching dependency
+ remotes = remotes.map { |(name, version,_),_| version }
+ rescue Gem::RemoteFetcher::FetchError => e
+ raise unless fetcher.warn_legacy e do
+ require 'rubygems/source_info_cache'
+
+ specs = Gem::SourceInfoCache.search_with_source dependency, true
+
+ remotes = specs.map { |spec,| spec.version }
+ end
+ end
+
+ latest = remotes.sort.last
+
+ outdateds << local.name if latest and local.version < latest
end
outdateds
@@ -387,7 +426,8 @@
end
def fetch_bulk_index(source_uri)
- say "Bulk updating Gem source index for: #{source_uri}"
+ say "Bulk updating Gem source index for: #{source_uri}" if
+ Gem.configuration.verbose
index = fetch_index_from(source_uri)
if index.nil? then
@@ -447,7 +487,7 @@
def unzip(string)
require 'zlib'
- Zlib::Inflate.inflate(string)
+ Gem.inflate string
end
##
@@ -513,7 +553,7 @@
# objects to load properly.
Cache = SourceIndex
- # :starddoc:
+ # :startdoc:
end
Modified: MacRuby/branches/experimental/lib/rubygems/source_info_cache.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/source_info_cache.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/source_info_cache.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -8,7 +8,7 @@
# SourceInfoCache stores a copy of the gem index for each gem source.
#
# There are two possible cache locations, the system cache and the user cache:
-# * The system cache is prefered if it is writable or can be created.
+# * The system cache is preferred if it is writable or can be created.
# * The user cache is used otherwise
#
# Once a cache is selected, it will be used for all operations.
@@ -284,6 +284,10 @@
cache_data.map do |source_uri, sic_entry|
next unless Gem.sources.include? source_uri
+ # TODO - Remove this gunk after 2008/11
+ unless pattern.kind_of?(Gem::Dependency)
+ pattern = Gem::Dependency.new(pattern, Gem::Requirement.default)
+ end
sic_entry.source_index.search pattern, platform_only
end.flatten.compact
end
@@ -300,6 +304,11 @@
cache_data.map do |source_uri, sic_entry|
next unless Gem.sources.include? source_uri
+ # TODO - Remove this gunk after 2008/11
+ unless pattern.kind_of?(Gem::Dependency)
+ pattern = Gem::Dependency.new(pattern, Gem::Requirement.default)
+ end
+
sic_entry.source_index.search(pattern, only_platform).each do |spec|
results << [spec, source_uri]
end
Copied: MacRuby/branches/experimental/lib/rubygems/spec_fetcher.rb (from rev 1886, MacRuby/trunk/lib/rubygems/spec_fetcher.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/spec_fetcher.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rubygems/spec_fetcher.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,249 @@
+require 'zlib'
+
+require 'rubygems'
+require 'rubygems/remote_fetcher'
+require 'rubygems/user_interaction'
+
+##
+# SpecFetcher handles metadata updates from remote gem repositories.
+
+class Gem::SpecFetcher
+
+ include Gem::UserInteraction
+
+ ##
+ # The SpecFetcher cache dir.
+
+ attr_reader :dir # :nodoc:
+
+ ##
+ # Cache of latest specs
+
+ attr_reader :latest_specs # :nodoc:
+
+ ##
+ # Cache of all spces
+
+ attr_reader :specs # :nodoc:
+
+ @fetcher = nil
+
+ def self.fetcher
+ @fetcher ||= new
+ end
+
+ def self.fetcher=(fetcher) # :nodoc:
+ @fetcher = fetcher
+ end
+
+ def initialize
+ @dir = File.join Gem.user_home, '.gem', 'specs'
+ @update_cache = File.stat(Gem.user_home).uid == Process.uid
+
+ @specs = {}
+ @latest_specs = {}
+
+ @fetcher = Gem::RemoteFetcher.fetcher
+ end
+
+ ##
+ # Retuns the local directory to write +uri+ to.
+
+ def cache_dir(uri)
+ File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path)
+ end
+
+ ##
+ # Fetch specs matching +dependency+. If +all+ is true, all matching
+ # versions are returned. If +matching_platform+ is false, all platforms are
+ # returned.
+
+ def fetch(dependency, all = false, matching_platform = true)
+ specs_and_sources = find_matching dependency, all, matching_platform
+
+ specs_and_sources.map do |spec_tuple, source_uri|
+ [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
+ end
+
+ rescue Gem::RemoteFetcher::FetchError => e
+ raise unless warn_legacy e do
+ require 'rubygems/source_info_cache'
+
+ return Gem::SourceInfoCache.search_with_source(dependency,
+ matching_platform, all)
+ end
+ end
+
+ def fetch_spec(spec, source_uri)
+ spec = spec - [nil, 'ruby', '']
+ spec_file_name = "#{spec.join '-'}.gemspec"
+
+ uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
+
+ cache_dir = cache_dir uri
+
+ local_spec = File.join cache_dir, spec_file_name
+
+ if File.exist? local_spec then
+ spec = Gem.read_binary local_spec
+ else
+ uri.path << '.rz'
+
+ spec = @fetcher.fetch_path uri
+ spec = Gem.inflate spec
+
+ if @update_cache then
+ FileUtils.mkdir_p cache_dir
+
+ open local_spec, 'wb' do |io|
+ io.write spec
+ end
+ end
+ end
+
+ # TODO: Investigate setting Gem::Specification#loaded_from to a URI
+ Marshal.load spec
+ end
+
+ ##
+ # Find spec names that match +dependency+. If +all+ is true, all matching
+ # versions are returned. If +matching_platform+ is false, gems for all
+ # platforms are returned.
+
+ def find_matching(dependency, all = false, matching_platform = true)
+ found = {}
+
+ list(all).each do |source_uri, specs|
+ found[source_uri] = specs.select do |spec_name, version, spec_platform|
+ dependency =~ Gem::Dependency.new(spec_name, version) and
+ (not matching_platform or Gem::Platform.match(spec_platform))
+ end
+ end
+
+ specs_and_sources = []
+
+ found.each do |source_uri, specs|
+ uri_str = source_uri.to_s
+ specs_and_sources.push(*specs.map { |spec| [spec, uri_str] })
+ end
+
+ specs_and_sources
+ end
+
+ ##
+ # Returns Array of gem repositories that were generated with RubyGems less
+ # than 1.2.
+
+ def legacy_repos
+ Gem.sources.reject do |source_uri|
+ source_uri = URI.parse source_uri
+ spec_path = source_uri + "specs.#{Gem.marshal_version}.gz"
+
+ begin
+ @fetcher.fetch_size spec_path
+ rescue Gem::RemoteFetcher::FetchError
+ begin
+ @fetcher.fetch_size(source_uri + 'yaml') # re-raise if non-repo
+ rescue Gem::RemoteFetcher::FetchError
+ alert_error "#{source_uri} does not appear to be a repository"
+ raise
+ end
+ false
+ end
+ end
+ end
+
+ ##
+ # Returns a list of gems available for each source in Gem::sources. If
+ # +all+ is true, all versions are returned instead of only latest versions.
+
+ def list(all = false)
+ list = {}
+
+ file = all ? 'specs' : 'latest_specs'
+
+ Gem.sources.each do |source_uri|
+ source_uri = URI.parse source_uri
+
+ if all and @specs.include? source_uri then
+ list[source_uri] = @specs[source_uri]
+ elsif not all and @latest_specs.include? source_uri then
+ list[source_uri] = @latest_specs[source_uri]
+ else
+ specs = load_specs source_uri, file
+
+ cache = all ? @specs : @latest_specs
+
+ cache[source_uri] = specs
+ list[source_uri] = specs
+ end
+ end
+
+ list
+ end
+
+ ##
+ # Loads specs in +file+, fetching from +source_uri+ if the on-disk cache is
+ # out of date.
+
+ def load_specs(source_uri, file)
+ file_name = "#{file}.#{Gem.marshal_version}"
+ spec_path = source_uri + "#{file_name}.gz"
+ cache_dir = cache_dir spec_path
+ local_file = File.join(cache_dir, file_name)
+ loaded = false
+
+ if File.exist? local_file then
+ spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file)
+
+ if spec_dump.nil? then
+ spec_dump = Gem.read_binary local_file
+ else
+ loaded = true
+ end
+ else
+ spec_dump = @fetcher.fetch_path spec_path
+ loaded = true
+ end
+
+ specs = Marshal.load spec_dump
+
+ if loaded and @update_cache then
+ begin
+ FileUtils.mkdir_p cache_dir
+
+ open local_file, 'wb' do |io|
+ Marshal.dump specs, io
+ end
+ rescue
+ end
+ end
+
+ specs
+ end
+
+ ##
+ # Warn about legacy repositories if +exception+ indicates only legacy
+ # repositories are available, and yield to the block. Returns false if the
+ # exception indicates some other FetchError.
+
+ def warn_legacy(exception)
+ uri = exception.uri.to_s
+ if uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ then
+ alert_warning <<-EOF
+RubyGems 1.2+ index not found for:
+\t#{legacy_repos.join "\n\t"}
+
+RubyGems will revert to legacy indexes degrading performance.
+ EOF
+
+ yield
+
+ return true
+ end
+
+ false
+ end
+
+end
+
Modified: MacRuby/branches/experimental/lib/rubygems/specification.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/specification.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/specification.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,6 +6,7 @@
require 'rubygems'
require 'rubygems/version'
+require 'rubygems/requirement'
require 'rubygems/platform'
# :stopdoc:
@@ -16,10 +17,14 @@
t - ((t.to_f + t.gmt_offset) % 86400)
end unless defined? Time.today
end
+
+class Date; end # for ruby_code if date.rb wasn't required
+
# :startdoc:
module Gem
+ ##
# == Gem::Specification
#
# The Specification class contains the metadata for a Gem. Typically
@@ -34,25 +39,35 @@
#
# There are many <em>gemspec attributes</em>, and the best place to learn
# about them in the "Gemspec Reference" linked from the RubyGems wiki.
- #
+
class Specification
+ ##
# Allows deinstallation of gems with legacy platforms.
+
attr_accessor :original_platform # :nodoc:
- # ------------------------- Specification version contstants.
-
+ ##
# The the version number of a specification that does not specify one
# (i.e. RubyGems 0.7 or earlier).
+
NONEXISTENT_SPECIFICATION_VERSION = -1
+ ##
# The specification version applied to any new Specification instances
# created. This should be bumped whenever something in the spec format
# changes.
+ #--
+ # When updating this number, be sure to also update #to_ruby.
+ #
+ # NOTE RubyGems < 1.2 cannot load specification versions > 2.
+
CURRENT_SPECIFICATION_VERSION = 2
+ ##
# An informal list of changes to the specification. The highest-valued
# key should be equal to the CURRENT_SPECIFICATION_VERSION.
+
SPECIFICATION_VERSION_HISTORY = {
-1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'],
1 => [
@@ -72,77 +87,98 @@
TODAY = now - ((now.to_i + now.gmt_offset) % 86400)
# :startdoc:
- # ------------------------- Class variables.
+ ##
+ # List of Specification instances.
- # List of Specification instances.
@@list = []
+ ##
# Optional block used to gather newly defined instances.
+
@@gather = nil
+ ##
# List of attribute names: [:name, :version, ...]
@@required_attributes = []
- # List of _all_ attributes and default values: [[:name, nil], [:bindir, 'bin'], ...]
+ ##
+ # List of _all_ attributes and default values:
+ #
+ # [[:name, nil],
+ # [:bindir, 'bin'],
+ # ...]
+
@@attributes = []
@@nil_attributes = []
@@non_nil_attributes = [:@original_platform]
+ ##
# List of array attributes
+
@@array_attributes = []
+ ##
# Map of attribute names to default values.
+
@@default_value = {}
- # ------------------------- Convenience class methods.
+ ##
+ # Names of all specification attributes
def self.attribute_names
@@attributes.map { |name, default| name }
end
+ ##
+ # Default values for specification attributes
+
def self.attribute_defaults
@@attributes.dup
end
+ ##
+ # The default value for specification attribute +name+
+
def self.default_value(name)
@@default_value[name]
end
+ ##
+ # Required specification attributes
+
def self.required_attributes
@@required_attributes.dup
end
+ ##
+ # Is +name+ a required attribute?
+
def self.required_attribute?(name)
@@required_attributes.include? name.to_sym
end
+ ##
+ # Specification attributes that are arrays (appendable and so-forth)
+
def self.array_attributes
@@array_attributes.dup
end
- # ------------------------- Infrastructure class methods.
+ ##
+ # A list of Specification instances that have been defined in this Ruby
+ # instance.
- # A list of Specification instances that have been defined in this Ruby instance.
def self.list
@@list
end
- # Used to specify the name and default value of a specification
- # attribute. The side effects are:
- # * the name and default value are added to the @@attributes list
- # and @@default_value map
- # * a standard _writer_ method (<tt>attribute=</tt>) is created
- # * a non-standard _reader method (<tt>attribute</tt>) is created
+ ##
+ # Specifies the +name+ and +default+ for a specification attribute, and
+ # creates a reader and writer method like Module#attr_accessor.
#
- # The reader method behaves like this:
- # def attribute
- # @attribute ||= (copy of default value)
- # end
- #
- # This allows lazy initialization of attributes to their default
- # values.
- #
+ # The reader method returns the default if the value hasn't been set.
+
def self.attribute(name, default=nil)
ivar_name = "@#{name}".intern
if default.nil? then
@@ -156,8 +192,10 @@
attr_accessor(name)
end
- # Same as :attribute, but ensures that values assigned to the
- # attribute are array values by applying :to_a to the value.
+ ##
+ # Same as :attribute, but ensures that values assigned to the attribute
+ # are array values by applying :to_a to the value.
+
def self.array_attribute(name)
@@non_nil_attributes << ["@#{name}".intern, []]
@@ -176,51 +214,60 @@
module_eval code, __FILE__, __LINE__ - 9
end
+ ##
# Same as attribute above, but also records this attribute as mandatory.
+
def self.required_attribute(*args)
@@required_attributes << args.first
attribute(*args)
end
- # Sometimes we don't want the world to use a setter method for a particular attribute.
+ ##
+ # Sometimes we don't want the world to use a setter method for a
+ # particular attribute.
+ #
# +read_only+ makes it private so we can still use it internally.
+
def self.read_only(*names)
names.each do |name|
private "#{name}="
end
end
- # Shortcut for creating several attributes at once (each with a default value of
- # +nil+).
+ # Shortcut for creating several attributes at once (each with a default
+ # value of +nil+).
+
def self.attributes(*args)
args.each do |arg|
attribute(arg, nil)
end
end
- # Some attributes require special behaviour when they are accessed. This allows for
- # that.
+ ##
+ # Some attributes require special behaviour when they are accessed. This
+ # allows for that.
+
def self.overwrite_accessor(name, &block)
remove_method name
define_method(name, &block)
end
- # Defines a _singular_ version of an existing _plural_ attribute
- # (i.e. one whose value is expected to be an array). This means
- # just creating a helper method that takes a single value and
- # appends it to the array. These are created for convenience, so
- # that in a spec, one can write
+ ##
+ # Defines a _singular_ version of an existing _plural_ attribute (i.e. one
+ # whose value is expected to be an array). This means just creating a
+ # helper method that takes a single value and appends it to the array.
+ # These are created for convenience, so that in a spec, one can write
#
# s.require_path = 'mylib'
#
- # instead of
+ # instead of:
#
# s.require_paths = ['mylib']
#
- # That above convenience is available courtesy of
+ # That above convenience is available courtesy of:
#
# attribute_alias_singular :require_path, :require_paths
- #
+
def self.attribute_alias_singular(singular, plural)
define_method("#{singular}=") { |val|
send("#{plural}=", [val])
@@ -231,10 +278,12 @@
}
end
+ ##
# Dump only crucial instance variables.
- #
+ #--
# MAINTAIN ORDER!
- def _dump(limit) # :nodoc:
+
+ def _dump(limit)
Marshal.dump [
@rubygems_version,
@specification_version,
@@ -256,7 +305,9 @@
]
end
+ ##
# Load custom marshal format, re-initializing defaults as needed
+
def self._load(str)
array = Marshal.load str
@@ -300,182 +351,46 @@
spec
end
- # REQUIRED gemspec attributes ------------------------------------
-
- required_attribute :rubygems_version, Gem::RubyGemsVersion
- required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION
- required_attribute :name
- required_attribute :version
- required_attribute :date, TODAY
- required_attribute :summary
- required_attribute :require_paths, ['lib']
+ ##
+ # List of depedencies that will automatically be activated at runtime.
- # OPTIONAL gemspec attributes ------------------------------------
-
- attributes :email, :homepage, :rubyforge_project, :description
- attributes :autorequire, :default_executable
+ def runtime_dependencies
+ dependencies.select { |d| d.type == :runtime || d.type == nil }
+ end
- attribute :bindir, 'bin'
- attribute :has_rdoc, false
- attribute :required_ruby_version, Gem::Requirement.default
- attribute :required_rubygems_version, Gem::Requirement.default
- attribute :platform, Gem::Platform::RUBY
+ ##
+ # List of dependencies that are used for development
- attribute :signing_key, nil
- attribute :cert_chain, []
- attribute :post_install_message, nil
+ def development_dependencies
+ dependencies.select { |d| d.type == :development }
+ end
- array_attribute :authors
- array_attribute :files
- array_attribute :test_files
- array_attribute :rdoc_options
- array_attribute :extra_rdoc_files
- array_attribute :executables
-
- # Array of extensions to build. See Gem::Installer#build_extensions for
- # valid values.
-
- array_attribute :extensions
- array_attribute :requirements
- array_attribute :dependencies
-
- read_only :dependencies
-
- # ALIASED gemspec attributes -------------------------------------
-
- attribute_alias_singular :executable, :executables
- attribute_alias_singular :author, :authors
- attribute_alias_singular :require_path, :require_paths
- attribute_alias_singular :test_file, :test_files
-
- # DEPRECATED gemspec attributes ----------------------------------
-
- def test_suite_file
+ def test_suite_file # :nodoc:
warn 'test_suite_file deprecated, use test_files'
test_files.first
end
- def test_suite_file=(val)
+ def test_suite_file=(val) # :nodoc:
warn 'test_suite_file= deprecated, use test_files='
@test_files = [] unless defined? @test_files
@test_files << val
end
+ ##
# true when this gemspec has been loaded from a specifications directory.
# This attribute is not persisted.
- attr_writer :loaded
+ attr_accessor :loaded
+ ##
# Path this gemspec was loaded from. This attribute is not persisted.
+
attr_accessor :loaded_from
- # Special accessor behaviours (overwriting default) --------------
-
- overwrite_accessor :version= do |version|
- @version = Version.create(version)
- end
+ ##
+ # Returns an array with bindir attached to each executable in the
+ # executables list
- overwrite_accessor :platform do
- @new_platform
- end
-
- overwrite_accessor :platform= do |platform|
- if @original_platform.nil? or
- @original_platform == Gem::Platform::RUBY then
- @original_platform = platform
- end
-
- case platform
- when Gem::Platform::CURRENT then
- @new_platform = Gem::Platform.local
- @original_platform = @new_platform.to_s
-
- when Gem::Platform then
- @new_platform = platform
-
- # legacy constants
- when nil, Gem::Platform::RUBY then
- @new_platform = Gem::Platform::RUBY
- when 'mswin32' then # was Gem::Platform::WIN32
- @new_platform = Gem::Platform.new 'x86-mswin32'
- when 'i586-linux' then # was Gem::Platform::LINUX_586
- @new_platform = Gem::Platform.new 'x86-linux'
- when 'powerpc-darwin' then # was Gem::Platform::DARWIN
- @new_platform = Gem::Platform.new 'ppc-darwin'
- else
- @new_platform = Gem::Platform.new platform
- end
-
- @platform = @new_platform.to_s
-
- @new_platform
- end
-
- overwrite_accessor :required_ruby_version= do |value|
- @required_ruby_version = Gem::Requirement.create(value)
- end
-
- overwrite_accessor :required_rubygems_version= do |value|
- @required_rubygems_version = Gem::Requirement.create(value)
- end
-
- overwrite_accessor :date= do |date|
- # We want to end up with a Time object with one-day resolution.
- # This is the cleanest, most-readable, faster-than-using-Date
- # way to do it.
- case date
- when String then
- @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then
- Time.local($1.to_i, $2.to_i, $3.to_i)
- else
- require 'time'
- Time.parse date
- end
- when Time then
- @date = Time.local(date.year, date.month, date.day)
- when Date then
- @date = Time.local(date.year, date.month, date.day)
- else
- @date = TODAY
- end
- end
-
- overwrite_accessor :date do
- self.date = nil if @date.nil? # HACK Sets the default value for date
- @date
- end
-
- overwrite_accessor :summary= do |str|
- @summary = if str then
- str.strip.
- gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').
- gsub(/\n[ \t]*/, " ")
- end
- end
-
- overwrite_accessor :description= do |str|
- @description = if str then
- str.strip.
- gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').
- gsub(/\n[ \t]*/, " ")
- end
- end
-
- overwrite_accessor :default_executable do
- begin
- if defined?(@default_executable) and @default_executable
- result = @default_executable
- elsif @executables and @executables.size == 1
- result = Array(@executables).first
- else
- result = nil
- end
- result
- rescue
- nil
- end
- end
-
def add_bindir(executables)
return nil if executables.nil?
@@ -488,17 +403,9 @@
return nil
end
- overwrite_accessor :files do
- result = []
- result.push(*@files) if defined?(@files)
- result.push(*@test_files) if defined?(@test_files)
- result.push(*(add_bindir(@executables)))
- result.push(*@extra_rdoc_files) if defined?(@extra_rdoc_files)
- result.push(*@extensions) if defined?(@extensions)
- result.uniq.compact
- end
+ ##
+ # Files in the Gem under one of the require_paths
- # Files in the Gem under one of the require_paths
def lib_files
@files.select do |file|
require_paths.any? do |path|
@@ -507,34 +414,25 @@
end
end
- overwrite_accessor :test_files do
- # Handle the possibility that we have @test_suite_file but not
- # @test_files. This will happen when an old gem is loaded via
- # YAML.
- if defined? @test_suite_file then
- @test_files = [@test_suite_file].flatten
- @test_suite_file = nil
- end
- if defined?(@test_files) and @test_files then
- @test_files
- else
- @test_files = []
- end
+ ##
+ # True if this gem was loaded from disk
+
+ alias :loaded? :loaded
+
+ ##
+ # True if this gem has files in test_files
+
+ def has_unit_tests?
+ not test_files.empty?
end
- # Predicates -----------------------------------------------------
+ alias has_test_suite? has_unit_tests? # :nodoc: deprecated
- def loaded?; @loaded ? true : false ; end
- def has_rdoc?; has_rdoc ? true : false ; end
- def has_unit_tests?; not test_files.empty?; end
- alias has_test_suite? has_unit_tests? # (deprecated)
-
- # Constructors ---------------------------------------------------
-
+ ##
# Specification constructor. Assigns the default values to the
# attributes, adds this spec to the list of loaded specs (see
# Specification.list), and yields itself for further initialization.
- #
+
def initialize
@new_platform = nil
assign_defaults
@@ -547,11 +445,13 @@
@@gather.call(self) if @@gather
end
- # Each attribute has a default value (possibly nil). Here, we
- # initialize all attributes to their default value. This is
- # done through the accessor methods, so special behaviours will
- # be honored. Furthermore, we take a _copy_ of the default so
- # each specification instance has its own empty arrays, etc.
+ ##
+ # Each attribute has a default value (possibly nil). Here, we initialize
+ # all attributes to their default value. This is done through the
+ # accessor methods, so special behaviours will be honored. Furthermore,
+ # we take a _copy_ of the default so each specification instance has its
+ # own empty arrays, etc.
+
def assign_defaults
@@nil_attributes.each do |name|
instance_variable_set name, nil
@@ -570,13 +470,14 @@
instance_variable_set :@new_platform, Gem::Platform::RUBY
end
- # Special loader for YAML files. When a Specification object is
- # loaded from a YAML file, it bypasses the normal Ruby object
- # initialization routine (#initialize). This method makes up for
- # that and deals with gems of different ages.
+ ##
+ # Special loader for YAML files. When a Specification object is loaded
+ # from a YAML file, it bypasses the normal Ruby object initialization
+ # routine (#initialize). This method makes up for that and deals with
+ # gems of different ages.
#
# 'input' can be anything that YAML.load() accepts: String or IO.
- #
+
def self.from_yaml(input)
input = normalize_yaml_input input
spec = YAML.load input
@@ -599,6 +500,9 @@
spec
end
+ ##
+ # Loads ruby format gemspec from +filename+
+
def self.load(filename)
gemspec = nil
fail "NESTED Specification.load calls not allowed!" if @@gather
@@ -610,22 +514,25 @@
@@gather = nil
end
- # Make sure the yaml specification is properly formatted with dashes.
+ ##
+ # Make sure the YAML specification is properly formatted with dashes
+
def self.normalize_yaml_input(input)
result = input.respond_to?(:read) ? input.read : input
result = "--- " + result unless result =~ /^--- /
result
end
- # Instance methods -----------------------------------------------
-
- # Sets the rubygems_version to Gem::RubyGemsVersion.
- #
+ ##
+ # Sets the rubygems_version to the current RubyGems version
+
def mark_version
@rubygems_version = RubyGemsVersion
end
- # Ignore unknown attributes if the
+ ##
+ # Ignore unknown attributes while loading
+
def method_missing(sym, *a, &b) # :nodoc:
if @specification_version > CURRENT_SPECIFICATION_VERSION and
sym.to_s =~ /=$/ then
@@ -635,31 +542,39 @@
end
end
- # Adds a dependency to this Gem. For example,
+ ##
+ # Adds a development dependency named +gem+ with +requirements+ to this
+ # Gem. For example:
#
- # spec.add_dependency('jabber4r', '> 0.1', '<= 0.5')
+ # spec.add_development_dependency 'jabber4r', '> 0.1', '<= 0.5'
#
- # gem:: [String or Gem::Dependency] The Gem name/dependency.
- # requirements:: [default=">= 0"] The version requirements.
+ # Development dependencies aren't installed by default and aren't
+ # activated when a gem is required.
+
+ def add_development_dependency(gem, *requirements)
+ add_dependency_with_type(gem, :development, *requirements)
+ end
+
+ ##
+ # Adds a runtime dependency named +gem+ with +requirements+ to this Gem.
+ # For example:
#
- def add_dependency(gem, *requirements)
- requirements = if requirements.empty? then
- Gem::Requirement.default
- else
- requirements.flatten
- end
+ # spec.add_runtime_dependency 'jabber4r', '> 0.1', '<= 0.5'
- unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements)
- gem = Dependency.new(gem, requirements)
- end
+ def add_runtime_dependency(gem, *requirements)
+ add_dependency_with_type(gem, :runtime, *requirements)
+ end
- dependencies << gem
- end
-
+ ##
+ # Adds a runtime dependency
+
+ alias add_dependency add_runtime_dependency
+
+ ##
# Returns the full name (name-version) of this Gem. Platform information
- # is included (name-version-platform) if it is specified (and not the
- # default Ruby platform).
- #
+ # is included (name-version-platform) if it is specified and not the
+ # default Ruby platform.
+
def full_name
if platform == Gem::Platform::RUBY or platform.nil? then
"#{@name}-#{@version}"
@@ -668,9 +583,10 @@
end
end
+ ##
# Returns the full name (name-version) of this gemspec using the original
- # platform.
- #
+ # platform. For use with legacy gems.
+
def original_name # :nodoc:
if platform == Gem::Platform::RUBY or platform.nil? then
"#{@name}-#{@version}"
@@ -679,42 +595,41 @@
end
end
+ ##
# The full path to the gem (install path + full name).
- #
- # return:: [String] the full gem path
- #
+
def full_gem_path
path = File.join installation_path, 'gems', full_name
return path if File.directory? path
File.join installation_path, 'gems', original_name
end
-
+
+ ##
# The default (generated) file name of the gem.
+
def file_name
full_name + ".gem"
end
-
- # The root directory that the gem was installed into.
- #
- # return:: [String] the installation path
- #
+
+ ##
+ # The directory that this gem was installed into.
+
def installation_path
- (File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]).
- join(File::SEPARATOR)
+ path = File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]
+ path = path.join File::SEPARATOR
+ File.expand_path path
end
-
- # Checks if this Specification meets the requirement of the supplied
- # dependency.
- #
- # dependency:: [Gem::Dependency] the dependency to check
- # return:: [Boolean] true if dependency is met, otherwise false
- #
+
+ ##
+ # Checks if this specification meets the requirement of +dependency+.
+
def satisfies_requirement?(dependency)
return @name == dependency.name &&
dependency.version_requirements.satisfied_by?(@version)
end
- # Comparison methods ---------------------------------------------
+ ##
+ # Returns an object you can use to sort specifications in #sort_by.
def sort_obj
[@name, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1]
@@ -724,19 +639,25 @@
sort_obj <=> other.sort_obj
end
+ ##
# Tests specs for equality (across all attributes).
+
def ==(other) # :nodoc:
self.class === other && same_attributes?(other)
end
alias eql? == # :nodoc:
+ ##
+ # True if this gem has the same attributes as +other+.
+
def same_attributes?(other)
@@attributes.each do |name, default|
return false unless self.send(name) == other.send(name)
end
true
end
+
private :same_attributes?
def hash # :nodoc:
@@ -746,8 +667,6 @@
} / @@attributes.length # XXX because NSObject#hash is 'unsigned long' and this returns a bignum
end
- # Export methods (YAML and Ruby code) ----------------------------
-
def to_yaml(opts = {}) # :nodoc:
mark_version
@@ -784,12 +703,16 @@
self.platform = Gem::Platform.new @platform
end
+ ##
# Returns a Ruby code representation of this specification, such that it
# can be eval'ed and reconstruct the same specification later. Attributes
# that still have their default values are omitted.
+
def to_ruby
mark_version
result = []
+ result << "# -*- encoding: utf-8 -*-"
+ result << nil
result << "Gem::Specification.new do |s|"
result << " s.name = #{ruby_code name}"
@@ -798,8 +721,6 @@
result << " s.platform = #{ruby_code original_platform}"
end
result << ""
- result << " s.specification_version = #{specification_version} if s.respond_to? :specification_version="
- result << ""
result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
handled = [
@@ -822,29 +743,55 @@
end
end
- result << "" unless dependencies.empty?
+ result << nil
+ result << " if s.respond_to? :specification_version then"
+ result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION"
+ result << " s.specification_version = #{specification_version}"
+ result << nil
- dependencies.each do |dep|
- version_reqs_param = dep.requirements_list.inspect
- result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ result << " if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then"
+
+ unless dependencies.empty? then
+ dependencies.each do |dep|
+ version_reqs_param = dep.requirements_list.inspect
+ dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
+ result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ end
end
+ result << " else"
+
+ unless dependencies.empty? then
+ dependencies.each do |dep|
+ version_reqs_param = dep.requirements_list.inspect
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ end
+ end
+
+ result << ' end'
+
+ result << " else"
+ dependencies.each do |dep|
+ version_reqs_param = dep.requirements_list.inspect
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ end
+ result << " end"
+
result << "end"
- result << ""
+ result << nil
result.join "\n"
end
- # Validation and normalization methods ---------------------------
+ ##
+ # Checks that the specification contains all required fields, and does a
+ # very basic sanity check.
+ #
+ # Raises InvalidSpecificationException if the spec does not pass the
+ # checks..
- # Checks that the specification contains all required fields, and
- # does a very basic sanity check.
- #
- # Raises InvalidSpecificationException if the spec does not pass
- # the checks..
def validate
extend Gem::UserInteraction
-
normalize
if rubygems_version != RubyGemsVersion then
@@ -899,13 +846,14 @@
true
end
+ ##
# Normalize the list of files so that:
# * All file lists have redundancies removed.
- # * Files referenced in the extra_rdoc_files are included in the
- # package file list.
+ # * Files referenced in the extra_rdoc_files are included in the package
+ # file list.
#
- # Also, the summary and description are converted to a normal
- # format.
+ # Also, the summary and description are converted to a normal format.
+
def normalize
if defined?(@extra_rdoc_files) and @extra_rdoc_files then
@extra_rdoc_files.uniq!
@@ -915,15 +863,12 @@
@files.uniq! if @files
end
- # Dependency methods ---------------------------------------------
-
- # Return a list of all gems that have a dependency on this
- # gemspec. The list is structured with entries that conform to:
+ ##
+ # Return a list of all gems that have a dependency on this gemspec. The
+ # list is structured with entries that conform to:
#
# [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]]
- #
- # return:: [Array] [[dependent_gem, dependency, [list_of_satisfiers]]]
- #
+
def dependent_gems
out = []
Gem.source_index.each do |name,gem|
@@ -944,8 +889,24 @@
"#<Gem::Specification name=#{@name} version=#{@version}>"
end
- private
+ def add_dependency_with_type(dependency, type, *requirements)
+ requirements = if requirements.empty? then
+ Gem::Requirement.default
+ else
+ requirements.flatten
+ end
+ unless dependency.respond_to?(:name) &&
+ dependency.respond_to?(:version_requirements)
+
+ dependency = Dependency.new(dependency, requirements, type)
+ end
+
+ dependencies << dependency
+ end
+
+ private :add_dependency_with_type
+
def find_all_satisfiers(dep)
Gem.source_index.each do |name,gem|
if(gem.satisfies_requirement?(dep)) then
@@ -954,8 +915,12 @@
end
end
- # Return a string containing a Ruby code representation of the
- # given object.
+ private :find_all_satisfiers
+
+ ##
+ # Return a string containing a Ruby code representation of the given
+ # object.
+
def ruby_code(obj)
case obj
when String then '%q{' + obj + '}'
@@ -970,7 +935,327 @@
else raise Exception, "ruby_code case not handled: #{obj.class}"
end
end
+
+ private :ruby_code
+ # :section: Required gemspec attributes
+
+ ##
+ # The version of RubyGems used to create this gem
+
+ required_attribute :rubygems_version, Gem::RubyGemsVersion
+
+ ##
+ # The Gem::Specification version of this gemspec
+
+ required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION
+
+ ##
+ # This gem's name
+
+ required_attribute :name
+
+ ##
+ # This gem's version
+
+ required_attribute :version
+
+ ##
+ # The date this gem was created
+
+ required_attribute :date, TODAY
+
+ ##
+ # A short summary of this gem's description. Displayed in `gem list -d`.
+
+ required_attribute :summary
+
+ ##
+ # Paths in the gem to add to $LOAD_PATH when this gem is activated
+
+ required_attribute :require_paths, ['lib']
+
+ # :section: Optional gemspec attributes
+
+ ##
+ # A contact email for this gem
+
+ attribute :email
+
+ ##
+ # The URL of this gem's home page
+
+ attribute :homepage
+
+ ##
+ # The rubyforge project this gem lives under. i.e. RubyGems'
+ # rubyforge_project is "rubygems".
+
+ attribute :rubyforge_project
+
+ ##
+ # A long description of this gem
+
+ attribute :description
+
+ ##
+ # Autorequire was used by old RubyGems to automatically require a file.
+ # It no longer is supported.
+
+ attribute :autorequire
+
+ ##
+ # The default executable for this gem.
+
+ attribute :default_executable
+
+ ##
+ # The path in the gem for executable scripts
+
+ attribute :bindir, 'bin'
+
+ ##
+ # True if this gem is RDoc-compliant
+
+ attribute :has_rdoc, false
+
+ ##
+ # True if this gem supports RDoc
+
+ alias :has_rdoc? :has_rdoc
+
+ ##
+ # The ruby of version required by this gem
+
+ attribute :required_ruby_version, Gem::Requirement.default
+
+ ##
+ # The RubyGems version required by this gem
+
+ attribute :required_rubygems_version, Gem::Requirement.default
+
+ ##
+ # The platform this gem runs on. See Gem::Platform for details.
+
+ attribute :platform, Gem::Platform::RUBY
+
+ ##
+ # The key used to sign this gem. See Gem::Security for details.
+
+ attribute :signing_key, nil
+
+ ##
+ # The certificate chain used to sign this gem. See Gem::Security for
+ # details.
+
+ attribute :cert_chain, []
+
+ ##
+ # A message that gets displayed after the gem is installed
+
+ attribute :post_install_message, nil
+
+ ##
+ # The list of authors who wrote this gem
+
+ array_attribute :authors
+
+ ##
+ # Files included in this gem
+
+ array_attribute :files
+
+ ##
+ # Test files included in this gem
+
+ array_attribute :test_files
+
+ ##
+ # An ARGV-style array of options to RDoc
+
+ array_attribute :rdoc_options
+
+ ##
+ # Extra files to add to RDoc
+
+ array_attribute :extra_rdoc_files
+
+ ##
+ # Executables included in the gem
+
+ array_attribute :executables
+
+ ##
+ # Extensions to build when installing the gem. See
+ # Gem::Installer#build_extensions for valid values.
+
+ array_attribute :extensions
+
+ ##
+ # An array or things required by this gem. Not used by anything
+ # presently.
+
+ array_attribute :requirements
+
+ ##
+ # A list of Gem::Dependency objects this gem depends on. Only appendable.
+
+ array_attribute :dependencies
+
+ read_only :dependencies
+
+ # :section: Aliased gemspec attributes
+
+ ##
+ # Singular accessor for executables
+
+ attribute_alias_singular :executable, :executables
+
+ ##
+ # Singular accessor for authors
+
+ attribute_alias_singular :author, :authors
+
+ ##
+ # Singular accessor for require_paths
+
+ attribute_alias_singular :require_path, :require_paths
+
+ ##
+ # Singular accessor for test_files
+
+ attribute_alias_singular :test_file, :test_files
+
+ overwrite_accessor :version= do |version|
+ @version = Version.create(version)
+ end
+
+ overwrite_accessor :platform do
+ @new_platform
+ end
+
+ overwrite_accessor :platform= do |platform|
+ if @original_platform.nil? or
+ @original_platform == Gem::Platform::RUBY then
+ @original_platform = platform
+ end
+
+ case platform
+ when Gem::Platform::CURRENT then
+ @new_platform = Gem::Platform.local
+ @original_platform = @new_platform.to_s
+
+ when Gem::Platform then
+ @new_platform = platform
+
+ # legacy constants
+ when nil, Gem::Platform::RUBY then
+ @new_platform = Gem::Platform::RUBY
+ when 'mswin32' then # was Gem::Platform::WIN32
+ @new_platform = Gem::Platform.new 'x86-mswin32'
+ when 'i586-linux' then # was Gem::Platform::LINUX_586
+ @new_platform = Gem::Platform.new 'x86-linux'
+ when 'powerpc-darwin' then # was Gem::Platform::DARWIN
+ @new_platform = Gem::Platform.new 'ppc-darwin'
+ else
+ @new_platform = Gem::Platform.new platform
+ end
+
+ @platform = @new_platform.to_s
+
+ @new_platform
+ end
+
+ overwrite_accessor :required_ruby_version= do |value|
+ @required_ruby_version = Gem::Requirement.create(value)
+ end
+
+ overwrite_accessor :required_rubygems_version= do |value|
+ @required_rubygems_version = Gem::Requirement.create(value)
+ end
+
+ overwrite_accessor :date= do |date|
+ # We want to end up with a Time object with one-day resolution.
+ # This is the cleanest, most-readable, faster-than-using-Date
+ # way to do it.
+ case date
+ when String then
+ @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then
+ Time.local($1.to_i, $2.to_i, $3.to_i)
+ else
+ require 'time'
+ Time.parse date
+ end
+ when Time then
+ @date = Time.local(date.year, date.month, date.day)
+ when Date then
+ @date = Time.local(date.year, date.month, date.day)
+ else
+ @date = TODAY
+ end
+ end
+
+ overwrite_accessor :date do
+ self.date = nil if @date.nil? # HACK Sets the default value for date
+ @date
+ end
+
+ overwrite_accessor :summary= do |str|
+ @summary = if str then
+ str.strip.
+ gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').
+ gsub(/\n[ \t]*/, " ")
+ end
+ end
+
+ overwrite_accessor :description= do |str|
+ @description = if str then
+ str.strip.
+ gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').
+ gsub(/\n[ \t]*/, " ")
+ end
+ end
+
+ overwrite_accessor :default_executable do
+ begin
+ if defined?(@default_executable) and @default_executable
+ result = @default_executable
+ elsif @executables and @executables.size == 1
+ result = Array(@executables).first
+ else
+ result = nil
+ end
+ result
+ rescue
+ nil
+ end
+ end
+
+ overwrite_accessor :test_files do
+ # Handle the possibility that we have @test_suite_file but not
+ # @test_files. This will happen when an old gem is loaded via
+ # YAML.
+ if defined? @test_suite_file then
+ @test_files = [@test_suite_file].flatten
+ @test_suite_file = nil
+ end
+ if defined?(@test_files) and @test_files then
+ @test_files
+ else
+ @test_files = []
+ end
+ end
+
+ overwrite_accessor :files do
+ result = []
+ result.push(*@files) if defined?(@files)
+ result.push(*@test_files) if defined?(@test_files)
+ result.push(*(add_bindir(@executables)))
+ result.push(*@extra_rdoc_files) if defined?(@extra_rdoc_files)
+ result.push(*@extensions) if defined?(@extensions)
+ result.uniq.compact
+ end
+
end
end
Copied: MacRuby/branches/experimental/lib/rubygems/test_utilities.rb (from rev 1886, MacRuby/trunk/lib/rubygems/test_utilities.rb)
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/test_utilities.rb (rev 0)
+++ MacRuby/branches/experimental/lib/rubygems/test_utilities.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -0,0 +1,131 @@
+require 'tempfile'
+require 'rubygems'
+require 'rubygems/remote_fetcher'
+
+##
+# A fake Gem::RemoteFetcher for use in tests or to avoid real live HTTP
+# requests when testing code that uses RubyGems.
+#
+# Example:
+#
+# @fetcher = Gem::FakeFetcher.new
+# @fetcher.data['http://gems.example.com/yaml'] = source_index.to_yaml
+# Gem::RemoteFetcher.fetcher = @fetcher
+#
+# # invoke RubyGems code
+#
+# paths = @fetcher.paths
+# assert_equal 'http://gems.example.com/yaml', paths.shift
+# assert paths.empty?, paths.join(', ')
+#
+# See RubyGems' tests for more examples of FakeFetcher.
+
+class Gem::FakeFetcher
+
+ attr_reader :data
+ attr_accessor :paths
+
+ def initialize
+ @data = {}
+ @paths = []
+ end
+
+ def fetch_path path, mtime = nil
+ path = path.to_s
+ @paths << path
+ raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
+
+ unless @data.key? path then
+ raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
+ end
+
+ data = @data[path]
+
+ if data.respond_to?(:call) then
+ data.call
+ else
+ if path.to_s =~ /gz$/ and not data.nil? and not data.empty? then
+ data = Gem.gunzip data
+ end
+
+ data
+ end
+ end
+
+ def fetch_size(path)
+ path = path.to_s
+ @paths << path
+
+ raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
+
+ unless @data.key? path then
+ raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
+ end
+
+ data = @data[path]
+
+ data.respond_to?(:call) ? data.call : data.length
+ end
+
+ def download spec, source_uri, install_dir = Gem.dir
+ name = "#{spec.full_name}.gem"
+ path = File.join(install_dir, 'cache', name)
+
+ Gem.ensure_gem_subdirectories install_dir
+
+ if source_uri =~ /^http/ then
+ File.open(path, "wb") do |f|
+ f.write fetch_path(File.join(source_uri, "gems", name))
+ end
+ else
+ FileUtils.cp source_uri, path
+ end
+
+ path
+ end
+
+end
+
+# :stopdoc:
+class Gem::RemoteFetcher
+
+ def self.fetcher=(fetcher)
+ @fetcher = fetcher
+ end
+
+end
+# :startdoc:
+
+##
+# A StringIO duck-typed class that uses Tempfile instead of String as the
+# backing store.
+#--
+# This class was added to flush out problems in Rubinius' IO implementation.
+
+class TempIO
+
+ @@count = 0
+
+ def initialize(string = '')
+ @tempfile = Tempfile.new "TempIO-#{@@count += 1}"
+ @tempfile.binmode
+ @tempfile.write string
+ @tempfile.rewind
+ end
+
+ def method_missing(meth, *args, &block)
+ @tempfile.send(meth, *args, &block)
+ end
+
+ def respond_to?(meth)
+ @tempfile.respond_to? meth
+ end
+
+ def string
+ @tempfile.flush
+
+ Gem.read_binary @tempfile.path
+ end
+
+end
+
Modified: MacRuby/branches/experimental/lib/rubygems/uninstaller.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/uninstaller.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/uninstaller.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -18,10 +18,24 @@
include Gem::UserInteraction
##
- # Constructs an Uninstaller instance
- #
- # gem:: [String] The Gem name to uninstall
+ # The directory a gem's executables will be installed into
+ attr_reader :bin_dir
+
+ ##
+ # The gem repository the gem will be installed into
+
+ attr_reader :gem_home
+
+ ##
+ # The Gem::Specification for the gem being uninstalled, only set during
+ # #uninstall_gem
+
+ attr_reader :spec
+
+ ##
+ # Constructs an uninstaller that will uninstall +gem+
+
def initialize(gem, options = {})
@gem = gem
@version = options[:version] || Gem::Requirement.default
@@ -30,43 +44,64 @@
@force_executables = options[:executables]
@force_all = options[:all]
@force_ignore = options[:ignore]
- @bin_dir = options[:bin_dir]
+ @bin_dir = options[:bin_dir]
+
+ spec_dir = File.join @gem_home, 'specifications'
+ @source_index = Gem::SourceIndex.from_gems_in spec_dir
end
##
- # Performs the uninstall of the Gem. This removes the spec, the
- # Gem directory, and the cached .gem file,
+ # Performs the uninstall of the gem. This removes the spec, the Gem
+ # directory, and the cached .gem file.
def uninstall
- list = Gem.source_index.search(/^#{@gem}$/, @version)
+ list = @source_index.find_name @gem, @version
if list.empty? then
- raise Gem::InstallError, "Unknown gem #{@gem}-#{@version}"
- elsif list.size > 1 && @force_all
- remove_all(list.dup)
- remove_executables(list.last)
- elsif list.size > 1
- say
+ raise Gem::InstallError, "Unknown gem #{@gem} #{@version}"
+
+ elsif list.size > 1 and @force_all then
+ remove_all list.dup
+
+ elsif list.size > 1 then
gem_names = list.collect {|gem| gem.full_name} + ["All versions"]
- gem_name, index =
- choose_from_list("Select gem to uninstall:", gem_names)
- if index == list.size
- remove_all(list.dup)
- remove_executables(list.last)
- elsif index >= 0 && index < list.size
- to_remove = list[index]
- remove(to_remove, list)
- remove_executables(to_remove)
+
+ say
+ gem_name, index = choose_from_list "Select gem to uninstall:", gem_names
+
+ if index == list.size then
+ remove_all list.dup
+ elsif index >= 0 && index < list.size then
+ uninstall_gem list[index], list.dup
else
say "Error: must enter a number [1-#{list.size+1}]"
end
else
- remove(list[0], list.dup)
- remove_executables(list.last)
+ uninstall_gem list.first, list.dup
end
end
-
+
##
+ # Uninstalls gem +spec+
+
+ def uninstall_gem(spec, specs)
+ @spec = spec
+
+ Gem.pre_uninstall_hooks.each do |hook|
+ hook.call self
+ end
+
+ specs.each { |s| remove_executables s }
+ remove spec, specs
+
+ Gem.post_uninstall_hooks.each do |hook|
+ hook.call self
+ end
+
+ @spec = nil
+ end
+
+ ##
# Removes installed executables and batch files (windows only) for
# +gemspec+.
@@ -76,7 +111,7 @@
if gemspec.executables.size > 0 then
bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
- list = Gem.source_index.search(gemspec.name).delete_if { |spec|
+ list = @source_index.find_name(gemspec.name).delete_if { |spec|
spec.version == gemspec.version
}
@@ -111,14 +146,14 @@
end
end
end
-
+
##
# Removes all gems in +list+.
#
# NOTE: removes uninstalled gems from +list+.
def remove_all(list)
- list.dup.each { |spec| remove spec, list }
+ list.dup.each { |spec| uninstall_gem spec, list }
end
##
@@ -176,16 +211,16 @@
end
def path_ok?(spec)
- match_path = File.join @gem_home, 'gems', spec.full_name
+ full_path = File.join @gem_home, 'gems', spec.full_name
+ original_path = File.join @gem_home, 'gems', spec.original_name
- match_path == spec.full_gem_path
+ full_path == spec.full_gem_path || original_path == spec.full_gem_path
end
def dependencies_ok?(spec)
return true if @force_ignore
- source_index = Gem::SourceIndex.from_installed_gems
- deplist = Gem::DependencyList.from_source_index source_index
+ deplist = Gem::DependencyList.from_source_index @source_index
deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec)
end
Modified: MacRuby/branches/experimental/lib/rubygems/user_interaction.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/user_interaction.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/user_interaction.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,54 +6,71 @@
module Gem
- ####################################################################
- # Module that defines the default UserInteraction. Any class
- # including this module will have access to the +ui+ method that
- # returns the default UI.
+ ##
+ # Module that defines the default UserInteraction. Any class including this
+ # module will have access to the +ui+ method that returns the default UI.
+
module DefaultUserInteraction
+ ##
+ # The default UI is a class variable of the singleton class for this
+ # module.
+
+ @ui = nil
+
+ ##
# Return the default UI.
+
+ def self.ui
+ @ui ||= Gem::ConsoleUI.new
+ end
+
+ ##
+ # Set the default UI. If the default UI is never explicitly set, a simple
+ # console based UserInteraction will be used automatically.
+
+ def self.ui=(new_ui)
+ @ui = new_ui
+ end
+
+ ##
+ # Use +new_ui+ for the duration of +block+.
+
+ def self.use_ui(new_ui)
+ old_ui = @ui
+ @ui = new_ui
+ yield
+ ensure
+ @ui = old_ui
+ end
+
+ ##
+ # See DefaultUserInteraction::ui
+
def ui
DefaultUserInteraction.ui
end
- # Set the default UI. If the default UI is never explicity set, a
- # simple console based UserInteraction will be used automatically.
+ ##
+ # See DefaultUserInteraction::ui=
+
def ui=(new_ui)
DefaultUserInteraction.ui = new_ui
end
+ ##
+ # See DefaultUserInteraction::use_ui
+
def use_ui(new_ui, &block)
DefaultUserInteraction.use_ui(new_ui, &block)
end
- # The default UI is a class variable of the singleton class for
- # this module.
-
- @ui = nil
-
- class << self
- def ui
- @ui ||= Gem::ConsoleUI.new
- end
- def ui=(new_ui)
- @ui = new_ui
- end
- def use_ui(new_ui)
- old_ui = @ui
- @ui = new_ui
- yield
- ensure
- @ui = old_ui
- end
- end
end
- ####################################################################
+ ##
# Make the default UI accessable without the "ui." prefix. Classes
- # including this module may use the interaction methods on the
- # default UI directly. Classes may also reference the +ui+ and
- # <tt>ui=</tt> methods.
+ # including this module may use the interaction methods on the default UI
+ # directly. Classes may also reference the ui and ui= methods.
#
# Example:
#
@@ -64,22 +81,30 @@
# n = ask("What is the meaning of life?")
# end
# end
+
module UserInteraction
+
include DefaultUserInteraction
- [
- :choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning,
- :alert_error, :terminate_interaction
- ].each do |methname|
+
+ [:alert,
+ :alert_error,
+ :alert_warning,
+ :ask,
+ :ask_yes_no,
+ :choose_from_list,
+ :say,
+ :terminate_interaction ].each do |methname|
class_eval %{
def #{methname}(*args)
ui.#{methname}(*args)
end
- }
+ }, __FILE__, __LINE__
end
end
- ####################################################################
+ ##
# StreamUI implements a simple stream based user interface.
+
class StreamUI
attr_reader :ins, :outs, :errs
@@ -89,15 +114,19 @@
@outs = out_stream
@errs = err_stream
end
-
- # Choose from a list of options. +question+ is a prompt displayed
- # above the list. +list+ is a list of option strings. Returns
- # the pair [option_name, option_index].
+
+ ##
+ # Choose from a list of options. +question+ is a prompt displayed above
+ # the list. +list+ is a list of option strings. Returns the pair
+ # [option_name, option_index].
+
def choose_from_list(question, list)
@outs.puts question
+
list.each_with_index do |item, index|
@outs.puts " #{index+1}. #{item}"
end
+
@outs.print "> "
@outs.flush
@@ -109,28 +138,32 @@
return list[result], result
end
- # Ask a question. Returns a true for yes, false for no. If not
- # connected to a tty, raises an exception if default is nil,
- # otherwise returns default.
+ ##
+ # Ask a question. Returns a true for yes, false for no. If not connected
+ # to a tty, raises an exception if default is nil, otherwise returns
+ # default.
+
def ask_yes_no(question, default=nil)
- if not @ins.tty? then
+ unless @ins.tty? then
if default.nil? then
- raise(
- Gem::OperationNotSupportedError,
- "Not connected to a tty and no default specified")
+ raise Gem::OperationNotSupportedError,
+ "Not connected to a tty and no default specified"
else
return default
end
end
+
qstr = case default
- when nil
- 'yn'
- when true
- 'Yn'
- else
- 'yN'
- end
+ when nil
+ 'yn'
+ when true
+ 'Yn'
+ else
+ 'yN'
+ end
+
result = nil
+
while result.nil?
result = ask("#{question} [#{qstr}]")
result = case result
@@ -144,51 +177,68 @@
nil
end
end
+
return result
end
-
- # Ask a question. Returns an answer if connected to a tty, nil
- # otherwise.
+
+ ##
+ # Ask a question. Returns an answer if connected to a tty, nil otherwise.
+
def ask(question)
return nil if not @ins.tty?
+
@outs.print(question + " ")
@outs.flush
+
result = @ins.gets
result.chomp! if result
result
end
-
+
+ ##
# Display a statement.
+
def say(statement="")
@outs.puts statement
end
-
- # Display an informational alert.
+
+ ##
+ # Display an informational alert. Will ask +question+ if it is not nil.
+
def alert(statement, question=nil)
@outs.puts "INFO: #{statement}"
- return ask(question) if question
+ ask(question) if question
end
-
- # Display a warning in a location expected to get error messages.
+
+ ##
+ # Display a warning in a location expected to get error messages. Will
+ # ask +question+ if it is not nil.
+
def alert_warning(statement, question=nil)
@errs.puts "WARNING: #{statement}"
- ask(question) if question
+ ask(question) if question
end
-
- # Display an error message in a location expected to get error
- # messages.
+
+ ##
+ # Display an error message in a location expected to get error messages.
+ # Will ask +question+ if it is not nil.
+
def alert_error(statement, question=nil)
@errs.puts "ERROR: #{statement}"
ask(question) if question
end
- # Terminate the appliation normally, running any exit handlers
- # that might have been defined.
+ ##
+ # Terminate the application with exit code +status+, running any exit
+ # handlers that might have been defined.
+
def terminate_interaction(status = 0)
raise Gem::SystemExitException, status
end
- # Return a progress reporter object
+ ##
+ # Return a progress reporter object chosen from the current verbosity.
+
def progress_reporter(*args)
case Gem.configuration.verbose
when nil, false
@@ -200,6 +250,9 @@
end
end
+ ##
+ # An absolutely silent progress reporter.
+
class SilentProgressReporter
attr_reader :count
@@ -213,6 +266,9 @@
end
end
+ ##
+ # A basic dotted progress reporter.
+
class SimpleProgressReporter
include DefaultUserInteraction
@@ -228,17 +284,27 @@
@out.puts initial_message
end
+ ##
+ # Prints out a dot and ignores +message+.
+
def updated(message)
@count += 1
@out.print "."
@out.flush
end
+ ##
+ # Prints out the terminal message.
+
def done
@out.puts "\n#{@terminal_message}"
end
+
end
+ ##
+ # A progress reporter that prints out messages about the current progress.
+
class VerboseProgressReporter
include DefaultUserInteraction
@@ -254,32 +320,41 @@
@out.puts initial_message
end
+ ##
+ # Prints out the position relative to the total and the +message+.
+
def updated(message)
@count += 1
@out.puts "#{@count}/#{@total}: #{message}"
end
+ ##
+ # Prints out the terminal message.
+
def done
@out.puts @terminal_message
end
end
end
- ####################################################################
- # Subclass of StreamUI that instantiates the user interaction using
- # standard in, out and error.
+ ##
+ # Subclass of StreamUI that instantiates the user interaction using STDIN,
+ # STDOUT, and STDERR.
+
class ConsoleUI < StreamUI
def initialize
super(STDIN, STDOUT, STDERR)
end
end
- ####################################################################
+ ##
# SilentUI is a UI choice that is absolutely silent.
+
class SilentUI
def method_missing(sym, *args, &block)
self
end
end
+
end
Modified: MacRuby/branches/experimental/lib/rubygems/validator.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/validator.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/validator.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -10,118 +10,130 @@
require 'rubygems/format'
require 'rubygems/installer'
-module Gem
+##
+# Validator performs various gem file and gem database validation
- ##
- # Validator performs various gem file and gem database validation
- class Validator
- include UserInteraction
+class Gem::Validator
- ##
- # Given a gem file's contents, validates against its own MD5 checksum
- # gem_data:: [String] Contents of the gem file
- def verify_gem(gem_data)
- raise VerificationError, 'empty gem file' if gem_data.size == 0
+ include Gem::UserInteraction
- unless gem_data =~ /MD5SUM/ then
- return # Don't worry about it...this sucks. Need to fix MD5 stuff for
- # new format
- # FIXME
- end
+ ##
+ # Given a gem file's contents, validates against its own MD5 checksum
+ # gem_data:: [String] Contents of the gem file
- sum_data = gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/,
- "MD5SUM = \"#{"F" * 32}\"")
+ def verify_gem(gem_data)
+ raise Gem::VerificationError, 'empty gem file' if gem_data.size == 0
- unless Gem::MD5.hexdigest(sum_data) == $1.to_s then
- raise VerificationError, 'invalid checksum for gem file'
- end
+ unless gem_data =~ /MD5SUM/ then
+ return # Don't worry about it...this sucks. Need to fix MD5 stuff for
+ # new format
+ # FIXME
end
- ##
- # Given the path to a gem file, validates against its own MD5 checksum
- #
- # gem_path:: [String] Path to gem file
- def verify_gem_file(gem_path)
- File.open gem_path, 'rb' do |file|
- gem_data = file.read
- verify_gem gem_data
- end
- rescue Errno::ENOENT
- raise Gem::VerificationError.new("missing gem file #{gem_path}")
+ sum_data = gem_data.gsub(/MD5SUM = "([a-z0-9]+)"/,
+ "MD5SUM = \"#{"F" * 32}\"")
+
+ unless Gem::MD5.hexdigest(sum_data) == $1.to_s then
+ raise Gem::VerificationError, 'invalid checksum for gem file'
end
+ end
- private
- def find_files_for_gem(gem_directory)
- installed_files = []
- Find.find(gem_directory) {|file_name|
- fn = file_name.slice((gem_directory.size)..(file_name.size-1)).sub(/^\//, "")
- if(!(fn =~ /CVS/ || File.directory?(fn) || fn == "")) then
- installed_files << fn
- end
-
- }
- installed_files
+ ##
+ # Given the path to a gem file, validates against its own MD5 checksum
+ #
+ # gem_path:: [String] Path to gem file
+
+ def verify_gem_file(gem_path)
+ open gem_path, Gem.binary_mode do |file|
+ gem_data = file.read
+ verify_gem gem_data
end
-
+ rescue Errno::ENOENT
+ raise Gem::VerificationError, "missing gem file #{gem_path}"
+ end
- public
- ErrorData = Struct.new(:path, :problem)
+ private
- ##
- # Checks the gem directory for the following potential
- # inconsistencies/problems:
- # * Checksum gem itself
- # * For each file in each gem, check consistency of installed versions
- # * Check for files that aren't part of the gem but are in the gems directory
- # * 1 cache - 1 spec - 1 directory.
- #
- # returns a hash of ErrorData objects, keyed on the problem gem's name.
- def alien
- errors = {}
- Gem::SourceIndex.from_installed_gems.each do |gem_name, gem_spec|
- errors[gem_name] ||= []
- gem_path = File.join(Gem.dir, "cache", gem_spec.full_name) + ".gem"
- spec_path = File.join(Gem.dir, "specifications", gem_spec.full_name) + ".gemspec"
- gem_directory = File.join(Gem.dir, "gems", gem_spec.full_name)
- installed_files = find_files_for_gem(gem_directory)
-
- if(!File.exist?(spec_path)) then
- errors[gem_name] << ErrorData.new(spec_path, "Spec file doesn't exist for installed gem")
- end
-
- begin
- verify_gem_file(gem_path)
- File.open(gem_path, 'rb') do |file|
- format = Gem::Format.from_file_by_path(gem_path)
- format.file_entries.each do |entry, data|
- # Found this file. Delete it from list
- installed_files.delete remove_leading_dot_dir(entry['path'])
+ def find_files_for_gem(gem_directory)
+ installed_files = []
+ Find.find(gem_directory) {|file_name|
+ fn = file_name.slice((gem_directory.size)..(file_name.size-1)).sub(/^\//, "")
+ if(!(fn =~ /CVS/ || File.directory?(fn) || fn == "")) then
+ installed_files << fn
+ end
- next unless data # HACK `gem check -a mkrf`
+ }
+ installed_files
+ end
- File.open(File.join(gem_directory, entry['path']), 'rb') do |f|
- unless Gem::MD5.hexdigest(f.read).to_s ==
- Gem::MD5.hexdigest(data).to_s then
- errors[gem_name] << ErrorData.new(entry['path'], "installed file doesn't match original from gem")
- end
+ public
+
+ ErrorData = Struct.new :path, :problem
+
+ ##
+ # Checks the gem directory for the following potential
+ # inconsistencies/problems:
+ #
+ # * Checksum gem itself
+ # * For each file in each gem, check consistency of installed versions
+ # * Check for files that aren't part of the gem but are in the gems directory
+ # * 1 cache - 1 spec - 1 directory.
+ #
+ # returns a hash of ErrorData objects, keyed on the problem gem's name.
+
+ def alien
+ errors = {}
+
+ Gem::SourceIndex.from_installed_gems.each do |gem_name, gem_spec|
+ errors[gem_name] ||= []
+
+ gem_path = File.join(Gem.dir, "cache", gem_spec.full_name) + ".gem"
+ spec_path = File.join(Gem.dir, "specifications", gem_spec.full_name) + ".gemspec"
+ gem_directory = File.join(Gem.dir, "gems", gem_spec.full_name)
+
+ installed_files = find_files_for_gem(gem_directory)
+
+ unless File.exist? spec_path then
+ errors[gem_name] << ErrorData.new(spec_path, "Spec file doesn't exist for installed gem")
+ end
+
+ begin
+ verify_gem_file(gem_path)
+
+ open gem_path, Gem.binary_mode do |file|
+ format = Gem::Format.from_file_by_path(gem_path)
+ format.file_entries.each do |entry, data|
+ # Found this file. Delete it from list
+ installed_files.delete remove_leading_dot_dir(entry['path'])
+
+ next unless data # HACK `gem check -a mkrf`
+
+ open File.join(gem_directory, entry['path']), Gem.binary_mode do |f|
+ unless Gem::MD5.hexdigest(f.read).to_s ==
+ Gem::MD5.hexdigest(data).to_s then
+ errors[gem_name] << ErrorData.new(entry['path'], "installed file doesn't match original from gem")
end
end
end
- rescue VerificationError => e
- errors[gem_name] << ErrorData.new(gem_path, e.message)
end
- # Clean out directories that weren't explicitly included in the gemspec
- # FIXME: This still allows arbitrary incorrect directories.
- installed_files.delete_if {|potential_directory|
- File.directory?(File.join(gem_directory, potential_directory))
- }
- if(installed_files.size > 0) then
- errors[gem_name] << ErrorData.new(gem_path, "Unmanaged files in gem: #{installed_files.inspect}")
- end
+ rescue Gem::VerificationError => e
+ errors[gem_name] << ErrorData.new(gem_path, e.message)
end
- errors
+
+ # Clean out directories that weren't explicitly included in the gemspec
+ # FIXME: This still allows arbitrary incorrect directories.
+ installed_files.delete_if {|potential_directory|
+ File.directory?(File.join(gem_directory, potential_directory))
+ }
+ if(installed_files.size > 0) then
+ errors[gem_name] << ErrorData.new(gem_path, "Unmanaged files in gem: #{installed_files.inspect}")
+ end
end
+ errors
+ end
+
+ if RUBY_VERSION < '1.9' then
class TestRunner
def initialize(suite, ui)
@suite = suite
@@ -147,40 +159,51 @@
end
autoload :TestRunner, 'test/unit/ui/testrunnerutilities'
-
- ##
- # Runs unit tests for a given gem specification
- def unit_test(gem_spec)
- start_dir = Dir.pwd
- Dir.chdir(gem_spec.full_gem_path)
- $: << File.join(Gem.dir, "gems", gem_spec.full_name)
- # XXX: why do we need this gem_spec when we've already got 'spec'?
- test_files = gem_spec.test_files
- if test_files.empty?
- say "There are no unit tests to run for #{gem_spec.full_name}"
- require 'test/unit/ui/console/testrunner'
- return Test::Unit::TestResult.new
- end
- gem gem_spec.name, "= #{gem_spec.version.version}"
- test_files.each do |f| require f end
+ end
+
+ ##
+ # Runs unit tests for a given gem specification
+
+ def unit_test(gem_spec)
+ start_dir = Dir.pwd
+ Dir.chdir(gem_spec.full_gem_path)
+ $: << File.join(Gem.dir, "gems", gem_spec.full_name)
+ # XXX: why do we need this gem_spec when we've already got 'spec'?
+ test_files = gem_spec.test_files
+
+ if test_files.empty? then
+ say "There are no unit tests to run for #{gem_spec.full_name}"
+ return nil
+ end
+
+ gem gem_spec.name, "= #{gem_spec.version.version}"
+
+ test_files.each do |f| require f end
+
+ if RUBY_VERSION < '1.9' then
suite = Test::Unit::TestSuite.new("#{gem_spec.name}-#{gem_spec.version}")
+
ObjectSpace.each_object(Class) do |klass|
suite << klass.suite if (klass < Test::Unit::TestCase)
end
- result = TestRunner.run(suite, ui())
- unless result.passed?
- alert_error(result.to_s)
- #unless ask_yes_no(result.to_s + "...keep Gem?", true) then
- #Gem::Uninstaller.new(gem_spec.name, gem_spec.version.version).uninstall
- #end
- end
- result
- ensure
- Dir.chdir(start_dir)
+
+ result = TestRunner.run suite, ui
+
+ alert_error result.to_s unless result.passed?
+ else
+ result = MiniTest::Unit.new
+ result.run
end
- def remove_leading_dot_dir(path)
- path.sub(/^\.\//, "")
- end
+ result
+ ensure
+ Dir.chdir(start_dir)
end
+
+ private
+ def remove_leading_dot_dir(path)
+ path.sub(/^\.\//, "")
+ end
+
end
+
Modified: MacRuby/branches/experimental/lib/rubygems/version.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems/version.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems/version.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -8,6 +8,7 @@
##
# The Version class processes string versions into comparable values
+
class Gem::Version
include Comparable
@@ -17,11 +18,8 @@
attr_reader :version
##
- # Checks if version string is valid format
- #
- # str:: [String] the version string
- # return:: [Boolean] true if the string format is correct, otherwise false
- #
+ # Returns true if +version+ is a valid version string.
+
def self.correct?(version)
case version
when Integer, /\A\s*(\d+(\.\d+)*)*\s*\z/ then true
@@ -36,7 +34,7 @@
# ver1 = Version.create('1.3.17') # -> (Version object)
# ver2 = Version.create(ver1) # -> (ver1)
# ver3 = Version.create(nil) # -> nil
- #
+
def self.create(input)
if input.respond_to? :version then
input
@@ -48,10 +46,9 @@
end
##
- # Constructs a version from the supplied string
- #
- # version:: [String] The version string. Format is digit.digit...
- #
+ # Constructs a Version from the +version+ string. A version string is a
+ # series of digits separated by dots.
+
def initialize(version)
raise ArgumentError, "Malformed version number string #{version}" unless
self.class.correct?(version)
@@ -73,7 +70,9 @@
self.version = array[0]
end
+ ##
# Strip ignored trailing zeros.
+
def normalize
@ints = build_array_from_version_string
@@ -94,10 +93,8 @@
end
##
- # Convert version to integer array
- #
- # return:: [Array] list of integers
- #
+ # Returns an integer array representation of this Version.
+
def to_ints
normalize unless @ints
@ints
@@ -117,20 +114,25 @@
end
##
- # Compares two versions
- #
- # other:: [Version or .ints] other version to compare to
- # return:: [Fixnum] -1, 0, 1
- #
+ # Compares this version with +other+ returning -1, 0, or 1 if the other
+ # version is larger, the same, or smaller than this one.
+
def <=>(other)
+ return nil unless self.class === other
return 1 unless other
@ints <=> other.ints
end
- alias eql? == # :nodoc:
+ ##
+ # A Version is only eql? to another version if it has the same version
+ # string. "1.0" is not the same version as "1".
+ def eql?(other)
+ self.class === other and @version == other.version
+ end
+
def hash # :nodoc:
- to_ints.inject { |hash_code, n| hash_code + n }
+ @version.hash
end
# Return a new version object where the next to the last revision
Modified: MacRuby/branches/experimental/lib/rubygems.rb
===================================================================
--- MacRuby/branches/experimental/lib/rubygems.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/rubygems.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -43,12 +43,14 @@
#
# GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
- def gem(gem_name, *version_requirements)
+ def gem(gem_name, *version_requirements) # :doc:
skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
Gem.activate(gem_name, *version_requirements)
end
+ private :gem
+
end
##
@@ -67,11 +69,14 @@
:RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
:arch => RbConfig::CONFIG["arch"],
:bindir => RbConfig::CONFIG["bindir"],
+ :datadir => RbConfig::CONFIG["datadir"],
:libdir => RbConfig::CONFIG["libdir"],
:ruby_install_name => RbConfig::CONFIG["ruby_install_name"],
:ruby_version => RbConfig::CONFIG["ruby_version"],
:sitedir => RbConfig::CONFIG["sitedir"],
- :sitelibdir => RbConfig::CONFIG["sitelibdir"]
+ :sitelibdir => RbConfig::CONFIG["sitelibdir"],
+ :vendordir => RbConfig::CONFIG["vendordir"] ,
+ :vendorlibdir => RbConfig::CONFIG["vendorlibdir"]
)
DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES)
@@ -101,6 +106,11 @@
@ruby = nil
@sources = []
+ @post_install_hooks ||= []
+ @post_uninstall_hooks ||= []
+ @pre_uninstall_hooks ||= []
+ @pre_install_hooks ||= []
+
##
# Activates an installed gem matching +gem+. The gem must satisfy
# +version_requirements+.
@@ -137,7 +147,7 @@
unless matches.any? { |spec| spec.version == existing_spec.version } then
raise Gem::Exception,
- "can't activate #{gem}, already activated #{existing_spec.full_name}]"
+ "can't activate #{gem}, already activated #{existing_spec.full_name}"
end
return false
@@ -151,7 +161,7 @@
@loaded_specs[spec.name] = spec
# Load dependent gems first
- spec.dependencies.each do |dep_gem|
+ spec.runtime_dependencies.each do |dep_gem|
activate dep_gem
end
@@ -204,6 +214,20 @@
private_class_method :all_partials
##
+ # See if a given gem is available.
+
+ def self.available?(gem, *requirements)
+ requirements = Gem::Requirement.default if requirements.empty?
+
+ unless gem.respond_to?(:name) and
+ gem.respond_to?(:version_requirements) then
+ gem = Gem::Dependency.new gem, requirements
+ end
+
+ !Gem.source_index.search(gem).empty?
+ end
+
+ ##
# The mode needed to read a file as straight binary.
def self.binary_mode
@@ -227,7 +251,10 @@
def self.clear_paths
@gem_home = nil
@gem_path = nil
+ @user_home = nil
+
@@source_index = nil
+
MUTEX.synchronize do
@searcher = nil
end
@@ -244,9 +271,7 @@
# The standard configuration object for gems.
def self.configuration
- return @configuration if @configuration
- require 'rubygems/config_file'
- @configuration = Gem::ConfigFile.new []
+ @configuration ||= Gem::ConfigFile.new []
end
##
@@ -268,11 +293,19 @@
end
##
+ # A Zlib::Deflate.deflate wrapper
+
+ def self.deflate(data)
+ require 'zlib'
+ Zlib::Deflate.deflate data
+ end
+
+ ##
# The path where gems are to be installed.
def self.dir
@gem_home ||= nil
- set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
+ set_home(ENV['GEM_HOME'] || Gem.configuration.home || default_dir) unless @gem_home
@gem_home
end
@@ -313,6 +346,22 @@
end
##
+ # Returns a list of paths matching +file+ that can be used by a gem to pick
+ # up features from other gems. For example:
+ #
+ # Gem.find_files('rdoc/discover').each do |path| load path end
+ #
+ # find_files does not search $LOAD_PATH for files, only gems.
+
+ def self.find_files(path)
+ specs = searcher.find_all path
+
+ specs.map do |spec|
+ searcher.matching_files spec, path
+ end.flatten
+ end
+
+ ##
# Finds the user's home directory.
#--
# Some comments from the ruby-talk list regarding finding the home
@@ -329,7 +378,7 @@
end
if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
- return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
+ return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
end
begin
@@ -346,6 +395,38 @@
private_class_method :find_home
##
+ # Zlib::GzipReader wrapper that unzips +data+.
+
+ def self.gunzip(data)
+ require 'stringio'
+ require 'zlib'
+ data = StringIO.new data
+
+ Zlib::GzipReader.new(data).read
+ end
+
+ ##
+ # Zlib::GzipWriter wrapper that zips +data+.
+
+ def self.gzip(data)
+ require 'stringio'
+ require 'zlib'
+ zipped = StringIO.new
+
+ Zlib::GzipWriter.wrap zipped do |io| io.write data end
+
+ zipped.string
+ end
+
+ ##
+ # A Zlib::Inflate#inflate wrapper
+
+ def self.inflate(data)
+ require 'zlib'
+ Zlib::Inflate.inflate data
+ end
+
+ ##
# Return a list of all possible load paths for the latest version for all
# gems in the Gem installation.
@@ -406,22 +487,20 @@
# The file name and line number of the caller of the caller of this method.
def self.location_of_caller
- file, lineno = caller[1].split(':')
- lineno = lineno.to_i
+ caller[1] =~ /(.*?):(\d+)$/i
+ file = $1
+ lineno = $2.to_i
+
[file, lineno]
end
- private_class_method :location_of_caller
-
##
# manage_gems is useless and deprecated. Don't call it anymore.
- #--
- # TODO warn w/ RubyGems 1.2.x release.
- def self.manage_gems
- #file, lineno = location_of_caller
+ def self.manage_gems # :nodoc:
+ file, lineno = location_of_caller
- #warn "#{file}:#{lineno}:Warning: Gem#manage_gems is deprecated and will be removed on or after September 2008."
+ warn "#{file}:#{lineno}:Warning: Gem::manage_gems is deprecated and will be removed on or after March 2009."
end
##
@@ -438,7 +517,7 @@
@gem_path ||= nil
unless @gem_path then
- paths = [ENV['GEM_PATH']] || [default_path]
+ paths = [ENV['GEM_PATH'] || Gem.configuration.path || default_path]
if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then
paths << APPLE_GEM_HOME
@@ -459,7 +538,7 @@
##
# Array of platforms this RubyGems supports.
-
+
def self.platforms
@platforms ||= []
if @platforms.empty?
@@ -469,6 +548,40 @@
end
##
+ # Adds a post-install hook that will be passed an Gem::Installer instance
+ # when Gem::Installer#install is called
+
+ def self.post_install(&hook)
+ @post_install_hooks << hook
+ end
+
+ ##
+ # Adds a post-uninstall hook that will be passed a Gem::Uninstaller instance
+ # and the spec that was uninstalled when Gem::Uninstaller#uninstall is
+ # called
+
+ def self.post_uninstall(&hook)
+ @post_uninstall_hooks << hook
+ end
+
+ ##
+ # Adds a pre-install hook that will be passed an Gem::Installer instance
+ # when Gem::Installer#install is called
+
+ def self.pre_install(&hook)
+ @pre_install_hooks << hook
+ end
+
+ ##
+ # Adds a pre-uninstall hook that will be passed an Gem::Uninstaller instance
+ # and the spec that will be uninstalled when Gem::Uninstaller#uninstall is
+ # called
+
+ def self.pre_uninstall(&hook)
+ @pre_uninstall_hooks << hook
+ end
+
+ ##
# The directory prefix this RubyGems was installed at.
def self.prefix
@@ -545,6 +658,9 @@
@ruby = File.join(ConfigMap[:bindir],
ConfigMap[:ruby_install_name])
@ruby << ConfigMap[:EXEEXT]
+
+ # escape string in case path to ruby executable contain spaces.
+ @ruby.sub!(/.*\s.*/m, '"\&"')
end
@ruby
@@ -586,20 +702,30 @@
def self.set_paths(gpaths)
if gpaths
@gem_path = gpaths.split(File::PATH_SEPARATOR)
-
+
if File::ALT_SEPARATOR then
@gem_path.map! do |path|
path.gsub File::ALT_SEPARATOR, File::SEPARATOR
end
end
-
+
@gem_path << Gem.dir
else
+ # TODO: should this be Gem.default_path instead?
@gem_path = [Gem.dir]
end
@gem_path.uniq!
- @gem_path.each do |gp| ensure_gem_subdirectories(gp) end
+ @gem_path.each do |path|
+ if 0 == File.expand_path(path).index(Gem.user_home)
+ next unless File.directory? Gem.user_home
+ unless win_platform? then
+ # only create by matching user
+ next if Etc.getpwuid.uid != File::Stat.new(Gem.user_home).uid
+ end
+ end
+ ensure_gem_subdirectories path
+ end
end
private_class_method :set_paths
@@ -630,6 +756,14 @@
end
##
+ # Need to be able to set the sources without calling
+ # Gem.sources.replace since that would cause an infinite loop.
+
+ def self.sources=(new_sources)
+ @sources = new_sources
+ end
+
+ ##
# Glob pattern for require-able path suffixes.
def self.suffix_pattern
@@ -675,6 +809,27 @@
attr_reader :loaded_specs
+ ##
+ # The list of hooks to be run before Gem::Install#install does any work
+
+ attr_reader :post_install_hooks
+
+ ##
+ # The list of hooks to be run before Gem::Uninstall#uninstall does any
+ # work
+
+ attr_reader :post_uninstall_hooks
+
+ ##
+ # The list of hooks to be run after Gem::Install#install is finished
+
+ attr_reader :pre_install_hooks
+
+ ##
+ # The list of hooks to be run after Gem::Uninstall#uninstall is finished
+
+ attr_reader :pre_uninstall_hooks
+
# :stopdoc:
alias cache source_index # an alias for the old name
@@ -683,24 +838,25 @@
end
-end
+ MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/"
-# Modify the non-gem version of datadir to handle gem package names.
+ YAML_SPEC_DIR = 'quick/'
-require 'rbconfig/datadir'
+end
-module Config # :nodoc:
+module Config
+ # :stopdoc:
class << self
- alias gem_original_datadir datadir
-
# Return the path to the data directory associated with the named
# package. If the package is loaded as a gem, return the gem
# specific data directory. Otherwise return a path to the share
# area as define by "#{ConfigMap[:datadir]}/#{package_name}".
def datadir(package_name)
- Gem.datadir(package_name) || Config.gem_original_datadir(package_name)
+ Gem.datadir(package_name) ||
+ File.join(Gem::ConfigMap[:datadir], package_name)
end
end
+ # :startdoc:
end
require 'rubygems/exceptions'
@@ -712,7 +868,22 @@
require 'rubygems/platform'
require 'rubygems/builder' # HACK: Needed for rake's package task.
+begin
+ require 'rubygems/defaults/operating_system'
+rescue LoadError
+end
+
+if defined?(RUBY_ENGINE) then
+ begin
+ require "rubygems/defaults/#{RUBY_ENGINE}"
+ rescue LoadError
+ end
+end
+
+require 'rubygems/config_file'
+
if RUBY_VERSION < '1.9' then
require 'rubygems/custom_require'
end
+Gem.clear_paths
Modified: MacRuby/branches/experimental/lib/scanf.rb
===================================================================
--- MacRuby/branches/experimental/lib/scanf.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/scanf.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,9 +1,9 @@
# scanf for Ruby
#
# $Release Version: 1.1.2 $
-# $Revision: 14912 $
-# $Id: scanf.rb 14912 2008-01-06 15:49:38Z akr $
-# $Author: akr $
+# $Revision: 19094 $
+# $Id: scanf.rb 19094 2008-09-03 12:54:13Z dblack $
+# $Author: dblack $
#
# A product of the Austin Ruby Codefest (Austin, Texas, August 2002)
@@ -325,7 +325,7 @@
end
def count_space?
- /(?:\A|\S)%\*?\d*c|\[/.match(@spec_string)
+ /(?:\A|\S)%\*?\d*c|%\d*\[/.match(@spec_string)
end
def initialize(str)
@@ -357,7 +357,7 @@
# %i
when /%\*?i/
- [ "([-+]?(?:(?:0[0-7]+)|(?:0[Xx]#{h}+)|(?:[1-9]\\d+)))", :extract_integer ]
+ [ "([-+]?(?:(?:0[0-7]+)|(?:0[Xx]#{h}+)|(?:[1-9]\\d*)))", :extract_integer ]
# %5i
when /%\*?(\d+)i/
Modified: MacRuby/branches/experimental/lib/shell/command-processor.rb
===================================================================
--- MacRuby/branches/experimental/lib/shell/command-processor.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/shell/command-processor.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# shell/command-controller.rb -
# $Release Version: 0.7 $
-# $Revision: 14912 $
+# $Revision: 20880 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -26,7 +26,7 @@
#
m = [:initialize, :expand_path]
if Object.methods.first.kind_of?(String)
- NoDelegateMethods = m.collect{|m| m.id2name}
+ NoDelegateMethods = m.collect{|x| x.id2name}
else
NoDelegateMethods = m
end
@@ -124,7 +124,7 @@
f = File.open(path, mode, perm)
File.chmod(perm & ~@shell.umask, path)
if block_given?
- f.each &b
+ f.each(&b)
end
f
else
Modified: MacRuby/branches/experimental/lib/shell/process-controller.rb
===================================================================
--- MacRuby/branches/experimental/lib/shell/process-controller.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/shell/process-controller.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# shell/process-controller.rb -
# $Release Version: 0.7 $
-# $Revision: 14912 $
+# $Revision: 20880 $
# by Keiju ISHITSUKA(keiju at ruby-lang.org)
#
# --
@@ -62,7 +62,7 @@
end
def block_output_synchronize(&b)
- @BlockOutputMonitor.synchronize &b
+ @BlockOutputMonitor.synchronize(&b)
end
def wait_to_finish_all_process_controllers
@@ -84,7 +84,7 @@
end
end
- # for shell-command complete finish at this prosess exit.
+ # for shell-command complete finish at this process exit.
USING_AT_EXIT_WHEN_PROCESS_EXIT = true
at_exit do
wait_to_finish_all_process_controllers unless $@
Modified: MacRuby/branches/experimental/lib/shell.rb
===================================================================
--- MacRuby/branches/experimental/lib/shell.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/shell.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -79,7 +79,7 @@
@default_record_separator = rs
end
- # os resource mutecs
+ # os resource mutex
mutex_methods = ["unlock", "lock", "locked?", "synchronize", "try_lock", "exclusive_unlock"]
for m in mutex_methods
def_delegator("@debug_output_mutex", m, "debug_output_"+m.to_s)
Modified: MacRuby/branches/experimental/lib/singleton.rb
===================================================================
--- MacRuby/branches/experimental/lib/singleton.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/singleton.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -9,9 +9,9 @@
# * this ensures that only one instance of Klass lets call it
# ``the instance'' can be created.
#
-# a,b = Klass.instance, Klass.instance
-# a == b # => true
-# a.new # NoMethodError - new is private ...
+# a,b = Klass.instance, Klass.instance
+# a == b # => true
+# Klass.new # NoMethodError - new is private ...
#
# * ``The instance'' is created at instantiation time, in other
# words the first call of Klass.instance(), thus
@@ -30,7 +30,7 @@
# * Klass.new and Klass.allocate - as private
#
# Providing (or modifying) the class methods
-# * Klass.inherited(sub_klass) and Klass.clone() -
+# * Klass.inherited(sub_klass) and Klass.clone() -
# to ensure that the Singleton pattern is properly
# inherited and cloned.
#
@@ -70,17 +70,15 @@
def dup
raise TypeError, "can't dup instance of singleton #{self.class}"
end
-
- private
# default marshalling strategy
- def _dump(depth = -1)
+ def _dump(depth = -1)
''
end
- module SingletonClassMethods
+ module SingletonClassMethods
# properly clone the Singleton pattern - did you know
- # that duping doesn't copy class methods?
+ # that duping doesn't copy class methods?
def clone
Singleton.__init__(super)
end
@@ -90,8 +88,8 @@
end
private
-
- # ensure that the Singleton pattern is properly inherited
+
+ # ensure that the Singleton pattern is properly inherited
def inherited(sub_klass)
super
Singleton.__init__(sub_klass)
@@ -114,12 +112,12 @@
end
klass
end
-
+
private
# extending an object with Singleton is a bad idea
undef_method :extend_object
-
+
def append_features(mod)
# help out people counting on transitive mixins
unless mod.instance_of?(Class)
@@ -127,7 +125,7 @@
end
super
end
-
+
def included(klass)
super
klass.private_class_method :new, :allocate
@@ -135,7 +133,7 @@
Singleton.__init__(klass)
end
end
-
+
end
@@ -143,14 +141,14 @@
def num_of_instances(klass)
"#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
-end
+end
# The basic and most important example.
class SomeSingletonClass
include Singleton
end
-puts "There are #{num_of_instances(SomeSingletonClass)}"
+puts "There are #{num_of_instances(SomeSingletonClass)}"
a = SomeSingletonClass.instance
b = SomeSingletonClass.instance # a and b are same object
@@ -173,23 +171,23 @@
puts "initialize called by thread ##{Thread.current[:i]}"
end
end
-
+
class << Ups
def _instantiate?
@enter.push Thread.current[:i]
while false.equal?(@singleton__instance__)
@singleton__mutex__.unlock
- sleep 0.08
+ sleep 0.08
@singleton__mutex__.lock
end
@leave.push Thread.current[:i]
@singleton__instance__
end
-
+
def __sleep
sleep(rand(0.08))
end
-
+
def new
begin
__sleep
@@ -201,12 +199,12 @@
end
end
end
-
+
def instantiate_all
@enter = []
@leave = []
- 1.upto(9) {|i|
- Thread.new {
+ 1.upto(9) {|i|
+ Thread.new {
begin
Thread.current[:i] = i
__sleep
@@ -296,7 +294,7 @@
class Down < Middle; end
puts "and basic \"Down test\" is #{Down.instance == Down.instance}\n
-Various exceptions"
+Various exceptions"
begin
module AModule
Modified: MacRuby/branches/experimental/lib/sync.rb
===================================================================
--- MacRuby/branches/experimental/lib/sync.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/sync.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# sync.rb - 2 phase lock with counter
# $Release Version: 1.0$
-# $Revision: 14912 $
+# $Revision: 19280 $
# by Keiju ISHITSUKA(keiju at ishitsuka.com)
#
# --
@@ -126,9 +126,9 @@
# locking methods.
def sync_try_lock(mode = EX)
- return unlock if sync_mode == UN
+ return unlock if mode == UN
@sync_mutex.synchronize do
- ret = sync_try_lock_sub(sync_mode)
+ ret = sync_try_lock_sub(mode)
end
ret
end
Modified: MacRuby/branches/experimental/lib/tempfile.rb
===================================================================
--- MacRuby/branches/experimental/lib/tempfile.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/tempfile.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# tempfile - manipulates temporary files
#
-# $Id: tempfile.rb 13428 2007-09-11 08:28:29Z knu $
+# $Id: tempfile.rb 19833 2008-10-18 10:32:26Z matz $
#
require 'delegate'
@@ -29,7 +29,12 @@
# Dir::tmpdir provided by 'tmpdir.rb'.
# When $SAFE > 0 and the given tmpdir is tainted, it uses
# /tmp. (Note that ENV values are tainted by default)
- def initialize(basename, tmpdir=Dir::tmpdir)
+ def initialize(basename, *rest)
+ # I wish keyword argument settled soon.
+ if opts = Hash.try_convert(rest[-1])
+ rest.pop
+ end
+ tmpdir = rest[0] || Dir::tmpdir
if $SAFE > 0 and tmpdir.tainted?
tmpdir = '/tmp'
end
@@ -56,7 +61,12 @@
@clean_proc = Tempfile.callback(@data)
ObjectSpace.define_finalizer(self, @clean_proc)
- @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
+ if opts.nil?
+ opts = []
+ else
+ opts = [opts]
+ end
+ @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600, *opts)
@tmpname = tmpname
@@cleanlist << @tmpname
@data[1] = @tmpfile
@@ -187,8 +197,6 @@
ensure
tempfile.close
end
-
- nil
else
tempfile
end
Deleted: MacRuby/branches/experimental/lib/test/unit/assertionfailederror.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/assertionfailederror.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/assertionfailederror.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,14 +0,0 @@
-#--
-#
-# Author:: Nathaniel Talbott.
-# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
-# License:: Ruby license.
-
-module Test
- module Unit
-
- # Thrown by Test::Unit::Assertions when an assertion fails.
- class AssertionFailedError < StandardError
- end
- end
-end
Modified: MacRuby/branches/experimental/lib/test/unit/assertions.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/assertions.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/assertions.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,622 +1,122 @@
-# Author:: Nathaniel Talbott.
-# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
-# License:: Ruby license.
+require 'minitest/unit'
+require 'pp'
-require 'test/unit/assertionfailederror'
-require 'test/unit/util/backtracefilter'
-
-module Test # :nodoc:
- module Unit # :nodoc:
-
- ##
- # Test::Unit::Assertions contains the standard Test::Unit assertions.
- # Assertions is included in Test::Unit::TestCase.
- #
- # To include it in your own code and use its functionality, you simply
- # need to rescue Test::Unit::AssertionFailedError. Additionally you may
- # override add_assertion to get notified whenever an assertion is made.
- #
- # Notes:
- # * The message to each assertion, if given, will be propagated with the
- # failure.
- # * It is easy to add your own assertions based on assert_block().
- #
- # = Example Custom Assertion
- #
- # def deny(boolean, message = nil)
- # message = build_message message, '<?> is not false or nil.', boolean
- # assert_block message do
- # not boolean
- # end
- # end
-
+module Test
+ module Unit
module Assertions
+ include MiniTest::Assertions
- ##
- # The assertion upon which all other assertions are based. Passes if the
- # block yields true.
- #
- # Example:
- # assert_block "Couldn't do the thing" do
- # do_the_thing
- # end
-
- public
- def assert_block(message="assert_block failed.") # :yields:
- _wrap_assertion do
- if (! yield)
- raise AssertionFailedError.new(message.to_s)
- end
- end
+ def mu_pp(obj)
+ obj.pretty_inspect.chomp
end
- ##
- # Asserts that +boolean+ is not false or nil.
- #
- # Example:
- # assert [1, 2].include?(5)
-
- public
- def assert(boolean, message=nil)
- _wrap_assertion do
- assert_block("assert should not be called with a block.") { !block_given? }
- assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
- end
+ def assert_raise(*args, &b)
+ assert_raises(*args, &b)
end
- ##
- # Passes if +expected+ == +actual.
- #
- # Note that the ordering of arguments is important, since a helpful
- # error message is generated when this one fails that tells you the
- # values of expected and actual.
- #
- # Example:
- # assert_equal 'MY STRING', 'my string'.upcase
-
- public
- def assert_equal(expected, actual, message=nil)
- full_message = build_message(message, <<EOT, expected, actual)
-<?> expected but was
-<?>.
-EOT
- assert_block(full_message) { expected == actual }
- end
-
- private
- def _check_exception_class(args) # :nodoc:
- args.partition do |klass|
- next if klass.instance_of?(Module)
- assert(Exception >= klass, "Should expect a class of exception, #{klass}")
- true
+ def assert_nothing_raised(*args)
+ self._assertions += 1
+ if Module === args.last
+ msg = nil
+ else
+ msg = args.pop
end
- end
-
- private
- def _expected_exception?(actual_exception, exceptions, modules) # :nodoc:
- exceptions.include?(actual_exception.class) or
- modules.any? {|mod| actual_exception.is_a?(mod)}
- end
-
- ##
- # Passes if the block raises one of the given exceptions.
- #
- # Example:
- # assert_raise RuntimeError, LoadError do
- # raise 'Boom!!!'
- # end
-
- public
- def assert_raise(*args)
- _wrap_assertion do
- if Module === args.last
- message = ""
+ begin
+ line = __LINE__; yield
+ rescue Exception => e
+ bt = e.backtrace
+ as = e.instance_of?(MiniTest::Assertion)
+ if as
+ ans = /\A#{Regexp.quote(__FILE__)}:#{line}:in /o
+ bt.reject! {|line| ans =~ line}
+ end
+ if ((args.empty? && !as) ||
+ args.any? {|a| a.instance_of?(Module) ? e.is_a?(a) : e.class == a })
+ msg = message(msg) { "Exception raised:\n<#{mu_pp(e)}>" }
+ raise MiniTest::Assertion, msg.call, bt
else
- message = args.pop
+ raise
end
- exceptions, modules = _check_exception_class(args)
- expected = args.size == 1 ? args.first : args
- actual_exception = nil
- full_message = build_message(message, "<?> exception expected but none was thrown.", expected)
- assert_block(full_message) do
- begin
- yield
- rescue Exception => actual_exception
- break
- end
- false
- end
- full_message = build_message(message, "<?> exception expected but was\n?", expected, actual_exception)
- assert_block(full_message) {_expected_exception?(actual_exception, exceptions, modules)}
- actual_exception
end
+ nil
end
- ##
- # Alias of assert_raise.
- #
- # Will be deprecated in 1.9, and removed in 2.0.
-
- public
- def assert_raises(*args, &block)
- assert_raise(*args, &block)
- end
-
- ##
- # Passes if +object+ .instance_of? +klass+
- #
- # Example:
- # assert_instance_of String, 'foo'
-
- public
- def assert_instance_of(klass, object, message="")
- _wrap_assertion do
- assert_equal(Class, klass.class, "assert_instance_of takes a Class as its first argument")
- full_message = build_message(message, <<EOT, object, klass, object.class)
-<?> expected to be an instance of
-<?> but was
-<?>.
-EOT
- assert_block(full_message){object.instance_of?(klass)}
+ def assert_nothing_thrown(msg=nil)
+ begin
+ yield
+ rescue ArgumentError => error
+ raise error if /\Auncaught throw (.+)\z/m !~ error.message
+ msg = message(msg) { "<#{$1}> was thrown when nothing was expected" }
+ flunk(msg)
end
+ assert(true, "Expected nothing to be thrown")
end
- ##
- # Passes if +object+ is nil.
- #
- # Example:
- # assert_nil [1, 2].uniq!
-
- public
- def assert_nil(object, message="")
- assert_equal(nil, object, message)
- end
-
- ##
- # Passes if +object+ .kind_of? +klass+
- #
- # Example:
- # assert_kind_of Object, 'foo'
-
- public
- def assert_kind_of(klass, object, message="")
- _wrap_assertion do
- assert(klass.kind_of?(Module), "The first parameter to assert_kind_of should be a kind_of Module.")
- full_message = build_message(message, "<?>\nexpected to be kind_of\\?\n<?> but was\n<?>.", object, klass, object.class)
- assert_block(full_message){object.kind_of?(klass)}
- end
- end
-
- ##
- # Passes if +object+ .respond_to? +method+
- #
- # Example:
- # assert_respond_to 'bugbear', :slice
-
- public
- def assert_respond_to(object, method, message="")
- _wrap_assertion do
- full_message = build_message(nil, "<?>\ngiven as the method name argument to #assert_respond_to must be a Symbol or #respond_to\\?(:to_str).", method)
-
- assert_block(full_message) do
- method.kind_of?(Symbol) || method.respond_to?(:to_str)
- end
- full_message = build_message(message, <<EOT, object, object.class, method)
-<?>
-of type <?>
-expected to respond_to\\?<?>.
-EOT
- assert_block(full_message) { object.respond_to?(method) }
- end
- end
-
- ##
- # Passes if +string+ =~ +pattern+.
- #
- # Example:
- # assert_match(/\d+/, 'five, 6, seven')
-
- public
- def assert_match(pattern, string, message="")
- _wrap_assertion do
- pattern = case(pattern)
- when String
- Regexp.new(Regexp.escape(pattern))
+ def assert_equal(exp, act, msg = nil)
+ msg = message(msg) {
+ exp_str = mu_pp(exp)
+ act_str = mu_pp(act)
+ exp_comment = ''
+ act_comment = ''
+ if exp_str == act_str
+ if (exp.is_a?(String) && act.is_a?(String)) ||
+ (exp.is_a?(Regexp) && act.is_a?(Regexp))
+ exp_comment = " (#{exp.encoding})"
+ act_comment = " (#{act.encoding})"
+ elsif exp.is_a?(Float) && act.is_a?(Float)
+ exp_str = "%\#.#{Float::DIG+2}g" % exp
+ act_str = "%\#.#{Float::DIG+2}g" % act
+ elsif exp.is_a?(Time) && act.is_a?(Time)
+ exp_comment = " (nsec=#{exp.nsec})"
+ act_comment = " (nsec=#{act.nsec})"
+ end
+ elsif !Encoding.compatible?(exp_str, act_str)
+ if exp.is_a?(String) && act.is_a?(String)
+ exp_str = exp.dump
+ act_str = act.dump
+ exp_comment = " (#{exp.encoding})"
+ act_comment = " (#{act.encoding})"
else
- pattern
+ exp_str = exp_str.dump
+ act_str = act_str.dump
+ end
end
- full_message = build_message(message, "<?> expected to be =~\n<?>.", string, pattern)
- assert_block(full_message) { string =~ pattern }
- end
+ "<#{exp_str}>#{exp_comment} expected but was\n<#{act_str}>#{act_comment}"
+ }
+ assert(exp == act, msg)
end
- ##
- # Passes if +actual+ .equal? +expected+ (i.e. they are the same
- # instance).
- #
- # Example:
- # o = Object.new
- # assert_same o, o
-
- public
- def assert_same(expected, actual, message="")
- full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
-<?>
-with id <?> expected to be equal\\? to
-<?>
-with id <?>.
-EOT
- assert_block(full_message) { actual.equal?(expected) }
+ def assert_not_nil(exp, msg=nil)
+ msg = message(msg) { "<#{mu_pp(exp)}> expected to not be nil" }
+ assert(!exp.nil?, msg)
end
- ##
- # Compares the +object1+ with +object2+ using +operator+.
- #
- # Passes if object1.__send__(operator, object2) is true.
- #
- # Example:
- # assert_operator 5, :>=, 4
-
- public
- def assert_operator(object1, operator, object2, message="")
- _wrap_assertion do
- full_message = build_message(nil, "<?>\ngiven as the operator for #assert_operator must be a Symbol or #respond_to\\?(:to_str).", operator)
- assert_block(full_message){operator.kind_of?(Symbol) || operator.respond_to?(:to_str)}
- full_message = build_message(message, <<EOT, object1, AssertionMessage.literal(operator), object2)
-<?> expected to be
-?
-<?>.
-EOT
- assert_block(full_message) { object1.__send__(operator, object2) }
- end
+ def assert_not_equal(exp, act, msg=nil)
+ msg = message(msg) { "<#{mu_pp(exp)}> expected to be != to\n<#{mu_pp(act)}>" }
+ assert(exp != act, msg)
end
- ##
- # Passes if block does not raise an exception.
- #
- # Example:
- # assert_nothing_raised do
- # [1, 2].uniq
- # end
-
- public
- def assert_nothing_raised(*args)
- _wrap_assertion do
- if Module === args.last
- message = ""
- else
- message = args.pop
- end
- exceptions, modules = _check_exception_class(args)
- begin
- yield
- rescue Exception => e
- if ((args.empty? && !e.instance_of?(AssertionFailedError)) ||
- _expected_exception?(e, exceptions, modules))
- assert_block(build_message(message, "Exception raised:\n?", e)){false}
- else
- raise
- end
- end
- nil
- end
+ def assert_no_match(regexp, string, msg=nil)
+ assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
+ self._assertions -= 1
+ msg = message(msg) { "<#{mu_pp(regexp)}> expected to not match\n<#{mu_pp(string)}>" }
+ assert(regexp !~ string, msg)
end
- ##
- # Flunk always fails.
- #
- # Example:
- # flunk 'Not done testing yet.'
-
- public
- def flunk(message="Flunked")
- assert_block(build_message(message)){false}
- end
-
- ##
- # Passes if ! +actual+ .equal? +expected+
- #
- # Example:
- # assert_not_same Object.new, Object.new
-
- public
def assert_not_same(expected, actual, message="")
- full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
+ msg = message(msg) { build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__) }
<?>
with id <?> expected to not be equal\\? to
<?>
with id <?>.
EOT
- assert_block(full_message) { !actual.equal?(expected) }
+ assert(!actual.equal?(expected), msg)
end
- ##
- # Passes if +expected+ != +actual+
- #
- # Example:
- # assert_not_equal 'some string', 5
-
- public
- def assert_not_equal(expected, actual, message="")
- full_message = build_message(message, "<?> expected to be != to\n<?>.", expected, actual)
- assert_block(full_message) { expected != actual }
- end
-
- ##
- # Passes if ! +object+ .nil?
- #
- # Example:
- # assert_not_nil '1 two 3'.sub!(/two/, '2')
-
- public
- def assert_not_nil(object, message="")
- full_message = build_message(message, "<?> expected to not be nil.", object)
- assert_block(full_message){!object.nil?}
- end
-
- ##
- # Passes if +regexp+ !~ +string+
- #
- # Example:
- # assert_no_match(/two/, 'one 2 three')
-
- public
- def assert_no_match(regexp, string, message="")
- _wrap_assertion do
- assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
- full_message = build_message(message, "<?> expected to not match\n<?>.", regexp, string)
- assert_block(full_message) { regexp !~ string }
- end
- end
-
- UncaughtThrow = {
- ArgumentError => /^uncaught throw (.+)$/,
- } #`
-
- ##
- # Passes if the block throws +expected_object+
- #
- # Example:
- # assert_throws :done do
- # throw :done
- # end
-
- public
- def assert_throws(expected_object, message="", &proc)
- _wrap_assertion do
- assert_block("Should have passed a block to assert_throws."){block_given?}
- caught = true
- begin
- catch(expected_object) do
- proc.call
- caught = false
- end
- full_message = build_message(message, "<?> should have been thrown.", expected_object)
- assert_block(full_message){caught}
- rescue ArgumentError => error
- if UncaughtThrow[error.class] !~ error.message
- raise error
- end
- full_message = build_message(message, "<?> expected to be thrown but\n<#$1> was thrown.", expected_object)
- flunk(full_message)
- end
- end
- end
-
- ##
- # Passes if block does not throw anything.
- #
- # Example:
- # assert_nothing_thrown do
- # [1, 2].uniq
- # end
-
- public
- def assert_nothing_thrown(message="", &proc)
- _wrap_assertion do
- assert(block_given?, "Should have passed a block to assert_nothing_thrown")
- begin
- proc.call
- rescue ArgumentError => error
- if UncaughtThrow[error.class] !~ error.message
- raise error
- end
- full_message = build_message(message, "<#$1> was thrown when nothing was expected")
- flunk(full_message)
- end
- assert(true, "Expected nothing to be thrown")
- end
- end
-
- ##
- # Passes if +expected_float+ and +actual_float+ are equal
- # within +delta+ tolerance.
- #
- # Example:
- # assert_in_delta 0.05, (50000.0 / 10**6), 0.00001
-
- public
- def assert_in_delta(expected_float, actual_float, delta, message="")
- _wrap_assertion do
- {expected_float => "first float", actual_float => "second float", delta => "delta"}.each do |float, name|
- assert_respond_to(float, :to_f, "The arguments must respond to to_f; the #{name} did not")
- end
- assert_operator(delta, :>=, 0.0, "The delta should not be negative")
- full_message = build_message(message, <<EOT, expected_float, actual_float, delta)
-<?> and
-<?> expected to be within
-<?> of each other.
-EOT
- assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f }
- end
- end
-
- ##
- # Passes if the method send returns a true value.
- #
- # +send_array+ is composed of:
- # * A receiver
- # * A method
- # * Arguments to the method
- #
- # Example:
- # assert_send [[1, 2], :include?, 4]
-
- public
- def assert_send(send_array, message="")
- _wrap_assertion do
- assert_instance_of(Array, send_array, "assert_send requires an array of send information")
- assert(send_array.size >= 2, "assert_send requires at least a receiver and a message name")
- full_message = build_message(message, <<EOT, send_array[0], AssertionMessage.literal(send_array[1].to_s), send_array[2..-1])
-<?> expected to respond to
-<?(?)> with a true value.
-EOT
- assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) }
- end
- end
-
- ##
- # Builds a failure message. +head+ is added before the +template+ and
- # +arguments+ replaces the '?'s positionally in the template.
-
- public
- def build_message(head, template=nil, *arguments) # :nodoc:
+ def build_message(head, template=nil, *arguments)
template &&= template.chomp
- return AssertionMessage.new(head, template, arguments)
+ template.gsub(/\?/) { mu_pp(arguments.shift) }
end
-
- private
- def _wrap_assertion # :nodoc:
- @_assertion_wrapped ||= false
- unless (@_assertion_wrapped)
- @_assertion_wrapped = true
- begin
- add_assertion
- return yield
- ensure
- @_assertion_wrapped = false
- end
- else
- return yield
- end
- end
-
- ##
- # Called whenever an assertion is made. Define this in classes that
- # include Test::Unit::Assertions to record assertion counts.
-
- private
- def add_assertion
- end
-
- ##
- # Select whether or not to use the pretty-printer. If this option is set
- # to false before any assertions are made, pp.rb will not be required.
-
- public
- def self.use_pp=(value)
- AssertionMessage.use_pp = value
- end
-
- # :stopdoc:
-
- class AssertionMessage
- @use_pp = true
- class << self
- attr_accessor :use_pp
- end
-
- class Literal
- def initialize(value)
- @value = value
- end
-
- def inspect
- @value.to_s
- end
- end
-
- class Template
- def self.create(string)
- parts = (string ? string.scan(/(?=[^\\])\?|(?:\\\?|[^\?])+/m) : [])
- self.new(parts)
- end
-
- attr_reader :count
-
- def initialize(parts)
- @parts = parts
- @count = parts.find_all{|e| e == '?'}.size
- end
-
- def result(parameters)
- raise "The number of parameters does not match the number of substitutions." if(parameters.size != count)
- params = parameters.dup
- @parts.collect{|e| e == '?' ? params.shift : e.gsub(/\\\?/m, '?')}.join('')
- end
- end
-
- def self.literal(value)
- Literal.new(value)
- end
-
- include Util::BacktraceFilter
-
- def initialize(head, template_string, parameters)
- @head = head
- @template_string = template_string
- @parameters = parameters
- end
-
- def convert(object)
- case object
- when Exception
- <<EOM.chop
-Class: <#{convert(object.class)}>
-Message: <#{convert(object.message)}>
----Backtrace---
-#{filter_backtrace(object.backtrace).join("\n")}
----------------
-EOM
- else
- if(self.class.use_pp)
- begin
- require 'pp'
- rescue LoadError
- self.class.use_pp = false
- return object.inspect
- end unless(defined?(PP))
- PP.pp(object, '').chomp
- else
- object.inspect
- end
- end
- end
-
- def template
- @template ||= Template.create(@template_string)
- end
-
- def add_period(string)
- (string =~ /\.\Z/ ? string : string + '.')
- end
-
- def to_s
- message_parts = []
- if (@head)
- head = @head.to_s
- unless(head.empty?)
- message_parts << add_period(head)
- end
- end
- tail = template.result(@parameters.collect{|e| convert(e)})
- message_parts << tail unless(tail.empty?)
- message_parts.join("\n")
- end
- end
-
- # :startdoc:
-
end
end
end
Deleted: MacRuby/branches/experimental/lib/test/unit/autorunner.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/autorunner.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/autorunner.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,220 +0,0 @@
-require 'test/unit'
-require 'test/unit/ui/testrunnerutilities'
-require 'optparse'
-
-module Test
- module Unit
- class AutoRunner
- def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block)
- r = new(force_standalone || standalone?, &block)
- r.base = default_dir
- r.process_args(argv)
- r.run
- end
-
- def self.standalone?
- return false unless("-e" == $0)
- ObjectSpace.each_object(Class) do |klass|
- return false if(klass < TestCase)
- end
- true
- end
-
- RUNNERS = {
- :console => proc do |r|
- require 'test/unit/ui/console/testrunner'
- Test::Unit::UI::Console::TestRunner
- end,
- :gtk => proc do |r|
- require 'test/unit/ui/gtk/testrunner'
- Test::Unit::UI::GTK::TestRunner
- end,
- :gtk2 => proc do |r|
- require 'test/unit/ui/gtk2/testrunner'
- Test::Unit::UI::GTK2::TestRunner
- end,
- :fox => proc do |r|
- require 'test/unit/ui/fox/testrunner'
- Test::Unit::UI::Fox::TestRunner
- end,
- :tk => proc do |r|
- require 'test/unit/ui/tk/testrunner'
- Test::Unit::UI::Tk::TestRunner
- end,
- }
-
- OUTPUT_LEVELS = [
- [:silent, UI::SILENT],
- [:progress, UI::PROGRESS_ONLY],
- [:normal, UI::NORMAL],
- [:verbose, UI::VERBOSE],
- ]
-
- COLLECTORS = {
- :objectspace => proc do |r|
- require 'test/unit/collector/objectspace'
- c = Collector::ObjectSpace.new
- c.filter = r.filters
- c.collect($0.sub(/\.rb\Z/, ''))
- end,
- :dir => proc do |r|
- require 'test/unit/collector/dir'
- c = Collector::Dir.new
- c.filter = r.filters
- c.pattern.concat(r.pattern) if(r.pattern)
- c.exclude.concat(r.exclude) if(r.exclude)
- c.base = r.base
- $:.push(r.base) if r.base
- c.collect(*(r.to_run.empty? ? ['.'] : r.to_run))
- end,
- }
-
- attr_reader :suite
- attr_accessor :output_level, :filters, :to_run, :pattern, :exclude, :base, :workdir
- attr_writer :runner, :collector
-
- def initialize(standalone)
- Unit.run = true
- @standalone = standalone
- @runner = RUNNERS[:console]
- @collector = COLLECTORS[(standalone ? :dir : :objectspace)]
- @filters = []
- @to_run = []
- @output_level = UI::NORMAL
- @workdir = false
- yield(self) if(block_given?)
- end
-
- def process_args(args = ARGV)
- begin
- options.order!(args) {|arg| @to_run << arg}
- rescue OptionParser::ParseError => e
- puts e
- puts options
- $! = nil
- abort
- else
- @filters << proc{false} unless(@filters.empty?)
- end
- not @to_run.empty?
- end
-
- def options
- @options ||= OptionParser.new do |o|
- o.banner = "Test::Unit automatic runner."
- o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
-
- o.on
- o.on('-r', '--runner=RUNNER', RUNNERS,
- "Use the given RUNNER.",
- "(" + keyword_display(RUNNERS) + ")") do |r|
- @runner = r
- end
-
- if(@standalone)
- o.on('-b', '--basedir=DIR', "Base directory of test suites.") do |b|
- @base = b
- end
-
- o.on('-w', '--workdir=DIR', "Working directory to run tests.") do |w|
- @workdir = w
- end
-
- o.on('-a', '--add=TORUN', Array,
- "Add TORUN to the list of things to run;",
- "can be a file or a directory.") do |a|
- @to_run.concat(a)
- end
-
- @pattern = []
- o.on('-p', '--pattern=PATTERN', Regexp,
- "Match files to collect against PATTERN.") do |e|
- @pattern << e
- end
-
- @exclude = []
- o.on('-x', '--exclude=PATTERN', Regexp,
- "Ignore files to collect against PATTERN.") do |e|
- @exclude << e
- end
- end
-
- o.on('-n', '--name=NAME', String,
- "Runs tests matching NAME.",
- "(patterns may be used).") do |n|
- n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
- case n
- when Regexp
- @filters << proc{|t| n =~ t.method_name ? true : nil}
- else
- @filters << proc{|t| n == t.method_name ? true : nil}
- end
- end
-
- o.on('-t', '--testcase=TESTCASE', String,
- "Runs tests in TestCases matching TESTCASE.",
- "(patterns may be used).") do |n|
- n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
- case n
- when Regexp
- @filters << proc{|t| n =~ t.class.name ? true : nil}
- else
- @filters << proc{|t| n == t.class.name ? true : nil}
- end
- end
-
- o.on('-I', "--load-path=DIR[#{File::PATH_SEPARATOR}DIR...]",
- "Appends directory list to $LOAD_PATH.") do |dirs|
- $LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR))
- end
-
- o.on('-v', '--verbose=[LEVEL]', OUTPUT_LEVELS,
- "Set the output level (default is verbose).",
- "(" + keyword_display(OUTPUT_LEVELS) + ")") do |l|
- @output_level = l || UI::VERBOSE
- end
-
- o.on('--',
- "Stop processing options so that the",
- "remaining options will be passed to the",
- "test."){o.terminate}
-
- o.on('-h', '--help', 'Display this help.'){puts o; exit}
-
- o.on_tail
- o.on_tail('Deprecated options:')
-
- o.on_tail('--console', 'Console runner (use --runner).') do
- warn("Deprecated option (--console).")
- @runner = RUNNERS[:console]
- end
-
- o.on_tail('--gtk', 'GTK runner (use --runner).') do
- warn("Deprecated option (--gtk).")
- @runner = RUNNERS[:gtk]
- end
-
- o.on_tail('--fox', 'Fox runner (use --runner).') do
- warn("Deprecated option (--fox).")
- @runner = RUNNERS[:fox]
- end
-
- o.on_tail
- end
- end
-
- def keyword_display(array)
- list = array.collect {|e, *| e.to_s}
- Array === array or list.sort!
- list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ")
- end
-
- def run
- @suite = @collector[self]
- result = @runner[self] or return false
- Dir.chdir(@workdir) if @workdir
- result.run(@suite, @output_level).passed?
- end
- end
- end
-end
Deleted: MacRuby/branches/experimental/lib/test/unit/collector.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/collector.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/collector.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,43 +0,0 @@
-module Test
- module Unit
- module Collector
- def initialize
- @filters = []
- end
-
- def filter=(filters)
- @filters = case(filters)
- when Proc
- [filters]
- when Array
- filters
- end
- end
-
- def add_suite(destination, suite)
- to_delete = suite.tests.find_all{|t| !include?(t)}
- to_delete.each{|t| suite.delete(t)}
- destination << suite unless(suite.size == 0)
- end
-
- def include?(test)
- return true if(@filters.empty?)
- @filters.each do |filter|
- result = filter[test]
- if(result.nil?)
- next
- elsif(!result)
- return false
- else
- return true
- end
- end
- true
- end
-
- def sort(suites)
- suites.sort_by{|s| s.name}
- end
- end
- end
-end
Deleted: MacRuby/branches/experimental/lib/test/unit/error.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/error.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/error.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,56 +0,0 @@
-#--
-#
-# Author:: Nathaniel Talbott.
-# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
-# License:: Ruby license.
-
-require 'test/unit/util/backtracefilter'
-
-module Test
- module Unit
-
- # Encapsulates an error in a test. Created by
- # Test::Unit::TestCase when it rescues an exception thrown
- # during the processing of a test.
- class Error
- include Util::BacktraceFilter
-
- attr_reader(:test_name, :exception)
-
- SINGLE_CHARACTER = 'E'
-
- # Creates a new Error with the given test_name and
- # exception.
- def initialize(test_name, exception)
- @test_name = test_name
- @exception = exception
- end
-
- # Returns a single character representation of an error.
- def single_character_display
- SINGLE_CHARACTER
- end
-
- # Returns the message associated with the error.
- def message
- "#{@exception.class.name}: #{@exception.message}"
- end
-
- # Returns a brief version of the error description.
- def short_display
- "#@test_name: #{message.split("\n")[0]}"
- end
-
- # Returns a verbose version of the error description.
- def long_display
- backtrace = filter_backtrace(@exception.backtrace).join("\n ")
- "Error:\n#@test_name:\n#{message}\n #{backtrace}"
- end
-
- # Overridden to return long_display.
- def to_s
- long_display
- end
- end
- end
-end
Deleted: MacRuby/branches/experimental/lib/test/unit/failure.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/failure.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/failure.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,51 +0,0 @@
-#--
-#
-# Author:: Nathaniel Talbott.
-# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
-# License:: Ruby license.
-
-module Test
- module Unit
-
- # Encapsulates a test failure. Created by Test::Unit::TestCase
- # when an assertion fails.
- class Failure
- attr_reader :test_name, :location, :message
-
- SINGLE_CHARACTER = 'F'
-
- # Creates a new Failure with the given location and
- # message.
- def initialize(test_name, location, message)
- @test_name = test_name
- @location = location
- @message = message
- end
-
- # Returns a single character representation of a failure.
- def single_character_display
- SINGLE_CHARACTER
- end
-
- # Returns a brief version of the error description.
- def short_display
- "#@test_name: #{@message.split("\n")[0]}"
- end
-
- # Returns a verbose version of the error description.
- def long_display
- location_display = if(location.size == 1)
- location[0].sub(/\A(.+:\d+).*/, ' [\\1]')
- else
- "\n [#{location.join("\n ")}]"
- end
- "Failure:\n#@test_name#{location_display}:\n#@message"
- end
-
- # Overridden to return long_display.
- def to_s
- long_display
- end
- end
- end
-end
Modified: MacRuby/branches/experimental/lib/test/unit/testcase.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/testcase.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/testcase.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,158 +1,12 @@
-#--
-#
-# Author:: Nathaniel Talbott.
-# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
-# License:: Ruby license.
-
require 'test/unit/assertions'
-require 'test/unit/failure'
-require 'test/unit/error'
-require 'test/unit/testsuite'
-require 'test/unit/assertionfailederror'
-require 'test/unit/util/backtracefilter'
module Test
module Unit
-
- # Ties everything together. If you subclass and add your own
- # test methods, it takes care of making them into tests and
- # wrapping those tests into a suite. It also does the
- # nitty-gritty of actually running an individual test and
- # collecting its results into a Test::Unit::TestResult object.
- class TestCase
+ class TestCase < MiniTest::Unit::TestCase
include Assertions
- include Util::BacktraceFilter
-
- attr_reader :method_name
-
- STARTED = name + "::STARTED"
- FINISHED = name + "::FINISHED"
-
- ##
- # These exceptions are not caught by #run.
-
- PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt,
- SystemExit]
-
- # Creates a new instance of the fixture for running the
- # test represented by test_method_name.
- def initialize(test_method_name)
- unless(respond_to?(test_method_name) && method(test_method_name).arity == 0)
- throw :invalid_test
- end
- @method_name = test_method_name
- @test_passed = true
+ def self.test_order
+ :sorted
end
-
- # Rolls up all of the test* methods in the fixture into
- # one suite, creating a new instance of the fixture for
- # each method.
- def self.suite
- method_names = public_instance_methods(true).map { |m| m.to_s }
- tests = method_names.delete_if {|method_name| method_name !~ /^test./}
- suite = TestSuite.new(name)
- tests.sort.each do
- |test|
- catch(:invalid_test) do
- suite << new(test)
- end
- end
- if (suite.empty?)
- catch(:invalid_test) do
- suite << new(:default_test)
- end
- end
- return suite
- end
-
- # Runs the individual test method represented by this
- # instance of the fixture, collecting statistics, failures
- # and errors in result.
- def run(result)
- yield(STARTED, name)
- @_result = result
- begin
- setup
- __send__(@method_name)
- rescue AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue Exception
- raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
- add_error($!)
- ensure
- begin
- teardown
- rescue AssertionFailedError => e
- add_failure(e.message, e.backtrace)
- rescue Exception
- raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
- add_error($!)
- end
- end
- result.add_run
- yield(FINISHED, name)
- end
-
- # Called before every test method runs. Can be used
- # to set up fixture information.
- def setup
- end
-
- # Called after every test method runs. Can be used to tear
- # down fixture information.
- def teardown
- end
-
- def default_test
- flunk("No tests were specified")
- end
-
- # Returns whether this individual test passed or
- # not. Primarily for use in teardown so that artifacts
- # can be left behind if the test fails.
- def passed?
- return @test_passed
- end
- private :passed?
-
- def size # :nodoc:
- 1
- end
-
- def add_assertion # :nodoc:
- @_result.add_assertion
- end
- private :add_assertion
-
- def add_failure(message, all_locations=caller()) # :nodoc:
- @test_passed = false
- @_result.add_failure(Failure.new(name, filter_backtrace(all_locations), message))
- end
- private :add_failure
-
- def add_error(exception) # :nodoc:
- @test_passed = false
- @_result.add_error(Error.new(name, exception))
- end
- private :add_error
-
- # Returns a human-readable name for the specific test that
- # this instance of TestCase represents.
- def name
- "#{@method_name}(#{self.class.name})"
- end
-
- # Overridden to return #name.
- def to_s
- name
- end
-
- # It's handy to be able to compare TestCase instances.
- def ==(other)
- return false unless(other.kind_of?(self.class))
- return false unless(@method_name == other.method_name)
- self.class == other.class
- end
end
end
end
Deleted: MacRuby/branches/experimental/lib/test/unit/testresult.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/testresult.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/testresult.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,81 +0,0 @@
-#--
-#
-# Author:: Nathaniel Talbott.
-# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
-# License:: Ruby license.
-
-require 'test/unit/util/observable'
-
-module Test
- module Unit
-
- # Collects Test::Unit::Failure and Test::Unit::Error so that
- # they can be displayed to the user. To this end, observers
- # can be added to it, allowing the dynamic updating of, say, a
- # UI.
- class TestResult
- include Util::Observable
-
- CHANGED = "CHANGED"
- FAULT = "FAULT"
-
- attr_reader(:run_count, :assertion_count)
-
- # Constructs a new, empty TestResult.
- def initialize
- @run_count, @assertion_count = 0, 0
- @failures, @errors = Array.new, Array.new
- end
-
- # Records a test run.
- def add_run
- @run_count += 1
- notify_listeners(CHANGED, self)
- end
-
- # Records a Test::Unit::Failure.
- def add_failure(failure)
- @failures << failure
- notify_listeners(FAULT, failure)
- notify_listeners(CHANGED, self)
- end
-
- # Records a Test::Unit::Error.
- def add_error(error)
- @errors << error
- notify_listeners(FAULT, error)
- notify_listeners(CHANGED, self)
- end
-
- # Records an individual assertion.
- def add_assertion
- @assertion_count += 1
- notify_listeners(CHANGED, self)
- end
-
- # Returns a string contain the recorded runs, assertions,
- # failures and errors in this TestResult.
- def to_s
- "#{run_count} tests, #{assertion_count} assertions, #{failure_count} failures, #{error_count} errors"
- end
-
- # Returns whether or not this TestResult represents
- # successful completion.
- def passed?
- return @failures.empty? && @errors.empty?
- end
-
- # Returns the number of failures this TestResult has
- # recorded.
- def failure_count
- return @failures.size
- end
-
- # Returns the number of errors this TestResult has
- # recorded.
- def error_count
- return @errors.size
- end
- end
- end
-end
Deleted: MacRuby/branches/experimental/lib/test/unit/testsuite.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit/testsuite.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit/testsuite.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,76 +0,0 @@
-#--
-#
-# Author:: Nathaniel Talbott.
-# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
-# License:: Ruby license.
-
-module Test
- module Unit
-
- # A collection of tests which can be #run.
- #
- # Note: It is easy to confuse a TestSuite instance with
- # something that has a static suite method; I know because _I_
- # have trouble keeping them straight. Think of something that
- # has a suite method as simply providing a way to get a
- # meaningful TestSuite instance.
- class TestSuite
- attr_reader :name, :tests
-
- STARTED = name + "::STARTED"
- FINISHED = name + "::FINISHED"
-
- # Creates a new TestSuite with the given name.
- def initialize(name="Unnamed TestSuite")
- @name = name
- @tests = []
- end
-
- # Runs the tests and/or suites contained in this
- # TestSuite.
- def run(result, &progress_block)
- yield(STARTED, name)
- @tests.each do |test|
- test.run(result, &progress_block)
- end
- yield(FINISHED, name)
- end
-
- # Adds the test to the suite.
- def <<(test)
- @tests << test
- self
- end
-
- def delete(test)
- @tests.delete(test)
- end
-
- # Retuns the rolled up number of tests in this suite;
- # i.e. if the suite contains other suites, it counts the
- # tests within those suites, not the suites themselves.
- def size
- total_size = 0
- @tests.each { |test| total_size += test.size }
- total_size
- end
-
- def empty?
- tests.empty?
- end
-
- # Overridden to return the name given the suite at
- # creation.
- def to_s
- @name
- end
-
- # It's handy to be able to compare TestSuite instances.
- def ==(other)
- return false unless(other.kind_of?(self.class))
- return false unless(@name == other.name)
- @tests == other.tests
- end
- end
- end
-end
Modified: MacRuby/branches/experimental/lib/test/unit.rb
===================================================================
--- MacRuby/branches/experimental/lib/test/unit.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/test/unit.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,280 +1,66 @@
+# test/unit compatibility layer using minitest.
+
+require 'minitest/unit'
+require 'test/unit/assertions'
require 'test/unit/testcase'
-require 'test/unit/autorunner'
-module Test # :nodoc:
- #
- # = Test::Unit - Ruby Unit Testing Framework
- #
- # == Introduction
- #
- # Unit testing is making waves all over the place, largely due to the
- # fact that it is a core practice of XP. While XP is great, unit testing
- # has been around for a long time and has always been a good idea. One
- # of the keys to good unit testing, though, is not just writing tests,
- # but having tests. What's the difference? Well, if you just _write_ a
- # test and throw it away, you have no guarantee that something won't
- # change later which breaks your code. If, on the other hand, you _have_
- # tests (obviously you have to write them first), and run them as often
- # as possible, you slowly build up a wall of things that cannot break
- # without you immediately knowing about it. This is when unit testing
- # hits its peak usefulness.
- #
- # Enter Test::Unit, a framework for unit testing in Ruby, helping you to
- # design, debug and evaluate your code by making it easy to write and
- # have tests for it.
- #
- #
- # == Notes
- #
- # Test::Unit has grown out of and superceded Lapidary.
- #
- #
- # == Feedback
- #
- # I like (and do my best to practice) XP, so I value early releases,
- # user feedback, and clean, simple, expressive code. There is always
- # room for improvement in everything I do, and Test::Unit is no
- # exception. Please, let me know what you think of Test::Unit as it
- # stands, and what you'd like to see expanded/changed/improved/etc. If
- # you find a bug, let me know ASAP; one good way to let me know what the
- # bug is is to submit a new test that catches it :-) Also, I'd love to
- # hear about any successes you have with Test::Unit, and any
- # documentation you might add will be greatly appreciated. My contact
- # info is below.
- #
- #
- # == Contact Information
- #
- # A lot of discussion happens about Ruby in general on the ruby-talk
- # mailing list (http://www.ruby-lang.org/en/ml.html), and you can ask
- # any questions you might have there. I monitor the list, as do many
- # other helpful Rubyists, and you're sure to get a quick answer. Of
- # course, you're also welcome to email me (Nathaniel Talbott) directly
- # at mailto:testunit at talbott.ws, and I'll do my best to help you out.
- #
- #
- # == Credits
- #
- # I'd like to thank...
- #
- # Matz, for a great language!
- #
- # Masaki Suketa, for his work on RubyUnit, which filled a vital need in
- # the Ruby world for a very long time. I'm also grateful for his help in
- # polishing Test::Unit and getting the RubyUnit compatibility layer
- # right. His graciousness in allowing Test::Unit to supercede RubyUnit
- # continues to be a challenge to me to be more willing to defer my own
- # rights.
- #
- # Ken McKinlay, for his interest and work on unit testing, and for his
- # willingness to dialog about it. He was also a great help in pointing
- # out some of the holes in the RubyUnit compatibility layer.
- #
- # Dave Thomas, for the original idea that led to the extremely simple
- # "require 'test/unit'", plus his code to improve it even more by
- # allowing the selection of tests from the command-line. Also, without
- # RDoc, the documentation for Test::Unit would stink a lot more than it
- # does now.
- #
- # Everyone who's helped out with bug reports, feature ideas,
- # encouragement to continue, etc. It's a real privilege to be a part of
- # the Ruby community.
- #
- # The guys at RoleModel Software, for putting up with me repeating, "But
- # this would be so much easier in Ruby!" whenever we're coding in Java.
- #
- # My Creator, for giving me life, and giving it more abundantly.
- #
- #
- # == License
- #
- # Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free
- # software, and is distributed under the Ruby license. See the COPYING
- # file in the standard Ruby distribution for details.
- #
- #
- # == Warranty
- #
- # This software is provided "as is" and without any express or
- # implied warranties, including, without limitation, the implied
- # warranties of merchantibility and fitness for a particular
- # purpose.
- #
- #
- # == Author
- #
- # Nathaniel Talbott.
- # Copyright (c) 2000-2003, Nathaniel Talbott
- #
- # ----
- #
- # = Usage
- #
- # The general idea behind unit testing is that you write a _test_
- # _method_ that makes certain _assertions_ about your code, working
- # against a _test_ _fixture_. A bunch of these _test_ _methods_ are
- # bundled up into a _test_ _suite_ and can be run any time the
- # developer wants. The results of a run are gathered in a _test_
- # _result_ and displayed to the user through some UI. So, lets break
- # this down and see how Test::Unit provides each of these necessary
- # pieces.
- #
- #
- # == Assertions
- #
- # These are the heart of the framework. Think of an assertion as a
- # statement of expected outcome, i.e. "I assert that x should be equal
- # to y". If, when the assertion is executed, it turns out to be
- # correct, nothing happens, and life is good. If, on the other hand,
- # your assertion turns out to be false, an error is propagated with
- # pertinent information so that you can go back and make your
- # assertion succeed, and, once again, life is good. For an explanation
- # of the current assertions, see Test::Unit::Assertions.
- #
- #
- # == Test Method & Test Fixture
- #
- # Obviously, these assertions have to be called within a context that
- # knows about them and can do something meaningful with their
- # pass/fail value. Also, it's handy to collect a bunch of related
- # tests, each test represented by a method, into a common test class
- # that knows how to run them. The tests will be in a separate class
- # from the code they're testing for a couple of reasons. First of all,
- # it allows your code to stay uncluttered with test code, making it
- # easier to maintain. Second, it allows the tests to be stripped out
- # for deployment, since they're really there for you, the developer,
- # and your users don't need them. Third, and most importantly, it
- # allows you to set up a common test fixture for your tests to run
- # against.
- #
- # What's a test fixture? Well, tests do not live in a vacuum; rather,
- # they're run against the code they are testing. Often, a collection
- # of tests will run against a common set of data, also called a
- # fixture. If they're all bundled into the same test class, they can
- # all share the setting up and tearing down of that data, eliminating
- # unnecessary duplication and making it much easier to add related
- # tests.
- #
- # Test::Unit::TestCase wraps up a collection of test methods together
- # and allows you to easily set up and tear down the same test fixture
- # for each test. This is done by overriding #setup and/or #teardown,
- # which will be called before and after each test method that is
- # run. The TestCase also knows how to collect the results of your
- # assertions into a Test::Unit::TestResult, which can then be reported
- # back to you... but I'm getting ahead of myself. To write a test,
- # follow these steps:
- #
- # * Make sure Test::Unit is in your library path.
- # * require 'test/unit' in your test script.
- # * Create a class that subclasses Test::Unit::TestCase.
- # * Add a method that begins with "test" to your class.
- # * Make assertions in your test method.
- # * Optionally define #setup and/or #teardown to set up and/or tear
- # down your common test fixture.
- # * You can now run your test as you would any other Ruby
- # script... try it and see!
- #
- # A really simple test might look like this (#setup and #teardown are
- # commented out to indicate that they are completely optional):
- #
- # require 'test/unit'
- #
- # class TC_MyTest < Test::Unit::TestCase
- # # def setup
- # # end
- #
- # # def teardown
- # # end
- #
- # def test_fail
- # assert(false, 'Assertion was false.')
- # end
- # end
- #
- #
- # == Test Runners
- #
- # So, now you have this great test class, but you still need a way to
- # run it and view any failures that occur during the run. This is
- # where Test::Unit::UI::Console::TestRunner (and others, such as
- # Test::Unit::UI::GTK::TestRunner) comes into play. The console test
- # runner is automatically invoked for you if you require 'test/unit'
- # and simply run the file. To use another runner, or to manually
- # invoke a runner, simply call its run class method and pass in an
- # object that responds to the suite message with a
- # Test::Unit::TestSuite. This can be as simple as passing in your
- # TestCase class (which has a class suite method). It might look
- # something like this:
- #
- # require 'test/unit/ui/console/testrunner'
- # Test::Unit::UI::Console::TestRunner.run(TC_MyTest)
- #
- #
- # == Test Suite
- #
- # As more and more unit tests accumulate for a given project, it
- # becomes a real drag running them one at a time, and it also
- # introduces the potential to overlook a failing test because you
- # forget to run it. Suddenly it becomes very handy that the
- # TestRunners can take any object that returns a Test::Unit::TestSuite
- # in response to a suite method. The TestSuite can, in turn, contain
- # other TestSuites or individual tests (typically created by a
- # TestCase). In other words, you can easily wrap up a group of
- # TestCases and TestSuites like this:
- #
- # require 'test/unit/testsuite'
- # require 'tc_myfirsttests'
- # require 'tc_moretestsbyme'
- # require 'ts_anothersetoftests'
- #
- # class TS_MyTests
- # def self.suite
- # suite = Test::Unit::TestSuite.new
- # suite << TC_MyFirstTests.suite
- # suite << TC_MoreTestsByMe.suite
- # suite << TS_AnotherSetOfTests.suite
- # return suite
- # end
- # end
- # Test::Unit::UI::Console::TestRunner.run(TS_MyTests)
- #
- # Now, this is a bit cumbersome, so Test::Unit does a little bit more
- # for you, by wrapping these up automatically when you require
- # 'test/unit'. What does this mean? It means you could write the above
- # test case like this instead:
- #
- # require 'test/unit'
- # require 'tc_myfirsttests'
- # require 'tc_moretestsbyme'
- # require 'ts_anothersetoftests'
- #
- # Test::Unit is smart enough to find all the test cases existing in
- # the ObjectSpace and wrap them up into a suite for you. It then runs
- # the dynamic suite using the console TestRunner.
- #
- #
- # == Questions?
- #
- # I'd really like to get feedback from all levels of Ruby
- # practitioners about typos, grammatical errors, unclear statements,
- # missing points, etc., in this document (or any other).
- #
-
+module Test
module Unit
- # If set to false Test::Unit will not automatically run at exit.
- def self.run=(flag)
- @run = flag
- end
+ TEST_UNIT_IMPLEMENTATION = 'test/unit compatibility layer using minitest'
- # Automatically run tests at exit?
- def self.run?
- @run ||= false
+ def self.setup_argv(original_argv=ARGV)
+ minitest_argv = []
+ files = []
+ reject = []
+ original_argv = original_argv.dup
+ while arg = original_argv.shift
+ case arg
+ when '-v'
+ minitest_argv << '-v'
+ when '-n', '--name'
+ minitest_argv << arg
+ minitest_argv << original_argv.shift
+ when '-x'
+ reject << original_argv.shift
+ else
+ files << arg
+ end
+ end
+
+ if block_given?
+ files = yield files
+ end
+
+ files.map! {|f|
+ f = f.gsub(Regexp.compile(Regexp.quote(File::ALT_SEPARATOR)), File::SEPARATOR) if File::ALT_SEPARATOR
+ if File.directory? f
+ Dir["#{f}/**/test_*.rb"]
+ elsif File.file? f
+ f
+ else
+ raise ArgumentError, "file not found: #{f}"
+ end
+ }
+ files.flatten!
+
+ reject_pat = Regexp.union(reject.map {|r| /#{r}/ })
+ files.reject! {|f| reject_pat =~ f }
+
+ files.each {|f|
+ d = File.dirname(File.expand_path(f))
+ unless $:.include? d
+ $: << d
+ end
+ begin
+ require f
+ rescue LoadError
+ puts "#{f}: #{$!}"
+ end
+ }
+
+ ARGV.replace minitest_argv
end
end
end
-at_exit do
- unless $! || Test::Unit.run?
- exit Test::Unit::AutoRunner.run
- end
-end
+MiniTest::Unit.autorun
Modified: MacRuby/branches/experimental/lib/thwait.rb
===================================================================
--- MacRuby/branches/experimental/lib/thwait.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/thwait.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -53,7 +53,7 @@
class ThreadsWait
RCS_ID='-$Id: thwait.rb,v 1.3 1998/06/26 03:19:34 keiju Exp keiju $-'
- Exception2MessageMapper.extend_to(binding)
+ extend Exception2MessageMapper
def_exception("ErrNoWaitingThread", "No threads for waiting.")
def_exception("ErrNoFinishedThread", "No finished threads.")
Modified: MacRuby/branches/experimental/lib/time.rb
===================================================================
--- MacRuby/branches/experimental/lib/time.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/time.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,37 +1,37 @@
#
# == Introduction
-#
+#
# This library extends the Time class:
# * conversion between date string and time object.
# * date-time defined by RFC 2822
# * HTTP-date defined by RFC 2616
# * dateTime defined by XML Schema Part 2: Datatypes (ISO 8601)
# * various formats handled by Date._parse (string to time only)
-#
+#
# == Design Issues
-#
+#
# === Specialized interface
-#
+#
# This library provides methods dedicated to special purposes:
# * RFC 2822, RFC 2616 and XML Schema.
# * They makes usual life easier.
-#
+#
# === Doesn't depend on strftime
-#
+#
# This library doesn't use +strftime+. Especially #rfc2822 doesn't depend
# on +strftime+ because:
-#
+#
# * %a and %b are locale sensitive
-#
+#
# Since they are locale sensitive, they may be replaced to
# invalid weekday/month name in some locales.
# Since ruby-1.6 doesn't invoke setlocale by default,
# the problem doesn't arise until some external library invokes setlocale.
# Ruby/GTK is the example of such library.
-#
+#
# * %z is not portable
-#
+#
# %z is required to generate zone in date-time of RFC 2822
# but it is not portable.
#
@@ -61,9 +61,9 @@
'PST' => -8, 'PDT' => -7,
# Following definition of military zones is original one.
# See RFC 1123 and RFC 2822 for the error in RFC 822.
- 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5, 'F' => +6,
+ 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5, 'F' => +6,
'G' => +7, 'H' => +8, 'I' => +9, 'K' => +10, 'L' => +11, 'M' => +12,
- 'N' => -1, 'O' => -2, 'P' => -3, 'Q' => -4, 'R' => -5, 'S' => -6,
+ 'N' => -1, 'O' => -2, 'P' => -3, 'Q' => -4, 'R' => -5, 'S' => -6,
'T' => -7, 'U' => -8, 'V' => -9, 'W' => -10, 'X' => -11, 'Y' => -12,
}
def zone_offset(zone, year=self.now.year)
@@ -84,8 +84,26 @@
end
def zone_utc?(zone)
- # * +0000 means localtime. [RFC 2822]
- # * GMT is a localtime abbreviation in Europe/London, etc.
+ # * +0000
+ # In RFC 2822, +0000 indicate a time zone at Universal Time.
+ # Europe/London is "a time zone at Universal Time" in Winter.
+ # Europe/Lisbon is "a time zone at Universal Time" in Winter.
+ # Atlantic/Reykjavik is "a time zone at Universal Time".
+ # Africa/Dakar is "a time zone at Universal Time".
+ # So +0000 is a local time such as Europe/London, etc.
+ # * GMT
+ # GMT is used as a time zone abbreviation in Europe/London,
+ # Africa/Dakar, etc.
+ # So it is a local time.
+ #
+ # * -0000, -00:00
+ # In RFC 2822, -0000 the date-time contains no information about the
+ # local time zone.
+ # In RFC 3339, -00:00 is used for the time in UTC is known,
+ # but the offset to local time is unknown.
+ # They are not appropriate for specific time zone such as
+ # Europe/London because time zone neutral,
+ # So -00:00 and -0000 are treated as UTC.
if /\A(?:-00:00|-0000|-00|UTC|Z|UT)\z/i =~ zone
true
else
@@ -374,7 +392,7 @@
(-?\d+)-(\d\d)-(\d\d)
T
(\d\d):(\d\d):(\d\d)
- (\.\d*)?
+ (\.\d+)?
(Z|[+-]\d\d:\d\d)?
\s*\z/ix =~ date
year = $1.to_i
@@ -384,7 +402,9 @@
min = $5.to_i
sec = $6.to_i
usec = 0
- usec = $7.to_f * 1000000 if $7
+ if $7
+ usec = Rational($7) * 1000000
+ end
if $8
zone = $8
year, mon, day, hour, min, sec =
@@ -434,8 +454,8 @@
#
# Returns a string which represents the time as rfc1123-date of HTTP-date
- # defined by RFC 2616:
- #
+ # defined by RFC 2616:
+ #
# day-of-week, DD month-name CCYY hh:mm:ss GMT
#
# Note that the result is always UTC (GMT).
@@ -622,6 +642,7 @@
Time.xmlschema("2000-01-12T12:13:14Z"))
assert_equal(Time.utc(2001, 4, 17, 19, 23, 17, 300000),
Time.xmlschema("2001-04-17T19:23:17.3Z"))
+ assert_raise(ArgumentError) { Time.xmlschema("2000-01-01T00:00:00.+00:00") }
end
def test_encode_xmlschema
@@ -645,6 +666,8 @@
t = Time.utc(1960, 12, 31, 23, 0, 0, 123456)
assert_equal("1960-12-31T23:00:00.123456Z", t.xmlschema(6))
end
+
+ assert_equal(249, Time.xmlschema("2008-06-05T23:49:23.000249+09:00").usec)
end
def test_completion
@@ -736,6 +759,18 @@
assert_equal(true, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 UTC").utc?)
end
+ def test_rfc2822_utc_roundtrip_winter
+ t1 = Time.local(2008,12,1)
+ t2 = Time.rfc2822(t1.rfc2822)
+ assert_equal(t1.utc?, t2.utc?, "[ruby-dev:37126]")
+ end
+
+ def test_rfc2822_utc_roundtrip_summer
+ t1 = Time.local(2008,8,1)
+ t2 = Time.rfc2822(t1.rfc2822)
+ assert_equal(t1.utc?, t2.utc?)
+ end
+
def test_parse_leap_second
t = Time.utc(1998,12,31,23,59,59)
assert_equal(t, Time.parse("Thu Dec 31 23:59:59 UTC 1998"))
@@ -763,21 +798,21 @@
def test_rfc2822_leap_second
t = Time.utc(1998,12,31,23,59,59)
assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:59 UTC"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 -0000"));t.localtime
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:59 +0900"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:59 +0100"))
assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 +0000"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:59 -0100"));t.utc
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:59 -0100"));t.utc
t += 1
assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:60 UTC"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 -0000"));t.localtime
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:60 +0900"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:60 +0100"))
assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 +0000"))
- assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:60 -0100"));t.utc
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:60 -0100"));t.utc
t += 1 if t.sec == 60
assert_equal(t, Time.rfc2822("Thu, 1 Jan 1999 00:00:00 UTC"))
- assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 -0000"));t.localtime
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 09:00:00 +0900"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 01:00:00 +0100"))
assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 +0000"))
@@ -808,6 +843,10 @@
assert_equal(t, Time.xmlschema("1998-12-31T23:00:00-01:00"))
end
+ def test_xmlschema_fraction
+ assert_equal(500000, Time.xmlschema("2000-01-01T00:00:00.5+00:00").tv_usec)
+ end
+
def test_ruby_talk_152866
t = Time::xmlschema('2005-08-30T22:48:00-07:00')
assert_equal(31, t.day)
Modified: MacRuby/branches/experimental/lib/tmpdir.rb
===================================================================
--- MacRuby/branches/experimental/lib/tmpdir.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/tmpdir.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
#
# tmpdir - retrieve temporary directory path
#
-# $Id: tmpdir.rb 13762 2007-10-24 06:03:48Z akr $
+# $Id: tmpdir.rb 19513 2008-09-24 05:39:39Z usa $
#
require 'fileutils'
@@ -12,16 +12,23 @@
begin
require 'Win32API'
+ CSIDL_LOCAL_APPDATA = 0x001c
max_pathlen = 260
- windir = ' '*(max_pathlen+1)
+ windir = "\0"*(max_pathlen+1)
begin
- getdir = Win32API.new('kernel32', 'GetSystemWindowsDirectory', 'PL', 'L')
+ getdir = Win32API.new('shell32', 'SHGetFolderPath', 'LLLLP', 'L')
+ raise RuntimeError if getdir.call(0, CSIDL_LOCAL_APPDATA, 0, 0, windir) != 0
+ windir = File.expand_path(windir.rstrip)
rescue RuntimeError
- getdir = Win32API.new('kernel32', 'GetWindowsDirectory', 'PL', 'L')
+ begin
+ getdir = Win32API.new('kernel32', 'GetSystemWindowsDirectory', 'PL', 'L')
+ rescue RuntimeError
+ getdir = Win32API.new('kernel32', 'GetWindowsDirectory', 'PL', 'L')
+ end
+ len = getdir.call(windir, windir.size)
+ windir = File.expand_path(windir[0, len])
end
- len = getdir.call(windir, windir.size)
- windir = File.expand_path(windir[0, len])
- temp = File.join(windir, 'temp')
+ temp = File.join(windir.untaint, 'temp')
@@systmpdir = temp if File.directory?(temp) and File.writable?(temp)
rescue LoadError
end
@@ -41,8 +48,8 @@
break
end
end
+ File.expand_path(tmp)
end
- File.expand_path(tmp)
end
# Dir.mktmpdir creates a temporary directory.
Modified: MacRuby/branches/experimental/lib/tsort.rb
===================================================================
--- MacRuby/branches/experimental/lib/tsort.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/tsort.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -32,7 +32,7 @@
# array using the user-supplied block.
#
# require 'tsort'
-#
+#
# class Hash
# include TSort
# alias tsort_each_node each_key
@@ -40,10 +40,10 @@
# fetch(node).each(&block)
# end
# end
-#
+#
# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
# #=> [3, 2, 1, 4]
-#
+#
# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
# #=> [[4], [2, 3], [1]]
#
@@ -52,19 +52,19 @@
# A very simple `make' like tool can be implemented as follows:
#
# require 'tsort'
-#
+#
# class Make
# def initialize
# @dep = {}
# @dep.default = []
# end
-#
+#
# def rule(outputs, inputs=[], &block)
# triple = [outputs, inputs, block]
# outputs.each {|f| @dep[f] = [triple]}
# @dep[triple] = inputs
# end
-#
+#
# def build(target)
# each_strongly_connected_component_from(target) {|ns|
# if ns.length != 1
@@ -88,18 +88,18 @@
# end
# }
# end
-#
+#
# def tsort_each_child(node, &block)
# @dep[node].each(&block)
# end
# include TSort
# end
-#
+#
# def command(arg)
# print arg, "\n"
# system arg
# end
-#
+#
# m = Make.new
# m.rule(%w[t1]) { command 'date > t1' }
# m.rule(%w[t2]) { command 'date > t2' }
@@ -189,7 +189,7 @@
end
#
- # Iterates over strongly connected component in the subgraph reachable from
+ # Iterates over strongly connected component in the subgraph reachable from
# _node_.
#
# Return value is unspecified.
Modified: MacRuby/branches/experimental/lib/un.rb
===================================================================
--- MacRuby/branches/experimental/lib/un.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/un.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -19,6 +19,8 @@
# ruby -run -e install -- [OPTION] SOURCE DEST
# ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE
# ruby -run -e touch -- [OPTION] FILE
+# ruby -run -e wait_writable -- [OPTION] FILE
+# ruby -run -e mkmf -- [OPTION] EXTNAME [OPTION]
# ruby -run -e help [COMMAND]
require "fileutils"
@@ -29,30 +31,32 @@
@fileutils_output = $stdout
end
-def setup(options = "")
- ARGV.map! do |x|
- case x
- when /^-/
- x.delete "^-#{options}v"
- when /[*?\[{]/
- Dir[x]
- else
- x
- end
- end
- ARGV.flatten!
- ARGV.delete_if{|x| x == "-"}
+def setup(options = "", *long_options)
opt_hash = {}
+ argv = []
OptionParser.new do |o|
options.scan(/.:?/) do |s|
+ opt_name = s.delete(":").intern
o.on("-" + s.tr(":", " ")) do |val|
- opt_hash[s.delete(":").intern] = val
+ opt_hash[opt_name] = val
end
end
+ long_options.each do |s|
+ opt_name = s[/\A(?:--)?([^\s=]+)/, 1].intern
+ o.on(s.sub(/\A(?!--)/, '--')) do |val|
+ opt_hash[opt_name] = val
+ end
+ end
o.on("-v") do opt_hash[:verbose] = true end
- o.parse!
+ o.order!(ARGV) do |x|
+ if /[*?\[{]/ =~ x
+ argv.concat(Dir[x])
+ else
+ argv << x
+ end
+ end
end
- yield ARGV, opt_hash
+ yield argv, opt_hash
end
##
@@ -154,11 +158,13 @@
#
# ruby -run -e rmdir -- [OPTION] DIR
#
+# -p remove DIRECTORY and its ancestors.
# -v verbose
#
def rmdir
- setup do |argv, options|
+ setup("p") do |argv, options|
+ options[:parents] = true if options.delete :p
FileUtils.rmdir argv, options
end
end
@@ -218,6 +224,10 @@
#
# ruby -run -e wait_writable -- [OPTION] FILE
#
+# -n RETRY count to retry
+# -w SEC each wait time in seconds
+# -v verbose
+#
def wait_writable
setup("n:w:v") do |argv, options|
@@ -241,6 +251,38 @@
end
##
+# Create makefile using mkmf.
+#
+# ruby -run -e mkmf -- [OPTION] EXTNAME [OPTION]
+#
+# -d ARGS run dir_config
+# -h ARGS run have_header
+# -l ARGS run have_library
+# -f ARGS run have_func
+# -v ARGS run have_var
+# -t ARGS run have_type
+# -m ARGS run have_macro
+# -c ARGS run have_const
+# --vendor install to vendor_ruby
+#
+
+def mkmf
+ setup("d:h:l:f:v:t:m:c:", "vendor") do |argv, options|
+ require 'mkmf'
+ opt = options[:d] and opt.split(/:/).each {|n| dir_config(*n.split(/,/))}
+ opt = options[:h] and opt.split(/:/).each {|n| have_header(*n.split(/,/))}
+ opt = options[:l] and opt.split(/:/).each {|n| have_library(*n.split(/,/))}
+ opt = options[:f] and opt.split(/:/).each {|n| have_func(*n.split(/,/))}
+ opt = options[:v] and opt.split(/:/).each {|n| have_var(*n.split(/,/))}
+ opt = options[:t] and opt.split(/:/).each {|n| have_type(*n.split(/,/))}
+ opt = options[:m] and opt.split(/:/).each {|n| have_macro(*n.split(/,/))}
+ opt = options[:c] and opt.split(/:/).each {|n| have_const(*n.split(/,/))}
+ $configure_args["--vendor"] = true if options[:vendor]
+ create_makefile(*argv)
+ end
+end
+
+##
# Display help message.
#
# ruby -run -e help [COMMAND]
Modified: MacRuby/branches/experimental/lib/uri/common.rb
===================================================================
--- MacRuby/branches/experimental/lib/uri/common.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/uri/common.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,7 +1,7 @@
# = uri/common.rb
#
# Author:: Akira Yamada <akira at ruby-lang.org>
-# Revision:: $Id: common.rb 15442 2008-02-12 06:18:06Z naruse $
+# Revision:: $Id: common.rb 19413 2008-09-18 11:05:09Z mame $
# License::
# You can redistribute it and/or modify it under the same term as Ruby.
#
@@ -38,22 +38,232 @@
# "$" | "," | "[" | "]" (RFC 2732)
RESERVED = ";/?:@&=+$,\\[\\]"
+ # domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ DOMLABEL = "(?:[#{ALNUM}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+ TOPLABEL = "(?:[#{ALPHA}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # hostname = *( domainlabel "." ) toplabel [ "." ]
+ HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
+
+ # :startdoc:
+ end # PATTERN
+
+ # :startdoc:
+ end # REGEXP
+
+ class Parser
+ include REGEXP
+
+ #
+ # == Synopsis
+ #
+ # URI::Parser.new([opts])
+ #
+ # == Args
+ #
+ # The constructor accepts a hash as options for parser.
+ # Keys of options are pattern names of URI components
+ # and values of options are pattern strings.
+ # The constructor generetes set of regexps for parsing URIs.
+ #
+ # You can use the following keys:
+ #
+ # * <tt>:ESCAPED</tt> (URI::PATTERN::ESCAPED in default)
+ # * <tt>:UNRESERVED</tt> (URI::PATTERN::UNRESERVED in default)
+ # * <tt>:DOMLABEL</tt> (URI::PATTERN::DOMLABEL in default)
+ # * <tt>:TOPLABEL</tt> (URI::PATTERN::TOPLABEL in default)
+ # * <tt>:HOSTNAME</tt> (URI::PATTERN::HOSTNAME in default)
+ #
+ # == Examples
+ #
+ # p = URI::Parser.new(:ESCPAED => "(?:%[a-fA-F0-9]{2}|%u[a-fA-F0-9]{4})"
+ # u = p.parse("http://example.jp/%uABCD") #=> #<URI::HTTP:0xb78cf4f8 URL:http://example.jp/%uABCD>
+ # URI.parse(u.to_s) #=> raises URI::InvalidURIError
+ #
+ # s = "http://examle.com/ABCD"
+ # u1 = p.parse(s) #=> #<URI::HTTP:0xb78c3220 URL:http://example.com/ABCD>
+ # u2 = URI.parse(s) #=> #<URI::HTTP:0xb78b6d54 URL:http://example.com/ABCD>
+ # u1 == u2 #=> true
+ # u1.eql?(u2) #=> false
+ #
+ def initialize(opts = {})
+ @pattern = initialize_pattern(opts)
+ @pattern.each_value {|v| v.freeze}
+ @pattern.freeze
+
+ @regexp = initialize_regexp(@pattern)
+ @regexp.each_value {|v| v.freeze}
+ @regexp.freeze
+ end
+ attr_reader :pattern, :regexp
+
+ def split(uri)
+ case uri
+ when ''
+ # null uri
+
+ when @regexp[:ABS_URI]
+ scheme, opaque, userinfo, host, port,
+ registry, path, query, fragment = $~[1..-1]
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ # opaque_part = uric_no_slash *uric
+
+ # abs_path = "/" path_segments
+ # net_path = "//" authority [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ if !scheme
+ raise InvalidURIError,
+ "bad URI(absolute but no scheme): #{uri}"
+ end
+ if !opaque && (!path && (!host && !registry))
+ raise InvalidURIError,
+ "bad URI(absolute but no path): #{uri}"
+ end
+
+ when @regexp[:REL_URI]
+ scheme = nil
+ opaque = nil
+
+ userinfo, host, port, registry,
+ rel_segment, abs_path, query, fragment = $~[1..-1]
+ if rel_segment && abs_path
+ path = rel_segment + abs_path
+ elsif rel_segment
+ path = rel_segment
+ elsif abs_path
+ path = abs_path
+ end
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+
+ # net_path = "//" authority [ abs_path ]
+ # abs_path = "/" path_segments
+ # rel_path = rel_segment [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ else
+ raise InvalidURIError, "bad URI(is not URI?): #{uri}"
+ end
+
+ path = '' if !path && !opaque # (see RFC2396 Section 5.2)
+ ret = [
+ scheme,
+ userinfo, host, port, # X
+ registry, # X
+ path, # Y
+ opaque, # Y
+ query,
+ fragment
+ ]
+ return ret
+ end
+
+ def parse(uri)
+ scheme, userinfo, host, port,
+ registry, path, opaque, query, fragment = self.split(uri)
+
+ if scheme && URI.scheme_list.include?(scheme.upcase)
+ URI.scheme_list[scheme.upcase].new(scheme, userinfo, host, port,
+ registry, path, opaque, query,
+ fragment, self)
+ else
+ Generic.new(scheme, userinfo, host, port,
+ registry, path, opaque, query,
+ fragment, self)
+ end
+ end
+
+ def join(*str)
+ u = self.parse(str[0])
+ str[1 .. -1].each do |x|
+ u = u.merge(x)
+ end
+ u
+ end
+
+ def extract(str, schemes = nil, &block)
+ if block_given?
+ str.scan(make_regexp(schemes)) { yield $& }
+ nil
+ else
+ result = []
+ str.scan(make_regexp(schemes)) { result.push $& }
+ result
+ end
+ end
+
+ def make_regexp(schemes = nil)
+ unless schemes
+ @regexp[:ABS_URI_REF]
+ else
+ /(?=#{Regexp.union(*schemes)}:)#{@pattern[:X_ABS_URI]}/x
+ end
+ end
+
+ def escape(str, unsafe = @regexp[:UNSAFE])
+ unless unsafe.kind_of?(Regexp)
+ # perhaps unsafe is String object
+ unsafe = Regexp.new("[#{Regexp.quote(unsafe)}]", false)
+ end
+ str.gsub(unsafe) do
+ us = $&
+ tmp = ''
+ us.each_byte do |uc|
+ tmp << sprintf('%%%02X', uc)
+ end
+ tmp
+ end
+ end
+
+ def unescape(str, escaped = @regexp[:ESCAPED])
+ str.gsub(escaped) { [$&[1, 2].hex].pack('U') }
+ end
+
+ @@to_s = Kernel.instance_method(:to_s)
+ def inspect
+ @@to_s.bind(self).call
+ end
+
+ private
+
+ def initialize_pattern(opts = {})
+ ret = {}
+ ret[:ESCAPED] = escaped = (opts.delete(:ESCAPED) || PATTERN::ESCAPED)
+ ret[:UNRESERVED] = unreserved = opts.delete(:UNRESERVED) || PATTERN::UNRESERVED
+ ret[:RESERVED] = reserved = opts.delete(:RESERVED) || PATTERN::RESERVED
+ ret[:DOMLABEL] = domlabel = opts.delete(:DOMLABEL) || PATTERN::DOMLABEL
+ ret[:TOPLABEL] = toplabel = opts.delete(:TOPLABEL) || PATTERN::TOPLABEL
+ ret[:HOSTNAME] = hostname = opts.delete(:HOSTNAME)
+
+ # RFC 2396 (URI Generic Syntax)
+ # RFC 2732 (IPv6 Literal Addresses in URL's)
+ # RFC 2373 (IPv6 Addressing Architecture)
+
# uric = reserved | unreserved | escaped
- URIC = "(?:[#{UNRESERVED}#{RESERVED}]|#{ESCAPED})"
+ ret[:URIC] = uric = "(?:[#{unreserved}#{reserved}]|#{escaped})"
# uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
# "&" | "=" | "+" | "$" | ","
- URIC_NO_SLASH = "(?:[#{UNRESERVED};?:@&=+$,]|#{ESCAPED})"
+ ret[:URIC_NO_SLASH] = uric_no_slash = "(?:[#{unreserved};?:@&=+$,]|#{escaped})"
# query = *uric
- QUERY = "#{URIC}*"
+ ret[:QUERY] = query = "#{uric}*"
# fragment = *uric
- FRAGMENT = "#{URIC}*"
+ ret[:FRAGMENT] = fragment = "#{uric}*"
- # domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
- DOMLABEL = "(?:[#{ALNUM}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
- # toplabel = alpha | alpha *( alphanum | "-" ) alphanum
- TOPLABEL = "(?:[#{ALPHA}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
# hostname = *( domainlabel "." ) toplabel [ "." ]
- HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
+ unless hostname
+ ret[:HOSTNAME] = hostname = "(?:#{domlabel}\\.)*#{toplabel}\\.?"
+ end
# RFC 2373, APPENDIX B:
# IPv6address = hexpart [ ":" IPv4address ]
@@ -66,153 +276,165 @@
# allowed too. Here is a replacement.
#
# IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
- IPV4ADDR = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
+ ret[:IPV4ADDR] = ipv4addr = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
# hex4 = 1*4HEXDIG
- HEX4 = "[#{HEX}]{1,4}"
+ hex4 = "[#{PATTERN::HEX}]{1,4}"
# lastpart = hex4 | IPv4address
- LASTPART = "(?:#{HEX4}|#{IPV4ADDR})"
+ lastpart = "(?:#{hex4}|#{ipv4addr})"
# hexseq1 = *( hex4 ":" ) hex4
- HEXSEQ1 = "(?:#{HEX4}:)*#{HEX4}"
+ hexseq1 = "(?:#{hex4}:)*#{hex4}"
# hexseq2 = *( hex4 ":" ) lastpart
- HEXSEQ2 = "(?:#{HEX4}:)*#{LASTPART}"
+ hexseq2 = "(?:#{hex4}:)*#{lastpart}"
# IPv6address = hexseq2 | [ hexseq1 ] "::" [ hexseq2 ]
- IPV6ADDR = "(?:#{HEXSEQ2}|(?:#{HEXSEQ1})?::(?:#{HEXSEQ2})?)"
+ ret[:IPV6ADDR] = ipv6addr = "(?:#{hexseq2}|(?:#{hexseq1})?::(?:#{hexseq2})?)"
# IPv6prefix = ( hexseq1 | [ hexseq1 ] "::" [ hexseq1 ] ) "/" 1*2DIGIT
# unused
# ipv6reference = "[" IPv6address "]" (RFC 2732)
- IPV6REF = "\\[#{IPV6ADDR}\\]"
+ ret[:IPV6REF] = ipv6ref = "\\[#{ipv6addr}\\]"
# host = hostname | IPv4address
# host = hostname | IPv4address | IPv6reference (RFC 2732)
- HOST = "(?:#{HOSTNAME}|#{IPV4ADDR}|#{IPV6REF})"
+ ret[:HOST] = host = "(?:#{hostname}|#{ipv4addr}|#{ipv6ref})"
# port = *digit
- PORT = '\d*'
+ port = '\d*'
# hostport = host [ ":" port ]
- HOSTPORT = "#{HOST}(?::#{PORT})?"
+ ret[:HOSTPORT] = hostport = "#{host}(?::#{port})?"
# userinfo = *( unreserved | escaped |
# ";" | ":" | "&" | "=" | "+" | "$" | "," )
- USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})*"
+ ret[:USERINFO] = userinfo = "(?:[#{unreserved};:&=+$,]|#{escaped})*"
# pchar = unreserved | escaped |
# ":" | "@" | "&" | "=" | "+" | "$" | ","
- PCHAR = "(?:[#{UNRESERVED}:@&=+$,]|#{ESCAPED})"
+ pchar = "(?:[#{unreserved}:@&=+$,]|#{escaped})"
# param = *pchar
- PARAM = "#{PCHAR}*"
+ param = "#{pchar}*"
# segment = *pchar *( ";" param )
- SEGMENT = "#{PCHAR}*(?:;#{PARAM})*"
+ segment = "#{pchar}*(?:;#{param})*"
# path_segments = segment *( "/" segment )
- PATH_SEGMENTS = "#{SEGMENT}(?:/#{SEGMENT})*"
+ ret[:PATH_SEGMENTS] = path_segments = "#{segment}(?:/#{segment})*"
# server = [ [ userinfo "@" ] hostport ]
- SERVER = "(?:#{USERINFO}@)?#{HOSTPORT}"
+ server = "(?:#{userinfo}@)?#{hostport}"
# reg_name = 1*( unreserved | escaped | "$" | "," |
# ";" | ":" | "@" | "&" | "=" | "+" )
- REG_NAME = "(?:[#{UNRESERVED}$,;:@&=+]|#{ESCAPED})+"
+ ret[:REG_NAME] = reg_name = "(?:[#{unreserved}$,;:@&=+]|#{escaped})+"
# authority = server | reg_name
- AUTHORITY = "(?:#{SERVER}|#{REG_NAME})"
+ authority = "(?:#{server}|#{reg_name})"
# rel_segment = 1*( unreserved | escaped |
# ";" | "@" | "&" | "=" | "+" | "$" | "," )
- REL_SEGMENT = "(?:[#{UNRESERVED};@&=+$,]|#{ESCAPED})+"
+ ret[:REL_SEGMENT] = rel_segment = "(?:[#{unreserved};@&=+$,]|#{escaped})+"
# scheme = alpha *( alpha | digit | "+" | "-" | "." )
- SCHEME = "[#{ALPHA}][-+.#{ALPHA}\\d]*"
+ ret[:SCHEME] = scheme = "[#{PATTERN::ALPHA}][-+.#{PATTERN::ALPHA}\\d]*"
# abs_path = "/" path_segments
- ABS_PATH = "/#{PATH_SEGMENTS}"
+ ret[:ABS_PATH] = abs_path = "/#{path_segments}"
# rel_path = rel_segment [ abs_path ]
- REL_PATH = "#{REL_SEGMENT}(?:#{ABS_PATH})?"
+ ret[:REL_PATH] = rel_path = "#{rel_segment}(?:#{abs_path})?"
# net_path = "//" authority [ abs_path ]
- NET_PATH = "//#{AUTHORITY}(?:#{ABS_PATH})?"
+ ret[:NET_PATH] = net_path = "//#{authority}(?:#{abs_path})?"
# hier_part = ( net_path | abs_path ) [ "?" query ]
- HIER_PART = "(?:#{NET_PATH}|#{ABS_PATH})(?:\\?(?:#{QUERY}))?"
+ ret[:HIER_PART] = hier_part = "(?:#{net_path}|#{abs_path})(?:\\?(?:#{query}))?"
# opaque_part = uric_no_slash *uric
- OPAQUE_PART = "#{URIC_NO_SLASH}#{URIC}*"
+ ret[:OPAQUE_PART] = opaque_part = "#{uric_no_slash}#{uric}*"
# absoluteURI = scheme ":" ( hier_part | opaque_part )
- ABS_URI = "#{SCHEME}:(?:#{HIER_PART}|#{OPAQUE_PART})"
+ ret[:ABS_URI] = abs_uri = "#{scheme}:(?:#{hier_part}|#{opaque_part})"
# relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
- REL_URI = "(?:#{NET_PATH}|#{ABS_PATH}|#{REL_PATH})(?:\\?#{QUERY})?"
+ ret[:REL_URI] = rel_uri = "(?:#{net_path}|#{abs_path}|#{rel_path})(?:\\?#{query})?"
# URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
- URI_REF = "(?:#{ABS_URI}|#{REL_URI})?(?:##{FRAGMENT})?"
+ ret[:URI_REF] = uri_ref = "(?:#{abs_uri}|#{rel_uri})?(?:##{fragment})?"
- # XXX:
- X_ABS_URI = "
- (#{PATTERN::SCHEME}): (?# 1: scheme)
+ ret[:X_ABS_URI] = "
+ (#{scheme}): (?# 1: scheme)
(?:
- (#{PATTERN::OPAQUE_PART}) (?# 2: opaque)
+ (#{opaque_part}) (?# 2: opaque)
|
(?:(?:
//(?:
- (?:(?:(#{PATTERN::USERINFO})@)? (?# 3: userinfo)
- (?:(#{PATTERN::HOST})(?::(\\d*))?))?(?# 4: host, 5: port)
+ (?:(?:(#{userinfo})@)? (?# 3: userinfo)
+ (?:(#{host})(?::(\\d*))?))? (?# 4: host, 5: port)
|
- (#{PATTERN::REG_NAME}) (?# 6: registry)
+ (#{reg_name}) (?# 6: registry)
)
|
- (?!//)) (?# XXX: '//' is the mark for hostport)
- (#{PATTERN::ABS_PATH})? (?# 7: path)
- )(?:\\?(#{PATTERN::QUERY}))? (?# 8: query)
+ (?!//)) (?# XXX: '//' is the mark for hostport)
+ (#{abs_path})? (?# 7: path)
+ )(?:\\?(#{query}))? (?# 8: query)
)
- (?:\\#(#{PATTERN::FRAGMENT}))? (?# 9: fragment)
+ (?:\\#(#{fragment}))? (?# 9: fragment)
"
- X_REL_URI = "
+
+ ret[:X_REL_URI] = "
(?:
(?:
//
(?:
- (?:(#{PATTERN::USERINFO})@)? (?# 1: userinfo)
- (#{PATTERN::HOST})?(?::(\\d*))? (?# 2: host, 3: port)
+ (?:(#{userinfo})@)? (?# 1: userinfo)
+ (#{host})?(?::(\\d*))? (?# 2: host, 3: port)
|
- (#{PATTERN::REG_NAME}) (?# 4: registry)
+ (#{reg_name}) (?# 4: registry)
)
)
|
- (#{PATTERN::REL_SEGMENT}) (?# 5: rel_segment)
+ (#{rel_segment}) (?# 5: rel_segment)
)?
- (#{PATTERN::ABS_PATH})? (?# 6: abs_path)
- (?:\\?(#{PATTERN::QUERY}))? (?# 7: query)
- (?:\\#(#{PATTERN::FRAGMENT}))? (?# 8: fragment)
+ (#{abs_path})? (?# 6: abs_path)
+ (?:\\?(#{query}))? (?# 7: query)
+ (?:\\#(#{fragment}))? (?# 8: fragment)
"
- # :startdoc:
- end # PATTERN
- # :stopdoc:
+ ret
+ end
- # for URI::split
- ABS_URI = Regexp.new('^' + PATTERN::X_ABS_URI + '$', #'
- Regexp::EXTENDED).freeze
- REL_URI = Regexp.new('^' + PATTERN::X_REL_URI + '$', #'
- Regexp::EXTENDED).freeze
+ def initialize_regexp(pattern)
+ ret = {}
- # for URI::extract
- URI_REF = Regexp.new(PATTERN::URI_REF).freeze
- ABS_URI_REF = Regexp.new(PATTERN::X_ABS_URI, Regexp::EXTENDED).freeze
- REL_URI_REF = Regexp.new(PATTERN::X_REL_URI, Regexp::EXTENDED).freeze
+ # for URI::split
+ ret[:ABS_URI] = Regexp.new('^' + pattern[:X_ABS_URI] + '$', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('^' + pattern[:X_REL_URI] + '$', Regexp::EXTENDED)
- # for URI::escape/unescape
- ESCAPED = Regexp.new(PATTERN::ESCAPED).freeze
- UNSAFE = Regexp.new("[^#{PATTERN::UNRESERVED}#{PATTERN::RESERVED}]").freeze
+ # for URI::extract
+ ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
+ ret[:ABS_URI_REF] = Regexp.new(pattern[:X_ABS_URI], Regexp::EXTENDED)
+ ret[:REL_URI_REF] = Regexp.new(pattern[:X_REL_URI], Regexp::EXTENDED)
- # for Generic#initialize
- SCHEME = Regexp.new("^#{PATTERN::SCHEME}$").freeze #"
- USERINFO = Regexp.new("^#{PATTERN::USERINFO}$").freeze #"
- HOST = Regexp.new("^#{PATTERN::HOST}$").freeze #"
- PORT = Regexp.new("^#{PATTERN::PORT}$").freeze #"
- OPAQUE = Regexp.new("^#{PATTERN::OPAQUE_PART}$").freeze #"
- REGISTRY = Regexp.new("^#{PATTERN::REG_NAME}$").freeze #"
- ABS_PATH = Regexp.new("^#{PATTERN::ABS_PATH}$").freeze #"
- REL_PATH = Regexp.new("^#{PATTERN::REL_PATH}$").freeze #"
- QUERY = Regexp.new("^#{PATTERN::QUERY}$").freeze #"
- FRAGMENT = Regexp.new("^#{PATTERN::FRAGMENT}$").freeze #"
- # :startdoc:
- end # REGEXP
+ # for URI::escape/unescape
+ ret[:ESCAPED] = Regexp.new(pattern[:ESCAPED])
+ ret[:UNSAFE] = Regexp.new("[^#{pattern[:UNRESERVED]}#{pattern[:RESERVED]}]")
+ # for Generic#initialize
+ ret[:SCHEME] = Regexp.new("^#{pattern[:SCHEME]}$")
+ ret[:USERINFO] = Regexp.new("^#{pattern[:USERINFO]}$")
+ ret[:HOST] = Regexp.new("^#{pattern[:HOST]}$")
+ ret[:PORT] = Regexp.new("^#{pattern[:PORT]}$")
+ ret[:OPAQUE] = Regexp.new("^#{pattern[:OPAQUE_PART]}$")
+ ret[:REGISTRY] = Regexp.new("^#{pattern[:REG_NAME]}$")
+ ret[:ABS_PATH] = Regexp.new("^#{pattern[:ABS_PATH]}$")
+ ret[:REL_PATH] = Regexp.new("^#{pattern[:REL_PATH]}$")
+ ret[:QUERY] = Regexp.new("^#{pattern[:QUERY]}$")
+ ret[:FRAGMENT] = Regexp.new("^#{pattern[:FRAGMENT]}$")
+
+ ret
+ end
+ end # class Parser
+
+ DEFAULT_PARSER = Parser.new
+ DEFAULT_PARSER.pattern.each_pair do |sym, str|
+ unless REGEXP::PATTERN.const_defined?(sym)
+ REGEXP::PATTERN.const_set(sym, str)
+ end
+ end
+ DEFAULT_PARSER.regexp.each_pair do |sym, str|
+ const_set(sym, str)
+ end
+
module Util # :nodoc:
def make_components_hash(klass, array_hash)
tmp = {}
@@ -246,8 +468,6 @@
end
module Escape
- include REGEXP
-
#
# == Synopsis
#
@@ -280,19 +500,8 @@
# p URI.escape("@?@!", "!?")
# # => "@%3F@%21"
#
- def escape(str, unsafe = UNSAFE)
- unless unsafe.kind_of?(Regexp)
- # perhaps unsafe is String object
- unsafe = Regexp.new("[#{Regexp.quote(unsafe)}]", false, 'N')
- end
- str.gsub(unsafe) do
- us = $&
- tmp = ''
- us.each_byte do |uc|
- tmp << sprintf('%%%02X', uc)
- end
- tmp
- end
+ def escape(*arg)
+ DEFAULT_PARSER.escape(*arg)
end
alias encode escape
#
@@ -316,18 +525,19 @@
# p URI.unescape(enc_uri)
# # => "http://example.com/?a=\t\r"
#
- def unescape(str)
- str.gsub(ESCAPED) do
- $&[1,2].hex.chr
- end
+ def unescape(*arg)
+ DEFAULT_PARSER.unescape(*arg)
end
alias decode unescape
end
+ extend Escape
include REGEXP
- extend Escape
@@schemes = {}
+ def self.scheme_list
+ @@schemes
+ end
#
# Base class for all URI exceptions.
@@ -378,75 +588,7 @@
# # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil]
#
def self.split(uri)
- case uri
- when ''
- # null uri
-
- when ABS_URI
- scheme, opaque, userinfo, host, port,
- registry, path, query, fragment = $~[1..-1]
-
- # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
-
- # absoluteURI = scheme ":" ( hier_part | opaque_part )
- # hier_part = ( net_path | abs_path ) [ "?" query ]
- # opaque_part = uric_no_slash *uric
-
- # abs_path = "/" path_segments
- # net_path = "//" authority [ abs_path ]
-
- # authority = server | reg_name
- # server = [ [ userinfo "@" ] hostport ]
-
- if !scheme
- raise InvalidURIError,
- "bad URI(absolute but no scheme): #{uri}"
- end
- if !opaque && (!path && (!host && !registry))
- raise InvalidURIError,
- "bad URI(absolute but no path): #{uri}"
- end
-
- when REL_URI
- scheme = nil
- opaque = nil
-
- userinfo, host, port, registry,
- rel_segment, abs_path, query, fragment = $~[1..-1]
- if rel_segment && abs_path
- path = rel_segment + abs_path
- elsif rel_segment
- path = rel_segment
- elsif abs_path
- path = abs_path
- end
-
- # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
-
- # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
-
- # net_path = "//" authority [ abs_path ]
- # abs_path = "/" path_segments
- # rel_path = rel_segment [ abs_path ]
-
- # authority = server | reg_name
- # server = [ [ userinfo "@" ] hostport ]
-
- else
- raise InvalidURIError, "bad URI(is not URI?): #{uri}"
- end
-
- path = '' if !path && !opaque # (see RFC2396 Section 5.2)
- ret = [
- scheme,
- userinfo, host, port, # X
- registry, # X
- path, # Y
- opaque, # Y
- query,
- fragment
- ]
- return ret
+ DEFAULT_PARSER.split(uri)
end
#
@@ -481,18 +623,7 @@
# # => "www.ruby-lang.org"
#
def self.parse(uri)
- scheme, userinfo, host, port,
- registry, path, opaque, query, fragment = self.split(uri)
-
- if scheme && @@schemes.include?(scheme.upcase)
- @@schemes[scheme.upcase].new(scheme, userinfo, host, port,
- registry, path, opaque, query,
- fragment)
- else
- Generic.new(scheme, userinfo, host, port,
- registry, path, opaque, query,
- fragment)
- end
+ DEFAULT_PARSER.parse(uri)
end
#
@@ -517,11 +648,7 @@
# # => #<URI::HTTP:0x2022ac02 URL:http://localhost/main.rbx>
#
def self.join(*str)
- u = self.parse(str[0])
- str[1 .. -1].each do |x|
- u = u.merge(x)
- end
- u
+ DEFAULT_PARSER.join(*str)
end
#
@@ -549,14 +676,7 @@
# # => ["http://foo.example.com/bla", "mailto:test at example.com"]
#
def self.extract(str, schemes = nil, &block)
- if block_given?
- str.scan(regexp(schemes)) { yield $& }
- nil
- else
- result = []
- str.scan(regexp(schemes)) { result.push $& }
- result
- end
+ DEFAULT_PARSER.extract(str, schemes, &block)
end
#
@@ -591,11 +711,7 @@
# end
#
def self.regexp(schemes = nil)
- unless schemes
- ABS_URI_REF
- else
- /(?=#{Regexp.union(*schemes)}:)#{PATTERN::X_ABS_URI}/xn
- end
+ DEFAULT_PARSER.make_regexp(schemes)
end
end
Modified: MacRuby/branches/experimental/lib/uri/generic.rb
===================================================================
--- MacRuby/branches/experimental/lib/uri/generic.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/uri/generic.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,7 +3,7 @@
#
# Author:: Akira Yamada <akira at ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id: generic.rb 15456 2008-02-13 07:26:52Z naruse $
+# Revision:: $Id: generic.rb 20258 2008-11-18 16:46:16Z yugui $
#
require 'uri/common'
@@ -16,7 +16,6 @@
#
class Generic
include URI
- include REGEXP
DEFAULT_PORT = nil
@@ -74,7 +73,7 @@
if args.kind_of?(Array)
return self.build(args.collect{|x|
if x
- URI.escape(x)
+ @parser.escape(x)
else
x
end
@@ -83,7 +82,7 @@
tmp = {}
args.each do |key, value|
tmp[key] = if value
- URI.escape(value)
+ @parser.escape(value)
else
value
end
@@ -122,6 +121,7 @@
"expected Array of or Hash of components of #{self.class} (#{self.class.component.join(', ')})"
end
+ tmp << DEFAULT_PARSER
tmp << true
return self.new(*tmp)
end
@@ -146,6 +146,8 @@
# Query data
# +fragment+::
# A part of URI after '#' sign
+ # +parser+::
+ # Parser for internal use [URI::DEFAULT_PARSER by default]
# +arg_check+::
# Check arguments [false by default]
#
@@ -158,6 +160,7 @@
path, opaque,
query,
fragment,
+ parser = DEFAULT_PARSER,
arg_check = false)
@scheme = nil
@user = nil
@@ -169,6 +172,7 @@
@opaque = nil
@registry = nil
@fragment = nil
+ @parser = parser
if arg_check
self.scheme = scheme
@@ -208,6 +212,7 @@
attr_reader :query
attr_reader :opaque
attr_reader :fragment
+ attr_reader :parser
# replace self by other URI object
def replace!(oth)
@@ -226,7 +231,7 @@
end
def check_scheme(v)
- if v && SCHEME !~ v
+ if v && @parser.regexp[:SCHEME] !~ v
raise InvalidComponentError,
"bad component(expected scheme component): #{v}"
end
@@ -265,7 +270,7 @@
return v unless v
- if USERINFO !~ v
+ if @parser.regexp[:USERINFO] !~ v
raise InvalidComponentError,
"bad component(expected userinfo component or user component): #{v}"
end
@@ -286,7 +291,7 @@
"password component depends user component"
end
- if USERINFO !~ v
+ if @parser.regexp[:USERINFO] !~ v
raise InvalidComponentError,
"bad component(expected user component): #{v}"
end
@@ -351,7 +356,7 @@
private :split_userinfo
def escape_userpass(v)
- v = URI.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
+ v = @parser.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
end
private :escape_userpass
@@ -379,7 +384,7 @@
if @registry || @opaque
raise InvalidURIError,
"can not set host with registry or opaque"
- elsif HOST !~ v
+ elsif @parser.regexp[:HOST] !~ v
raise InvalidComponentError,
"bad component(expected host component): #{v}"
end
@@ -405,7 +410,7 @@
if @registry || @opaque
raise InvalidURIError,
"can not set port with registry or opaque"
- elsif !v.kind_of?(Fixnum) && PORT !~ v
+ elsif !v.kind_of?(Fixnum) && @parser.regexp[:PORT] !~ v
raise InvalidComponentError,
"bad component(expected port component): #{v}"
end
@@ -441,7 +446,7 @@
if @host || @port || @user # userinfo = @user + ':' + @password
raise InvalidURIError,
"can not set registry with host, port, or userinfo"
- elsif v && REGISTRY !~ v
+ elsif v && @parser.regexp[:REGISTRY] !~ v
raise InvalidComponentError,
"bad component(expected registry component): #{v}"
end
@@ -471,12 +476,12 @@
end
if @scheme
- if v && v != '' && ABS_PATH !~ v
+ if v && v != '' && @parser.regexp[:ABS_PATH] !~ v
raise InvalidComponentError,
"bad component(expected absolute path component): #{v}"
end
else
- if v && v != '' && ABS_PATH !~ v && REL_PATH !~ v
+ if v && v != '' && @parser.regexp[:ABS_PATH] !~ v && @parser.regexp[:REL_PATH] !~ v
raise InvalidComponentError,
"bad component(expected relative path component): #{v}"
end
@@ -508,10 +513,10 @@
"query conflicts with opaque"
end
- if v && v != '' && QUERY !~ v
+ if v && v != '' && @parser.regexp[:QUERY] !~ v
raise InvalidComponentError,
"bad component(expected query component): #{v}"
- end
+ end
return true
end
@@ -537,7 +542,7 @@
if @host || @port || @user || @path # userinfo = @user + ':' + @password
raise InvalidURIError,
"can not set opaque with host, port, userinfo or path"
- elsif v && OPAQUE !~ v
+ elsif v && @parser.regexp[:OPAQUE] !~ v
raise InvalidComponentError,
"bad component(expected opaque component): #{v}"
end
@@ -560,7 +565,7 @@
def check_fragment(v)
return v unless v
- if v && v != '' && FRAGMENT !~ v
+ if v && v != '' && @parser.regexp[:FRAGMENT] !~ v
raise InvalidComponentError,
"bad component(expected fragment component): #{v}"
end
@@ -772,7 +777,7 @@
case oth
when Generic
when String
- oth = URI.parse(oth)
+ oth = @parser.parse(oth)
else
raise ArgumentError,
"bad argument(expected URI object or URI string)"
@@ -843,7 +848,7 @@
case oth
when Generic
when String
- oth = URI.parse(oth)
+ oth = @parser.parse(oth)
else
raise ArgumentError,
"bad argument(expected URI object or URI string)"
@@ -864,7 +869,7 @@
rel = URI::Generic.new(nil, # it is relative URI
self.userinfo, self.host, self.port,
self.registry, self.path, self.opaque,
- self.query, self.fragment)
+ self.query, self.fragment, @parser)
if rel.userinfo != oth.userinfo ||
rel.host.to_s.downcase != oth.host.to_s.downcase ||
@@ -955,7 +960,7 @@
case oth
when Generic
when String
- oth = URI.parse(oth)
+ oth = @parser.parse(oth)
else
raise ArgumentError,
"bad argument(expected URI object or URI string)"
@@ -1054,6 +1059,7 @@
end
def eql?(oth)
+ @parser == oth.parser &&
self.component_ary.eql?(oth.component_ary)
end
@@ -1111,7 +1117,7 @@
def coerce(oth)
case oth
when String
- oth = URI.parse(oth)
+ oth = @parser.parse(oth)
else
super
end
Modified: MacRuby/branches/experimental/lib/uri/mailto.rb
===================================================================
--- MacRuby/branches/experimental/lib/uri/mailto.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/uri/mailto.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,7 +3,7 @@
#
# Author:: Akira Yamada <akira at ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id: mailto.rb 14565 2007-12-24 01:51:49Z drbrain $
+# Revision:: $Id: mailto.rb 19495 2008-09-23 18:16:08Z drbrain $
#
require 'uri/generic'
@@ -135,7 +135,7 @@
@headers = []
if MAILTO_REGEXP =~ @opaque
- if arg[-1]
+ if arg[-1]
self.to = $1
self.headers = $2
else
@@ -159,7 +159,7 @@
return true unless v
return true if v.size == 0
- if OPAQUE !~ v || /\A#{MAILBOX_PATTERN}*\z/o !~ v
+ if @parser.regexp[:OPAQUE] !~ v || /\A#{MAILBOX_PATTERN}*\z/o !~ v
raise InvalidComponentError,
"bad component(expected opaque component): #{v}"
end
@@ -183,7 +183,7 @@
return true unless v
return true if v.size == 0
- if OPAQUE !~ v ||
+ if @parser.regexp[:OPAQUE] !~ v ||
/\A(#{HEADER_PATTERN}(?:\&#{HEADER_PATTERN})*)\z/o !~ v
raise InvalidComponentError,
"bad component(expected opaque component): #{v}"
@@ -239,18 +239,18 @@
# # => "To: ruby-list at ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n"
#
def to_mailtext
- to = URI::unescape(@to)
+ to = @parser.unescape(@to)
head = ''
body = ''
@headers.each do |x|
case x[0]
when 'body'
- body = URI::unescape(x[1])
+ body = @parser.unescape(x[1])
when 'to'
- to << ', ' + URI::unescape(x[1])
+ to << ', ' + @parser.unescape(x[1])
else
- head << URI::unescape(x[0]).capitalize + ': ' +
- URI::unescape(x[1]) + "\n"
+ head << @parser.unescape(x[0]).capitalize + ': ' +
+ @parser.unescape(x[1]) + "\n"
end
end
Modified: MacRuby/branches/experimental/lib/webrick/cgi.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/cgi.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/cgi.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -5,7 +5,7 @@
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
-# $Id: cgi.rb 14260 2007-12-17 07:03:57Z gotoyuzo $
+# $Id: cgi.rb 18678 2008-08-17 17:33:13Z gotoyuzo $
require "webrick/httprequest"
require "webrick/httpresponse"
@@ -207,6 +207,10 @@
def each
input.each{|line| yield(line) }
end
+
+ def eof?
+ input.eof?
+ end
def <<(data)
@out_port << data
Modified: MacRuby/branches/experimental/lib/webrick/httpauth/digestauth.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httpauth/digestauth.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httpauth/digestauth.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -52,7 +52,7 @@
when 'SHA1','SHA1-sess' # it is a bonus feature :-)
@h = Digest::SHA1
else
- msg = format('Alogrithm "%s" is not supported.', @algorithm)
+ msg = format('Algorithm "%s" is not supported.', @algorithm)
raise ArgumentError.new(msg)
end
@@ -229,7 +229,7 @@
def split_param_value(string)
ret = {}
- while string.size != 0
+ while string.bytesize != 0
case string
when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/
key = $1
Modified: MacRuby/branches/experimental/lib/webrick/httpproxy.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httpproxy.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httpproxy.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -146,11 +146,11 @@
while fds = IO::select([ua, os])
if fds[0].member?(ua)
buf = ua.sysread(1024);
- @logger.debug("CONNECT: #{buf.size} byte from User-Agent")
+ @logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
os.syswrite(buf)
elsif fds[0].member?(os)
buf = os.sysread(1024);
- @logger.debug("CONNECT: #{buf.size} byte from #{host}:#{port}")
+ @logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
ua.syswrite(buf)
end
end
@@ -186,7 +186,7 @@
private
- # Some header fields shuold not be transfered.
+ # Some header fields should not be transferred.
HopByHop = %w( connection keep-alive proxy-authenticate upgrade
proxy-authorization te trailers transfer-encoding )
ShouldNotTransfer = %w( set-cookie proxy-connection )
@@ -272,7 +272,7 @@
response = yield(http, path, header)
end
- # Persistent connction requirements are mysterious for me.
+ # Persistent connection requirements are mysterious for me.
# So I will close the connection in every response.
res['proxy-connection'] = "close"
res['connection'] = "close"
Modified: MacRuby/branches/experimental/lib/webrick/httprequest.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httprequest.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httprequest.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -244,12 +244,12 @@
def read_request_line(socket)
@request_line = read_line(socket, 1024) if socket
- if @request_line.size >= 1024 and @request_line[-1, 1] != LF
+ if @request_line.bytesize >= 1024 and @request_line[-1, 1] != LF
raise HTTPStatus::RequestURITooLarge
end
@request_time = Time.now
raise HTTPStatus::EOFError unless @request_line
- if /^(\S+)\s+(\S+)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
+ if /^(\S+)\s+(\S++)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
@request_method = $1
@unparsed_uri = $2
@http_version = HTTPVersion.new($3 ? $3 : "0.9")
@@ -307,7 +307,7 @@
while @remaining_size > 0
sz = [@buffer_size, @remaining_size].min
break unless buf = read_data(socket, sz)
- @remaining_size -= buf.size
+ @remaining_size -= buf.bytesize
block.call(buf)
end
if @remaining_size > 0 && @socket.eof?
@@ -334,7 +334,7 @@
chunk_size, = read_chunk_size(socket)
while chunk_size > 0
data = read_data(socket, chunk_size) # read chunk-data
- if data.nil? || data.size != chunk_size
+ if data.nil? || data.bytesize != chunk_size
raise BadRequest, "bad chunk data size."
end
read_line(socket) # skip CRLF
Modified: MacRuby/branches/experimental/lib/webrick/httpresponse.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httpresponse.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httpresponse.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -131,7 +131,7 @@
end
end
- # Determin the message length (RFC2616 -- 4.4 Message Length)
+ # Determine the message length (RFC2616 -- 4.4 Message Length)
if @status == 304 || @status == 204 || HTTPStatus::info?(@status)
@header.delete('content-length')
@body = ""
@@ -142,7 +142,7 @@
@header.delete('content-length')
elsif @header['content-length'].nil?
unless @body.is_a?(IO)
- @header['content-length'] = @body ? @body.size : 0
+ @header['content-length'] = @body ? @body.bytesize : 0
end
end
@@ -260,10 +260,10 @@
while buf = @body.read(@buffer_size)
next if buf.empty?
data = ""
- data << format("%x", buf.size) << CRLF
+ data << format("%x", buf.bytesize) << CRLF
data << buf << CRLF
_write_data(socket, data)
- @sent_size += buf.size
+ @sent_size += buf.bytesize
end
_write_data(socket, "0#{CRLF}#{CRLF}")
else
@@ -280,20 +280,20 @@
if @request_method == "HEAD"
# do nothing
elsif chunked?
- remain = body ? @body.size : 0
+ remain = body ? @body.bytesize : 0
while buf = @body[@sent_size, @buffer_size]
break if buf.empty?
data = ""
- data << format("%x", buf.size) << CRLF
+ data << format("%x", buf.bytesize) << CRLF
data << buf << CRLF
_write_data(socket, data)
- @sent_size += buf.size
+ @sent_size += buf.bytesize
end
_write_data(socket, "0#{CRLF}#{CRLF}")
else
- if @body && @body.size > 0
+ if @body && @body.bytesize > 0
_write_data(socket, @body)
- @sent_size = @body.size
+ @sent_size = @body.bytesize
end
end
end
@@ -302,7 +302,7 @@
while offset > 0
sz = @buffer_size < size ? @buffer_size : size
buf = input.read(sz)
- offset -= buf.size
+ offset -= buf.bytesize
end
if size == 0
@@ -314,7 +314,7 @@
sz = @buffer_size < size ? @buffer_size : size
buf = input.read(sz)
_write_data(output, buf)
- size -= buf.size
+ size -= buf.bytesize
end
end
end
Modified: MacRuby/branches/experimental/lib/webrick/httpservlet/abstract.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httpservlet/abstract.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httpservlet/abstract.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -59,7 +59,7 @@
def redirect_to_directory_uri(req, res)
if req.path[-1] != ?/
location = WEBrick::HTTPUtils.escape_path(req.path + "/")
- if req.query_string && req.query_string.size > 0
+ if req.query_string && req.query_string.bytesize > 0
location << "?" << req.query_string
end
res.set_redirect(HTTPStatus::MovedPermanently, location)
Modified: MacRuby/branches/experimental/lib/webrick/httpservlet/cgi_runner.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httpservlet/cgi_runner.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httpservlet/cgi_runner.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -13,7 +13,7 @@
while size > 0
tmp = io.sysread(size)
buf << tmp
- size -= tmp.size
+ size -= tmp.bytesize
end
return buf
end
Modified: MacRuby/branches/experimental/lib/webrick/httpservlet/cgihandler.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httpservlet/cgihandler.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httpservlet/cgihandler.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -48,14 +48,14 @@
end
dump = Marshal.dump(meta)
- cgi_in.write("%8d" % cgi_out.path.size)
+ cgi_in.write("%8d" % cgi_out.path.bytesize)
cgi_in.write(cgi_out.path)
- cgi_in.write("%8d" % cgi_err.path.size)
+ cgi_in.write("%8d" % cgi_err.path.bytesize)
cgi_in.write(cgi_err.path)
- cgi_in.write("%8d" % dump.size)
+ cgi_in.write("%8d" % dump.bytesize)
cgi_in.write(dump)
- if req.body and req.body.size > 0
+ if req.body and req.body.bytesize > 0
cgi_in.write(req.body)
end
ensure
@@ -65,7 +65,7 @@
data = cgi_out.read
cgi_out.close(true)
if errmsg = cgi_err.read
- if errmsg.size > 0
+ if errmsg.bytesize > 0
@logger.error("CGIHandler: #{@script_filename}:\n" + errmsg)
end
end
Modified: MacRuby/branches/experimental/lib/webrick/httpservlet/filehandler.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httpservlet/filehandler.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httpservlet/filehandler.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -407,13 +407,13 @@
list.each{ |name, time, size|
if name == ".."
dname = "Parent Directory"
- elsif name.size > 25
+ elsif name.bytesize > 25
dname = name.sub(/^(.{23})(?:.*)/, '\1..')
else
dname = name
end
s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
- s << " " * (30 - dname.size)
+ s << " " * (30 - dname.bytesize)
s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
s << (size >= 0 ? size.to_s : "-") << "\n"
res.body << s
Modified: MacRuby/branches/experimental/lib/webrick/httputils.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/httputils.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/httputils.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -23,16 +23,8 @@
ret = path.dup
ret.gsub!(%r{/+}o, '/') # // => /
- while ret.sub!(%r{/\.(/|\Z)}o, '/'); end # /. => /
- begin # /foo/.. => /foo
- match = ret.sub!(%r{/([^/]+)/\.\.(/|\Z)}o){
- if $1 == ".."
- raise "abnormal path `#{path}'"
- else
- "/"
- end
- }
- end while match
+ while ret.sub!(%r'/\.(?:/|\Z)', '/'); end # /. => /
+ while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo
raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
ret
@@ -155,8 +147,8 @@
module_function :parse_header
def split_header_value(str)
- str.scan(/((?:"(?:\\.|[^"])+?"|[^",]+)+)
- (?:,\s*|\Z)/xn).collect{|v| v[0] }
+ str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]+)+)
+ (?:,\s*|\Z)'xn).flatten
end
module_function :split_header_value
Modified: MacRuby/branches/experimental/lib/webrick/server.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/server.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/server.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -130,9 +130,17 @@
addr = s.addr
@logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
end
- s.shutdown
- unless @config[:ShutdownSocketWithoutClose]
+ begin
+ s.shutdown
+ rescue Errno::ENOTCONN
+ # when `Errno::ENOTCONN: Socket is not connected' on some platforms,
+ # call #close instead of #shutdown.
+ # (ignore @config[:ShutdownSocketWithoutClose])
s.close
+ else
+ unless @config[:ShutdownSocketWithoutClose]
+ s.close
+ end
end
}
@listeners.clear
Modified: MacRuby/branches/experimental/lib/webrick/utils.rb
===================================================================
--- MacRuby/branches/experimental/lib/webrick/utils.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/webrick/utils.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -89,7 +89,7 @@
"abcdefghijklmnopqrstuvwxyz"
def random_string(len)
- rand_max = RAND_CHARS.size
+ rand_max = RAND_CHARS.bytesize
ret = ""
len.times{ ret << RAND_CHARS[rand(rand_max)] }
ret
Modified: MacRuby/branches/experimental/lib/xmlrpc/client.rb
===================================================================
--- MacRuby/branches/experimental/lib/xmlrpc/client.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/xmlrpc/client.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -268,7 +268,7 @@
= History
- $Id: client.rb 16042 2008-04-15 14:10:18Z kou $
+ $Id: client.rb 19657 2008-10-01 13:46:53Z mame $
=end
@@ -567,6 +567,7 @@
set_cookies = resp.get_fields("Set-Cookie")
if set_cookies and !set_cookies.empty?
+ require 'webrick/cookie'
@cookie = set_cookies.collect do |set_cookie|
cookie = WEBrick::Cookie.parse_set_cookie(set_cookie)
WEBrick::Cookie.new(cookie.name, cookie.value).to_s
@@ -604,16 +605,16 @@
class Proxy
def initialize(server, prefix, args=[], meth=:call, delim=".")
- @server = server
- @prefix = prefix ? prefix + delim : ""
- @args = args
+ @server = server
+ @prefix = prefix ? prefix + delim : ""
+ @args = args
@meth = meth
end
def method_missing(mid, *args)
- pre = @prefix + mid.to_s
- arg = @args + args
- @server.send(@meth, pre, *arg)
+ pre = @prefix + mid.to_s
+ arg = @args + args
+ @server.send(@meth, pre, *arg)
end
end # class Proxy
Modified: MacRuby/branches/experimental/lib/xmlrpc/create.rb
===================================================================
--- MacRuby/branches/experimental/lib/xmlrpc/create.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/xmlrpc/create.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,7 +3,7 @@
#
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann at ntecs.de)
#
-# $Id: create.rb 11816 2007-02-23 03:42:01Z knu $
+# $Id: create.rb 19657 2008-10-01 13:46:53Z mame $
#
require "date"
@@ -15,11 +15,11 @@
class Abstract
def ele(name, *children)
- element(name, nil, *children)
+ element(name, nil, *children)
end
def tag(name, txt)
- element(name, nil, text(txt))
+ element(name, nil, text(txt))
end
end
@@ -27,19 +27,19 @@
class Simple < Abstract
def document_to_str(doc)
- doc
+ doc
end
def document(*params)
- params.join("")
+ params.join("")
end
def pi(name, *params)
- "<?#{name} " + params.join(" ") + " ?>"
+ "<?#{name} " + params.join(" ") + " ?>"
end
def element(name, attrs, *children)
- raise "attributes not yet implemented" unless attrs.nil?
+ raise "attributes not yet implemented" unless attrs.nil?
if children.empty?
"<#{name}/>"
else
@@ -61,27 +61,27 @@
class XMLParser < Abstract
def initialize
- require "xmltreebuilder"
+ require "xmltreebuilder"
end
def document_to_str(doc)
- doc.to_s
+ doc.to_s
end
def document(*params)
- XML::SimpleTree::Document.new(*params)
+ XML::SimpleTree::Document.new(*params)
end
def pi(name, *params)
- XML::SimpleTree::ProcessingInstruction.new(name, *params)
+ XML::SimpleTree::ProcessingInstruction.new(name, *params)
end
def element(name, attrs, *children)
- XML::SimpleTree::Element.new(name, attrs, *children)
+ XML::SimpleTree::Element.new(name, attrs, *children)
end
def text(txt)
- XML::SimpleTree::Text.new(txt)
+ XML::SimpleTree::Text.new(txt)
end
end # class XMLParser
@@ -111,20 +111,20 @@
name = name.to_s
if name !~ /[a-zA-Z0-9_.:\/]+/
- raise ArgumentError, "Wrong XML-RPC method-name"
+ raise ArgumentError, "Wrong XML-RPC method-name"
end
parameter = params.collect do |param|
- @writer.ele("param", conv2value(param))
+ @writer.ele("param", conv2value(param))
end
tree = @writer.document(
- @writer.pi("xml", 'version="1.0"'),
- @writer.ele("methodCall",
- @writer.tag("methodName", name),
- @writer.ele("params", *parameter)
- )
- )
+ @writer.pi("xml", 'version="1.0"'),
+ @writer.ele("methodCall",
+ @writer.tag("methodName", name),
+ @writer.ele("params", *parameter)
+ )
+ )
@writer.document_to_str(tree) + "\n"
end
@@ -144,23 +144,23 @@
def methodResponse(is_ret, *params)
if is_ret
- resp = params.collect do |param|
- @writer.ele("param", conv2value(param))
- end
+ resp = params.collect do |param|
+ @writer.ele("param", conv2value(param))
+ end
- resp = [@writer.ele("params", *resp)]
+ resp = [@writer.ele("params", *resp)]
else
- if params.size != 1 or params[0] === XMLRPC::FaultException
- raise ArgumentError, "no valid fault-structure given"
- end
- resp = @writer.ele("fault", conv2value(params[0].to_h))
+ if params.size != 1 or params[0] === XMLRPC::FaultException
+ raise ArgumentError, "no valid fault-structure given"
+ end
+ resp = @writer.ele("fault", conv2value(params[0].to_h))
end
-
+
tree = @writer.document(
- @writer.pi("xml", 'version="1.0"'),
- @writer.ele("methodResponse", resp)
- )
+ @writer.pi("xml", 'version="1.0"'),
+ @writer.ele("methodResponse", resp)
+ )
@writer.document_to_str(tree) + "\n"
end
@@ -177,11 +177,11 @@
#
def conv2value(param)
- val = case param
- when Fixnum
- @writer.tag("i4", param.to_s)
+ val = case param
+ when Fixnum
+ @writer.tag("i4", param.to_s)
- when Bignum
+ when Bignum
if Config::ENABLE_BIGINT
@writer.tag("i4", param.to_s)
else
@@ -191,14 +191,14 @@
raise "Bignum is too big! Must be signed 32-bit integer!"
end
end
- when TrueClass, FalseClass
- @writer.tag("boolean", param ? "1" : "0")
+ when TrueClass, FalseClass
+ @writer.tag("boolean", param ? "1" : "0")
- when Symbol
- @writer.tag("string", param.to_s)
+ when Symbol
+ @writer.tag("string", param.to_s)
- when String
- @writer.tag("string", param)
+ when String
+ @writer.tag("string", param)
when NilClass
if Config::ENABLE_NIL_CREATE
@@ -207,51 +207,51 @@
raise "Wrong type NilClass. Not allowed!"
end
- when Float
- @writer.tag("double", param.to_s)
+ when Float
+ @writer.tag("double", param.to_s)
- when Struct
- h = param.members.collect do |key|
- value = param[key]
- @writer.ele("member",
- @writer.tag("name", key.to_s),
- conv2value(value)
- )
- end
+ when Struct
+ h = param.members.collect do |key|
+ value = param[key]
+ @writer.ele("member",
+ @writer.tag("name", key.to_s),
+ conv2value(value)
+ )
+ end
- @writer.ele("struct", *h)
+ @writer.ele("struct", *h)
- when Hash
- # TODO: can a Hash be empty?
-
- h = param.collect do |key, value|
- @writer.ele("member",
- @writer.tag("name", key.to_s),
- conv2value(value)
- )
- end
+ when Hash
+ # TODO: can a Hash be empty?
+
+ h = param.collect do |key, value|
+ @writer.ele("member",
+ @writer.tag("name", key.to_s),
+ conv2value(value)
+ )
+ end
- @writer.ele("struct", *h)
+ @writer.ele("struct", *h)
- when Array
- # TODO: can an Array be empty?
- a = param.collect {|v| conv2value(v) }
-
- @writer.ele("array",
- @writer.ele("data", *a)
- )
+ when Array
+ # TODO: can an Array be empty?
+ a = param.collect {|v| conv2value(v) }
+
+ @writer.ele("array",
+ @writer.ele("data", *a)
+ )
- when Time, Date, ::DateTime
- @writer.tag("dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S"))
+ when Time, Date, ::DateTime
+ @writer.tag("dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S"))
- when XMLRPC::DateTime
- @writer.tag("dateTime.iso8601",
- format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a))
+ when XMLRPC::DateTime
+ @writer.tag("dateTime.iso8601",
+ format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a))
- when XMLRPC::Base64
- @writer.tag("base64", param.encoded)
+ when XMLRPC::Base64
+ @writer.tag("base64", param.encoded)
- else
+ else
if Config::ENABLE_MARSHALLING and param.class.included_modules.include? XMLRPC::Marshallable
# convert Ruby object into Hash
ret = {"___class___" => param.class.name}
@@ -274,9 +274,9 @@
raise "Wrong type!"
end
end
- end
-
- @writer.ele("value", val)
+ end
+
+ @writer.ele("value", val)
end
def wrong_type(value)
Modified: MacRuby/branches/experimental/lib/xmlrpc/httpserver.rb
===================================================================
--- MacRuby/branches/experimental/lib/xmlrpc/httpserver.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/xmlrpc/httpserver.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -4,7 +4,7 @@
#
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann at ntecs.de)
#
-# $Id: httpserver.rb 11708 2007-02-12 23:01:19Z shyouhei $
+# $Id: httpserver.rb 19657 2008-10-01 13:46:53Z mame $
#
@@ -156,7 +156,7 @@
# parse HTTP headers
while (line=io.gets) !~ /^(\n|\r)/
if line =~ /^([\w-]+):\s*(.*)$/
- request.header[$1] = $2.strip
+ request.header[$1] = $2.strip
end
end
Modified: MacRuby/branches/experimental/lib/xmlrpc/parser.rb
===================================================================
--- MacRuby/branches/experimental/lib/xmlrpc/parser.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/xmlrpc/parser.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -3,7 +3,7 @@
#
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann at ntecs.de)
#
-# $Id: parser.rb 13770 2007-10-24 21:24:09Z jeg2 $
+# $Id: parser.rb 19657 2008-10-01 13:46:53Z mame $
#
@@ -160,11 +160,11 @@
class AbstractTreeParser
def parseMethodResponse(str)
- methodResponse_document(createCleanedTree(str))
+ methodResponse_document(createCleanedTree(str))
end
def parseMethodCall(str)
- methodCall_document(createCleanedTree(str))
+ methodCall_document(createCleanedTree(str))
end
private
@@ -174,11 +174,11 @@
# and all comments
#
def removeWhitespacesAndComments(node)
- remove = []
- childs = node.childNodes.to_a
- childs.each do |nd|
- case _nodeType(nd)
- when :TEXT
+ remove = []
+ childs = node.childNodes.to_a
+ childs.each do |nd|
+ case _nodeType(nd)
+ when :TEXT
# TODO: add nil?
unless %w(i4 int boolean string double dateTime.iso8601 base64).include? node.nodeName
@@ -189,190 +189,190 @@
else
remove << nd if nd.nodeValue.strip == ""
end
- end
- when :COMMENT
- remove << nd
- else
- removeWhitespacesAndComments(nd)
- end
- end
+ end
+ when :COMMENT
+ remove << nd
+ else
+ removeWhitespacesAndComments(nd)
+ end
+ end
- remove.each { |i| node.removeChild(i) }
+ remove.each { |i| node.removeChild(i) }
end
def nodeMustBe(node, name)
- cmp = case name
- when Array
- name.include?(node.nodeName)
- when String
- name == node.nodeName
- else
- raise "error"
- end
+ cmp = case name
+ when Array
+ name.include?(node.nodeName)
+ when String
+ name == node.nodeName
+ else
+ raise "error"
+ end
- if not cmp then
- raise "wrong xml-rpc (name)"
- end
+ if not cmp then
+ raise "wrong xml-rpc (name)"
+ end
- node
+ node
end
#
# returns, when successfully the only child-node
#
def hasOnlyOneChild(node, name=nil)
- if node.childNodes.to_a.size != 1
- raise "wrong xml-rpc (size)"
- end
- if name != nil then
- nodeMustBe(node.firstChild, name)
- end
+ if node.childNodes.to_a.size != 1
+ raise "wrong xml-rpc (size)"
+ end
+ if name != nil then
+ nodeMustBe(node.firstChild, name)
+ end
end
def assert(b)
- if not b then
- raise "assert-fail"
- end
+ if not b then
+ raise "assert-fail"
+ end
end
# the node `node` has empty string or string
def text_zero_one(node)
- nodes = node.childNodes.to_a.size
+ nodes = node.childNodes.to_a.size
- if nodes == 1
- text(node.firstChild)
- elsif nodes == 0
- ""
- else
- raise "wrong xml-rpc (size)"
- end
+ if nodes == 1
+ text(node.firstChild)
+ elsif nodes == 0
+ ""
+ else
+ raise "wrong xml-rpc (size)"
+ end
end
def integer(node)
- #TODO: check string for float because to_i returnsa
- # 0 when wrong string
- nodeMustBe(node, %w(i4 int))
- hasOnlyOneChild(node)
-
- Convert.int(text(node.firstChild))
+ #TODO: check string for float because to_i returnsa
+ # 0 when wrong string
+ nodeMustBe(node, %w(i4 int))
+ hasOnlyOneChild(node)
+
+ Convert.int(text(node.firstChild))
end
def boolean(node)
- nodeMustBe(node, "boolean")
- hasOnlyOneChild(node)
-
+ nodeMustBe(node, "boolean")
+ hasOnlyOneChild(node)
+
Convert.boolean(text(node.firstChild))
end
def v_nil(node)
nodeMustBe(node, "nil")
- assert( node.childNodes.to_a.size == 0 )
+ assert( node.childNodes.to_a.size == 0 )
nil
end
def string(node)
- nodeMustBe(node, "string")
- text_zero_one(node)
+ nodeMustBe(node, "string")
+ text_zero_one(node)
end
def double(node)
- #TODO: check string for float because to_f returnsa
- # 0.0 when wrong string
- nodeMustBe(node, "double")
- hasOnlyOneChild(node)
-
- Convert.double(text(node.firstChild))
+ #TODO: check string for float because to_f returnsa
+ # 0.0 when wrong string
+ nodeMustBe(node, "double")
+ hasOnlyOneChild(node)
+
+ Convert.double(text(node.firstChild))
end
def dateTime(node)
- nodeMustBe(node, "dateTime.iso8601")
- hasOnlyOneChild(node)
-
+ nodeMustBe(node, "dateTime.iso8601")
+ hasOnlyOneChild(node)
+
Convert.dateTime( text(node.firstChild) )
end
def base64(node)
- nodeMustBe(node, "base64")
- #hasOnlyOneChild(node)
-
+ nodeMustBe(node, "base64")
+ #hasOnlyOneChild(node)
+
Convert.base64(text_zero_one(node))
end
def member(node)
- nodeMustBe(node, "member")
- assert( node.childNodes.to_a.size == 2 )
+ nodeMustBe(node, "member")
+ assert( node.childNodes.to_a.size == 2 )
- [ name(node[0]), value(node[1]) ]
+ [ name(node[0]), value(node[1]) ]
end
def name(node)
- nodeMustBe(node, "name")
- #hasOnlyOneChild(node)
- text_zero_one(node)
+ nodeMustBe(node, "name")
+ #hasOnlyOneChild(node)
+ text_zero_one(node)
end
def array(node)
- nodeMustBe(node, "array")
- hasOnlyOneChild(node, "data")
- data(node.firstChild)
+ nodeMustBe(node, "array")
+ hasOnlyOneChild(node, "data")
+ data(node.firstChild)
end
def data(node)
- nodeMustBe(node, "data")
+ nodeMustBe(node, "data")
- node.childNodes.to_a.collect do |val|
- value(val)
- end
+ node.childNodes.to_a.collect do |val|
+ value(val)
+ end
end
def param(node)
- nodeMustBe(node, "param")
- hasOnlyOneChild(node, "value")
- value(node.firstChild)
+ nodeMustBe(node, "param")
+ hasOnlyOneChild(node, "value")
+ value(node.firstChild)
end
def methodResponse(node)
- nodeMustBe(node, "methodResponse")
- hasOnlyOneChild(node, %w(params fault))
- child = node.firstChild
+ nodeMustBe(node, "methodResponse")
+ hasOnlyOneChild(node, %w(params fault))
+ child = node.firstChild
- case child.nodeName
- when "params"
- [ true, params(child,false) ]
- when "fault"
- [ false, fault(child) ]
- else
- raise "unexpected error"
- end
+ case child.nodeName
+ when "params"
+ [ true, params(child,false) ]
+ when "fault"
+ [ false, fault(child) ]
+ else
+ raise "unexpected error"
+ end
end
def methodName(node)
- nodeMustBe(node, "methodName")
- hasOnlyOneChild(node)
- text(node.firstChild)
+ nodeMustBe(node, "methodName")
+ hasOnlyOneChild(node)
+ text(node.firstChild)
end
def params(node, call=true)
- nodeMustBe(node, "params")
+ nodeMustBe(node, "params")
- if call
- node.childNodes.to_a.collect do |n|
- param(n)
- end
- else # response (only one param)
- hasOnlyOneChild(node)
- param(node.firstChild)
- end
+ if call
+ node.childNodes.to_a.collect do |n|
+ param(n)
+ end
+ else # response (only one param)
+ hasOnlyOneChild(node)
+ param(node.firstChild)
+ end
end
def fault(node)
- nodeMustBe(node, "fault")
- hasOnlyOneChild(node, "value")
- f = value(node.firstChild)
+ nodeMustBe(node, "fault")
+ hasOnlyOneChild(node, "value")
+ f = value(node.firstChild)
Convert.fault(f)
end
@@ -380,76 +380,76 @@
# _nodeType is defined in the subclass
def text(node)
- assert( _nodeType(node) == :TEXT )
- assert( node.hasChildNodes == false )
- assert( node.nodeValue != nil )
+ assert( _nodeType(node) == :TEXT )
+ assert( node.hasChildNodes == false )
+ assert( node.nodeValue != nil )
- node.nodeValue.to_s
+ node.nodeValue.to_s
end
def struct(node)
- nodeMustBe(node, "struct")
+ nodeMustBe(node, "struct")
- hash = {}
- node.childNodes.to_a.each do |me|
- n, v = member(me)
- hash[n] = v
- end
+ hash = {}
+ node.childNodes.to_a.each do |me|
+ n, v = member(me)
+ hash[n] = v
+ end
Convert.struct(hash)
- end
+ end
def value(node)
- nodeMustBe(node, "value")
- nodes = node.childNodes.to_a.size
+ nodeMustBe(node, "value")
+ nodes = node.childNodes.to_a.size
if nodes == 0
return ""
elsif nodes > 1
- raise "wrong xml-rpc (size)"
+ raise "wrong xml-rpc (size)"
end
- child = node.firstChild
+ child = node.firstChild
- case _nodeType(child)
- when :TEXT
+ case _nodeType(child)
+ when :TEXT
text_zero_one(node)
- when :ELEMENT
- case child.nodeName
- when "i4", "int" then integer(child)
- when "boolean" then boolean(child)
- when "string" then string(child)
- when "double" then double(child)
- when "dateTime.iso8601" then dateTime(child)
- when "base64" then base64(child)
- when "struct" then struct(child)
- when "array" then array(child)
+ when :ELEMENT
+ case child.nodeName
+ when "i4", "int" then integer(child)
+ when "boolean" then boolean(child)
+ when "string" then string(child)
+ when "double" then double(child)
+ when "dateTime.iso8601" then dateTime(child)
+ when "base64" then base64(child)
+ when "struct" then struct(child)
+ when "array" then array(child)
when "nil"
if Config::ENABLE_NIL_PARSER
v_nil(child)
else
raise "wrong/unknown XML-RPC type 'nil'"
end
- else
- raise "wrong/unknown XML-RPC type"
- end
- else
- raise "wrong type of node"
- end
+ else
+ raise "wrong/unknown XML-RPC type"
+ end
+ else
+ raise "wrong type of node"
+ end
end
def methodCall(node)
- nodeMustBe(node, "methodCall")
- assert( (1..2).include?( node.childNodes.to_a.size ) )
- name = methodName(node[0])
+ nodeMustBe(node, "methodCall")
+ assert( (1..2).include?( node.childNodes.to_a.size ) )
+ name = methodName(node[0])
- if node.childNodes.to_a.size == 2 then
- pa = params(node[1])
- else # no parameters given
- pa = []
- end
- [name, pa]
+ if node.childNodes.to_a.size == 2 then
+ pa = params(node[1])
+ else # no parameters given
+ pa = []
+ end
+ [name, pa]
end
end # module TreeParserMixin
@@ -635,34 +635,34 @@
private
def _nodeType(node)
- tp = node.nodeType
- if tp == XML::SimpleTree::Node::TEXT then :TEXT
- elsif tp == XML::SimpleTree::Node::COMMENT then :COMMENT
- elsif tp == XML::SimpleTree::Node::ELEMENT then :ELEMENT
- else :ELSE
- end
+ tp = node.nodeType
+ if tp == XML::SimpleTree::Node::TEXT then :TEXT
+ elsif tp == XML::SimpleTree::Node::COMMENT then :COMMENT
+ elsif tp == XML::SimpleTree::Node::ELEMENT then :ELEMENT
+ else :ELSE
+ end
end
def methodResponse_document(node)
- assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
- hasOnlyOneChild(node, "methodResponse")
-
- methodResponse(node.firstChild)
+ assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
+ hasOnlyOneChild(node, "methodResponse")
+
+ methodResponse(node.firstChild)
end
def methodCall_document(node)
- assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
- hasOnlyOneChild(node, "methodCall")
-
- methodCall(node.firstChild)
+ assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
+ hasOnlyOneChild(node, "methodCall")
+
+ methodCall(node.firstChild)
end
def createCleanedTree(str)
- doc = XML::SimpleTreeBuilder.new.parse(str)
- doc.documentElement.normalize
- removeWhitespacesAndComments(doc)
- doc
+ doc = XML::SimpleTreeBuilder.new.parse(str)
+ doc.documentElement.normalize
+ removeWhitespacesAndComments(doc)
+ doc
end
end # class XMLParser
@@ -676,21 +676,21 @@
private
def _nodeType(node)
- node.nodeType
+ node.nodeType
end
def methodResponse_document(node)
- methodResponse(node)
+ methodResponse(node)
end
def methodCall_document(node)
- methodCall(node)
+ methodCall(node)
end
def createCleanedTree(str)
doc = ::NQXML::TreeParser.new(str).document.rootNode
- removeWhitespacesAndComments(doc)
- doc
+ removeWhitespacesAndComments(doc)
+ doc
end
end # class NQXMLTreeParser
@@ -715,7 +715,7 @@
def parse(str)
parser = REXML::Document.parse_stream(str, self)
- end
+ end
end
end
Modified: MacRuby/branches/experimental/lib/xmlrpc/server.rb
===================================================================
--- MacRuby/branches/experimental/lib/xmlrpc/server.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/xmlrpc/server.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -267,7 +267,9 @@
if obj.kind_of? Proc
methods << name
else
- obj.methods.each {|meth| methods << name + meth}
+ obj.class.public_instance_methods(false).each do |meth|
+ methods << "#{name}#{meth}"
+ end
end
end
methods
@@ -775,6 +777,6 @@
=begin
= History
- $Id: server.rb 14070 2007-12-01 16:01:49Z jeg2 $
+ $Id: server.rb 20879 2008-12-19 11:37:33Z yugui $
=end
Modified: MacRuby/branches/experimental/lib/xmlrpc/utils.rb
===================================================================
--- MacRuby/branches/experimental/lib/xmlrpc/utils.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/xmlrpc/utils.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -6,7 +6,7 @@
#
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann at ntecs.de)
#
-# $Id: utils.rb 13767 2007-10-24 18:46:08Z jeg2 $
+# $Id: utils.rb 19657 2008-10-01 13:46:53Z mame $
#
module XMLRPC
@@ -37,7 +37,7 @@
def create
# if set_writer was not already called then call it now
if @create.nil? then
- set_writer(Config::DEFAULT_WRITER.new)
+ set_writer(Config::DEFAULT_WRITER.new)
end
@create
end
@@ -45,7 +45,7 @@
def parser
# if set_parser was not already called then call it now
if @parser.nil? then
- set_parser(Config::DEFAULT_PARSER.new)
+ set_parser(Config::DEFAULT_PARSER.new)
end
@parser
end
Modified: MacRuby/branches/experimental/lib/yaml/baseemitter.rb
===================================================================
--- MacRuby/branches/experimental/lib/yaml/baseemitter.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/yaml/baseemitter.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -7,241 +7,236 @@
require 'yaml/error'
module YAML
+ module BaseEmitter
+ def options( opt = nil )
+ if opt
+ @options[opt] || YAML::DEFAULTS[opt]
+ else
+ @options
+ end
+ end
- module BaseEmitter
+ def options=( opt )
+ @options = opt
+ end
- def options( opt = nil )
- if opt
- @options[opt] || YAML::DEFAULTS[opt]
- else
- @options
- end
- end
+ #
+ # Emit binary data
+ #
+ def binary_base64( value )
+ self << "!binary "
+ self.node_text( [value].pack("m"), '|' )
+ end
- def options=( opt )
- @options = opt
+ #
+ # Emit plain, normal flowing text
+ #
+ def node_text( value, block = nil )
+ @seq_map = false
+ valx = value.dup
+ unless block
+ block =
+ if options(:UseBlock)
+ '|'
+ elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/
+ '|'
+ else
+ '>'
+ end
+ indt = $&.to_i if block =~ /\d+/
+ if valx =~ /(\A\n*[ \t#]|^---\s+)/
+ indt = options(:Indent) unless indt.to_i > 0
+ block += indt.to_s
end
- #
- # Emit binary data
- #
- def binary_base64( value )
- self << "!binary "
- self.node_text( [value].pack("m"), '|' )
+ block +=
+ if valx =~ /\n\Z\n/
+ "+"
+ elsif valx =~ /\Z\n/
+ ""
+ else
+ "-"
+ end
+ end
+ block += "\n"
+ if block[0] == ?"
+ esc_skip = ( "\t\n" unless valx =~ /^[ \t]/ ) || ""
+ valx = fold( YAML::escape( valx, esc_skip ) + "\"" ).chomp
+ self << '"' + indent_text( valx, indt, false )
+ else
+ if block[0] == ?>
+ valx = fold( valx )
end
+ #p [block, indt]
+ self << block + indent_text( valx, indt )
+ end
+ end
- #
- # Emit plain, normal flowing text
- #
- def node_text( value, block = nil )
- @seq_map = false
- valx = value.dup
- unless block
- block =
- if options(:UseBlock)
- '|'
- elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/
- '|'
- else
- '>'
- end
+ #
+ # Emit a simple, unqouted string
+ #
+ def simple( value )
+ @seq_map = false
+ self << value.to_s
+ end
- indt = $&.to_i if block =~ /\d+/
- if valx =~ /(\A\n*[ \t#]|^---\s+)/
- indt = options(:Indent) unless indt.to_i > 0
- block += indt.to_s
- end
+ #
+ # Emit double-quoted string
+ #
+ def double( value )
+ "\"#{YAML.escape( value )}\""
+ end
- block +=
- if valx =~ /\n\Z\n/
- "+"
- elsif valx =~ /\Z\n/
- ""
- else
- "-"
- end
- end
- block += "\n"
- if block[0] == ?"
- esc_skip = ( "\t\n" unless valx =~ /^[ \t]/ ) || ""
- valx = fold( YAML::escape( valx, esc_skip ) + "\"" ).chomp
- self << '"' + indent_text( valx, indt, false )
- else
- if block[0] == ?>
- valx = fold( valx )
- end
- #p [block, indt]
- self << block + indent_text( valx, indt )
- end
- end
+ #
+ # Emit single-quoted string
+ #
+ def single( value )
+ "'#{value}'"
+ end
- #
- # Emit a simple, unqouted string
- #
- def simple( value )
- @seq_map = false
- self << value.to_s
- end
+ #
+ # Write a text block with the current indent
+ #
+ def indent_text( text, mod, first_line = true )
+ return "" if text.to_s.empty?
+ spacing = indent( mod )
+ text = text.gsub( /\A([^\n])/, "#{ spacing }\\1" ) if first_line
+ return text.gsub( /\n^([^\n])/, "\n#{spacing}\\1" )
+ end
- #
- # Emit double-quoted string
- #
- def double( value )
- "\"#{YAML.escape( value )}\""
- end
+ #
+ # Write a current indent
+ #
+ def indent( mod = nil )
+ #p [ self.id, level, mod, :INDENT ]
+ if level <= 0
+ mod ||= 0
+ else
+ mod ||= options(:Indent)
+ mod += ( level - 1 ) * options(:Indent)
+ end
+ return " " * mod
+ end
- #
- # Emit single-quoted string
- #
- def single( value )
- "'#{value}'"
- end
+ #
+ # Add indent to the buffer
+ #
+ def indent!
+ self << indent
+ end
- #
- # Write a text block with the current indent
- #
- def indent_text( text, mod, first_line = true )
- return "" if text.to_s.empty?
- spacing = indent( mod )
- text = text.gsub( /\A([^\n])/, "#{ spacing }\\1" ) if first_line
- return text.gsub( /\n^([^\n])/, "\n#{spacing}\\1" )
- end
+ #
+ # Folding paragraphs within a column
+ #
+ def fold( value )
+ value.gsub( /(^[ \t]+.*$)|(\S.{0,#{options(:BestWidth) - 1}})(?:[ \t]+|(\n+(?=[ \t]|\Z))|$)/ ) do
+ $1 || $2 + ( $3 || "\n" )
+ end
+ end
- #
- # Write a current indent
- #
- def indent( mod = nil )
- #p [ self.id, level, mod, :INDENT ]
- if level <= 0
- mod ||= 0
- else
- mod ||= options(:Indent)
- mod += ( level - 1 ) * options(:Indent)
- end
- return " " * mod
- end
+ #
+ # Quick mapping
+ #
+ def map( type, &e )
+ val = Mapping.new
+ e.call( val )
+ self << "#{type} " if type.length.nonzero?
- #
- # Add indent to the buffer
- #
- def indent!
- self << indent
- end
+ #
+ # Empty hashes
+ #
+ if val.length.zero?
+ self << "{}"
+ @seq_map = false
+ else
+ # FIXME
+ # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero?
+ # @headless = 1
+ # end
- #
- # Folding paragraphs within a column
- #
- def fold( value )
- value.gsub( /(^[ \t]+.*$)|(\S.{0,#{options(:BestWidth) - 1}})(?:[ \t]+|(\n+(?=[ \t]|\Z))|$)/ ) do
- $1 || $2 + ( $3 || "\n" )
- end
- end
+ defkey = @options.delete( :DefaultKey )
+ if defkey
+ seq_map_shortcut
+ self << "= : "
+ defkey.to_yaml( :Emitter => self )
+ end
#
- # Quick mapping
+ # Emit the key and value
#
- def map( type, &e )
- val = Mapping.new
- e.call( val )
- self << "#{type} " if type.length.nonzero?
+ val.each { |v|
+ seq_map_shortcut
+ if v[0].is_complex_yaml?
+ self << "? "
+ end
+ v[0].to_yaml( :Emitter => self )
+ if v[0].is_complex_yaml?
+ self << "\n"
+ indent!
+ end
+ self << ": "
+ v[1].to_yaml( :Emitter => self )
+ }
+ end
+ end
- #
- # Empty hashes
- #
- if val.length.zero?
- self << "{}"
- @seq_map = false
- else
- # FIXME
- # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero?
- # @headless = 1
- # end
+ def seq_map_shortcut
+ # FIXME: seq_map needs to work with the new anchoring system
+ # if @seq_map
+ # @anchor_extras[@buffer.length - 1] = "\n" + indent
+ # @seq_map = false
+ # else
+ self << "\n"
+ indent!
+ # end
+ end
- defkey = @options.delete( :DefaultKey )
- if defkey
- seq_map_shortcut
- self << "= : "
- defkey.to_yaml( :Emitter => self )
- end
+ #
+ # Quick sequence
+ #
+ def seq( type, &e )
+ @seq_map = false
+ val = Sequence.new
+ e.call( val )
+ self << "#{type} " if type.length.nonzero?
- #
- # Emit the key and value
- #
- val.each { |v|
- seq_map_shortcut
- if v[0].is_complex_yaml?
- self << "? "
- end
- v[0].to_yaml( :Emitter => self )
- if v[0].is_complex_yaml?
- self << "\n"
- indent!
- end
- self << ": "
- v[1].to_yaml( :Emitter => self )
- }
- end
- end
+ #
+ # Empty arrays
+ #
+ if val.length.zero?
+ self << "[]"
+ else
+ # FIXME
+ # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero?
+ # @headless = 1
+ # end
- def seq_map_shortcut
- # FIXME: seq_map needs to work with the new anchoring system
- # if @seq_map
- # @anchor_extras[@buffer.length - 1] = "\n" + indent
- # @seq_map = false
- # else
- self << "\n"
- indent!
- # end
- end
-
#
- # Quick sequence
+ # Emit the key and value
#
- def seq( type, &e )
- @seq_map = false
- val = Sequence.new
- e.call( val )
- self << "#{type} " if type.length.nonzero?
-
- #
- # Empty arrays
- #
- if val.length.zero?
- self << "[]"
- else
- # FIXME
- # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero?
- # @headless = 1
- # end
-
- #
- # Emit the key and value
- #
- val.each { |v|
- self << "\n"
- indent!
- self << "- "
- @seq_map = true if v.class == Hash
- v.to_yaml( :Emitter => self )
- }
- end
- end
-
+ val.each { |v|
+ self << "\n"
+ indent!
+ self << "- "
+ @seq_map = true if v.class == Hash
+ v.to_yaml( :Emitter => self )
+ }
+ end
end
+ end
- #
- # Emitter helper classes
- #
- class Mapping < Array
- def add( k, v )
- push [k, v]
- end
+ #
+ # Emitter helper classes
+ #
+ class Mapping < Array
+ def add( k, v )
+ push [k, v]
end
+ end
- class Sequence < Array
- def add( v )
- push v
- end
+ class Sequence < Array
+ def add( v )
+ push v
end
-
+ end
end
Modified: MacRuby/branches/experimental/lib/yaml/rubytypes.rb
===================================================================
--- MacRuby/branches/experimental/lib/yaml/rubytypes.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/yaml/rubytypes.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -379,6 +379,44 @@
end
end
+class Rational
+ yaml_as "tag:ruby.yaml.org,2002:object:Rational"
+ def Rational.yaml_new( klass, tag, val )
+ if val.is_a? String
+ Rational( val )
+ else
+ Rational( val['numerator'], val['denominator'] )
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self, opts ) do |out|
+ out.map( taguri, nil ) do |map|
+ map.add( 'denominator', denominator )
+ map.add( 'numerator', numerator )
+ end
+ end
+ end
+end
+
+class Complex
+ yaml_as "tag:ruby.yaml.org,2002:object:Complex"
+ def Complex.yaml_new( klass, tag, val )
+ if val.is_a? String
+ Complex( val )
+ else
+ Complex( val['real'], val['image'] )
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self, opts ) do |out|
+ out.map( taguri, nil ) do |map|
+ map.add( 'image', imaginary )
+ map.add( 'real', real )
+ end
+ end
+ end
+end
+
class TrueClass
yaml_as "tag:yaml.org,2002:bool#yes"
def to_yaml( opts = {} )
Modified: MacRuby/branches/experimental/lib/yaml/yamlnode.rb
===================================================================
--- MacRuby/branches/experimental/lib/yaml/yamlnode.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/yaml/yamlnode.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -11,13 +11,13 @@
class YamlNode
include BaseNode
attr_accessor :kind, :type_id, :value, :anchor
- def initialize( t, v )
+ def initialize(t, v)
@type_id = t
if Hash === v
@kind = 'map'
@value = {}
- v.each { |k,v|
- @value[ k.transform ] = [ k, v ]
+ v.each {|key,val|
+ @value[key.transform] = [key, val]
}
elsif Array === v
@kind = 'seq'
Modified: MacRuby/branches/experimental/lib/yaml.rb
===================================================================
--- MacRuby/branches/experimental/lib/yaml.rb 2009-06-19 22:27:12 UTC (rev 1888)
+++ MacRuby/branches/experimental/lib/yaml.rb 2009-06-19 22:42:24 UTC (rev 1889)
@@ -1,5 +1,5 @@
# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
-# $Id: yaml.rb 13940 2007-11-15 17:54:32Z why $
+# $Id: yaml.rb 19495 2008-09-23 18:16:08Z drbrain $
#
# = yaml.rb: top-level module with methods for loading and parsing YAML documents
#
@@ -213,7 +213,7 @@
# end
# end
#
- def YAML.each_document( io, &block )
+ def YAML.each_document( io, &block )
yp = parser.load_documents( io, &block )
end
@@ -228,7 +228,7 @@
# end
# end
#
- def YAML.load_documents( io, &doc_proc )
+ def YAML.load_documents( io, &doc_proc )
YAML.each_document( io, &doc_proc )
end
@@ -243,7 +243,7 @@
# end
# end
#
- def YAML.each_node( io, &doc_proc )
+ def YAML.each_node( io, &doc_proc )
yp = generic_parser.load_documents( io, &doc_proc )
end
@@ -258,7 +258,7 @@
# end
# end
#
- def YAML.parse_documents( io, &doc_proc )
+ def YAML.parse_documents( io, &doc_proc )
YAML.each_node( io, &doc_proc )
end
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20090619/307ae424/attachment-0001.html>
More information about the macruby-changes
mailing list