Skip to content

Instantly share code, notes, and snippets.

@Nimster
Created April 8, 2012 16:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nimster/2338275 to your computer and use it in GitHub Desktop.
Save Nimster/2338275 to your computer and use it in GitHub Desktop.
Quick reference to some common Ruby idioms/capabilities - mainly to serve as a reminder for me
## This is an example encompassing much of ruby's basic syntax.
## It is derived from various examples in the Pragmatic Bookshelf's "Programming
## Ruby" book. It is intended as a quick reference, and you should grep (search
## the file) for various terms like "splat args", "exception" or "iterator" to
## get a quick look at how they are used. It is probably very hard to make any
## sense of this by reading it linearly, nor did I attempt to have the example
## make any logical sense at all - the operations done here are not at all
## coherent
##
## This is freely distributable with the following credit:
## Nimrod Priell, nimrod.priell@gmail.com
##
require 'csv' #Import libraries
require 'test/unit' #Any TestCase classes will run automatically if this is
#required
module Example # Can serve as a namespace/package or be mixed in
class ExampleClass #Define a class. Since it's inside a module, refer to it
#as Example::ExampleClass
attr_reader :param #Define a read-only attribute. also attr_writer
attr_accessor :is_enabled, :a_map #read-write attributes
def initialize (param) #Constructor
@param = param #Set an object member (a.k.a attribute, field, variable...)
@param = Integer(param) # convert to an integer
@a_map = { #Hash map initialization
a_key: 'value', #You can use symbols (:a_key is now a unique symbol)
'another_key' => 'value' # Or any key class (here it is a string).
# value can obviously be anything
}
@an_array = [ 1,2,3,4 ] # array init. Can contain anything including
# different types
@an_array << 5 # array push (but @an_array.push(5) will also work)
end
private :a_map, :is_enabled # private means access only by this instance
# (self, this)
def param_plus_one= (param) # setter method
@param = param.dup # For a string param, this gives us a copy constructor
# instead of just setting the reference to the same obj
end
def to_s # like toString() in java, this is called whenever you print the
# object
if defined? something_that_isnt_defined
something_that_isnt_defined
end
"an example " + @param # We always return the value of the last line, no
# need for 'return' keyword
end
#This shows some advanced ways of calling methods
#The method receives the first argument, then all other arguments except the
#last are packed into an array. Then the last argument is received.
def self.class_method(arg1, *args, arg3) #This is a "static" method.
#*args means "collect any arguments passed into an array"
#When called with class_method(1,2,3,4), we will get args==[2,3].
#If you pass it back into a function it expands it back to the function
#arguments:
file = File.open(*arg)
#This would do the same - pass in 'a','b', and 'c' into the method
file = File.open(*['a','b','c'])
#These arguments will be collected to a hash:
file = File.open('nonhashed', key: 'value1', key2: 'value2')
p arg
yield if block_given? # one-line if, and also demonstrating the
# block_given? method
file.close()
end
# Some examples of stuff you can do with arrays
def array_play
@an_array.push "end"
@an_array.unshift "beginning"
@an_array.unshift(@an_array.pop)
@an_array.push(@an_array.shift)
@an_array[1,3] = @an_array[4..-1] #Replace 3 items starting from 1 with the
#items from index 4 to the last, inclusive
for i in 0 ... @an_array.length # iterate up to @an_array.length, exclusive
@an_array[i] += 1
end
@an_array = @an_array.sort # can sort any Comparable
end
# Some examples of stuff you can do with hash maps (dictionaries)
def hash_play
length = @a_map.length #Iteration of hashes is insertion-ordered! nice...
@a_map = Hash.new(3) # default value for missing keys
@another_map = Hash.new() { |k, v| [] } # new, separate list for every key in default
@a_map[:a_key]
#Returns an array whose elements are two-element arrays: [key,val]
@a_map.sort_by { |key,value| value.lowercase }
end
# Some examples of stuff you can do with strings
# Note the default values, where expressions are also allowed
def useful_string_games(arg1="default", arg2=arg1.uppercase)
#The following examples show how you can specify string delimiters
doubleq = %!a double\n quoted string! #like "a double\n quoted string"
singleq = %q%a single \\quoted 'string has only the \\ escape%
heredoc = <<-HERE_DOC_END
this is a here document, that ends
on the next line
HERE_DOC_END
#Some operations on a string
string.chomp! #removes extra line at the end
string.squeeze! #trims runs of repeated spaces
matches = string.split(/regexp/) # returns array of matches between regexp
m1, m2, m3 = string.scan(/regexp/) #returns an array of matches, we
#assign it to several variables
#Examples of assignments from splat args, parallel assignment, etc
m1, (m2, m3), *m4 = [1,2], [3,4], 5, 6 #m1=[1,2], m2=3, m3=4, m4=[5,6]
return m1, m2 #Returning multiple parameters returns an array.
end
alias alias_of_method useful_string_games #you can call it by the alias
def execute_commands
result = `ls` #like perl you can call system commands this way
result = %x{echo "Hi"} #also works
exitval = $? #exit value
Kernel.`("echo Hi") #this is what this calls. (`)
end
#You can define operators on your class:
def +(other)
@param += other.param
self
end
#An example of using Structs (data classes)
def structure
a_struct = Struct.new(:field1, :field2)
a_struct.field1 = "hello"
end
#Some example of using ruby Ranges
def ranges_play
a_range = (1..10)
an_array = a_range.to_a
an_enum = a_range.to_enum
a_range.include?(4)
a_range.reject { |i| i % 2 == 0 }
a_range.max - a_range.min
# To be a range you need to implement succ, returning the next element,
# and the <=> method
while line = gets #When eof is reached, this returns nil which evaluates
#as false and the loop terminates
puts line if line =~ /start/ .. line =~ /end/ #This evaluates to true
#when the first part is
#true, until the second is
#Some loop controls
if (line =~ /fox/)
line = "start"
redo #Restart the loop without evaluating (i.e running line = gets)
elsif (line =~ /exit/)
break #exit the loop
elsif (line =~ /next/)
next #Would happen anyway (just go to the next loop iteration
end
end #There is also until which is while (!cond), and
#loop which is while(true)
0..10 === 5.2 # is true, 5.2 lies in the range
case 5.2 # Case (switch) statement example
when 0...3
puts 1
when 3...7
puts 2
else
puts 10
end
end
=begin This is a multiline comment:
Some examples of using regular expressions. I'm not giving examples of
regexp syntax - that belongs elsewhere and is mostly similar between
ruby/perl/java ...
=end
def regexp_play
"a string" !~ /strong/ #True, there's no strong in string
"a strand".sub(/a/, "p") #gives 'p strand'
"a strand".gsub(/a/, "p") #gives 'p strpnd'
%r|regexp| #useful regexp delimiter
val = 4
/looks for #{val += 1}/ # every time we match we get 4,5,... . But:
/looks for #{val += 1}/o # Will only evaluate val++ on creation.
/.*/m #regularly . doesn't eat newlines. with M (multiline), it does.
m = /ma.ch/.match("this matches stuff") #Now the following is set:
$& == "match" #==m[0]
$' == "es stuff" #== m.post_match()
$` == "this " #== m.pre_match()
end
# Example of throwing and catching exceptions
def exception_handling
begin #This is like "try"
f = File.open("doesnt-exist")
rescue SyntaxError, RangeError => ex #like "catch"ing two exceptions
p ex
rescue #Default is StandardError
p "Hi"
rescue Exception #All classes you can throw/raise are Exceptions, but you
#should make your custom exceptions extend StandardError
exception = $!
raise #re-raise the exception after handling
rescue stuff_i_can_rescue() #You can do any expression/method call that
#returns an Exception call as your parameter!
this_technique_allows_a_whole_lot_of_flexibility = true
else #Called if no exceptions were thrown
p "No errors"
ensure #Like finally, always called
f.close() #or something...
end
end
# This is used later, it's an example of using the 'yield' keyword. When we
# pass a block of code, wherever 'yield' is used that block of code will be
# called with these parameters
def up_and_down (an_array)
for i in 0 ... an_array.length
an_array[i] = yield an_array[i]
end
(an_array.length - 1).downto(0) do |i|
an_array[i] = yield an_array[i]
end
an_array
end
protected # all following methods will have access for any instance of this
# class and subclasses
def useful_iterators
# All of these methods are -iterators-: They get blocks of code and
# execute them on the enumerable (which can be a collection, a file, ...)
@an_array.each_with_index { |val, index| \# Cut a long expression into two
puts "#{index} = #{val}" }
array_plus_ones = @an_array.collect { |val| val + 1 }
first_val_over_30 = @an_array.find { |val| val > 30 }
sum = @an_array.inject(0) { |sum, current| sum+current } #param is initial
#value
product = @an_array.inject(:*) #call the * method on the elements (i.e
#multiply them all)
enum = @an_array.each #without a block, you get back a reusable enumerator
enum.next #and that's how I use it (like Java, C# Iterators)
#enumerators have with_index method built in:
"a string".each_char.with_index { |c,i| p "#{i}:#{c}" }
sliced_enum = @an_array.enum_for(:each_slice, 3) #or pass it a method
#with or without params
end
#This will be protected as well
#Some examples of using numbers to loop
def looping
3.times { puts "Hi" }
1.upto(5) { |i| puts i } #also .downto
50.step(80, 5) # don't supply a block to get an iterator
for val in [1,2,3,4] #like [1,2,3,4].each do |val| p val end
p val
end
end
def caller_fun (one, two, three)
one.call 1
two.call 2
three.call 3
end
# Anonymous functions (lambdas, or Procs).
def lambdas_procs_blocks
ablock = -> var { puts var } #1.9 lambda syntax
bblock = Proc.new do |var| #This gets a block and returns a Proc
puts var
end
ablock.call 5
bblock.call "Hello"
# Call a function which receives Procs with a bunch of lambdas
caller_fun -> arg { p arg*2 }, -> arg { p arg*4 }, -> arg { p arg * 5 }
end
# This is a closure: You get back a method, that whenever it's called, it
# also keeps 'value' in scope and maintains its value
def power_proc_generator
value = 1
-> { value += value }
end
def useful_file_methods
str = File.read("a_file_name") # all the text
end
private # all following methods will only be accessible by self (this)
def only_i_can_access
@a_map[:lala] = 'vavoom'
self.get_a_value_map(:lala)
end
public # Public again for the mixin
include Comparable # Mix-in the methods <=,==, etc. You can mixin any Module
# and all of its methods will become this class' methods
# This is the method that the Comparable mixin uses to define its own
def <=>(other)
self.param <=> other.param
end
def each
# This needs to return each of our internals when called. For instance,
@an_array.each do |elem|
yield elem
end
end
# Now when we include Enumerable we get map, find, etc.
include Enumerable
end
CONSTANT = 5 #Refer to this by Example::CONSTANT
def Example.module_func # Refer to this by Example.module_func
end
end
#You can redefine other classes' methods
class File
def open(*args)
p args
end
end
## Here the "main" starts
ARGV.each do |arg| #iterate over program arguments (Also ARGF for file contents)
# If returns a value - the last value evaluated
value_of_if = if (arg =~ /1/) # Regular expressions
ex = Example::ExampleClass.new(arg) #Initialize a class instance (object)
puts "Created #{ex.class} with id #{ex.object_id}" #object_id is unique
#object reference
elsif (arg == "2")
ex = Example::ExampleClass.new(0)
ex.param_plus_one = arg # Call a setter method
ex.freeze # Further modifications to ex will raise (throw) an exception
p ex # p is good for debug printing - it will print all of the object's
# attributes
else
ex = Example::ExampleClass.new(arg) # initialization
#The following is an example of the 'yield' keyword
#up_and_down calls this block on every element in the array
#and then again on every element in the array, in reverse order
#This block multiplies each item received by the former item seen
firstseen = 1
arr = ex.up_and_down ([3,5,7]) do |arg; ex| #the ;ex allows us to redefine
#'ex'
ex = arg #just in the scope of this block
arg *= firstseen # firstseen is taken from outside the block and saved
firstseen = ex # between block executions
arg
end
p arr
puts "This is the example: #{ex} of #{ex.param}\n" #arbitrary code in string
p value_of_if
end
STDERR.puts ex
end
# Test is any class which inherits from Test::Unit::TestCase
class TestWordsFromString < Test::Unit::TestCase
def test_something #Any method with a name beginning with test runs
expected = []
received = []
assert_equal(expected, received)
end
def show_my_parent
self.superclass
end
end
###
# THE FOLLOWING IS PRETTY ADVANCED, LESS CRITICAL STUFF TO FOLLOW
###
#Create an infinite enumerator: calls to next() will continuously give more and
#move numbers
pow_2_numbers = Enumerator.new do |yielder|
number = 1
loop do
number *= 2
yielder.yield number
end
end
# Useful method to select from infinite enumerators based on a condition.
class Enumerator
# This adds this method into the existing Enumerator class
def infinite_select(&block) #The parameter is always a block
Enumerator.new do |yielder|
self.each do |value|
yielder.yield(value) if block.call(value)
end
end
end
end
#Now use it: Take the first 5 numbers from the series that divide by 16 and
#contain 2
p pow_2_numbers
.infinite_select {|val| val % 16 == 0}
.infinite_select {|val| val.to_s =~ /2/ }
.first(5)
## END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment