From: Ilmari Heikkinen Date: 2004-12-12T09:03:54+09:00 Subject: Re: puts / print as method not keyword? On 12.12.2004, at 00:06, zuzu wrote: >> string.puts(STDOUT) creates a coupling, the string must call a method >> of the >> given object with itself as the argument. It must know that the >> object it's >> passed responds to #puts. > > i think to me this seems natural for the pure-OO "everything is an > object" nature of ruby (or smalltalk). strings know how to display > themselves, they just need to know where. again, i think "naked > objects" is the latest way to talk about this, though i often think of > self language which was built on smalltalk. Regarding the "string displaying itself"-bit, my view is that a string can suggest how it should be displayed, but the final call is in the hands of the display object. As dataflow: cat string | /dev/display >> The difference to Ruby's method.method.method is that the shell pipes >> aren't >> namespaced to the preceding object. Ie. With shell pipes it'd be >> quite doable >> to except the following to work in every situation: >> >> gets | reverse | puts , because they are in the global namespace. >> What you pass >> through the pipe doesn't change the available methods. >> >> In ruby >> >> gets.reverse.puts >> creates a dependency chain. >> >> gets must return something that responds to reverse, and reverse must >> return >> something that responds to puts. Which is bad. > > hmmm... is this any different than unix which works blindly in > bytestreams? The shell pipes differ in that shell commands are all "instance methods of class Bytestream", but in Ruby the input object and output object may be of different classes. So there's a possibility of interface mismatch. With shell commands, the methods all have the same input and output type, but they are liable to fail if the incoming stuff doesn't quite agree with what the command was built to handle. cat my.wav | lame - - | madplay - cat my.txt | lame - - | madplay - # if my.txt happens to be a valid wav file, it works # if not, lame blows up. Compared to: Wav.new('my.wav').lame.madplay # works, may fail inside call to lame if input is bad Txt.new('my.txt').lame.madplay #=> NoMethodError, even if my.txt is a valid wav file The way shell commands work could be rubyized as: require 'thread' class ShellPipe # create method for each command in each path with # paths in front overriding later ones # quite obtuse. ENV['PATH'].split(/:/).reverse.each{|path_root| Dir[File.join(path_root, "*")].each{|bin_name| define_method(File.split(bin_name).last){|*args| full_cmd = [bin_name, *args].join(" ") self.class.new( self, IO.popen(full_cmd, 'r+') ) } } } def initialize(input, filter, stopped=false) @input = input @filter = filter @stopped = stopped @mutex = Mutex.new @cond = ConditionVariable.new suck_on_input end def silently_fail yield rescue end def method_missing(m, *args, &block) @filter.send(m, *args, &block) rescue NoMethodError super end def suck_on_input silently_fail{ @input_sucker.kill } @input_sucker = Thread.new{ begin @mutex.synchronize{ until @input.eof? @cond.wait(@mutex) if @stopped @filter.write @input.read(4096) end } ensure @filter.close_write end } end def start return nil unless @stopped @mutex.synchronize{ @stopped = false @cond.signal } end def stop @stopped = true end def read(*args) @filter.read(*args) end def close @input.close if @input and not @input.closed? ensure @filter.close end end my_wav = ShellPipe.new(nil, File.open("my.wav")) player = my_wav.lame("-", "-").madplay("-") player.read player.close # cascades up the pipe Got a little carried away there, sorry about that :)