# install on linux
yum -y install ruby ruby-irb
# install on windows
use cygwin, install all ruby packages. includes gem and irb
# To quit IRB, type quit, exit or just hit Control-D.
# load file
# or put ruby code in ~/.irbrc
irb -r 'file'
# dynamically load file
irb
require 'file'
# equiv of print_r, var_dump (think "pretty print")
pp object
# hash tag
#!/usr/bin/env ruby
# comments
#
# define/set variable
a = 1
b = "some string"
c = nil
puts "hi"
# read line
line = gets
# define method/function
def hi(name)
puts "hi #{name}!"
end
# call function
hi
hi("name")
# introspection: print all methods of object
require 'pp'
pp 5.methods
# default values, functions in string
def hi(name = "World")
puts "Hello #{name.capitalize}!"
end
# simple class
class Greeter
# constructor
def initialize(name = "World")
# use meta char @ to access class properties
@name = name
end
def say_hi
puts "Hi #{@name}!"
end
def say_bye
puts "Bye #{@name}, come back soon."
end
end
# instantiate class with .new
g = Greeter.new("John Smith")
g.say_hi
g.say_bye
# list public methods for object:
# include all ancestors
Greeter.instance_methods
# only in immediate class
Greeter.instance_methods(false)
# test if a method is defined for the object
# is name defined on the object?
g.respond_to?("name")
# alter existing class
#NOTE: The changes will be present in any new objects you create and even available in existing objects of that class.
class Greeter
# public getter and setter
attr_accessor :name
# only getter
attr_reader :thing
end
# array
names = ["Albert", "Brenda", "Charles", "Dave", "Englebert"]
# size:
names.size
# loop over array
# pipes into bound variable 'name'
names.each do |name|
puts "Hello #{name}!"
end
# join
names.join(", ")
# hash
H = Hash["a" => 100, "b" => 200]
puts "#{H['a']}"
puts "#{H['b']}"
#This will produce following result:
#100
#200
# get hash keys as array
keys = H.keys
puts "#{keys}"
# loop over hash
hash.each do |key, array|
puts "#{key}-----"
puts array
end
hash.keys.sort.each do |key|
puts "#{key}-----"
hash[key].each { |val| puts val }
end
puts h.sort.map {|k,v| ["#{k}----"] + v}
# hash methods:
http://www.tutorialspoint.com/ruby/ruby_hashes.htm
# duck typing. you can probe capabilities of object
class Greeter2
def say_bye
if @names.nil?
puts "..."
elsif @names.respond_to?("join")
# Join the list elements with commas
puts "Goodbye #{@names.join(", ")}. "
else
puts "Goodbye #{@names}. "
end
end
end
# test if class file is called directly
if __FILE__ == $0
# I am filename
end
# parse standard input stdin
#!/usr/bin/env ruby
chars = 0
words = 0
lines = 0
# Count the stuff
while line = gets
chars += line.size
words += line.split.size
lines += 1
end
# Display the results
puts "Chars=%s, Words=%s, Lines=%s" % [
chars, words, lines
]
# read all lines
lines = readlines
puts "Chars=%d, Words=%d, Lines=%d" % [lines.join.size, lines.join.split.size, lines.size]
# code blocks
Procs are objects that can be called.
Sending the call method to the proc object executes the code block.
The [] operator is a synonym for call.
{
|boundvariable|
}
do |var|
end
block = proc { |x| puts "Called with #{x}" }
# calling
block.call(1)
block.call(2)
block[222]
# using blocks (notes from Matz and the interwebs)
Matz: Blocks are basically nameless functions. You may be familiar with the lambda from other languages like Lisp or Python. Basically, you can pass a nameless function to another function, and then that function can invoke the passed-in nameless function. For example, a function could perform iteration by passing one item at a time to the nameless function. This is a common style, called higher order function style, among languages that can handle functions as first class objects. Lisp does it. Python does it. Even C does it with function pointers. Many other languages do this style of programming. In Ruby, the difference is mainly a different kind of syntax for higher order functions. In other languages, you have to specify explicitly that a function can accept another function as an argument. But in Ruby, any method can be called with a block as an implicit argument. Inside the method, you can call the block using the yield keyword with a value.
SO: Blocks are not objects, they are syntactic structures; this is why they cannot be assigned to a variable. This is a privilege reserved for objects.
Blocks are closures -- they're functions that close over the enclosing scope. They don't conceptually "belong to" a given object.
Blocks that directly follow a method call are implicitly passed to the method.
The method may call the proc by invoking yield and passing any arguments.
You can test for the presence of a block using block_given?.
yield(arg) if block_given?
def three_times(arg)
yield(arg)
yield(arg)
yield(arg)
end
three_times("x") { |val| puts val }
three_times(22) do |thing|
puts "Got #{thing}"
end
# output
x
x
x
Got 22
Got 22
Got 22
The block may be explicitly passed using the & operator.
def three_times(arg, &block)
block.call(arg)
block.call(arg)
block.call(arg)
end
# lambda vs procs
There are two slight differences between lambda and Proc.new.
First, argument checking. The Ruby documentation for lambda states: Equivalent to Proc.new, except the resulting Proc objects check the number of parameters passed when called.. Here is an example to demonstrate this:
Second, there is a difference in the way returns are handled from the Proc. A return from Proc.new returns from the enclosing method (acting just like a return from a block.
def try_ret_procnew
ret = Proc.new { return "Baaam" }
ret.call
"This is not reached"
end
# prints "Baaam"
puts try_ret_procnew
While return from lambda acts more conventionally, returning to its caller:
def try_ret_lambda
ret = lambda { return "Baaam" }
ret.call
"This is printed"
end
# prints "This is printed"
puts try_ret_lambda
I would probably recommend using lambda instead of Proc.new when possible.
# unit testing
require 'test/unit'
require 'trio'
class TestTrio < Test::Unit::TestCase
def setup
@trio = Trio.new(1,2,3)
end
def test_trio
assert_equal [1,2,3], @trio.to_a
end
end
# database io
require 'dbi'
db = DBI.connect("DBI:Pg:poc", 'jim', nil)
sql = %{
SELECT name, id
FROM users
WHERE id < 4
}
db.select_all(sql) do |row|
puts "User #{row['name']}, id #{row['id']}"
end
db.disconnect
# composite pattern, loops
<< is the polymorphic append operator.
{ ... } is a code block. It is automatically passed to the each function.
|installer| is an argument to the code block.
list.each { |item| code } is equivalent to the for loop
do ... end is also code block.
class MasterInstaller
def initialize
@installers = []
end
def register(installer)
@installers << installer
end
def install
for installer in @installers
installer.install
end
end
def uninstall
@installers.each { |installer|
installer.uninstall
}
end
def backup
@installers.each do |installer|
installer.backup
end
end
end
# what's the ?
? as a method name suffix (e.g. include?) indicates that the method returns a true/false value.
# includes (load module)
require: finds the file and loads it (once)
new: sent to a Class object will cause a new instance to be created. Parameters passed to new will automatically be passed to initialize.
Parenthesis are optional if the result is not ambiguous.
ARGV is a list of command line arguments
No interface declaration is needed for installers. As long as they implement the needed methods, the master installer can use them.
# find file, load
require 'masterinstaller'
require 'fileinstaller'
require 'sqlinstaller'
m = MasterInstaller.new
m.register(FileInstaller.new("xyz.rb", "bin"))
m.register SqlInstaller.new(
"INSERT INTO a_table VALUES(1,2,3);" )
commands = ['install', 'uninstall', 'backup']
ARGV.each do |command|
puts "Command is #{command}"
m.send(command) if commands.include? command
puts
end
# reserved words
BEGIN do next then
END else nill true
alias elsif not undef
and end or unless
begin ensure redo until
break false rescue when
case for retry while
class if return while
def in self __FILE__
defined? module super __LINE__
# gotchas:
No Primitives. Integers and floats are objects
-1.abs => 1
No Semi-colons
nil, not null
nil is an object
nil.nil? => true
nil and false are false
everything else, including 0, is true
Picky about spaces
f (arg) gives warning.
f(arg) is ok.
Different type model
Duck Typing!
No interfaces needed.
Expression oriented syntax.
Almost everything returns a value
x = if a==0 then 5 else 1 end
Methods automatically return their last expression.
Single Inheritance
But mixins are available
Per-object methods are allowed (singleton methods)
Classes are always open (even built in classes)
# messages
OO in Java is defined in terms of calling functions
Member functions, static functions, etc.
OO in Ruby is defined in terms of sending messages
Sending messages, obj.send(:method_name)
class Proxy
def initialize(target)
@target = target
end
def method_missing(sym, *args, &block)
@target.send(sym, *args, &block)
end
end
class Dog
def talk; puts "WOOF"; end
end
d = Dog.new
p = Proxy.new(d)
p.talk
# file
Here, the File.open method takes a block. It then opens a new file (in "append" mode), and yields the open file into the block. When the block completes, Ruby closes the file. Except that Ruby doesn't just close the file when the block completes; it guarantees that the File will be closed, even if executing the block results in a raise. Let's take a look at the implementation of File in Rubinius:
def append(location, data)
path = Pathname.new(location)
raise "Location does not exist" unless path.exist?
File.open(path, "a") do |file|
file.puts YAML.dump(data)
end
return data
end
# modules are called "gems" or "ruby-gems"
# install with yum
yum install rubygem-json
# introspection
Depending on what information you're looking for, try:
obj.methods
and if you want just the methods defined for obj (as opposed to getting methods on Object as well)
obj.methods - Object.methods
Also interesting is doing stuff like:
obj.methods.grep /to_/
To get instance variables, do this:
obj.instance_variables
and for class variables:
obj.class_variables
# list all installed gems
gem list
# list all remote gems
gem list rhc --remote --all
No comments:
Post a Comment