首页 > 代码库 > 【ruby】ruby基础知识

【ruby】ruby基础知识

Install Ruby(安装)

For windows you can download Ruby from http://rubyforge.org/frs/?group_id=167 

for Linux tryhttp://www.rpmfind.net.

 

Our first program(从此开始)

Enter the following into the file, "test.rb".

?
1
puts "Howdy!"

At the C: prompt enter,

?
1
C:>ruby test.rb

This produces:

?
1
Howdy!

OK, daylight‘s burning, let‘s move on.

Output in Ruby

"puts" 写到屏幕上,并带有个换行,writes to the screen with a carriage return at the end. 
"print" 写到屏幕,但没有换行,does the same thing without the carriage return. 
"printf" 格式化输出到屏幕,formats variables like in C and Java 5.

?
1
2
3
4
5
6
7
8
puts "puts works"
puts " with line breaks."
 
print "print works"
print " with no line breaks."
 
printf("\n\nprintf formats numbers like %7.2f, and
strings like %s.",3.14156,"me")

This produces:

?
1
2
3
4
5
6
puts works
 with line breaks.
print works with no line breaks.
 
printf formats numbers like    3.14, and strings like
me.

Reading from the Console(接受控制台输入)

Use "gets"

?
1
2
3
puts "What is your name?"
$name = STDIN.gets
puts "Hi "+$name

  

Functions(函数)

  1. Our first Ruby function最开始的函数

    “def”用于定义函数,以“end”结束函数定义。

    ?
    1
    2
    3
    4
    def welcome(name)
       puts "howdy #{name}"   # 必须包含在双引号内, #{ } 用来包含变量
    end
    welcome("nana")           # traditional parens

    This Produces:

    howdy nana
    
  2. Parentheses are optional(括号可省略)
    ?
    1
    2
    3
    def welcome(name)
       puts "howdy #{name}"   # inside double quotes, #{ } will evaluate the variable
    end
    welcome "visitor" #look, ma, no parentheses

    This Produces:

    howdy visitor
    

    如果没有参数,用不用括号都一样

    ?
    1
    2
    "hello".upcase() => "HELLO"
    "hello".upcase => "HELLO"
  3. How to return values from a function(函数返回值)

    可以使用return语句

    ?
    1
    2
    3
    4
    5
    def multiply(a,b)
      product = a * b
      return product
    end
    puts multiply(2,3)   =>6

    函数定义内部最后一个表达式,默认做为返回值返回。

    ?
    1
    2
    3
    4
    def mult(a,b)
      product = a * b
    end
    puts mult(2,3)

    所以上面的product变量可以去掉了

    ?
    1
    2
    3
    4
    def mult(a,b)
       a * b
    end
    puts mult(3,3)   =>9
  4. Optional argument values(默认参数)

    Ruby lets you assign values to arguments which may, or may not be supplied as shown below:

    ?
    1
    2
    3
    4
    5
    6
    7
    def test(a=1,b=2,c=a+b)
      puts "#{a},#{b},#{c}"
    end
    test        =>  1,2,3
    test 5      =>  5,2,7
    test 4, 6   =>  4,6,10
    test 3, 4, 6   =>  3,4,6
  5. Extra arguments额外的参数

    Extra arguments是前面加一个星号的,并且要放在所有参数的最后 

    ?
    1
    2
    3
    4
    5
    def test(a=1,b=2,*c)
      puts "#{a},#{b}"
      c.each{|x| print " #{x}, "}  # We will learn about "each" very soon.  I promise.
    end
    test 3, 6, 9, 12, 15, 18

    This produces:

    3,6
     9,  12,  15,  18,
  6. Multiple return values(多个返回值)
    ?
    1
    2
    3
    4
    5
    6
    7
    def getCostAndMpg
        cost = 30000  # some fancy db calls go here
        mpg = 30
        return cost,mpg
    end
    AltimaCost, AltimaMpg = getCostAndMpg
    puts "AltimaCost = #{AltimaCost}, AltimaMpg = #{AltimaMpg}"

    Produces:

    AltimaCost = 30000, AltimaMpg = 30

Open Classes(开放的类)

You can add methods to existing library classes.可以给已经存在的库中的类,增加新的方法。

 

?
1
if(mystring != null && mystring != "")

给String类增加一个新的方法

?
1
2
3
4
5
6
7
class String
    def NullOrEmpty?
    (self == nil || self == "")
    end
end
puts "test".NullOrEmpty?
puts "".NullOrEmpty?

Is this way cool? Yes. Is this very dangerous? Yes. Remember, Ruby is a chainsaw.

Variable naming(变量命名规则)

  1. Global variables start with ‘$‘ 全局变量用$开头
  2. Class variables start with ‘@@‘ 类变量用@@开头
  3. Instance variables start with ‘@‘ 实例变量用@开头
  4. Local variables, method names, and method parameters start with a lower case letter 本地变量,方法名,方法参数用小写字母开头
  5. Class names, module names and constants start with an uppercase letter 类名,模块名,常量用大写字母开头
  6. Variables names are composed of letters, numbers and underscores 变量名由小写字母、数字和下划线组成
  7. Method names may end with "?", "!", or "=". Methods ending with a "?" imply a boolean operation (eg, "instance_of?"). Methods ending with "!" imply something dangerous, like strings being modified in place (eg, "upcase!") 方法名可以用?(表示执行一个布尔操作)、!(表示执行一些危险的操作,可能会修改传入的参数)、=结尾;

Interesting tidbits about Ruby有趣的花絮

  1. ‘#‘ is the line comment character, all characters after this are ignored. Confusingly ‘#‘ can appear within quotes with a different meaning. #注释一行
  2. No semi-colons are needed to end lines, but may be used to separate statements on the same line
  3. A backslash (\) at the end of a line is used for continuation
  4. Indenting is not significant, unlike python
  5. Types of variables do not need to be declared
  6. Lines between =begin and =end are ignored
  7. Lines following "__END__" on its own line with no white space, are ignored
  8. A tiny demonstration of these:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # sample program showing special characters like comments
    # I‘m a comment line
    a = 1  #notice no semicolon and no type declaration
    b = 2; c = 3 #notice two statements on one line
    name = "Abraham \
    Lincoln"   # a line continued by trailing \
    puts "#{name}"
    =begin
    I‘m ignored.
    So am I.
    =end
    puts "goodbye"
    __END__
    1
    2
    3
    4
    Abraham Lincoln
    goodbye

Variable Types

 

In Ruby, variables don‘t have a specific type associated with them.

All variables are objects, so we only play with pointers to those objects, and those pointers are type agnostic.

 

  1. bat = "Louisville slugger"
    bat = 1.23
  2. Quotes(引号)

    Like in Perl, single quotes and double quotes have different meanings.

    Double quotes means "please interpret special characters in this string". Things like backslash n (‘\n‘) are converted to their typical values. The #{name} construct is converted to its value.

    With single quotes, no special characters are interpreted.

    Examples:

    ?
    1
    2
    3
    4
    name="Mike"
    puts "hi #{name}" =>hi Mike
    puts "hi\n #{name}" => hi (carriage return)Mike
    puts ‘hi\n #{name}‘ => hi\n #{name}    (no substitutions are made since using single quote)

    The braces are optional for global and instance variables

    ?
    1
    2
    $myname="Ishmael"
    puts "hi #$myname" =>hi Ishmael
  3. Objects

    A great thing about Ruby is that numbers and strings are real objects.

    ?
    1
    1.5.floor()   => "1"

    This lets us do some cool things. Instead of

    if( x > 7 && x < 12 ) { ... }

    We can write

    if x.between?(7,12) do ...
  4. Big Numbers

    Ruby automatically increases the precision of variables

    ?
    1
    2
    3
    for i in 1..1000
      puts "2 ** #{i} = #{2**i}"
    end

    Produces:

    2 ** 1 = 2
    2 ** 2 = 4
    2 ** 3 = 8
    2 ** 4 = 16
    2 ** 5 = 32
    2 ** 6 = 64
    2 ** 7 = 128
    2 ** 8 = 256
    2 ** 9 = 512
    2 ** 10 = 1024
    ...
    2 ** 1000 = 107150860718626732094842504906000181056140481170553360744375038837035105112493612249319837881569585812759467291755314682518714528569231404
    359845775746985748039345677748242309854210746050623711418779541821530464749835819412673987675591655439460770629145711964776865421676604298316526243868
    37205668069376

    Ruby will increase the precision of the number, or decrease it as needed:

    ?
    1
    2
    3
    4
    5
    6
    x = 1000000
    puts "#{x}  "+x.class.to_s    => 1000000  Fixnum
    x = x * x
    puts "#{x}  "+x.class.to_s    => 1000000000000  Bignum
    x = x / 1000000
    puts "#{x}  "+x.class.to_s    => 1000000  Fixnum

Parallel Assignment(交换参数)

You can swap the values in variables without the use of a temp variable. Remember your first programming class: Swap the values in "i" and "j"? You had to use a "t" variable to store one of the values first. Not needed in Ruby.

?
1
2
3
4
5
i = 0
j = 1
puts "i = #{i}, j=#{j}"
i,j = j,i
puts "i = #{i}, j=#{j}"

Produces:

i = 0, j=1
i = 1, j=0

Collections

Arrays

  1. An array of known objects can be created by enclosing them in square brackets.
    ?
    1
    2
    nums = [1, 2.0, "three"]
    puts nums[2]  => three

    Ruby arrays, like all right-thinking collections, are zero based.

  2. You can use negative indexes to start from the end of the array

    ?
    1
    2
    nums = [1, 2.0, "three", "four"]
    puts nums[-1]   => four

    Using "-1" is so much more concise than "nums[nums.length()-1]".

  3. You can even use the handy "first" and "last" methods.
    ?
    1
    2
    [1,2,3].last  => 3
    [1,2,3].first => 1
  4. length

    To get the count, or size, of an array, use the "length" method.

    ?
    1
    2
    mystuff = ["tivo","nokia", "ipaq"# make a string array
    puts mystuff.length  => 3
  5. %w shortcut

    Since many arrays are composed of single words and all those commas and quote marks are troublesome, Ruby provides a handy shortcut, %w:

    ?
    1
    mystuff = %w{tivo nokia ipaq}  # make a string array
  6. inspect

    To look at contents of an object use the "inspect" method. Even more convenient is to use "p" as a shorthand for "puts obj.inspect"

    ?
    1
    2
    3
    4
    myarray = [1,2,5,7,11]
    puts myarray
    puts myarray.inspect
    p myarray

    Produces:

    1
    2
    5
    7
    11
    [1, 2, 5, 7, 11]
    [1, 2, 5, 7, 11]
  7. Arrays can act like queues and sets
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    # & is the intersection operator
    puts [1,2,3] & [3,4,5#  prints 3
     
    # + is the addition operator
    puts [1,2,3]+ [3,4,5#  prints 1,2,3,3,4,5
     
    # - removes items from the first array that appear in the second
    puts [1,2,3] - [3,4,5#  prints 1,2
     
    # pop returns the last element and removes it from the array
    alpha = ["a","b","c","d","e","f"]
    puts "pop="+alpha.pop   # pop=f
    puts alpha.inspect      # ["a", "b", "c", "d", "e"]
     
    # push appends elements to the end of an array
    alpha = ["a","b","c"]
    alpha.push("x","y","z")
    puts alpha.inspect      # ["a", "b", "c", "x", "y", "z"]
     
    # shift returns the first element and removes it from the array
    alpha = ["a","b","c","d","e","f"]
    puts "shift="+alpha.shift   # shift=a
    puts alpha.inspect      # ["b", "c", "d", "e", "f"]
     
    # unshift appends elements to the beginning of an array
    alpha = ["a","b","c"]
    alpha.unshift("x","y","z")
    puts alpha.inspect      # ["x", "y", "z", "a", "b", "c"]

Hashes

 

This type of collection is also called a dictionary or an associative array.

 

  1. Simple hash of cars and their makers
    ?
    1
    2
    3
    4
    5
    6
    cars = {
    ‘altima‘ => ‘nissan‘,
    ‘camry‘ => ‘toyota‘,
    ‘rx7‘ => ‘mazda‘
    }
    puts cars[‘rx7‘]   =>   mazda
  2. You can create a hash and fill it dynamically
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    dict = {}  # create a new dictionary
    dict[‘H‘] = ‘Hydrogen‘ #associate the key ‘H‘ to the value ‘Hydrogen‘
    dict[‘He‘] = ‘Helium‘
    dict[‘Li‘] = ‘Lithium‘
    p dict[‘H‘]       # prints "Hydrogen"
    p dict.length     # prints 3
    p dict.values     # prints ["Lithium", "Helium", "Hydrogen"]
    p dict.keys       # prints ["Li", "He", "H"]
    p dict            # prints {"Li"=>"Lithium", "He"=>"Helium", "H"=>"Hydrogen"}
  3. Hash[]

    You can also create Hashes with square brackets by prefixing with "Hash":

    ?
    1
    2
    toppings = Hash["pancakes","syrup","Pizza","Pepper","Cereal","Sugar"]
    puts toppings.inspect

    Produces:

    {"Pizza"=>"Pepper", "Cereal"=>"Sugar", "pancakes"=>"syrup"}
  4. each

    The "each" method is a wonderful way to iterate over the keys

    ?
    1
    2
    toppings = Hash["pancakes","syrup","Pizza","Pepper","Cereal","Sugar"]
    toppings.each{|key, value| puts "#{key} points to #{value}"}

    Produces:

    Pizza points to Pepper
    Cereal points to Sugar
    pancakes points to syrup
    
  5. select

    The "select" method populates a new array with members which meet a criteria

    ?
    1
    2
    3
    4
    salaries = Hash["bob",10.9,"larry",7.5,"jimmy",6.0,"jerry",6.5]
    salaries.inspect
    mySalaryArray = salaries.select{|name,salary| salary > 7.0}
    puts mySalaryArray.inspect #prints  [["larry", 7.5], ["bob", 10.9]]

Ranges

Ranges are composed of expr..expr or expr...expr. Two dots includes the last element, three dots excludes it.

?
1
(‘a‘..‘g‘).each{ |letter| puts letter }

Produces:

a
b
c
d
e
f
g
?
1
(1...3).each{ |num| puts num }

Produces only two numbers since "..." does not include the last element.:

1
2

Control Statements

  1. if 
    ?
    1
    2
    3
    4
    5
    6
    if 1<2
     rate = 0.28
    else
      rate = 0.5
    end
    puts rate
  2. case
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    grade = 10
    school = case grade
    when 0..5
       "elementary"
    when 6..8
       "middle school"
    when 9..12
       "high school"
    else
       "college"
    end
    puts "grade #{grade} is in #{school}"
  3. for
    ?
    1
    2
    3
    for i in 1..4
     puts "hi #{i}"
    end

    The ranges can of course have variables

    ?
    1
    2
    3
    4
    top = 6
    for i in 1..top
     puts "hi #{i}"
    end
  4. exit
    ?
    1
    2
    3
    4
    5
    lines = IO.readlines("data.txt")
    if lines.length < 100
       exit 2
    end
    puts lines.length
  5. loop

    iterates over code until a "break" or eternity ends

    ?
    1
    2
    3
    4
    5
    6
    i=0
    loop do
       break if i > 5
       puts i
       i += 1 
    end

Statement modifiers

 

These are just syntatic sugar.

 

  1. if

    The "if" clause may be placed at the end of a statement

    ?
    1
    2
    balance = -10.0
    puts "Bankrupt" if balance < 0.0
  2. unless

    "unless" is placed at the end of a statement

    ?
    1
    2
    balance = -10.0
    puts "Bankrupt" unless balance > 0.0
  3. while

    "while" may be after its block

    ?
    1
    2
    3
    4
    5
    6
    f=2
    puts f=f+2 while f < 10
    =>4
    =>6
    =>8
    =>10

Iterators

  1. while
    ?
    1
    2
    3
    4
    5
    6
    i = 0
    while i  < 5
     
      i = i+1
      puts i
    end
  2. "times"
    ?
    1
    2
    n = 10
    n.times { |i| print i}

    Produces:

    0123456789
  3. "each"
    ?
    1
    2
    animals = %w(lions tigers bears)
    animals.each{|kind| print kind}
    lionstigersbears
    
  4. "each" with ranges
    ?
    1
    (‘m‘..‘z‘).each {|ch| print ch}
    mnopqrstuvwxyz
    
  5. "upto"
    ?
    1
    2
    n=0 ; max=7
    n.upto(max) {|num| print num}
    01234567

You gotta have class.

  1. Classes

    Class definitions start with "class" and end with "end". Remember that class names start with a capital letter. Notice the syntax is "object.new" for creating an object and that the "initialize" method contains code normally found in the constructor. Here‘s a small example:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    class Person
      def initialize(fname, lname)
       @fname = fname
       @lname = lname
      end
    end
    person = Person.new("Augustus","Bondi")
    print person

    Produces:

    #<Person:0x257c020>
    

    which is true, but not helpful.

  2. The "ToString" method, to_s
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Person
      def initialize(fname, lname)
       @fname = fname
       @lname = lname
      end
      def to_s
         "Person: #@fname #@lname"
      end
    end
    person = Person.new("Augustus","Bondi")
    print person

    Produces:

    Person: Augustus Bondi
    
  3. Subclassing

    In Ruby subclassing is done with the "<" character

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Employee < Person
      def initialize(fname, lname, position)
        super(fname,lname)
        @position = position
      end
      def to_s
         super + ", #@position"
      end
    end
    employee = Employee.new("Augustus","Bondi","CFO")
    print employee

    Produces:

    Person: Augustus Bondi, CFO
    

    if we try to print the first name directly like

    print employee.fname
    
    we get the error message,
    CFOtest.rb:21: undefined method ‘fname‘

    But why is that? We‘ve printed variables a zillion times up til now and it‘s always worked. What changed? Up until now we‘ve created variables in a program without classes (actually all are variables were members of a default object that were accessable inside itself). Now we are using real classes and that brings up the point of visibility of members outside the class. We now have to specify if a variable is open to the outside, like "public", "private", "protected", "internal" in other languages.

    To grant access to read a variable we declare it after "attr_reader". attribute with the following:

    ?
    1
    attr_reader :fname, :lname
    then
    print employee.fname  =>   "Augustus"

    To allow writing to a variable use "attr_writer",

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Employee < Person
      def initialize(fname, lname, position)
        super(fname,lname)
        @position = position
      end
      def to_s
         super + ", #@position"
      end
      attr_writer :position
    end
    employee = Employee.new("Augustus","Bondi","CFO")
    puts employee
    puts employee.fname
    employee.position = "CEO"
    puts employee

Virtual Attributes

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Employee < Person
  def initialize(fname, lname, position)
    super(fname,lname)
    @position = position
  end
  def to_s
     super + ", #@position"
  end
  attr_writer :position
  def etype
     if @position == "CEO" || @position == "CFO"
         "executive"
     else
         "staff"
     end
  end
end
employee = Employee.new("Augustus","Bondi","CFO")
employee.position = "CEO"
puts employee.etype    =>  executive
employee.position = "Engineer"
puts employee.etype    =>  staff

  

Regular Expressions

 

Strings can be compared to a regular expression with "=~". Regular expressions are surrounded by "//" or "%r{}". Anything but the two special values, "false" and "nil" are considered true.

Expression Result Description
/a/ =~ "All Gaul is divided into three parts" 5 finds the first "a" at position 5
%r{a} =~ "All Gaul is divided into three parts" 5 same thing with alternate syntax
/ree/ =~ "All Gaul is divided into three parts" 27 finds "ree" at position 27
/^a/ =~ "All Gaul is divided into three parts" nil "^" implies at the beginning of a line. nil is false.
/^A/ =~ "All Gaul is divided into three parts" 0 case-sensitive, remember that "0" is true
/s$/ =~ "All Gaul is divided into three parts" 35 "$" implies at the end of a line
/p.r/ =~ "All Gaul is divided into three parts" 31 "." matches any character

 

Blocks

 

And now to one of the coolest things about Ruby - blocks. Blocks are nameless chunks of code that may be passed as an argument to a function.

 

Simple Example

 

?
1
2
3
4
5
6
def whereisit
   yield
   yield
   yield
end
whereisit {puts "where is the money?"}

Produces:

where is the money?
where is the money?
where is the money?

In the above example ‘{puts "where is the money?"}‘ is called a block. That chunk of code is passed to the method "whereisit" and executed each time the "yield" statement is executed. You can think of the "yield" being replaced by the block of code.

 

Blocks can take arguments

Here the method "cubes" takes the max value.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def cubes(max)
   i=1
   while i < max
      yield i**3
      i += 1
   end
end
cubes(8) { |x| print x, ", "}   => 1, 8, 27, 64, 125, 216, 343,
sum = 0
cubes(8) { |y| sum += y}
print "\nsum=",sum              => sum=784
product = 1
cubes(8) { |z| product *= z}
print "\nproduct=",product      => product=128024064000

Think of the "yield i**3" in the function cubes as being replaced with the block, ‘|x| print x, ", "‘. The value following the "yield" is passed as the value "x" to the block.

Multiple arguments may be passed to blocks.

?
1
2
3
4
5
6
7
def employee(empId)
      #next 2 lines simulated from calling a database on the empId
      lastname = "Croton"
      firstname = "Milo"
      yield lastname,firstname  #multiple arguments sent to block
end
employee(4) { |last,first| print "employee ",": ",first, " ",last}

Produces:

employee : Milo Croton

Local variables can be shared with a block

Even though rate is a local variable, it is used inside the block.

?
1
2
3
4
5
6
def tip(mealCost)
   yield mealCost
end
rate = 0.15
mytip = tip(10.0) { |cost| cost * rate }
print "tip should be: ",mytip

Produces:

tip should be: 1.5

Blocks are built in to many objects in ruby

each

iterates through each item of a collection

?
1
[1,2,3,4].each{|x| print x**2," "}

Produces:

1 4 9 16

detect

returns the first item matching a logical expression

?
1
2
3
numbers = [1,3,5,8,10,14]
firstDoubleDigit = numbers.detect {|x| x > 9}
print firstDoubleDigit  => 10

select

returns all items matching a logical expression

?
1
2
3
numbers = [1,2,3,4,5,6,7];
evens = numbers.select{|x| x % 2 == 0}
p evens =>   [2, 4, 6]

collect

returns an array created by doing the operation on each element.

?
1
2
[1,2,3,4].collect{|x| x**3}   => [1, 8, 27, 64]
["the","quick","brown", "lox"].collect{|x| x.upcase} => ["THE", "QUICK", "BROWN", "LOX"]

inject

"inject" is the "fold" or "reducer" function in Ruby. "inject" loops over an enumerable and performs an operation on each object and returns a single value.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
primes = [1,3,5,7,11,13];
#using "inject" to sum.  We pass in "0" as the initial value
sum = primes.inject(0){|cummulative,prime| cummulative+prime}
puts sum    =>40
#we pass in no initial value, so inject uses the first element
product = primes.inject{|cummulative,prime| cummulative*prime}
puts product    =>15015
#just for fun let‘s sum all the numbers from 1 to, oh, say a million
sum = (1..1000000).inject(0){|cummulative,n| cummulative+n}
puts sum   =>500000500000
#you can do interesting things like build hashes
hash = primes.inject({}) { |s,e| s.merge( { e.to_s => e } ) }
p hash  #   =>  {"11"=>11, "7"=>7, "13"=>13, "1"=>1, "3"=>3, "5"=>5}

  

File I/O

  1. Read an entire file into a string
    ?
    1
    2
    file = File.new( "t1.php" )
    mytext = file.read
  2. Read an entire file into an array of lines
    ?
    1
    2
    lines = IO.readlines("data.txt")
    puts lines[0#prints the first line
  3. Read a file line by line
    ?
    1
    2
    3
    4
    file = File.open("res.txt")
    while line = file.gets
       puts line
    end

    Or you can use the IO class

    ?
    1
    IO.foreach("data.txt") { |line| puts line }
  4. Read a file line by line

    You should ensure the file is closed as well.

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    begin
    file = File.open("res.txt")
       while line = file.gets
          puts line
       end
    ensure
       file.close
    end
  5. Read only a few bytes at a time

    The following snippet of code reads a file which may have no line breaks and chops it into 80 character lines

    ?
    1
    2
    3
    4
    5
    6
    require ‘readbytes‘
    file = File.new( "C:/installs/myapp_log.xml" )
    while bytes = file.readbytes(80)
       print bytes+"\r\n"
    end
    file.close
  6. Reads a large XML file and inserts line breaks

    Uses TruncatedDataError to grab the last few slacker bytes from the end.

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # reads an xml file without line breaks and puts a line break before each ‘<‘
    require ‘readbytes‘
    file = File.new( "C:/installs/SurveyDirector_log.xml" )
    begin
       while bytes = file.readbytes(80)
          print bytes.gsub(/</,"\r\n<")
       end
    rescue TruncatedDataError #the last read had less chars than specified
       #print the rest of the data.  $! is the exception.
       # ".data" has the extra bytes
       print $!.data.gsub(/</,"\r\n<")
    ensure
       file.close unless file.nil?
    end
  7. method_missing - a wonderful idea

    In most languages when a method cannot be found and error is thrown and your program stops. In ruby you can actually catch those errors and perhaps do something intelligent with the situation. A trivial example:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class MathWiz
      def add(a,b)
        return a+b
      end
      def method_missing(name, *args)
        puts "I don‘t know the method #{name}"
      end
    end
    mathwiz = MathWiz.new
    puts mathwiz.add(1,4)
    puts mathwiz.subtract(4,2)

    Produces:

    5
    I don‘t know the method subtract
    nil
  8. While the ruby program is loading, you can execute code inside a special block labeled "BEGIN" - pretty nifty. After the interpretor has loaded the code, but before execution, you can execute code in the "END" block.

    ?
    1
    2
    3
    4
    5
    6
    7
    puts "main program running"
    END {
    puts "program ending"
    }
    BEGIN {
    puts "I‘m loading"
    }

    Produces:

    I‘m loading
    main program running
    program ending
    
  9. converting between strings and ints

    Use the to_i and to_s methods

    ?
    1
    2
    "3".to_i  #return an integer
    3.to_s    # returns a string

Using XML Dom Parser

 

REXML goes standard with Ruby 1.8. Sample to print all "div" elements whose "class" attribute is set to "entry".

?
1
2
3
4
require "rexml/document"
file = File.new( "t0.xml" )
doc = REXML::Document.new file
doc.elements.each("//div[@class=‘entry‘]") { |element| puts element }

 

Run a few lines directly from the command line with the "-e" option

 

c:\home\mfincher>ruby -e ‘sleep 2‘
c:\home\mfincher>ruby -e ‘puts 3*4‘
12
c:\home\mfincher>ruby -e ‘puts 3*4; puts 4*4‘
12
16

 

  1. Editing files in place

    Ruby offers a simple way to make a string substitution in many files all at once with a single line of code. The "-p" option loops over the files, the "-i" is the backup extension. With this command we are changing all the documentation from version 1.5 to 1.6, but the original files are renamed to ".bak".

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    C:\home\mfincher\ruby>more v2.txt
    Regarding version 1.5 ...
    ....
    version 1.5 is easy to install
     
    C:\home\mfincher\ruby>ruby -pi.bak -e "gsub(/1.5/,‘1.6‘)" v*.txt
     
    C:\home\mfincher\ruby>more v2.txt
    Regarding version 1.6 ...
    ....
    version 1.6 is easy to install
     
    C:\home\mfincher\ruby>more v2.txt.bak
    Regarding version 1.5 ...
    ....
    version 1.5 is easy to install
  2. Example of printing duplicate lines in sorted file.
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #prints duplicate lines in sorted files in the file passed in as first arg
    file = File.open(ARGV[0])
    lastLine = ""
    counter = 0
       while line = file.gets
            counter += 1
        if lastLine == line
               puts "#{counter-1}: #{line}#{counter}: #{line}\r\n"
            end
        lastLine = line
       end
    puts "done. Processed #{counter} lines"
  3. Ruby has its own interpreted shell, irb.
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    C:\home\mfincher>irb
    irb(main):001:0> puts "Hello World"
    Hello World
    => nil
    irb(main):002:0> a=1
    => 1
    irb(main):003:0> a*2
    => 2
    irb(main):004:0>
  4. ruby can take input from stdin
    ?
    1
    echo ‘puts "hello"‘ | ruby
  5. to pass a string on the url it needs to be "escape"‘d first.
    ?
    1
    2
    3
    require ‘uri‘
    ...
    URI.escape("some string...")

Example to remove "funny" characters from a filename

 

Example of iterating over the filenames in a directory, using regular expression substitution in strings, and renaming files.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#replaces any "funny" characters in a filename in the current directory with an underscore
#if the new file name already exists, this skips it.
Dir.foreach(".") { |f|
  print "testing \"#{f}\""
  if f =~ /[^\w\-\.]/   #filename contains something other than letters, numbers, _,-, or .
     puts "\r\n   name with funny characters: #{f}"
     newName = f.gsub(/[^\w\.\-]/,"_")   # \w is any word character, letter,num or _
     if File.exist?(newName)
    puts "   File #{newName} already exists.  Not renaming."
     else
    puts "   renaming #{f} to #{newName}"
    File.rename(f,newName)
     end
  else
    puts "   it‘s ok."
  end
}

 

Looping over list of arguments

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ARGV.each {|f|
puts f
counter = 0
file = File.open(f,"r")
   ftmp = f+".tmp"
   tmp = File.open(ftmp,"w")
   while line = file.gets
   if line =~ /pid="2"/
     counter += 1
     line = line.gsub(/pid="2"/,"pid=\"#{f}:#{counter}\"")
     puts line
   end
   tmp.print line
end
file.close
tmp.close
puts "renaming #{ftmp} to #{f}"
File.rename(ftmp,f)
}

  

 

Miscellanous Commands

 

command description example result
global_variables returns all global variables
local_variables returns all local variables
sleep seconds sleeps specified seconds
rand returns a random number between 0 and 1
rand(max) returns int between 0 and max
warn like print, but writes to STDERR

Interesting string functions

 

command description example result
center centers string "City".center(20) "________City________"
ljust left justifies "State".ljust(30) "State_________________________"
rjust right justifies "State".rjust(30) "_________________________State"
include? does the string include this substring "this is a test".include?(‘is‘) true
gsub global regex replacesments "this is a test".gsub(/[aeiou]/,‘_\1‘) th_s _s _ t_st
tr translates "The greatest of these is".tr(‘aeiou‘,‘*‘) Th* gr**t*st *f th*s* *s
each splits and iterates "one:two:three".each(‘:‘){|x| puts x}
one: 
two: 
three

DateTime

?
1
2
3
puts DateTime.now  #prints 2006-11-25T14:26:15-0600
puts Date.today    #prints 2006-11-25
puts Time.now      #prints Sat Nov 25 14:29:57 Central Standard Time 2006

  

Using ‘require‘

require will let your access code from other files. ‘require‘ looks in directories specified in $LOAD_PATH to find the files. The environmental variable RUBYLIB can be used to load paths into $LOAD_PATH.

?
1
2
3
4
5
C:\home\mfincher\ruby>irb
irb(main):001:0> p $LOAD_PATH
["c:/opt/ruby/lib/ruby/site_ruby/1.8", "c:/opt/ruby/lib/ruby/site_ruby/1.8/i386-msvcrt", "c:/opt/ruby/lib/ruby/site_ruby", "c:/opt/ruby/lib/ruby/1.8",
 "c:/opt/ruby/lib/ruby/1.8/i386-mswin32", "."]
=> nil

You can put a library like ‘startClicker.rb‘ in any of those directories and ruby will find it.

?
1
require ‘startClicker‘

  

BuiltIn Command Interpreter

With the "eval" method you can create your own interpreter language and run it during execution.

?
1
2
3
4
5
6
7
8
irb(main):007:0> a = 6
=> 6
irb(main):008:0> b = 7
=> 7
irb(main):009:0> eval "c=a+b"
=> 13
irb(main):010:0> puts c
13

Introspection with ObjectSpace

You can find all the objects in your program of a particular type using ObjectSpace.each_object.

?
1
2
3
4
5
6
7
8
9
10
11
12
class Person
  def initialize(name,age)
    @name = name
    @age = age
  end
  attr_reader :name
end
p = Person.new("Alfred",34)
p2 = Person.new("Janie",31)
ObjectSpace.each_object(Person) {|s|
   puts s.name
}

Produces:

Janie
Alfred

Testing

Ruby comes right out of the box with a testing framework. Here‘s a quick example:

?
1
2
3
4
5
6
7
8
9
10
require ‘test/unit‘
 
class TestMe < Test::Unit::TestCase
 
  def test_add
    s = 1 + 1
    assert_equal(2, s)
  end
 
end

Read a URL and print the web page to the screen.

This will get a particular page and print to the screen:

?
1
2
require ‘open-uri‘
open(‘http://www.fincher.org/Misc/Pennies‘){ |f| print f.read }

This will read a file of urls and print all to the screen:

?
1
2
3
4
#Reads first argument as file containing urls and prints them
#usage:  ruby wget.rb wget.txt
require ‘open-uri‘
IO.foreach(ARGV[0]) { |line| open(line){ |f| print f.read } }

  

Example of drawing a line on a canvas in Tk

Tk is a graphical subsystem used in languages like Perl and Tcl.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#draws a single line on a big canvas
require ‘tk‘
include Math
 
TkRoot.new do |root|
   title "Solo Line"
   geometry "600x600"
   canvas2 = TkCanvas.new(root) do |canvas|
      width 600
      height 600
      pack(‘side‘ => ‘top‘, ‘fill‘=>‘both‘, ‘expand‘=>‘yes‘)
      points = []
   end
   TkcLine.new(canvas2, 0,0,100,100)
end
Tk.mainloop

  

irb - interactive ruby

Ruby comes with an REPL (Read Eval Print Loop) utility to let you try ruby interactively. ("inf-ruby.el" provides an internal shell in emacs for irb).

?
1
2
3
4
5
6
7
8
9
C:>irb
irb(main):001:0> puts "hello"
puts "hello"
hello
nil
irb(main):002:0> Object.methods
Object.methods
["send", "name", "class_eval", "object_id", "new", "singleton_methods", "__send__", "private_method_defined?", "equal?", "taint", "frozen?", "instance_variable_get", "constants", "kind_of?", "to_a", "instance_eval", "require", "ancestors", "const_missing", "type", "instance_methods", "protected_methods", "extend", "protected_method_defined?", "eql?", "public_class_method", "const_get", "instance_variable_set", "hash", "is_a?", "autoload", "to_s", "class_variables", "class", "tainted?", "private_methods", "public_instance_methods", "instance_method", "untaint", "included_modules", "private_class_method", "const_set", "id", "<", "inspect", "<=>", "==", "method_defined?", ">", "===", "clone", "public_methods", "protected_instance_methods", "require_gem", ">=", "respond_to?", "display", "freeze", "<=", "module_eval", "autoload?", "allocate", "__id__", "=~", "methods", "gem", "method", "public_method_defined?", "superclass", "nil?", "dup", "private_instance_methods", "instance_variables", "include?", "const_defined?", "instance_of?"]
irb(main):003:0>

  

RubyGems a ruby package installer

 

You can download RubyGems from http://rubyforge.org. Unzip the files (eg, C:\opt\ruby) then install by entering:

C:>cd C:\opt\ruby\rubygems-0.9.0
C:\opt\ruby\rubygems-0.9.0>ruby setup.rb all

 

Ruby on Rails

How to write a log message

You can use logger‘s methods "warn", "info", "error", and "fatal".

?
1
logger.info("request.remote_ip"+request.remote_ip);

Field names ending with "_at" are assumed to be datetime fields and are filled in automagically by rails for ActiveRecord objects. The suffix "_on" are assumed to be dates.

Console

 

to dubug applications it‘s convenient to use the console script

myapp>ruby script/console

 

debug method

 

You can use the debug() method inside web pages to dump info about an object.

<p>Thanks for visiting</p>
<%= debug(@myobject) %>

 

How to Freeze a version

 

Since your hosting company may upgrade the rails version you need to "freeze" the current version. The following copies all the 1.2.6 libraries from the shared directory to your own private one.

rake rails:freeze:edge TAG=rel_1-2-6

 

Active record notes

    1. Find all records meeting a criteria

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      def self.suitable_jokes(sort_key)
      if sort_key == "Newest"
         find(:all,
              :conditions => "suitable  = \"1\"",
              :order => "entry_date DESC"
             )
      elsif sort_key == "Worst"
         find(:all,
              :conditions => "suitable  = \"1\"",
              :order => "entry_date ASC"
             )
      else
         find(:all,
              :conditions => "suitable  = \"1\"",
              :order => "current_rating DESC"
             )
      end
      end

      The first argument to find can also be ":first" or ":last".

    2. Find the count of records meeting a criteria
      ?
      1
      2
      3
      4
      5
      6
      def self.waiting_jokes()
        count("suitable  = \"0\"")
      end
      def self.total()
        count("suitable  = \"1\"")
      end

      Find the total number of items

      ?
      1
      count = Joke.count

      Find the total number of items meeting a criteria

      ?
      1
      count = Joke.count(["suitable = \"1\""])
    3. Pagination

      The ":limit" and ":offset" options allow easy pagination.

      To return the fifth page of items use the following:

      ?
      1
      2
      3
      4
      5
      6
      find(:all,
              :conditions => "suitable  = \"1\"",
              :order => "current_rating DESC",
          :limit => 10,
          :offset => 40
             )
    4. Use raw SQL and return two values
      ?
      1
      2
      3
      4
      def getAverageRatingAndCount
          record = Rating.find_by_sql(["select count(*) as count,avg(rating) as average from ratings WHERE joke_id = ?",id]);
          return record[0].average.to_f , record[0].count.to_i
      end
    5. The "create" method in ActiveRecord will do "new" and "save" operations simultanously.
      ?
      1
      2
      3
      4
      mydog = Dog.create(
      :name => "Fido"
      :breed => "Collie"
      )

Watir

Watir is a GUI testing tool written in Ruby. Here is a script to open Google and search for pictures of kittens.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require "watir"
ie = Watir::IE.new  #create an object to drive the browser
ie.goto "http://www.google.com/"
ie.url == "http://www.google.com/"
ie.link(:text, "Images").flash #flash the item text "Images"
ie.link(:text, "Images").click #click on the link to the images search page
ie.text.include? "The most comprehensive image search on the web" #test to make sure it worked
searchTerm = "kittens" #set a variable to hold our search term
ie.text_field(:name, "q").set(searchTerm) # q is the name of the search field
ie.button(:name, "btnG").click # "btnG" is the name of the google button
if ie.contains_text(searchTerm)
  puts "Test Passed. Found the test string: #{searchTerm}. Actual Results match Expected Results."
else
   puts "Test Failed! Could not find: #{searchTerm}"
end

  

  1. Selecting a JavaScript popup box

    stolen from http://wiki.openqa.org/display/WTR/FAQ

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #Watir script to show clicking a JavaScript popup box
    require "watir"
    require ‘watir\contrib\enabled_popup‘
    require ‘startClicker‘
    require ‘net/http‘
    require ‘net/https‘
     
    $ie = Watir::IE.new  #create an object to drive the browser
    $ie.goto "http://mydomain.com/ListGroups.aspx"
    if $ie.contains_text("Log In")
      $ie.text_field(:name, "Login1$UserName").set("fincherm")
      $ie.text_field(:name, "Login1$Password").set("mitch")
      $ie.button(:name, "Login1$LoginButton").click
    end
    $ie.link(:text, "Baseline").click
    $ie.link(:text, "MoonManC").click
    def setDdlPriority(priority)
       ddlPriority = $ie.select_list( :name , /ddlPriority/)
       puts ddlPriority
       ddlPriority.select(priority)
       puts ddlPriority
       $ie.button(:name, "ctl00$btnSave").click_no_wait
          startClicker( "OK", 4 , "User Input" )
          sleep 1  
    end
    setDdlPriority("2")
    setDdlPriority("9")

      

    startClicker.rb:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #method startClicker from http://wiki.openqa.org/display/WTR/FAQ
    def startClicker( button , waitTime= 9, user_input=nil )
      # get a handle if one exists
      hwnd = $ie.enabled_popup(waitTime) 
      if (hwnd)  # yes there is a popup
        w = WinClicker.new
        if ( user_input )
          w.setTextValueForFileNameField( hwnd, "#{user_input}" )
        end
        # I put this in to see the text being input it is not necessary to work
        sleep 3        
        # "OK" or whatever the name on the button is
        w.clickWindowsButton_hwnd( hwnd, "#{button}" )
        #
        # this is just cleanup
        w=nil   
      end
    end

      

    1. How to use Watir with NUnit

      Here is an example of connecting it to NUnit.

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      using System;
      using System.Diagnostics;
      using System.Text.RegularExpressions;
      using NUnit.Framework;
       
      namespace SurveyDirector.Test.Watir
      {
          /// <summary>
          /// from http://www.hanselman.com/blog/IntegratingRubyAndWatirWithNUnit.aspx/// with small hacks from Liz Buenker
          /// </summary>
          public classWatirAssert
          {
              public static void TestPassed(string rubyFileName, string directoryPath)
              {
                  string output = String.Empty;
                  using (Process p = new Process())
                  {
                      p.StartInfo.UseShellExecute = false;
                      p.StartInfo.RedirectStandardOutput = true;
                      p.StartInfo.FileName = "ruby.exe";
                      p.StartInfo.Arguments = rubyFileName + " -b";
                      p.StartInfo.WorkingDirectory = directoryPath;
                      p.Start();
                      output = p.StandardOutput.ReadToEnd();
                      p.WaitForExit();
                  }
                  Console.Write(output);
                  Trace.Write(output);
                  Regex reg = new Regex(@"(?<tests>\d+) tests, (?<assertions>\d+) assertions, (?<failures>\d+) failures, (?<errors>\d+) errors", RegexOptions.Compiled);
                  Match m = reg.Match(output);
                  try
                  {
                      int tests = int.Parse(m.Groups["tests"].Value);
                      int assertions = int.Parse(m.Groups["assertions"].Value);
                      int failures = int.Parse(m.Groups["failures"].Value);
                      int errors = int.Parse(m.Groups["errors"].Value);
                      if (tests > 0 && failures > 0)
                      {
                          Assert.Fail(String.Format("WatirAssert: Failures {0}", failures));
                      }
                      else if (errors > 0)
                      {
                          Assert.Fail(String.Format("WatirAssert: Errors {0}", errors));
                      }
                  }
                  catch (Exception e)
                  {
                      Assert.Fail("WatirAssert EXCEPTION: " + e.ToString());
                  }
              }
          }
      }

        

      The above code would be used by something like this:

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      using System;
      using NUnit.Framework;
       
      namespace SurveyDirector.Test.Watir
      {
          [TestFixture]
          public classWatirTest
          {
              private static readonly string testDir = Utilities.Properties.baseDir + "\\Test\\Watir";
              public WatirTest() { }
              [Test]
              public void Sd01Test()
              {
                  WatirAssert.TestPassed("sd01_test.rb",testDir);
              }
              [Test]
              public void Sd02Test()
              {
                  WatirAssert.TestPassed("sd02_test.rb",testDir);
              }
       
          }
      }

        

  2. Ruby Quotes:


    "Ruby is a language for clever people." -Matz 
    "Ruby is the perlification of Lisp." -Matz 
    "Type Declarations are the Maginot line of programming." -Mitch Fincher