From: Robert Feldt Date: 2001-10-15T07:27:39+09:00 Subject: [ruby-talk:22530] Re: Open-ended ranges On Mon, 15 Oct 2001, Sean Russell wrote: > Hmmm. Well, Ranges are lazily evaluated; they are not converted internally > to arrays, and I don't see any reason why they open-ended ranges wouldn't > be possible -- EXCEPT for the to_a issue, which is indeed a stumper. But, > consider, for a moment, a number class tha wraps Integer, which provides > constants INFINITY and NEGATIVE_INFINITY, and which supports succ and <=> > such that x <=> MyInt::INFINITY is always -1. You could easily make a > range of this, although to_a would hang. > Ok, I agree that its possible (most things are! :-)) but then someone will do (1..).each and keep on waiting (granted there are always many things you can do to screw up) 'til mem runs out... ;-) > >> PS. I was following the "versioned require" discussion for a while, but > >> it > .... > > Many people seem to think that something along those lines would be a > > good thing. There is not yet a consensus on which semantics to use and how > > to implement. Maybe the RAA.succ/RubyGems discussion/solution can revive > > this discussion again? > > I've seen at least one extension which enabled this, but this is the sort > of thing that is almost useless unless it is ubiquitous. IE, the NEED for > this is usually in moving software around from one machine to another, so > if it isn't part of core Ruby, its usefullness is extremely limited. > I agree and I think Ryan Leavengoods upcoming RubyGems and the outcome from the Raa.succ DesignFest at RubyConf might make starting points for a standard way. So something is probably coming along this year... However, there is one way to do this that would work with only minimal support from the core/std Ruby: the gems/archives are simply Marshal.dump'ed objects with a simple Ruby loader. This way people can introduce custom gems as long as they respond to some standardized messages. I actually did a tracer bullet along those lines. The gems it produces has three parts: 1. Minimal Ruby program that unpacks string in 2 and eval's it and then loads object in 3 and sends it ARGV 2. Possibly empty string with source code that defines the object in 3 (or the differences between the object in 3 and a std Gem class but there is a trade off here) 3. Marshal.dump'ed Gem object that can list, unpack, install itself etc. Code is below for interested souls... Sorry for the length. (Note that it seems kind of strange to add the source code in a string (2) that is then eval'ed instead of including it in the loader directly; my thought with this was that it might be compressed...) A potential quirk with this scheme is if the Marshal format changes between Ruby versions. I don't think it has changed since 1.6 though, but I'm not sure... Regards, Robert gem.rb ------ require 'ftools' def loader_source_code(src_code_length, length) if src_code_length > 0 eval_src = <\n" + "Architecture:".ljust(20) + architecture + "\n" + "Dependencies:".ljust(20) + dependencies.inspect + "\n" + "Description:\n" + format_description(description) end # Subclasses should add their source code here so that they can be # unpacked even if the Gem user does not have pre-installed their source # code. def non_standard_ruby_source_code "" + @archive.non_standard_ruby_source_code end def add_file(filename) @archive.add_file(filename) end def marshal marshaled_gem = Marshal.dump(self) non_std_src = non_standard_ruby_source_code loader_source_code(non_std_src.length, marshaled_gem.length) + non_std_src + marshaled_gem end def Gem.load(str) Marshal.load(str) end def usage usage_message(name, version) end def install unpack puts "Installing..." install_files end def unpack puts "Unpacking..." unpack_files end def list puts "Files in gem:" if @archive.num_files > 0 @archive.each {|file, size, str| puts " #{file} (#{size})"} else puts " None." end end def unpack_files @archive.each do |filename, size, str| puts " #{filename}" File.makedirs(File.dirname(filename)) File.open(filename, "w") {|f| f.write str} end end def install_files raise NotImplementedError, "Install not yet implemented!" end def clean files.each do |filename, len, str| File.delete(filename) end # Also delete subdirs we have created? Maybe not... end def call(argv) argv.push nil if argv.length == 0 while argv.length > 0 case argv[0] when "--install", "-i" std_command(:install) when "--unpack", "-u" std_command(:unpack) when "--list", "-l" std_command(:list) when "--clean", "-c" std_command(:clean) else print "Invalid option!\n\n" if argv[0] puts usage puts "Gem info" puts "--------" puts inspect exit end argv.shift end end private def std_command(command, *args) puts inspect self.send(command, *args) end def usage_message(name, version) str = <(other) if name == other.name version <=> other.version else name <=> other.name end end def inspect_version version == "0.0" ? "" : " (#{version})" end def inspect "#{name}#{inspect_version}" end end def dep(*args) Dependency.new(*args) end class Stream def initialize @data = "" end def <<(aString) @data << aString end def length @data.length end def [](position, length) @data[position, length] end def delete_bytes(position, length) self[position, length] = "" self end # Subclasses should add their source code here so that their streams can be # unpacked even if the Gem user does not have installed the stream def non_standard_ruby_source_code "" # This stream is supposed to be included with Ruby so everyone has it! end end class FileArchive attr_reader :info def initialize @info, @stream = Hash.new, Stream.new end def add_file(filename) File.open(filename, "r") do |f| delete_content(filename) if include?(filename) content = f.read info[filename] = [stream.length, content.length] stream << content end end def num_files @info.length end def each @info.each do |filename, (position, length)| yield(filename, length, @stream[position, length]) end end def get_file(filename) stream[*info[filename]] end def include?(filename) info.include?(filename) end def non_standard_ruby_source_code # This archiver is supposed to be included with Ruby so everyone has it! "" + stream.non_standard_ruby_source_code end private attr_reader :stream def delete_content(filename) position, length = info[filename] stream.delete_bytes(position, length) delete_info(filename, position, length) end def delete_info(filename, position, length) info.each do |name, (pos, len)| info[name] = [pos-length, len] if pos > position end info.delete(name) end end if __FILE__ == $0 g = Gem.new("CryptoLib", :ruby => dep("ruby", "1.7.1"), :maintainer => "Robert Feldt", :maintainer_email => "feldt@ce.chalmers.se", :category => "algorithms/cryptography", :deps => [dep("zlib"), dep("optparse", "0.4")], :desc => "Cryptography algorithms implemented in pure Ruby (ie. *NO* C code).") puts g.inspect ARGV[1..-1].each do |filename| g.add_file(filename) end if ARGV[0] File.open(ARGV[0] + ".gem", "w") {|f| f.write g.marshal} end end