#!/usr/bin/env ruby # Playing around with ruby. # command line tools : # $ ruby file # $ irb # interactive # $ rdoc file # create /doc folder from embedded comments # $ gems # installation manager # $ rails # web framework creator/manager # $ rake # makefile tasky thing, in ruby # $ rvm # ruby version manager (greenriver step 0, 2012) # # scope classes and variable names : # local global instance class constant/class_name symbols # ---- ----- -------- ----- --------------- ------- # foo $size @height @@name MyClass :foo # bar $a @color @@who PI :bar # # An expression like :foo returns a Symbol object, which is # commonly used as hash keys and in class accessor definitions. # Symbol to String conversions are built-in, # i.e. :foo.to_s is "foo", while "foo".to_sym is :foo # Note that file 'local' variables are *not* visible from within functions; # if that's what you want, use @name, which adds that instance to 'self', # (self is 'main' by default, but becomes once you add # instances : # $ irb # irb> self # main # irb> @x = 3 # irb> self # # arrays are similar to perl # a = [ 1, 'cat', 3.14] # print a[0] # empty1 = [] # empty2 = Array.new # b1 = %w{ ant bee cat dog elk } # word array # b2 = ['ant', 'bee', 'cat', 'dog', 'elk'] # same # # so are hashes # jim = { # 'name' => 'Jim', # 'phone' => 'xxx-yyyy' # } # print jim['name'] # like javascript # # block constructs all finish with 'end' keyword # if a > 10 # puts "yes" # else # puts "no # end # # perl-ish conditional modifiers are allowed # puts "go" if a > 10 # square = square*square while square < 100 # # regular expressionqs are much like perl # if line =~ /P(erl|ython)/ # puts "line contains #{line}" # end # but with object-oriented stuff # line.sub(/Perl/, 'Ruby') # replace first Perl with Ruby # line.gsub(/Perl/, 'Ruby') # replace all Perl with Ruby # # code blocks either as # do # code.here() # end # or as # {code.here()} # a = 3.0 # Floating point. 0 is required. b = 'This is a string. ' # perl-lish single quotes c = "a = #{a}. \n" # string interpolation and newline print b + c # Semi-colons are allowed at end of lines, but not required. # The style guide I found suggested avoiding them. # # Line continuation can be explicit with \ # or implied by incomplete syntax. # # From wikipedia article on Ruby, under "gotchas" : # * Uppercase/lowercase first letter in name implies constant/variable # * $ (global), @ (instance), @@ (class) used for scope resolution # * 3. isn't allowed; the period may be method invocation. # * The only false booleans are [nil, false]; These are true: [0, "", []] # * Methods return nil (not false) when they fail; a smalltalk-like convention # * A construct "foo() until bar()" doesn't run foo() at all if bar() is true. # * Constants are references, so changing what the object it points to is OK. # * The 'and' operator does *not* bind tighter than 'or'. # But ||, && work as usual. # Define a subroutine (or function or method) that takes no args. def foo puts "This is foo." # puts (put_string) adds \n at end end foo # invoke foo method foo() # same def bar(x) puts "In bar(x) with x='" + x.to_s + "'." print " yield 1 : " yield print " yield 2 : " yield end bar('hi') {puts "bar"} # invoke with "associated code block" - new concept # Note that this "associated block" isn't actually a "parameter"; # control can pass back and forth between the two code segments. bar('bye') do # another way to create a code block puts "one" puts "two" end # However, also note that the precedence of {} and do..end is different; # "a b {...}" is a(b{...}) while "a b do...end" is (a b)do...end # The code block can have args, which show up between vertical bars. a = %w(one two three four) # array of words (like perl's qw() a.each { |x| print x+" " } # iterate, applying given code block puts # print newline # This generalizes to pretty funky syntax forms. # The following three lines prints '*****3456abcde' 5.times {print '*'} 3.upto(6) {|i| print i } ('a'..'e').each {|char| print char} puts # I/O is pretty typical. # print "What is your input? " # line = gets # printf "You said '%s' \n", line # gets also stores into global $_ which is default match, like perl # # perlish printing lines that contain 'Ruby' : # while gets # if /Ruby/ # print # end # end # # same with more Ruby styling, using ARGF input stream object # ARGF.each { |line| print line if line =~ /Ruby/ } # Classes (i.e. objects) class Person # must start with uppercase def initialize(name, haircolor) # special constructor method @name = name # instance variables @haircolor = haircolor end def to_s # override default to_s method return "Person #{@name} has #{@haircolor} hair" end end him = Person.new('Jim', 'black') # create an instance of Person object. puts him.inspect # Print its representation. # e.g. # puts him.to_s # to_string method # inheritance class Child < Person def initialize(name, haircolor, age) super(name, haircolor) @age = age end end # The to_s method will be the first found in inheritance chain, i.e. Child's kid = Child.new('Sally', 'blond', 4) puts kid.to_s class FixedChild < Person def initialize(name, haircolor, age) super(name, haircolor) @age = age end def to_s return super + " and is #{@age} years old" end end # Now it has its own to_s which uses parent's. kid2 = FixedChild.new('Tiny', 'red', 7) puts kid2.to_s # Note that we can "drop into" the Person class to add more stuff on the fly. # This defines "attribute readers", namely accessors for some instance vars. # Like JavaScript, all these classes are malleable at run time. class Person attr_reader :name, :haircolor, :lastname # getter, i.e. him.name attr_writer :lastname # setter, i.e. him.lastname = 'x' end # In that example, the symbol :name is used to specifiy that # an instance variable @name and its accessor should be created. him.lastname = 'Smith' # printf "The person named %s %s has %s hair.\n", him.name, him.lastname, him.haircolor # We can also create functions that act like accessors. # The "pickaxe book" calls this "virtual attributes". class Person def fullname "#{name} #{lastname}" # Note that 'return' is optional. end def fullname=(fullname) # what to do for him.fullname = foo names = fullname.split @lastname = names.pop @name = names.join(' ') end end puts "fullname was '" + him.fullname + "'" him.fullname = 'Bob Jones' puts "fullname is now '" + him.fullname + "'" # Class variables have names that start with @@ . # Class methods have names that start ClassName.* # Here's an example with a counter for total number of instances. class Tree @@count = 0 # initialize class variable def initialize(name) # (special) instance method @name = name @@count += 1 end def Tree.total # class method return @@count end end # Each ruby instance has its own "metaclass", an object that represents # its class. And there's a syntax for working within that explicitly; # see http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/ # mostly same as above, take 1 class << Tree # set self to Tree's metaclass def total # ... so this is in Tree, not in instance return @@count end end # still mostly same as above, and a regularly used idiom class Tree class << self # start class methods; "<< self" is metaclass of self ie Tree def total return @@count end end end # and this is also pretty much the same class Tree def self.total # define method of Tree, not instance return @@count end end trees = (1..5).map {|x| Tree.new(x.to_s)} puts "Tree.total is " + Tree.total.to_s puts "trees.length is " + trees.length.to_s # You can preface method declaration blocks # with keywords protected, private, public # class Foo # def meth1 # default public # # ... # end # protected # def meth2 # # ... # end # def meth3 # # ... # end # end # # initialize is private by default. # Note that everything in Ruby is a reference, like in lisp. foo1 = "This is foo" foo2 = foo1 # another name for same object printf "id of foo1 is %i \n", foo1.object_id printf "id of foo2 is %i \n", foo2.object_id # Modify last element of string. foo2[-1] = "*" # modifications to foo2 puts "foo1 is now '#{foo1}'" # ... apply to foo1; same object. # Ruby provides a .dup method to duplicate, and .freeze to prevent changes. foo3 = foo1.dup printf "id of foo3 is %i \n", foo2.object_id foo3.freeze # foo3[-1] = "*" # error, since foo3 has been 'frozen' # Array slices have form array[index, count] # returns new array of refs to the values in original array. # On assignment, the count entries are replaced # Ranges like 1..3 are also allowed, meaning (1,2,3), # as well as 1...5 meaning (1,2,3,4) (three dots excludes ending value) # and negative numbers, which count back from end. # Lambda functions look something like this : p = proc {|x| 3*x} puts "p.call(5) is " + p.call(5).to_s # Which can then be passed as associated code blocks. def call_p(y) yield(y) end def call2_p(y, *rest, &z) # *rest are optional args; &z is code block z.call(y) # same end answer = call_p(7, &p) puts "call_p(7) &p is #{answer}" answer2 = call2_p(7, &p) puts "call2_p(7) &p is #{answer2}" answer3 = call_p(7) {|x| 3*x} # same puts "call2_p(7) {...} is #{answer3}" # Ruby has lisp-lish 'symbols' that start with :, i.e. :foo # These are particularly common in hashes. hash = { :name => 'Jim', :age => 17 } # The perl-ish backticks run the expression in the shell. # For a good time : Object.methods # "parallel assignment" is allowed. a,b = 1,2 # The * works here, too, in a 'shortcut' way. a = [10, 11, 12, 13] b, *c = a # c = [11, 12, 13] # This can even be nested. b, (c, d), e = 1, 2, 3, 4 # (c,d)=2; so d=nil, and e=3 # Ruby has the notion of a "module" analagous to perl's package; # a file stypically starts with "module Foo" and # then is "required" from other files. # The module methods can be "mixed in" to classes like this : module Debug def whoAmI return "#{self.class.name} (\##{self.id}): #{self.to_s}" end end class Phonograph include Debug # "mix in" the methods from Debug module. # ... end # The 'self' keyword references the object that invoked the method. # You can also modify the built-in classes on the fly : class Array def rotate! self.unshift(self.pop) return self[0] end end # ... so the classes are very dynamic, like JavaScript's. # Ducktyping is very Ruby. # BEGIN { ... }, END { ... } are allowed and function same as perl's. # ------------ # # # Ruby has a lot of variations on functions. # And since it doesn't require parens to invoke a function, # it's not obvious to see how to pass them around. # In fact, usually ruby-ists use code blocks rather than # pass functions. # # But coming from other languages, I'd be tempted to try this : # # # ------------- wrong # def f(x) # return 2*x # end # # def g(z) # return z(3) # end # # g(f) # # --------------- # # The problem here is that when it sees g(f), # ruby will try to invoke f, and since there's no argument there, # it'll just complain. g(f 3) would be g(f(3)), for example; # without an arg it just sees g(f ). # # One way to do this is to use symbols, method(), and .call : # # irb> ff = method(:f) # here :f is the 'symbol' for f, which can access f(). # # # This ff thing can be passed along as a parameter. And it can be invoked # with ".call" , which will run f. # # So this works : # ------------------ legal, though not very ruby-ish # # def f(x) # return 2*x # end # # def g(z) # return z.call(3) # here z is a proc object # end # # g(proc {|x| 3*x}) # creating a "lambda" proc object on the fly # # g(method(:f)) # passing a "method" proc object, that can call f(x). # # ------------------ # # Another way to do this is with the .send method, # which uses a symbol to invoke a method in a class. # # class Wrapper # def f(x) # return 2*x # end # end # # def g(z) # return Wrapper.new.send(z, 3) # end # # g(:f) # this sends :f to a Wrapper object, which then invokes f(3) # # -------------------- # or you could use eval() and put it in a string. Hmmm. # # -------------------- # # # and arguably the most ruby-ish is to simply write g so that it takes a block: # # def g # yield 3 # invoke the block # end # g {|x| 2*x} # pass the block # # # or you can name the block in g's args, which then requires it, # and treats it as a proc object which can be called. # # def g(&foo) # foo.call(3) # end # # g {|x| 3*x} # # # ------- # recursive irb # # There's a trick with interactive ruby (irb) within irb, # to manipulate class/instance variables of an object within # the context that methods of the object can see. # # introspection # # The following methods are available to see what names are in scope. # global_variables # local_variables # instance_variables # methods class Demo def initialize @size = 'big' end def foo return @size end end # >> self # main # >> d = Demo.new # >> d.foo # >> 'big' # >> irb d # enter recursive irb, within d # >> self # <#Demo:... @size="big"> # >> instance_variables # same as self.instance_variables # ["@size"] # which is now d.instance_variables # >> exit # exit recursive irb, back to self=main