Ruby mini-doc

From http://docs.opscode.com/just_enough_ruby_for_chef.html, completed for my own vision ...

Ruby is a Dynamic Object language, with closure, Threading and coroutine

Sommaire

Run ruby
assigning a variable: splat operator, some basic arithmetic:, make a big number
Strings
Symboles
Boolean
Range
Arrays
arrays <==> String format % [values]
$#wqrx{} stuff
Predefined Variables / Constantes Predefined variables, Predefined Constantes
Hash : dictionary
Hash with default distinct value list and count distinct words in a txt file
Hash <==> Array
regular expressions Specs (from Rebular and Pickaxe), Lookhead, Options /xxx/options, Extraction
replace Execute a system command and transform output
defines a function, a method Arguments
BLOCK block parameter/yield, explicit block parameter/explicit call, block object / block call
conditionally with an if (or unless) statement simple if, postfix if/unless (a la perl)
case statement
Looping while statement, until statement, loop
Thread
Catch Exceptions Exception and StandardError, postfixe form, generate a exception, Thread and exception
File / Directory access File read, File write
Execute process, get output get output, get input for sending data to subprocess
Socket Socket TCP client, Socket TCP server, Socket UDP Send and Receive, Socket Multicast
Serialisation
Class
Module
Class : singleton, statics...
Introspection 1 Introspection 1 :level class, Introspection 2 : level Instructions
Meta programming From a Hash, create a Class which each method gives access of members of the hash
DSL with Ruby Bloc Define a micro-test framework
Ruby and Functional Programming Principles, Closure, Examples from onliner classical code, print methods which match a regexp of an object, print big line of ruby code in current directory, rotation of a polyline
Source code sructure includes, context : where is current instructions?, class scope, Here docs, source here doc
the end...


Run ruby
$ ruby file.rb $ ruby -e "puts 'CouCou'" $ tasklist /v | ruby -ne "puts $_.split(/\s+/).join(',') if $_ =~ /ruby/i" ruby.exe,4752,Console,1 ruby.exe,4832,Console,1 ruby.exe,4284,Console,1 ruby.exe,4220,Console,1 $ ruby -e "puts RUBY_VERSION" $ ruby -e "def triple(n) n+n+n; end; p triple(ARGV[0].to_i)" 11 33
assigning a variable:
x = 1 x,y= 1,2 # x=1 , y=2 a,b=b,a # swap a,b=b,nil # put b in a and reset b a=b=c=x # x to all a||={} # if a is nil , set a empty Hash
splat operator
x,y= *[10,12,13] # x=10, y=12 x,*y=*[1,2,3,4] # x=1 , y=[2,3,4] x,y,*z=*[1,2,3,4] # x=1 , y=2 ; z=[3,4] def sum(*args) args.inject(0) { |sum,v| sum+v } end sum(1,2,3,4,5) # 15
some basic arithmetic:
1 + 2 # => 3 5 / 2 # => 2 (because both arguments are whole numbers) 5 / 2.0 # => 2.5 (because one of the numbers had a decimal place) 1 + (2 * 3) # => 7 (you can use parens to group expressions) 1000_000 # _ delimiter acceped in numeric literal 1000_000_000_000 # Ok; ruby know bignum, conversion to bignum are automatics
make a big number
# ruby automaticly mute int to bignum def fact(n) fact=1 1.upto(n < 1 ? 1 : n) { |i| fact=fact*i } return(fact) end p fact 1000 4023872600770937735437024339230039857193 7486421071463254379991042993851239862902 0592044208486969404800479988610197196058 6316668729948085589013238296699445909974 2450408707375991882362772718873251977950 5950995276120874975462497043601418278094 ....000000000000000000000000000000000000 0000000000000000000000000000000000000000 00000000
Strings
'single quoted' # => "single quoted" "double quoted" # => "double quoted" 'It\'s alive' # => "It's alive!" "1 + 2 = 5" # => "1 + 2 = 5" (numbers surrounded by quotes may exhibit string-like behavior) x = "Bob" "Hi, #{ x }" # => "Hi, Bob" 'Hello, #{ x }' # => "Hello, \#{ x }" Notice that single quotes don't work with #{} "\n".size # 1 "" expand \n to LF, so One char '\n'.size # 2 '' do not expand \.. , 2 char : '\' and 'n' string.upcase string.downcase string.capitalize string.center(len) # make a big string, str centered (blanc are padding left/right) string.center(len,'-') # make a big string, str centered ('-' are padding left/right) string.ljust(lenth) string.rjust(lenth) string.strip # supress blancs characteres at start/end of sring string.lstrip string.rstrip string.chomp # supress \r,\n.. at end of the string, if exist string[2..4] # 2 to 4 char of string string[-4..-1] # 4 last chars string[position,length] string[1,2] # 2 char from second position string << "az" # append string to string !!!! String are not immutable!!! "ab"+"bc" # 'abbc' "ab"*10 # "ababababababababab" string.each_line {|line| line.chomp} string.encode(encode) # change encoding, binaries values are changed string.force_encoding # chenge the type marker, not the binary values string.end_with?("end") string.empty? string.include?("data") string.insert(position,value) string.to_i string.to_s string.hex "a".ord # ascii value of one char string string.match('pattern') string.match(/pattern/)
Symboles
# Returns the Symbol corresponding to str, creating the symbol # if it did not previously exist string.to_sym symbolt.to_str (:eeee).class # Symbol # Symbols are unique "abc".to_sym.object_id == ["a","b","c"].join.to_sym.object_id "abc".to_sym.object_id == :abc.object_id
Boolean
true # => true false # => false nil # => nil 1 == 1 # => true ( == tests for equality ) 1 == true # => false ( == tests for equality ) !true # => false !false # => true !nil # => true 1 != 2 # => true (1 is not equal to 2) 1 != 1 # => false (1 is not not equal to itself)
Range
('A'..'Z').to_a # => ['A','B'...'Z'] ('avs'..'bre').to_a # => ["avs", "avt", "avu", "avv", "avw", .... "bre"] ('A'..'Z').each {|c| p c} (10..40).step(10) { |n| p n } 10 20 30 40 (1..10).member?(1) # true # Ranges are lazy evaluation (1..1000_000_000_000).member?(333) # true
Arrays
x = ["a", "b", "c"] # => ["a", "b", "c"] x[0] # => "a" (zero is the first index) x.first # => "a" (see?) x.last # => "c" x[-2..-1] # => ["b", "c"] x[1,2] # => ["a',"b"] x + ["d"] # => ["a", "b", "c", "d"] x # => ["a", "b", "c"] ( x is unchanged) x << 1 # => ["a", "b", "c",1] x << [1,2] # => ["a", "b", "c",1,[1,2]] array.each { |element| } array.reverse_each { |element| } array.size array.sort array.sort {|a,b| a <=> b } # specify comparaison array.sort {|a,b| a[0] <=> b[0] } array.shift # pop array.unshit # push array << element see here for many more...
arrays <==> String
"a,b,c,d".split(',') # ["a","b","c","d"] ["a","b","c","d"].join(",") # "a,b,c,d" "a,b,2,c,d,1".scan(/[\w]+/) # ["a","b","c","d"] "qsdf : fqsdf qdsf, gqsd".scan(/\w+/) # ["qsdf","fqsdf","qdsf","gqsd"] "abcd bcde cd".split(/\s+/).map { |word| word.capitalize}.select {|w| w.size>=3}.join(",") "Abcd,Bcde"
format % [values]
# format % operator : as C sprintf() str= format % [data...] === sprintf(str,format,data...) Dir.glob("*.rb")[0..5].each { |name| puts "%-20s | %05d , %s" % [ name, File.size(name), File.mtime(name).to_s ] } a.rb | 04353 , 2012-03-22 22:23:37 +0100 allways.rb | 00698 , 2012-12-13 19:58:44 +0100 anac.rb | 05777 , 2011-10-26 21:58:39 +0200 anar.rb | 01316 , 2011-10-26 21:58:39 +0200 au3.rb | 00046 , 2011-10-26 21:58:39 +0200 audio.rb | 02607 , 2011-10-26 21:58:39 +0200
$#wqrx{} stuff
# front/end chars '{' and '}' can be replaced by () [] || ... "string #{ 2 }" # string 2 'string #{ 2 }' # string #{ 2 } %q{string '#{ 2 }'} # string '#{ 2 }' %Q{string '#{ 2 }'} # string '2' %w{ aa bb cc } # ["aa","bb","cc"] %w(aa b\ b) #["aa", "b b"] %w{aa bb #{ cc }} # ["aa","bb","#{ cc }"] cc="ccc" %W{ aa bb #{ cc } } # ["aa","bb","ccc"] %i # [:aa, :bb, :cc] %x{ cmd.exe /c dir } a="c:/" %X{ cmd.exe /c dir # } %r{ regexp without accolades... }
Predefined Variables / Constantes
Predefined variables
$! The exception information message set by ‘raise’. $@ Array of backtrace of the last exception thrown. $& The string matched by the last successful pattern match in this scope. $` The string to the left of the last successful match. $' The string to the right of the last successful match. $+ The last bracket matched by the last successful match. $1 The Nth group of the last successful match. May be > 1. $~ The information about the last match in the current scope. $= The flag for case insensitive, nil by default. $/ The input record separator, newline by default. $\ The output record separator for the print and IO#write. Default is nil. $, The output field separator for the print and Array#join. $; The default separator for String#split. $. The current input line number of the last file that was read. $< The virtual concatenation file of the files given on command line. $> The default output for print, printf. $stdout by default. $_ The last input line of string by gets or readline. $0 Contains the name of the script being executed. May be assignable. $* Command line arguments given for the script sans args. $$ The process number of the Ruby running this script. $? The status of the last executed child process. $: Load path for scripts and binary modules by load or require. $" The array contains the module names loaded by require. $DEBUG The status of the -d switch. $FILENAME Current input file from $<. Same as $<.filename. $LOAD_PATH Load path for scripts and binary modules by load or require (as $:). $stderr The current standard error output. $stdin The current standard input. $stdout The current standard output. $VERBOSE The verbose flag, which is set by the -v switch. $-0 The alias to $
Predefined Constantes
TRUE The typical true value. FALSE The false itself. NIL The nil itself. STDIN The standard input. The default value for $stdin. STDOUT The standard output. The default value for $stdout. STDERR The standard error output. The default value for $stderr. ENV The hash contains current environment variables. ARGF The alias to the $<. ARGV The alias to the $*. DATA The file object of the script, pointing just after __END__. RUBY_VERSION The ruby version string (VERSION was depricated). RUBY_RELEASE_DATE The relase date string. RUBY_PLATFORM The platform identifier.
Hash : dictionary
h=Hash.new #{} h=Hash[1,11,2,22] # {1=>11, 2=>22} h = { "first_name" => "Bob", "last_name" => "Jones" } # { "first_name => "Bob", "last_name" => "Jones" } h = { first_name: "Bob", last_name: "Jones" } # { :first_name => "Bob", :last_name => "Jones" } h.keys # => ["first_name", "last_name"] h["first_name"] # => "Bob" h["last_name"] # => "Jones" h["age"] = 23 h.keys # => ["first_name", "age", "last_name"] h.values # => ["Jones", "Bob", 23] h.size # Hash, (from ruby 1.9) conserve creation order h.each { |key,value|} h.each_pair{ |key,value|} h.each_key { |key|} h.each_value{ |value|} h.merge(otherHash) {a: 1,b: 2}.merge({b:3,c:4}) # =>{:a=>1, :b=>3, :c=>4} h.delete_if { |k,v| k.size==2 && v =~ /^a/} h.delete(key)
Hash with default distinct value
# Hash.new : each time a entry is create bloc is evaluate for pre-initialisation of the entry h=Hash.new() { |h,k| h[k]=[] } # always initialize a entry with a empty array Dir.glob("*.rb").each { |filename| h[filename.size] << [ filename, File.size(filename) ] } p h {4=> [["a.rb", 4353], ["b.rb", 1109], ["y.rb", 4815]], 10=> [["allways.rb", 698], ["unicode.rb", 3776]], 7=> [["anac.rb", 5777], ["disq.rb", 144], ["game.rb", 6872], ....
list and count distinct words in a txt file
reserved=Hash[*( "which their said have will from when this were upon there then came into come that shall unto they them with thou thee king hath .... whom good took thus bring word offering place more sent like again mine about heard time". split(/ +/).map {|w| [w,0]}.flatten )] dico=File.read('bibleEng.txt'). split(/[^\w]+/). map {|word| word.downcase}. select { |word| word.upcase!=word.downcase}. tap {|l| puts "#{ l.size } words"}. inject(Hash.new { |h,k| h[k]=0}) { |h,word| h[word]+=1 if word.size>3 && !reserved[word] h } puts "#{ dico.size } real, unreserved words" dico.sort {|a,b| b[1]<=>a[1]}[0..20]. each { |k,v| puts "%20s %d" % [k,v]} 795073 words 12046 real, unreserved words lord 7971 israel 2576 people 2143 house 2026 children 1822 saith 1262 david 1139 father 1126 sons 1105 earth 987 great 962 thine 937 city 870 days 868 brought 864 called 632 evil 615 holy 611 egypt 611 hundred 590 spake 588
Hash <==> Array
{1=>1,2=>2,3=>3}.to_a # [[1, 1], [2, 2], [3, 3]] Hash[1,2,3,4] # {1=>2, 3=>4} x= ["a", "abc", "b", "bcd", "c", "cde"] Hash[*x] # {"a"=>"abc", "b"=>"bcd", "c"=>"cde"}
regular expressions
"I believe" =~ /I/ # => 0 "I believe" =~ /i/i # => 0 "I believe" =~ /lie/ # => 4 "I am human" =~ /bacon/ # => nil "I am human" !~ /bacon/ # => true /give me a ([0-9]+)/ =~ "give me a 7" # => 0 (matched)
Specs (from Rebular and Pickaxe)
[abc] A single character of: a, b or c [^abc] Any single character except: a, b, or c [a-z] Any single character [a-zA-Z] Any single character in the range a-z or A-Z ^ Start of line $ End of line \A Start of string \z End of string . Any single character \s Any whitespace character \S Any non-whitespace character \d Any digit \D Any non-digit \w Any word character (a..zA..Z0..9_) \W Any non-word character \b Any word boundary word ,word!.word:other ^--^ ^--^ ^--^ ^---^ (...) Capture everything enclosed, can be restitute by \1 \2.. : '<div class="ddd">'.gsub(/class=(")([^"]+)(")/,'class =\'\2__\'') => "<div class ='ddd__'>" (a|b) a or b (ab)|(cd) ab or cd a? Zero or one of a a* Zero or more of a a+ One or more of a a{ 3 } Exactly 3 of a a{3,} 3 or more of a a{3,6} Between 3 and 6 of a
Lookhead
(?=re) Matches re at this point, but does not consume (?:re) Makes re into a group without generating backreferences (?=re) Matches re at this point, but does not consume it : 'positive lookahead' (?!re) Matches if re does not match at this point.Does not consume the match 'negative lookahead' (?>re) match without backtrack
Options /xxx/options
Options: i case insensitive # "abc" /ABC/i ==> 0 (match) m make dot match newlines x ignore whitespace in regex o perform #{...} substitutions only once
Extraction
"aa213bb4234cc244" =~ /(a*)\d*(b*)\d*(c*)/ && ($1+' '+$2+' '+$3) => "aa bb cc" "aa213bb4234cc244".scan(/[a-z]+/) => ["aa", "bb", "cc"] See here: Rubular
replace
"abc".gsub(/.b./,"b") # "b" "axc".gsub(/a(.)c/,"'\1'") # 'x' "abc".gsub('b',"x") # axc
Execute a system command and transform output
puts `tasklist`.gsub(/\w+.exe/) { |exp| exp.upcase} Nom de l'image PID Nom de la sessio Numéro de s Utilisation ========================= ======== ================ =========== ============ System Idle Process 0 Services 0 24 Ko System 4 Services 0 15 376 Ko SMSS.EXE 376 Services 0 1 420 Ko CSRSS.EXE 544 Services 0 6 124 Ko . . .
defines a function, a method
def a(x,y) puts "You gave me #{ x } and #{ y }" end def b(x,y) puts("You gave me #{ x } and #{ y }") end a( "apple", "banana") class A def method(a,b) end end A.new().method(1,2) # or A.new.method 1,2
Arguments
def a(a,b,c) end ; a(1,2,3) def a(a,b,*c) end ; a(1,2,3,4,5,6) # => c=[3,4,5,6] def a(a,b=nil,*c) p [a,b,c] ; end ; a(1) # [1,nil,[]] def a(options) p options ; end ; a(x: 1,y: 2) # {:x=>1, :y=>2} def a(opt) p [opt,yield] end ; a(x: 11) { p 22 } # 22 # [{:x=>11}, 22]
BLOCK
block parameter/yield
def evaluate() print "Evaluate give: " yield #!!!! here, the bloc is executed puts "done" end evaluate { p 'CouCou' } Evaluate give: "CouCou" done # test if a bloc is in parameter list : block_given? def transform_and_add(*args) args=args.map { |a| yield(a)} if block_given? args.inject(0) { |a,v| a+v } end transform_and_add(2,3,4) { |i| i**3}
explicit block parameter/explicit call
def e1(&blk) print "e1:" ; &blk.call(1) end def e2(&blk) print "e2:" ; &blk.call(2) end def evaluate(&bloc) if true e1(&bloc) else e2(&bloc) end evaluate { p 'CouCou' }
block object / block call
def ifthenelse(options) if options[:condition].call options[:then_code].call else options[:else_code].call end end ifthenelse({ condition: proc { (Time.now.to_i % 2) == 0}, then_code: proc { p "Ok"}, else_code: proc { p "NOk"} })
conditionally with an if (or unless) statement
simple if
if false # this won't happen elsif nil # this won't either else # code here will run though end unless false # this won't happen else # code here will run though end
postfix if/unless (a la perl)
p 'x' if true p 'y' unless true def div(a,b) (p 'error';return(9999999)) if b.abs<0.0000000001 a/b end
case statement
x = "dog" case x when "fish" # this won't happen when "dog", "cat", "monkey" # this will run else # the else is an optional catch-all end # case X when Y <===> if Y === X then
if String === var code1 elsif (1..10) === var code2 elsif "eee"==var code3 else code4 end case var when Sting code1 when 1..10 code2 when "eee" code3 else code4 end
Looping
while statement
while true ... end do ... while true
until statement
until false .. end do ... until true
loop
loop { p Time.now; sleep 1} loop { break unless ... # while form next if ... # === continue .... break if ... # until form } 100.times { |i| } 1.upto(10) { |i| } 10.downto(1) { |i| } (1..100).step(2) { |i| }
Thread
require 'thread' th=Thread.new { .... } th=Thread.new(a,b,c) { |a1,b1,c1| ... } ... th.alive?() # test th.join() # wait thread is finish ths=Dir.glob("*").map { |d| Thread.new(d) {|d1| trait(d1) if Dir.exists?(d1) }.compact ths.each {|th| th.join} # !!!!! WARNING Array [] and Hash {} are not thread safe !!!!! # use Queue in place of Array if the arrays shared between thread # bug are not visible with MRI (Giant lock), # by are with IronRuby/JRuby use Monitor (mutex optimized) require 'thread' require 'monitor' mon=Monitor.new h={} cpt=0 10.times do Thread.new do 10.times do mon.synchronize { h[cpt]=Time.now ; cpt+=1} end end end
Catch Exceptions
# with exception type selector begin ... rescue SocketException => e .. rescue IOError => e .. else .. (anonymous rescue) : code if not selected exception : Exception object is $! ensure .. code always executed (==finally) end
Exception and StandardError
# All exception derives from Exception Class # Main son of Exception is StandardError Exception: NoMemoryError ScriptError: LoadError (gem) SyntaxError SignalException fatal SystemExit StandardError: SystemStackError LocalJumpError IOError RegexpError ZeroDivisionError ThreadError SystemCallError SecurityError RuntimeError NameError RangeError IndexError ArgumentError TypeError # anonymous rescue catch only StandardError class and sons

" A rescue clause without an explicit Exception class will rescue all StandardErrors (and only those)."

begin begin eval(" def 23234() end") rescue puts "Syntaxe error catched ?" end rescue Exception => e puts "Syntaxe error catched by a 'rescue Exception => e' : Yes!" end # give: Syntaxe error catched by a 'rescue Exception => e' : Yes!
postfixe form
f.close rescue nil (s.close if s ) rescue nil a=(f.gets rescue nil) || (g.gets rescue "") log.text=log.text+txt rescue puts("ECHO " + txt.to_s) # Syntaxe Error are not catches : eval(" def 23234() end") rescue nil # not work !!! gem 'unknown_gem" rescue gem 'known_gem' # not work !!!
generate a exception
raise "comment" class MyException < Exception ; end raise MyException,"comment"
Thread and exception
# by default, untrapped Exception in thread kill # the thread, and say nothing.. # say to ruby to kill process, # with explanation, if uncatched exception occurs : Thread.abort_on_exception=true or, only for that thread: Thread.current.abort_on_exception=true # pretty print an exception trapped : Thread.new { loop { begin .... rescue puts "#{ $! } : \n #{ $!.backtrace[0..10].join("\n ") }" end } }
def parallele(nb,*args) $queue_parallele=Queue.new unless defined?($queue_parallele) (sleep 0.1 while $queue_parallele.size>([2,nb-3].max)) while $queue_parallele.size> nb $queue_parallele << 1 Thread.new(args) do |a| begin yield(*a) rescue puts $!.to_s ensure $queue_parallele.pop end end end def wait_end_paralle return unless defined?($queue_parallele) sleep 0.1 while $queue_parallele.size>0 end hosts.each {|host| parallele(10,host) { |h| ssh(h,commande) } }
File / Directory access
File('x.rb').size # size un byte File('x.rb').mtime # date (seconds from epoch) of last modification File('x.rb').exists? # existence , file or link or fifi or dir... Dir.exists?('s') # directory existence File.basename(filename) File.extname(fileneme) # sss/sssss/s/ddddddddddd.rrr filr.dirname(filename ^dirname ^basename ^extname def rec(dir) Dir.glob("#{ dir }/*").each do |name| (Dir.exists?(name)) ? rec(name) : puts("%-80s | %d" % [name,File.size(name)/1024]) end end ./anar.rb | 1 ./ang/ang-9.13.0.gem | 227 ./ang/bin/ang | 0 ./ang/CHANGELOG.txt | 2 . . . . . .
File read
content= IO.read(filename) content=File.read(filename,"utf-8") lines=File.read(filename).split(/\r?\n/) open(filename,"rb") { |f| content=f.read } open(filename,"rb") { |f| case f.gets when /a/ then p 1 ; when /b/ then p 2 ; when nil then break; end } IO.foreach("filename") {|x| puts "Line : ", x.chomp }
File write
open(filename,"w") { |f| f.write(content) } open(filename,"a") { |f| f.write("end-of-file\n") }
Execute process, get output
system("ls","-l") # return exit status, output is loose cmd="ls -l".split(/\s+/) cmd << "a filename with blanc chars.txt" system(*cmd)
get output
`ls -l`.split(/\r?\n/).grep(/.rb/).each {|line| puts line} IO.popen "ls -l" do |io| io.each {|line| puts line } end # if sub-process do stdout buffereing : require 'pty' PTY.spawn "some-command" do |r,w,p| loop { puts r.gets } end
get input for sending data to subprocess
require 'open3' include Open3 popen3('ruby', '-e', 'str=gets;puts("out="+str.upcase)') do |stdin, stdout, stderr| stdin.write("hello from parent") stdin.close_write stdout.read.split("\n").each do |line| puts "[parent] stdout: #{ line }" end end # => [parent] stdout: out=HELLO FROM PARENT
Socket
require 'socket' BasicSocket.do_not_reverse_lookup = true # !!!
Socket TCP client
begin socket = TCPSocket.new(host,port) socket.sync=true rescue puts "not connected : " + $!.to_s exit end th=Thread.new(socket) {|s| b="" until s.closed? b=s.recv(64*1024) break if ! b || b.size==0 print "!!!!!!!!!! "+ b + "!!!!!!" s.puts "Ok#{ b.size }" end puts "exited" } th.join s4=Time.now s.close rescue nil
Socket TCP server
class EchoServer < GServer def serve( io ) line="a" until io.closed? || line.length==0 line = io.recv(1000) puts "Received: " + line.inspect end end end echo_server = EchoServer.new( 9999 ,"0.0.0.0",20 ) echo_server.start # Start server echo_server.join # Prevent exiting while server is running
Socket UDP Send and Receive
client = UDPSocket.new client.bind(host, port) puts "Serve UDP on %s:%d" % [host,port] loop do data, addr = client.recvfrom(1024) puts "From addr: '%s', msg: '%s'" % [addr.join(' / '), data] client.send(data,0,addr.last,addr[1]) end
Socket Multicast
require 'socket' require 'timeout' require 'enumerator' require 'ipaddr' Thread.abort_on_exception=true BasicSocket.do_not_reverse_lookup = true class MulticastEcho def initialize(ip,port) @ip,@port=[ip,port] "";@addr = IPAddr.new(ip).hton + IPAddr.new("0.0.0.0").hton;"" @bip=IPAddr.new(ip).hton version=0 puts "Mutlticast on group " + ip + ":" + port.to_s + " ..." create_socket Thread.new { sender } Thread.new { loop { receiver } } end # create a socket binded to Multicast address, for receive datagramme def create_multicast_socket "" @sock = UDPSocket.new @sock.bind(Socket::INADDR_ANY, @port) if File.exists?("c:/windows") @sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, @addr) # LOOP: \001 for receive self message, \000 else @sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_LOOP, "\001") if defined?(Socket::IP_MULTICAST_LOOP) @sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_TTL, 3) if defined?(Socket::IP_MULTICAST_TTL) @sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 3) if defined?(Socket::IP_TTL) @sock.bind(Socket::INADDR_ANY, @port) unless File.exists?("c:/windows") "" end def receiver puts "waiting on #{ @sock }..." "";mess, adr = @sock.recvmsg(1024);"" puts "Received : #{ mess } from #{ adr.inspect }" if mess rescue Exception => e puts "Receiver Exception "+ $!.to_s @sock.close rescue nil exit(1) end # create a UDPsocket not binded, and use it to send datagramme to a Multicast group def sender socket = UDPSocket.open ; socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, [1].pack('i')) sleep(0.3) ; loop { mess=Time.now.to_s puts "sending mc " + mess ""; so.send(mess, 0, @ip , @port) ; "" sleep 3 } rescue Exception => e puts "Sender Exception "+ $!.to_s @sock.close rescue nil puts "Sender Exception "+ $!.to_s end end MulticastEcho.new("230.1.1.1",9994) sleep
Serialisation
data=[{a:1,b:2,c:[1,2,3]},"eeeeeee",1.1] File.open("data.conf","wb") { |f| Marshal.dump(data,f) } File.open("data.conf","rb") { |f| data= Marshal.load(f) } # do a real duplication data_cloned=Marshal.load(Marshal.dump(data)) # dup and clone are shallow cloning a=[1,[2,4]] b=a.dup b[1][0]=22 p a # [1, [22, 4]] b[0]=33 p a # [1, [22, 4]] ; unchanged !
Class
class Myclass def initialize(a,b) @a=[a,b] end def doit() p @a end end a=Myclass.new(1,2) a.doit() # [1,2] class Myclass2 < Myclass def initialize(a,b) @a=[a,b] end def doit2() p @a*2 end end a=Myclass2.new(1,2) a.doit() # [1,2] (inherited) a.doit2() # [1,2,1,2]
Module
module Mymodule def minit(x) @b=x+[111] end def doit() p @a end def doit_b() p @b end end class Myclass include Mymodule def initialize(a,b) @a=[a,b] ; minit(@a) end end a=Myclass.new(1,2) a.doit() # [1,2] a.doit_b() # [1,2,111]
Class : singleton, statics...
class Myclass def initialize(a,b) @a=[a,b] ; end class << self # define static resources X=222 def x() X end def a() p 'c a'; end def b() p 'c b'; self.a() end end def a() p 'i a' ; self.class.a() end end >> a=Myclass.new(1,2) >> a.a # "i a" ; "c a" >> Myclass.a # "c a" >> Myclass.b # "c b" ; "c a" >> Myclass.x # 222
Introspection 1
Introspection 1 :level class
class A def intialize() @a=1;@b=2 end def amethod() p instance_variables end end a= A.new p a.methods [:intialize, :amethod, :__, :a, :po, :poc, :pretty_print, :pretty_print_cycle, :pretty_print_instance_variables, :pretty_print_inspect, :nil?, :===, :=~, :!~, :eql?, :hash, <=>, :class, :singleton_class, :clone, :dup, .......... :object_id, :to_enum, :enum_for, :gem_loaded, :pretty_inspect, :ri, :==, :equal?, :!, :!=, ....] p (a.methods-Object.methods) # [:intialize, :amethod] p a.class # A p a.instance_variables # [] a.amethod
Introspection 2 : level Instructions
# AST from string code require 'ripper' p Ripper.sexp("a=1;b=2;a+b") # Ruby source from ruby analysed code ; define .to_source require 'sourcify' def bloc_source(a,&blk) p blk.to_source end bloc_source(1) # "proc { a = 2 }"
Meta programming
Meta x: x of x Metaphysics: Physics of physics Meta language : language which permit to describe language ( BNF ) Meta Programming: Instructions which generate instructions Program which generate Program which resolve problems...
From a Hash, create a Class which each method gives access of members of the hash
Definition Usage

def hash_to_class(h)
  Class.new() {               # build a class
    def initialize(x=nil)     # define object constructor
      @values= {}
      self.def_init()
      @values= @values.merge(x) if x  
    end
    define_method(:def_init) { 
      h.each  { |key,value|  
        @values[key]=value   
    } }
    define_method(:keys) {  h.keys }   # define getters/setters 
    h.each do |key,value|
      define_method(key) { @values[key] }
      define_method("#{ key }=") { |x| @values[key]=x }   
    end
  }
end

C=hash_to_class({a: 11, b: 22})
o=C.new
p o.keys        # [:a,:b]
p o.a           # 11
p o.b           # 22

o=C.new o.a=1 o.b=2 p o.a # 1 p o.b # 2
f=C.new({a:111,c:999}) p [f.a,f.b] # [111,22] f.c # Error: Exception
DSL with Ruby Bloc
Define a micro-test framework
DSL DefinitionDSL Usage

class Test
  class << self
    def program(&blk) 
      Test.new.instance_eval(&blk) 
    end
  end
  
  def ressource(method,http,descr)
   @descr=descr
   puts "Starting '#{ @descr }' ..."
   yield
   puts "done, OK"
  end
  
  def test(comment)
   @comment=comment
   begin
   yield
    puts "  #{ @comment } ok" 
   rescue
    puts "Error on #{ comment }: #{ $! }"
   end
  end
  
  def body() @comment end
  
  def assert(&blk)
    pp=yield  
    raise("fault ") unless pp
  end
end

Test.program do
  ressource "GET","http://localhost:9990/test","autotest" do
    test("testA with error") do
      assert { body.size>0 }
      assert { body.size>100 }
      assert { body.size>0 }
      assert { body.size<0 }
    end
    test("testB1 without error") do
      assert { body.size>0 }
    end
    test("testC with error") do
      assert { body.size<0 }
    end
  end
end

Starting 'autotest' ... Error on testA with error: fault testB1 without error ok Error on testC with error: fault done, OK See jsontester gem for the complete DSL.
Ruby and Functional Programming
Principles
(From http://code.google.com/p/tokland/wiki/RubyFunctionalProgramming) Critics ======== "I think the lack of reusability comes in object-oriented languages, not in functional languages. Because the problem with object-oriented Languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle." Principles ========= "Don't update variables : smake a new one" => So don't use bang methodes : methodes!() => less variable mean less state mean less bug & less concurrent access issue "Blocks as higher order functions" => name the block, give block to treatment, as data "Pure functional languages, having no implicit state, use recursion a lot." => Recursion minimize variable rewriting, => Warning, Ruby do not optimize tail recursion "Lazy evaluation delays the evaluation of the expression until it's needed, as opposed to eager evaluation, where expressions are calculated when a variable is assigned, a function called" => Lazy evaluation is new in ruby
Closure
a=1 ; 1.times { a=2 ;p a} ; p a # 2 ; 2 x=1 ; 1.times { a=2 ;p a} ; p a # 2 ; Exception unknown variable 'a' # A bloc in ruby is exactly the same as bloc in C : variables scoping # follow same rules. # but blocs are closures : we can manipulate them, (almost) as data. so 'times' method can be redefined by : class Numeric def mtimes() c=0 while c < self yield(c) c+=1 end end end # Remember : 'while' (as' if', 'case' ...) is not a closure, variable defined # will exist outside looping
Array 2
(from http://www.pracht.org/dokuwiki/prog/algo1/cm4 ) [2,4,6].all? { |e| (e % 2) == 0 } # => true [2,5,6].all? { |e| (e % 2) == 0 } # => false [2,5,6].any? { |e| (e % 2) == 1 } # => true [2,4,6].any? { |e| (e % 2) == 1 } # => false [2,4,6].include?(4) # => true [2,5,6].include?(4) # => false ['un','deux','trois','quatre'].each_with_index do |nom, indice| puts("le nom est '#{ nom }'") puts("pour l'indice: #{ chiffre }") end [1,2,3,4,5].each_cons(2) { |a,b| p [a,b] } # [1, 2] [2, 3] [3, 4] [4, 5] [1,2,3,4,5].each_slice(2) { |a,b| p [a,b] } # [1, 2] [3, 4] [5, nil] [1,2,3].zip([101,102,103]) { |a,b| p [a,b] } # [1, 101] [2, 102] [3, 103] %w.zip(%w) { |a,b| puts a+","+b } Romeo,Juliette Bonie,Clyde Dupond,Dupont [2,4,6].inject(0) {|somme, n| somme + n } # => 12 [2,4,6].inject(1) {|produit, n| produit * n } # => 48 (2..6).to_a # => [2, 3, 4, 5, 6] [2,4,6].collect { |e| e * 2 } # => [4, 8, 12] [2,4,6].map { |e| e * 2 } # => [4, 8, 12] [2,5,6].find_all { |e| (e % 2) == 0 } # => [2, 6] [2,5,6].reject { |e| (e % 2) == 0 } # => [5] ["Pierre", "Paul", "Jacques", "Alain"].grep(/a/) # => ["Paul", "Jacques", "Alain"] (0..14).partition { |e| (e % 3) == 0 }; "" # => [ [0, 3, 6, 9, 12], [1, 2, 4, 5, 7, 8, 10, 11, 13, 14] ] w.group_by {|lang| lang[0] } # => { "j"=>["java"], "p"=>["perl", "python"], "r"=>["ruby"]} [2,4,6].max # => 6 [2,4,6].min # => 2 [2,16,6].minmax # => [2,16] ["Pi", "Pa", "Ja", "Al"].sort { |a, b| b <=> a }# => ["Pi", "Pa", "Ja", "Al"] ["Pi", "Pa", "Ja", "Al"].sort { |a, b| a <=> b }# => ["Al", "Ja", "Pa", "Pi"] ["Pi", "Pa", "Ja", "Al"].sort_by { |e| e.reverse } # => ["Ja", "Pa", "Pi", "Al"]
Examples from onliner classical code
print methods which match a regexp of an object
puts (self.methods - Object.methods) .sort .select {|s| s=~filter} .map { |e| "%-15s" % [e]}.join("\n") paths = ARGV.select { |f| f !~ /^-\\w+/ && File.exists?(f) && File.directory?(f) }.map {|d| d.gsub(/(\\/)|(\\\\)$/,\"\")}.uniq
print big line of ruby code in current directory
Dir.glob("*.rb").each do |filename| File.read(filename) .split(/r?\n/) .select {|l| l.size>100 && l !~ /\\n/ && l=~/(map)|(sort)/} .each {|l|puts "%-20s | %s" % [filename,l]} end
rotation of a polyline
def rotation(a=180,x0=0,y0=0) @lpoints.map { |(x,y)| [ x0+(x-x0)*Math.cos(a)+(y-y0)*Math.sin(a), y0-(x-x0)*Math.sin(a)+(y-y0)*Math.cos(a) ] } end
Source code sructure
includes
require './toto.rb' # load a local file in current directory, old style require_relative 'aa/x.rb' # pretty form for load local file require 'toto' # load a package (gem) name 'toto', # gems are in one directory of $: $: << "." # local dir become a gem directory: require 'titi' # file titi.rb is loading, because . is in load path!
context : where is current instructions?
a=1 # instruction, in context of object 'main' p self # main p self.class # Object def a() ; end # append a method to Object : # methode is known by everybody
class scope
class A # create a class in Oject namespace : # class A is known by everybody end class B class BA # BA is known as ::B::BA end BA.new # BA known in context of B end B.new ::B::BA.new # BA known only in B spacename
Here docs
Thread.new { p DATA.read # DATA is a stream of the DATA section of current source p (DATA.read rescue "nop") # bug: DATA can be read only one time } # Here doc : string literal can be multilines # "" form html=<<EEND <html><head><title>#{ title }</head> <body><h1>#{ title }</h1>#{ content }</body> EEND # '' from : #{} are not evaluated html=<<'EEND' <html><head><title>#{ title }</head> <body><h1>#{ title }</h1>#{ content }</body> EEND html.gsub(/title/,"Title") a=calledproc(<<EEND1,<<EEND2) content of first parameter #{ data } EEND1 content of second parameter #{ data } EEND2
source here doc
# end source section, starting DATA section, # each source can have a data sections : # puts html template, css declaraions, output instructions / Usage... __END__ blabla boubou
the end...