1 | 2 | 3 | 4

Ruby за двадцать минут

Давайте теперь создадим приветствующий объект и используем его:

irb(main):035:0> g = Greeter.new("Pat")
=> #<Greeter:0x16cac @name="Pat">
irb(main):036:0> g.say_hi
Hi Pat!
=> nil
irb(main):037:0> g.say_bye
Bye Pat, come back soon.
=> nil

Как только объект g создан, он запоминает, что имя – Pat. Хмм, а что если мы попробуем получить имя напрямую?

irb(main):038:0> g.@name
SyntaxError: compile error
(irb):52: syntax error
        from (irb):52

Неа, не можем этого сделать.

Внутренности объекта

Переменные объекта скрыты внутри объекта. Они не супер скрыты, вы можете увидеть их, когда вы просматриваете объект и есть несколько других способов доступа к ним, но Ruby использует хороший объектно-ориентированный подход – держать данные такого типа скрытыми.

Итак, какие методы существуют для объектов класса Greeter?

irb(main):039:0> Greeter.instance_methods
=> ["method", "send", "object_id", "singleton_methods",
    "__send__", "equal?", "taint", "frozen?",
    "instance_variable_get", "kind_of?", "to_a",
    "instance_eval", "type", "protected_methods", "extend",
    "eql?", "display", "instance_variable_set", "hash",
    "is_a?", "to_s", "class", "tainted?", "private_methods",
    "untaint", "say_hi", "id", "inspect", "==", "===",
    "clone", "public_methods", "respond_to?", "freeze",
    "say_bye", "__id__", "=~", "methods", "nil?", "dup",
    "instance_variables", "instance_of?"]

Ого! Это куча методов. А мы объявили всего два. Что здесь происходит? Ну, это все методы для объектов класса Greeter, полный список, включая те, которые определены родительскими классами. Если мы хотим список тех методов, которые мы определили в классе Greeter, мы можем сказать не включать методы родительских классов, посредством передачи параметра false, означающего как раз это.

irb(main):040:0> Greeter.instance_methods(false)
=> ["say_bye", "say_hi"]

Ага, это больше похоже на то, что нам нужно. Итак, давайте посмотрим на какие методы наш объект может отвечать:

irb(main):041:0> g.respond_to?("name")
=> false
irb(main):042:0> g.respond_to?("say_hi")
=> true
irb(main):043:0> g.respond_to?("to_s")
=> true

Как мы видим, он знает say_hi и to_s (метод, служащий для превращения чего-либо в строку. Определен для всех объектов по-умолчанию), но он не знает name.

Изменяем классы – никогда не поздно

Но что если вы хотите иметь возможность получить или изменить имя? Ruby предоставляет простой способ для получения доступа к переменным объекта.

irb(main):044:0> class Greeter
irb(main):045:1>   attr_accessor :name
irb(main):046:1> end
=> nil

В Ruby вы можете открыть класс снова и изменить его. Изменения будут актуальны для всех новых объектов, которые вы создаете, и даже доступны для существующих объектов класса. Итак, давайте создадим новый объект и поиграем с его свойством @name.

irb(main):047:0> g = Greeter.new("Andy")
=> #<Greeter:0x3c9b0 @name="Andy">
irb(main):048:0> g.respond_to?("name")
=> true
irb(main):049:0> g.respond_to?("name=")
=> true
irb(main):050:0> g.say_hi
Hi Andy!
=> nil
irb(main):051:0> g.name="Betty"
=> "Betty"
irb(main):052:0> g
=> #<Greeter:0x3c9b0 @name="Betty">
irb(main):053:0> g.name
=> "Betty"
irb(main):054:0> g.say_hi
Hi Betty!
=> nil

Использование attr_accessor определяет два новых метода для нас: name, чтобы получать значение переменной @name и name=, чтобы это значение назначать.

Приветствовать всех и вся, MegaGreeter никого не забудет!

Этот приветствующий не особо интересен, он может иметь дело только с одним человеком. Что если мы хотим некий класс MegaGreeter, который может приветствовать и весь мир, и одного человека, и целый список людей?

Давайте запишем его в файл, вместо того чтоб писать построчно в IRB.

Чтобы выйти из IRB, наберите quit, exit или просто нажмите Control-D.

#!/usr/bin/env ruby

class MegaGreeter
  attr_accessor :names

  # Create the object
  def initialize(names = "World")
    @names = names
  end

  # Say hi to everybody
  def say_hi
    if @names.nil?
      puts "..."
    elsif @names.respond_to?("each")
      # @names is a list of some kind, iterate!
      @names.each do |name|
        puts "Hello #{name}!"
      end
    else
      puts "Hello #{@names}!"
    end
  end

  # Say bye to everybody
  def say_bye
    if @names.nil?
      puts "..."
    elsif @names.respond_to?("join")
      # Join the list elements with commas
      puts "Goodbye #{@names.join(", ")}.  Come back soon!"
    else
      puts "Goodbye #{@names}.  Come back soon!"
    end
  end

end


if __FILE__ == $0
  mg = MegaGreeter.new
  mg.say_hi
  mg.say_bye

  # Change name to be "Zeke"
  mg.names = "Zeke"
  mg.say_hi
  mg.say_bye

  # Change the name to an array of names
  mg.names = ["Albert", "Brenda", "Charles",
    "Dave", "Engelbert"]
  mg.say_hi
  mg.say_bye

  # Change to nil
  mg.names = nil
  mg.say_hi
  mg.say_bye
end

Сохраните этот файл как “ri20min.rb” и запустите его как “ruby ri20min.rb”. Результат должен быть следующим:

Hello World!
Goodbye World.  Come back soon!
Hello Zeke!
Goodbye Zeke.  Come back soon!
Hello Albert!
Hello Brenda!
Hello Charles!
Hello Dave!
Hello Engelbert!
Goodbye Albert, Brenda, Charles, Dave, Engelbert.  Come
back soon!
...
...

Здесь куча новых вещей в последнем примере, на которые мы взглянем подробнее.