Ruby Notes

advertisement




Write more understandable code in less lines
Free (Very open license)
Extensible
Dynamic languages such as Ruby can be very
productive in specific areas, such as
prototyping, web development or for gluing
various applications together.



Dynamic programming languages are often
referred to as 'weakly typed' which means
that variables are not bound to a particular
type until runtime.
It typically features "dynamic typing," which
gives the programmer more freedom to pass
parameters at runtime without having to
define them beforehand.
This dynamic typing is accomplished by using
an interpreted language, rather than a
compiled language.


A scripting language is a programming language
that allows control of one or more software
applications. "Scripts" are distinct from the core
code of the application, which is usually written in
a different language, and are often created or at
least modified by the end-user. Scripts are often
interpreted from source code whereas the
applications they control are traditionally
compiled.
The name "script" is derived from the written
script of the performing arts, in which dialogue is
set down to be spoken by human actors.











interpreted (no linking or compilation)
less efficient to run
faster to develop (factor of 5 to 10)
high level – a single statement is more powerful
no type declarations, typeless
powerful built-in types
can generate and interpret source code at run time
(via eval)
interface to underlying operating system – can call
operating system commands
plays well with others – glue systems together
rarely used for complex algorithms or data
structures.
Examples: javascript, perl, php, smalltalk, ruby,
vbscript, groovy, emacslisp, awk, python, tcl, unix
shell.
Japanese Design Aesthetics Shine Through
 Focus on human factors
 Principle of Least Surprise: when two
elements of an interface conflict, or are
ambiguous, the behavior should be that
which will least surprise the human user or
programmer at the time the conflict arises.

Principle of Succinctness
The supreme design goal of Ruby
Makes programmers happy and makes Ruby easy to learn
Examples




What class does an object belong to ? – can ask
o.class (returns the class object)
Is it Array#size or Array#length?
Either one - same method – they’re aliased
Is this good or bad design?
Array operators? – if it makes sense, they are defined.
diff = ary1 – ary2 (removes elements of ary2 from ary1)
union = ary1 + ary2 (concatenate arrays)
intersection = ary1 & ary2 (elements common to both)
also known as the Principle of Least Effort
Motivation…


We don’t like to waste time
The quicker we program, the more we
accomplish
Sounds reasonable enough, right?


Less code means less bugs
Common needs are built-in.
All classes derived from Object including Class (like
Java) but there are no primitives (not like Java at
all). EVERYTHING is an Object.
 Ruby uses single-inheritance
What are the dangers of multiple inheritance?
 Mixins give you the power of multiple inheritance
without the headaches
 Modules allow addition of behaviors to a class
 Reflection is built in along with lots of other highly
dynamic metadata features
 Things like ‘=‘ and ‘+’ that you might think are
operators are actually methods (like Smalltalk)


a mixin is a class that is mixed with a
module. In other words the implementation
of the class and module are joined,
intertwined, combined, etc.
a module as a degenerate abstract class. A
module can’t be instantiated and no class can
directly extend it but a module can fully
implement methods.
# Convert a integer value to English.
02.module Stringify
03. # Requires an instance variable
@value
04. def stringify
05. if @value == 1
06.
"One"
07. elsif @value == 2
08.
"Two"
09. elsif @value == 3
10.
"Three"
11. end
12. end
13.end
Stringify
makes use
of a @value instance
variable.
The class that will be
mixed with this
module needs to
define and set a
@value instance
variable
a module could
invoke methods
defined not in the
module itself but in the
class that it will be
mixed with.
# A Math module akin to Java Math class.
2.module Math
3. # Could be called as a class, static, method
4. def add(val_one, val_two)
5. BigInteger.new(val_one + val_two)
6. end
7.end
# Base Number class
02.class Number
03. def intValue
04. @value
05. end
06.end
07.
08.# BigInteger extends Number
09.class BigInteger < Number
10.
11. # Add instance methods from Stringify
12. include Stringify
// mix methods at the instance level
13.
14. # Add class methods from Math
15. extend Math // mix methods at the class level
16.
17. # Add a constructor with one parameter
18. def initialize(value)
19. @value = value
20. end
21.end
# Call class method extended from Math
bigint2 = BigInteger.add(-2, 4)
puts bigint2.intValue # --> 2
# Call a method included from Stringify
puts bigint2.stringify # --> 'Two'
# Format a numeric value as a currency
module CurrencyFormatter
def format
"$#{@value}"
end
end
# Add the module methods to.# this object
instance, only!
bigint2.extend (CurrencyFormatter)
puts bigint2.format # --> '$2'







Method Chaining – applies methods from left to right
print array.uniq.sort.reverse
Method Names include ! and ?
ary.sort!
? returns a true or false
◦ examples: defined? equal?
! changes the object that calls the method
(do it here!)
Iterators and Blocks vs. Loops
files.each { |file| process(file) }
Case usage:
◦ Class names begin with a Capital letter
◦ Constants are ALL_CAPS
◦ Everything else - method call or a local variable
Convention in naming: Under_score instead of
camelCase
0.upto(9) do |x|
print x, " "
end
Produces: 0 1 2 3 4 5 6 7 8 9
[ 1, 1, 2, 3, 5 ].each {|val| print val, " " }
Produces: 1 1 2 3 5
songList.each do |aSong|
aSong.play
end




Duck Typing
Based on signatures, not class inheritance
In Ruby, we rely less on the type (or class) of an object and
more on its capabilities.
Duck Typing is a type of dynamic typing in which the object’s
current set of methods and properties determines the valid
semantics, rather than its inheritance from a particular class
or implementation of a specific interface.
The name of the concept refers to the duck test, attributed to
James Whitcomb Riley which may be phrased as follows:
"when I see a bird that walks like a duck and swims like a duck
and quacks like a duck, I call that bird a duck."

# Check whether the object defines the to_str method
puts ('A string'.respond_to? :to_str) # => true
puts (Exception.new.respond_to? :to_str) # => true
puts (4.respond_to? :to_str) # => false
The above example is the simplest example of Ruby's
philosophy of "duck typing:" if an object quacks like a
duck (or acts like a string), just go ahead and treat it
as a duck (or a string). Whenever possible, you should
treat objects according to the methods they define
rather than the classes from which they inherit or the
modules they include.
class Duck
def quack
'Quack!'
end
def swim
'Paddle paddle paddle...'
end
end
class Goose
def honk
'Honk!'
end
def swim
'Splash splash splash...'
end
end
class DuckRecording
def quack
play
end
def play
'Quack!'
end
end
If you are calling
“quack”, Duck and
DuckRecording are
interchangeable
If you are calling
“swim”, Duck and
Goose are
interchangeable.
Method isn’t
expecting a specific
type JUST one with
the needed
functionality.



Dynamic Dispatch
A key concept of OOP: methods are actually messages that
are sent to an object instance
It is a runtime decision to decide which code to execute
Can’t be determined statically (method could have been
overridden dynamically)
foobar = Array.new
foobar.class # => Array
foobar.size # => 0
Conceptually: First we search for “size” in Array. If it isn’t there, we look to
Object to define size. If the method isn’t found at the top of the
hierarchy, we have a “method_missing” message.
The new search path
foobar = Array.new
def foobar.size // Adds a size method ONLY for the instance
“Infinity and beyond" ;
end


Languages with weak or no typing
systems often carry a dispatch table as
part of the object data for each object.
This allows instance behavior as each
instance may map a given message to a
separate method.
Languages with strong (static) typing use
a virtual table which defines method to
code mapping. Instances of the type
store a pointer to this table.




Suppose a program contains several classes in
an inheritance hierarchy: a superclass Cat, and
two subclasses, HouseCat and Lion. Class Cat
defines a virtual function named speak, so its
subclasses may provide an appropriate
implementation (e.g. either meow or roar).
When a Cat variable calls speak, we need to be
able to tell which method to call.
dispatch table contains addresses of methods
Use same layout for type-compatible methods
– so we look for address at a constant offset

Dynamic Behavior
◦
◦
◦
◦
Reflection
Scope Reopening (Kind of like AOP)
Eval
Breakpoint debugger


One of the many advantages of dynamic
languages such as Ruby is the ability to
introspect---to examine aspects of the
program from within the program itself.
Some call this feature reflection.
We might discover:
◦
◦
◦
◦
what objects it contains,
the current class hierarchy,
the contents and behaviors of objects, and
information on methods.







Everything is an object EVEN the class.
5.class
#=> Fixnum
"hello".class #=> String
class Foo
end
Foo.class #=> Class
Foo is a constant known to the system as a
class


Have you ever craved the ability to traverse all the living objects
in your program? Ruby lets you perform this trick with
ObjectSpace::each_object .
For example, to iterate over all objects of type Numeric, you'd
write the following.
a = 102.7
b = 95
# Won't be returned
c = 12345678987654321 # Won't be returned
count = ObjectSpace.each_object(Numeric) {|x| p x }
puts "Total count: #{count}“
Produces:
102.7
3.14159265358979
2.71828182845904
2.22044604925031e-16
1.79769313486232e+308
4.9e-324
Total count: 6

Hey, where did those last numbers come from? We didn't define
them in our program. The Math module defines constants for e
and PI; since we are examining all living objects in the system,
these turn up as well.
Right
click on Project name,
Select Properties
Click Run
Define Ruby Option

For instance, we can get a list of all the methods to
which an object will respond.
r=1..10
num = 14
list = r.methods
list.length
# 60
list[0..3]
# [exclude_end?, to_a, include?, member?]
r.respond_to?("frozen?") #>> true
r.respond_to?("hasKey") #>> true
num.instance_of? Fixnum #>> true
one_class = Fixnum
begin
print one_class
one_class = one_class .superclass
print " < " if one_class
end while one_class
puts p Fixnum.ancestors
puts self.class # scripts are automatically in Object
puts 3.class
# numbers are Fixnum
Produces
Fixnum<Integer<Numeric<Object
[Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]
Object
Fixnum
trane="John Coltrane".method(:length)
miles="Miles Davis".method("sub")
puts trane.call
puts miles.call(/iles/,'.')
trane=%q{"John Coltrane".length}
miles=%q{"Miles Davis".sub(/iles/,'.')}
puts eval trane
puts eval miles
produces 14
M. Davis
14
M. Davis
class CoinSlot
At instance variable begins with @
def initialize(amt)
and its scope is confined to
@amt=amt
whatever object self refers to
$here=binding
A global variable begins with $
end
It can be referred to from anywhere
end
in the program
a=CoinSlot.new(35)
eval ("puts @amt", $here) # 35
eval ("puts @amt” )
# nil
class CoinSlot
def initialize(amt)
@amt=amt
$here=binding
We can even modify
end
variables in original scope
end
a=CoinSlot.new(35)
eval ("puts @amt", $here) # 35
eval ("puts @amt” )
# nil
eval "@amt=23.92", $here
eval "puts @amt", $here
# 23.92

One nice feature of globals, is that they can
be traced; you can specify a procedure which
is invoked whenever the value of the variable
is changed.
trace_var :$here, proc{print "$here is now",
$here, "\n"}
class Demo
def initialize(secret)
@secret = secret
end
def getBinding
return binding()
end
end
k1 = Demo.new(99)
b1 = k1.getBinding
k2 = Demo.new(-3)
b2 = k2.getBinding
puts eval("@secret", b1) #=> 99
puts eval("@secret", b2) #=> -3
puts eval("@secret")
#=> nil


caller is a Ruby method of the Kernel class,
which all objects inherit.
It returns an array of strings representing the
call stack.
class Demo
def initialize(secret)
@secret = secret
@other = 2*@secret
$here = binding
end
def aA
puts caller.join("\n")
end
def aB
aA
end
def aC
aB
end
def getBinding
return binding()
end
end
k1 = Demo.new(99)
k1.aC # prints call stack
Produces:
C\cs4700\simple_ruby_application\lib\
Scoping.rb:25:in `aB'
C:\cs4700\simple_ruby_application\lib\
Scoping.rb:28:in `aC'
C:\cs4700\simple_ruby_application\lib\
Scoping.rb:49




Java features the ability to serialize objects,
letting you store them somewhere and
reconstitute them when needed. You might
use this facility, for instance, to save a tree of
objects that represent some portion of
application state, save it, and recreate is later.
Ruby calls this kind of serialization
marshalling.
Marshal: Def: To arrange or place (troops, for
example) in line for a parade, maneuver, or
review.
Can be used to create a distributed object
system.
class Note
attr :value
def initialize(val)
@value = val
end
def to_s
@value.to_s
end
end
class Chord
def initialize(arr)
@arr = arr
end
def play
@arr.join('-')
end
end
c = Chord.new( [ Note.new("G"),
Note.new("Bb"), Note.new("Db"),
Note.new("E") ] )
File.open("posterity", "w+") do |f|
Marshal.dump(c, f)
end
File.open("posterity") do |f|
chord = Marshal.load(f)
puts chord.play #» G-Bb-Db-E
end





the format used by Marshal has changed a few
times in the past.
it's Ruby-only. AFAIK nothing else can read data
serialized with Marshal.
serializing objects with Marshal exposes
implementation details.
The basic problem with Marshal is that it
serializes an object by saving the name of its
class and all its instance variables
Makes interoperability harder – encoding may
change and implementation details are leaked by
default.
Using Ruby's alias it is possible to reopen a class, override a method and still call the original.
class ClockRadio
def on!
@on = true
end
def on?
@on
end
end
Clock Radio is complete,
Sometime later we can redefine methods and add new ones:
class ClockRadio
alias :old_on! :on!
def on!
old_on! # Calling original code
@display_time = true
end
def display_time?
@display_time
end
end

The advantage you gain from being able to
reopen classes is that you can patch the
framework you’re using unobtrusively in order
to fix bugs or improve performance.




Violates OO principle by allowing anyone to add
members and methods to an existing class, even
outside of the original class definition.
The methods thus added have full access to all
other members and methods, including private
ones.
This is a “violation of the Open/Closed Principle,”
in that the original class is not kept closed
against modifications.
It certainly isn’t pure. There really isn’t a Grand
Unifying Idea. It clearly copies features from very
different languages and allows for a variety of
programming styles.
A closure is a nameless function.
 It has code to run (the executable)
 It has state around the code (the scope)
You capture the environment (the local variables)
in the closure.
Even after the function has returned, and its local
scope has been destroyed, the local variables
remain in existence as part of the closure object.
Local variables are shared between the closure and
the method. It's a real closure. It's not just a
copy. (This is a Perl feature that is copied.)



You can reconvert a closure back into a block, so
a closure can be used anywhere a block can be
used.
Often, closures are used to store the status of a
block into an instance variable, because once you
convert a block into a closure, it is an object that
can be referenced by a variable.
And of course closures can be used like they are
used in other languages, such as passing around
the object to customize behavior of methods.
The important thing to remember about Ruby
is that there isn't a big difference between
``compile time'' and ``runtime.‘’ This
allows:
 adding code to a running process.
 redefining methods on the fly, change their
scope from public to private, and so on.
 altering basic types, such as Class and
Object.
eval "puts 2+2" # => 4
eval "'hello world!'.upcase “# => HELLO WORLD
Useful:
 Tutoring – allows users to run their code in a
protected environment
 evaluating math expression (like in spreadsheet)
 more flexible than a parser as can call functions that
were just created.
 implemented with the same interpreter as normal
code
Dangers:
 input = # ... read from some form
eval input
What if they input 'rm -rf *‘ ?

Slow – string must be parsed

Side Effects: As an everyday example, the statement
x = gets
reads a line and sticks it into x just like you’d probably expect,
but it also has the side effect of sticking the line into the
global variable $_.


Ruby also blurs the line between names of variables,
classes, and methods. Some languages allow the
same identifier to be both a class and a variable, but
context distinguishes. Ruby does not.
Ruby delights in the bizarre usage of operator
overloading. ”<<” normally means “left shift”, but it
also means ”here document follows,” “append to
string,” and “extend array.” Other operators have
similar illogical overloadings; for example, the ”<"
operator not only means "less than" but also "is a
subclass of."
Download