wiki:SummaryRuby

Version 7 (modified by Thanatermesis, 10 years ago) ( diff )

--

Reserved words in ruby 2.1:

ENCODING def in self
LINE defined? module super
FILE do next then
BEGIN else nil true
END elsif not undef
alias end or unless
and ensure redo until
begin false rescue when
break for retry while
case if return yield
class
#!/usr/bin/env ruby

$SAFE = 4 # set the security value at the highest prior unless we need more freedom
require 'a'  # will load a.rb, only one time
load 'a.rb'  # will load a.rb, every time called this line

#
# Structure
#

# Classes:
class Animal
    # this sets a public id/varible that can be used for our objects,gg
    # independently of being or not declared in the class, we can already use them
    attr_accessor :lenght, :color  # both read and write
    attr_reader :lenght, :color    # read-only
    attr_writer :lenght, :color    # write-only (setter)

    # this method will be called when creating a new object, it requires
    # arguments if we state them
    def initialize(given_name="Unknown", given_age)

        # the funny here is that if you call "dog = Animal.new(5)", it will
        # understand that you are passing the second parameter and set Unknown
        # to name automatically
        @name = given_name  # create variables for the objects that we can use,
                            # from the given arguments
        @age = given_age

        # variables with @@ instead of @ are accessible only for classes as a
        # whole, not by the instance of the objects, useful for store
        # information relevant to all objects of the same class
        #
        # WARNING: there's an important side-effect behaviour important to consider: http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
        if defined?(@@number_of_squares)
            # variable already exists for all objects when the first one is created
            @@number_of_squares += 1
        else
            # create the variable first time when the first object is created
            @@number_of_squares = 1
        end
    end

    def self.count
        @@number_of_squares  # will return the number of objects when Animal.count is called
    end

    # this is a simple method, used to perform actions to objects (like a function)
    def name
        # when calling "dog.name" will return "lassie" for example, "return" is optional
        return @name
    end

    # if you want to set the age in another moment than when initialized, you
    # need to use this setter function
    def age=(new_age)
        # new_age is the parameter given to the call, like "dog.age = 10",
        # adding = in the name of the method means that the method is used for
        # assign values
        @age = new_age
    end

    def some_error_handled_case
        # raise an error type with a message if a specific case
        raise ArgumentError, "Age is too young" if @age < 2
    end

    # using self here is only accessible when calling the class, instead the
    # object, like: Animal.show_name
    def self.show_name  # self acts like an alias to Animal, so:  def Animal.show_name ...
        puts self  # when called, will print "Animal" (class name)
    end

    # create an alias to whatever thing to another name
    alias show_name list_name


    private
    # things from here only accessible from the object itself (instances)
    # calls: method of an object can call its private methods (inside's class code)
    # cannot call:  dog.method_private
    # note: using 'send' you can do it, it invokes methods by name: dog.send(:method_private)
    def privated_area
        puts "I will only be shown when another method of this class/object calls this private method"
    end

    protected
    # same as private, but callable from different objects of the same class
    # allowing to communicate between them, like for do comparisons between objects
    def age_difference_with(other_person)
        (self.age - other_person.age).abs
    end
    # example:  fred = Person.new(34) ; chris = Person.new(25) ;  puts chris.age_difference_with(fred)

    def compare(c)
        if c.age > age
            "The other object's age is bigger."
        else
            "The other object's age is the same or smaller."
        end
        # example:  chris = Person.new(25) ; marcos = Person.new(34) ; puts chris.compare_age(marcos)
    end

    # back to the elements of code which are public
    public
    def summary
        puts "I can be called normally"
    end

    private :method1, :method2  # we can also define them on this way
end



# Inheritance:
# create a superclass called Doctor from the Person's class (inheritance)
class Doctor < Person
    # we have already defined the method "name" in the Person's class, so we extend it:
    def name
        # when we call .name method from a Doctor's object, will append like: "Dr. Name"
        # "super" walks in inheritance of the same methods in their parents
        "Dr. " + super
    end
end

somebody = Person.new("John")
puts somebody.name # John
who = Doctor.new("James")
puts who.name # Dr. James
puts who.instance_variables # @name



#
# Polymorfism:
#   - calling the method "talk" in Dogs or Cats they do different things, but
#   you can use it in the same way this is very useful when you need to do the
#   same action for different objects, for a example a loop of talking animals
#
class Dog < Animal
    def talk
        puts "Woof!"
    end
end

class Cat < Animal
    def talk
        puts "Meeew"
    end
end



#
# Nested classes:
#   - useful for improve structure/grouping/dependency
#
class Drawing
    class Line
    end

    class Circle
    end

    # you can call circle subclasses directly from Drawing too on this way:
    def Drawing.give_me_a_circle
        Circle.new
    end
end

# the way to call a class inside another class is by using ::
# trying to use directly Circle will fail, so:
my_circle = Drawing::Circle.new



#
# Extensions
#   - already-defined classes (any of them) can be extended or replaced for
#     example to the Fixnum objects we can use .minutes to translate a number to
#     minutes
class Fixnum
    # we can extend any class with improvings or modifications
    def minutes
        self * 60
    end
end
# and so we can use now:
puts 5.minutes # 300



#
# Modules
#
#   They allows to separate things to avoid clashes, similar to classes, but
#   they doesn't allows you to instantiate objects, their methods can be
#   directly used without the need to create objects, they are more like
#   functions that just processes things, let's say toolboxes of features

module Country
    class Ruler
        attr_accessor :name
    end

    def hello
        puts "I can say hello without the need to create an object"
    end
end

module Toolbox
    class Ruler
        attr_accessor :lenght
    end

    # Mix-in: this will print the name of the class which loaded this module
    # useful for introducing methods in other's classes
    #
    # basically is a method defined outside of any class form, so when
    # integrating a module inside an external class it will simply work on it
    def class_name
        self.class.to_s
    end
end

# direct call of the module with its contents
a = Toolbox::Ruler.new
a.lenght = 50
b = Country::Ruler.new
b.name = "Ghengis Khan"

# get the module features and directly use it
include Country
governor = Ruler.new

# you can use the method hello from the module Country, without the need to
# create or use of objects
hello



#
# Flow examples
#

# loops
loop do
    i += 1
    break if i > 100  # break da loop
end

while (i == 10)
    puts "i = 10"
end

until (i > 10)
    puts "i finally reached 10"
end


# if
if 2 == 1
    puts "universe is broken"
end

unless 2 == 2
    puts "universe is broken"
end


# case
fruit = "orange"
case fruit
    when "orange"
        color = "orange"
    when "apple"
        color = "green"
    else
        color = "unknown"
end


# ternary operator
puts  x > 10 ? "higher than 10" : "lower or equal to 10"



#
# Structures:
# it is an easy and fast way to define attributes and hold data, same as to
# create an entire class with defined initializators (but without require
# strictly to set all the parameters)
Person = Struct.new(:name, :gender, :age)
fred = Person.new("Fred", "male", 50)



#
# variable substitution ways:
#
foo = "12345678901234567890"
# puts the first 10 chars in different ways:
puts foo[1,10]
puts foo[0..9]
puts foo.slice(0,10)
puts foo.match("^..........").to_s



#
# Arrays:
#
a = [1, 2, 3, "four"]
a << 5      # append an item
a.push 6    # append an item
puts a.empty? # false
puts a.include?("four") # true



#
# Hashes (associative arrays)
#
dictionary = { 'cat' => 'feline animal',
               'dog' => 'canine animal',
               'tags' => ['books',
                   'libraries',
                   'knowledge'] }
puts dictionary['cat'] # shows description
puts dictionary['tags'].first # shows the first array element for the subarray tags
dictionary.delete("cat")
dictionary.delete_if { |key, value| value =~ /feline/ }



#
# Code blocks
#
def each_vowel
    %w{a e i o u}.each { |vowel| yield vowel }
end

each_vowel { |v| puts v + " is a vowel" }
# the delimiter of "each" can be modified passing an argument like .each(',') or
# changing the input delimiter variable $/
#
# More exactly, yield runs "in the inverse way" including its parameters, what you pass to a call to the function that includes a yield, this is a more clear example:
def test
   yield 5
   puts "method test"
   yield 100
end
test {|i| puts "You are in the block #{i}"}

# so you got:
# You are in the block 5
# method test
# You are in the block 100




#
# Errors handling
#
class GetWeb
    begin
        # block of code where can happen errors
        a = (10 / 0)  # if this doesn't works...
    rescue => e
        a = 10  # instead of just exiting we can simply assign a default behaviour and continue
        puts "class of exception is: " + e.class.to_s  # show some debug reason of our error
    end
end

# another example:
data = ""
begin
    #<..code to retrieve the contents of a Web page..>
    #data = <..content of Web page..>
rescue ZeroDivisionError
    #... code to rescue the zero division exception here ...
rescue YourOwnException
    #... code to rescue a different type of exception here ...
rescue
    puts "The Web page could not be loaded! Using default data instead."
    #data = <..load data from local file..>
end
puts data



#
# Debug
#
# If you want to run a script in debug mode, "ruby -r debug script.rb", use
# step, break, watch, cont, etc... 
# you can found some basic example in the book "beginning ruby" pag. 190



# Catch & Throw:
# note: catch & throw is useless and confusing, avoid it
# catch & throw works in a similar way as rescue/raise, but using symbols rather than exceptions
catch(:finish) do
    1000.times do
        x = rand(1000)
        # we will exit the loop "catch" containing the symbol :finish if we got this case
        throw :finish if x == 123
    end

    puts "Generated 1000 random numbers withoutgenerating 123!"
end

# another catch & throw example
def routine(n)
    puts n # final result will produce: 3 2 1 0
    throw :done if n <= 0 # exit inifite nested loop if case happens
    routine(n-1) # nested itself, infinitely
end

catch(:done) { routine(3) }



#
# Dynamic Code Execution
#

# executes the code created dynamically
eval "puts 2 + 2"

# similar:
my_number = 15
my_code = %Q{#{my_number} * 2}
puts eval(my_code)

# String interpolation makes the eval methods powerful tools for generating
# different features on the fly. This ability is a power unseen in the majority
# of programming languages
# - this method adds an accessor to another class,
# dynamically using class_eval and interpolating a name given externally
def add_accessor_to_person(accessor_name)
    Person.class_eval %Q{
    attr_accessor :#{accessor_name}
    }
end

# It’s possible to take the previous example a lot further and add an
# add_accessor method to every class by putting your class_eval cleverness in a
# new method, defined within the Class class (from which all other classes descend):
class Class
    def add_accessor(accessor_name)
        self.class_eval %Q{
            attr_accessor :#{accessor_name}
        }
    end
end

# You can eval code in instances of objects too:
class MyClass
    def initialize
        @my_variable = 'Hello, world!'
    end
end

obj = MyClass.new
obj.instance_eval { puts @my_variable }  # add extra code to an existing object and execute it

# In the same way you can define methods for existing objects
obj.instance_eval do def new_method ; puts 'born new method in an alive object' ; end ; end
# better way to define a method for an existing object, if you dont want to use
# dynamic code execution of eval
obj.new_method do def new_method ; puts 'born new method in an alive object' ; end ; end


# example of an "attr_accessor" equivalent writed in dynamic code, which will work in the same way:
class Class
    def add_accessor(accessor_name)
        self.class_eval %Q{
        def #{accessor_name}
            @#{accessor_name}
        end
        def #{accessor_name}=(value)
            @#{accessor_name} = value
        end
        }
    end
end

# You can use this technique to create a multitude of different “code
# generators” and methods that can act as a “macro” language to perform things
# in Ruby that are otherwise lengthy to type out.



#
# Forks & Processes
#

# process-ID is returned in the parent but 'nil' in the child, use this to
# determine which process a script is in
if fork.nil?
    exec "ruby other-file.rb"
end

# fork a child and wait for it
child = fork do
    sleep 3
    puts "Child says 'hi'!"
end
puts "Waiting for the child process..."
Process.wait child
puts "All done!"



#
# Unit Testing
#
#   - default in stdlib is Minitest, but there's also: rspec, minitest,
#     test/unit, minispec, bacon, testrocket... rspec is also very used

# create a method for test it later
class String
    def titleize
        # we want to use this method for print results like: "This Is A Test"
        self.capitalize
        # and we will end later in improving the code like these next commented
        # lines, because we haven see that the previous test failed in some cases
        #self.gsub(/\b\w/) {|letter| letter.upcase }
        #self.gsub(/\s\w/) {|letter| letter.upcase }
        #self.gsub(/(\A|\s)\w/) {|letter| letter.upcase }
    end
end

puts "this is a test".titleize # this will print "This is a test", but we don't want this result, so for avoid bugs:

# method 1 for testing (manual testing)
# let's add some "assertion" checkers
#
# if our method doesn't give us what we exactly expect, fail
raise "Fail 1" unless "this is a test".titleize         == "This Is A Test"
# what happens with numbers? let's add this because this is what we expect in any case
raise "Fail 2" unless "another test 1234".titleize      == "Another Test 1234"
# and with other chars like ' ?
raise "Fail 2" unless "We're testing titleize".titleize == "We're Testing Titleize"

# method 2 for testing
require 'test/unit'
# there's also "Minitest" (included in stdlib) and "Bacon" for unit testing
class TestTitleize < Test::Unit::TestCase
    def test_basic
        assert_equal("This Is A Test", "this is a test".titleize)
        assert_equal("Another Test 1234", "another test 1234".titleize)
        assert_equal("We're Testing", "we're testing".titleize)
    end
end

# method 3 for testing: use Minitest instead, seems like more easier and fast to use option
module DataStore::AdapterSpec
    it "returns nil for an invalid key" do
        @adapter.get(:invalid).must_equal nil
    end

    it "can set a value" do
        @adapter.set(:foo, 42)
        @adapter.get(:foo).must_equal 42
    end
end
# from:  http://wojtekmach.pl/blog/2013/07/17/sharing-examples-in-minitest/
# more: http://blog.arvidandersson.se/2012/03/28/minimalicous-testing-in-ruby-1-9



#
# Misc
#
# run when finish
at_exit { puts "#{Dir.pwd}" } # enqueue a job for when the application has finished to run
# trap
trap("SIGINT") do
    puts "signal INT trapped!, what we should do?"
end



# Using Files
puts "It exists" if File.exist?("text.txt")

File.open("text.txt") { |f| puts f.gets }  # show file contents (cat)

f = File.new("text.txt", "r")
puts f.gets
f.close

data = File.read("text.txt")
array_of_lines = File.readlines("text.txt")

f = File.open("text.txt")
puts f.gets
puts "position in file is: " + f.pos
f.pos = 8
puts "position in file has moved to: " + f.pos
f.close # close the file when finish!

# by using File.open in a block, is not needed to close the file when finish
File.open("text.txt", "w") do |file| file.puts "Hi" end

# appending
f = File.open "text.txt", "a"
f.puts Time.now
f.close

# See the modified time of a file
f = File.mtime "text.txt"
puts f.hour # since the object returned (f) is a Time object, we can treat him as it


#
# Typos / Pitfails
#

# if you put a space before ( it is then not interpreted as arguments, ( needs to be always joint with the call
File.open ("text.txt", "w")

# if you put more than one space like here it is then interpreted as a(+b)
a  + b


#
# Documentation (generating your own)
#
# Similar to doxygen, just write comments using this syntax and by running
# "rdoc file.rb" (or yard?) you will obtain nicely html's generated
#
# In documentations, "Array#sample" means the instance method of a class

#= RDoc Example
#
#== This is a heading
#
#* First item in an outer list
# * First item in an inner list
# * Second item in an inner list
#* Second item in an outer list
# * Only item in this inner list
#
#== This is a second heading
#
#Visit www.rubyinside.com
#
#== Test of text formatting features
#
#Want to see *bold* or _italic_ text? You can even embed
#+text that looks like code+ by surrounding it with plus
#symbols. Indented code will be automatically formatted:
#
# class MyClass
#   def method_name
#     puts "test"
#   end
# end
#
#--
# This section is hidden from RDoc and could contain developer
# notes, private messages between developers, etc.
#++
# RDoc begins processing again here after the ++.
#

# by adding in a class or element "#:nodoc: all" there's no docs generated (for
# the entire class)




# SAFE levels
#
# Value of $SAFE         Description
# 0     No restrictions. This is the default safe level.
# 1     Potentially unsafe methods can’t use tainted data. Also, the current
#       directory is not added to Ruby’s search path for loading libraries.
# 2     The restrictions of safe level 1, plus Ruby won’t load any external
#       program files from globally writable locations in the filesystem. This is to
#       prevent attacks where hackers upload malicious code and manipulate existing
#       programs to load them. Some potentially dangerous methods are also
#       deactivated, such as File#chmod, Kernel#fork, and Process::setpriority.
# 3     The restrictions of level 2, plus newly created objects within the
#       program are considered tainted automatically. You also cannot untaint objects.
# 4     The restrictions of level 3, plus nontainted objects created prior to
#       the safe level being set cannot be modified. You can use this to set up an
#       execution environ- ment in a lower safe mode, and then provide a way to
#       continue execution while protecting the original objects and environment.





#
# MORE:
#
# regex:
# Try regular expressions in realtime!  http://rubular.com/
#
# references:
# http://www.zenspider.com/Languages/Ruby/QuickRef.html
# http://www.tutorialspoint.com/ruby/ruby_quick_guide.htm
# "Begining Ruby - book": useful classes and methods, errors, regex, variables, etc...  from pag. 575
#
# standard lib:
# http://www.ruby-doc.org/stdlib-2.1.0/

Note: See TracWiki for help on using the wiki.