Created
April 8, 2012 16:22
-
-
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## 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, [email protected] | |
## | |
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