{{{ #!ruby #!/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, # 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 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 Doctro 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 a.push 6 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 $/ # # Errors handling # class GetWeb begin # block of code where can happen errors a = (10 / 0) # if this doesn't works... rescue => e a = 10 # let's still use the original value in failed cases, instead of exit/error puts "class of exception is: " + e.class.to_s 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 # by using File.open in a block, is not needed to close the file when finish File.open("text.txt", "w") do |entry| entry.puts "Hi" end # note that the next line is invalid, if you put a space before ( then is not an argument list what you pass to it #File.open ("text.txt", "w") do |entry| entry.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 # # 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/ }}}