Покорим Ruby вместе! Капля девятая

Сегодня у нас действительно маленькая капля — немного остаточной, но серьезной и важной информации о Руби, которая, возможно, будет интересна для пишущих на других языках.

Замораживание объектов



Метод freeze в классе Object защищает объект от изменений, превращая его в константу. После «заморозки» объекта, любая попытка изменить его превратится в ошибку TypeError. Метод frozen? позволит узнать, заморожен ли объект:


a = b = 'Original String'
b.freeze
puts a.frozen? # true
puts b.frozen? # true
a = 'New String'
puts a
puts b
puts a.frozen? # false
puts b.frozen? # true


Переменые a и b сначала ссылаются на один объект, поэтому true в двух первых случаях.

Иногда Руби самостоятельно копирует объекты и замораживает копии. Например, когда вы используете строку в качестве ключа хэша, Руби замораживает ее копию и в дальнейшем использует именно ее. Поэтому даже если строка изменится, на ключ это не повлияет.

Сериализация объектов



В Java есть возможность сериализировать объекты, позволяя сохранять их в бинарном виде в файл и извлекать по мере надобности. Руби называет этот вид сериализации маршалингом (marshaling), при этом используется встроенная библиотека Marshal. Основные методы – dump и load:

f = File.open( 'peoples.sav', 'w' ) 
Marshal.dump( ["bred", "bert", "kate"], f ) 
f.close 

File.open( 'peoples2.sav', 'w' ){ |friendsfile|   
    Marshal.dump( ["anny", "agnes", "john" ], friendsfile ) 
} 

myfriends = Marshal.load(File.open('peoples.sav' )) 
morefriends = Marshal.load(File.open('peoples2.sav' )) 
puts myfriends
puts morefriends


В отличие от Marshal, библиотека YAML позволяет сохранять данные в текстовом формате, о ней как-нибудь в другой раз.

Модули и Примеси



Модули в Руби похожи на классы в том, что они содержат набор методов, константы, другие модули и определения классов. Модули задаются как классы, только слово module используется вместо class. В отличие от классов создать объекты на основе модуля нельзя, модуль не может иметь подклассы. Вместо этого вы добавляете недостающую функциональность класса или отдельного объекта с помощью модуля. Модули – одиночки, нет иерархии и наследования. (Вообще класс Module имеет суперкласс — Object, однако любой созданный модуль суперкласса не имеет).

Есть два предназначения модулей. Во-первых, они служат централизованного хранения констант и методов, например:

module Trig 
	PI = 3.1416 
	# методы класса
	def Trig.sin(x) 
		# ... 
	end 
	def Trig.cos(x) 
		# ... 
	end 
end


Во-вторых, модули позволяют делить функциональность между классами, при включении (include) модуля в класс, его методы добавляются в класс. Такой способ называется примесью (mixin):

module MyModule
   GOODMOOD = "happy"
   BADMOOD = "grumpy"
   
	def greet
		return "I'm #{GOODMOOD}. How are you?"
	end
	
	def MyModule.greet
		return "I'm #{BADMOOD}. How are you?"
	end
end

class MyClass
   include MyModule
	
	def sayHi
		puts( greet )
	end
end

ob = MyClass.new
ob.sayHi
puts(ob.greet)


Руби (в отличие от С++) не разрешает множественное наследование, заменяют его mixin’ы.

Эпилог



Это, конечно, не всё, что хочется рассказать о Руби. Однако в практике чистый Ruby не столь интересен — за рамки консоли мы, пока, выйти не можем, что достаточно ограничивает нас. Я думаю, что и у вас, как у меня, есть желание перейти на огромный полигон Rails, где мы максимально сможем реализовать накопленные знания о языке и развиваться в Руби дальше. Так что я с предложением — давайте не закрывая капли Руби перейдем на изучение фрэймворка Rails/ Как вам? Можно? Жду комментариев!
_________
Текст подготовлен в ХабраРедакторе
+27
17 января 2009, 00:23
24
MaxElc 47,9

комментарии (32)

0
l_nagash #
дерзайте! Главное не останавливаться.

Успехов Вам и новых капель :)
0
invisiblekid #
ех. а я неуспеваю… бысренько капает.
но всеравно спасибо — в избранное!
0
dsCode #
> при включении (include) модуля в класс, его методы добавляются в класс

если более точно, то методы не добавляются в класс, а создается хидден прокси-класс, который вклинивается в цепь наследования классов, и становится родителем для класса объекта. При этом, если класс «А» уже был отнаследован от какого-нибудь класса (например, «B»), то, повторю, родителем класса «А» становится прокси-класс, а уже родителем прокси-класса — класс «B»
НЛО прилетело и опубликовало эту надпись здесь
0
antage #
Он, наверное, имел в виду:

irb(main):001:0> class A; end
irb(main):002:0> class B < A; end
irb(main):003:0> B.ancestors
=> [B, A, Object, Kernel]
irb(main):004:0> module C; end
irb(main):005:0> class B; include C; end
irb(main):006:0> B.ancestors
=> [B, C, A, Object, Kernel]

0
dsCode #
> => [B, C, A, Object, Kernel]

ага, только это .ancestors выдает «C», и, с человеческой точки зрения, это, действительно, так; с машинной же точки зрения — там создается прокси-класс, который имеет ссылку на «C»
0
dsCode #
> Такое происходит, когда объявляешь метод для конкретного экземпляра.
Когда объявляешь методы для экземпляра, создает также virtual-класс для объекта, который и хранит методы конкретного инстанса. Таким образом цепь наследования класса следующая: virtual-класс объекта -> класс объекта -> прокси-класс на модуль (может быть несколько) -> super-класс -> и т.д. super-классы.

> А вот при включении модулей методы, кажется, никуда не копируются.

Так методы никуда и не копируются. Создается прокси-класс, который ссылается на модуль. И этот прокси класс становится super-классом для класса объекта.
0
albedo #
судя по документации, методы, константы и тд. именно добавляются:

www.ruby-doc.org/core/classes/Module.html#M001659

0
dsCode #
Там сказано, опять же, с человеческой точки зрения (т.е. подмешанные методы будут доступны инстансу класса), однако, там ничего не сказано про техническую реализацию. А техническая реализация (с машинной точки зрения) такая, что, все-таки, создается хидден-прокси-класс, который ссылается на модуль, и который становится super-классом для классам объекта.

Это можно посмотреть на примере (модуль подмешивается, однако, затем метод модуля изменяется и объект использует уже новый метод; в то время, как, если рассматривать ваше предположение, то слоты модуля должны подмешиваться к каждому классу свои и становится независимыми от модуля, но это не так):

irb(main):001:0> module A
irb(main):002:1>   def a
irb(main):003:2>     puts "A.a"
irb(main):004:2>   end
irb(main):005:1> end
=> nil
irb(main):006:0> class B; end
=> nil
irb(main):007:0> class C < B
irb(main):008:1>   include A
irb(main):009:1> end
=> C
irb(main):010:0> d = C.new
=> #<C:0x81d22f4>
irb(main):011:0> d.a
A.a
=> nil
irb(main):012:0> module A
irb(main):013:1>   def a
irb(main):014:2>     puts "New A.a"
irb(main):015:2>   end
irb(main):016:1> end
=> nil
irb(main):017:0> d.a
New A.a
0
albedo #
Спасибо за пример, конечно это более логично. Я сам не выдержал, поставил себе Ruby и уже поигрался в консоли )
+1
kronos #
Можно еще было рассказать про extend
Подробнее по ссылке: blog.jayfields.com/2006/05/ruby-extend-and-include.html
+2
VolCh #
Что-то мне кажется рановото для рельс, хотя и в консоли не интересно :)

Может следующим шагом работа с БД (мускул прежде всего), потом как подключить руби к веб-серверу (апачу :) ), написать какое-нить несложное приложение, (вывести форму, записать ее в БД и выслать копию на мыло), а уж потом рельсы. Так хоть будет понятно сколько много рельсы делают для нас у себя внутри.
0
albedo #
Я тут прочем по поводу mixin's сильную фразу

«You have “mixin’s” instead of interfaces.»

www.ruby-lang.org/en/documentation/ruby-from-other-languages/to-ruby-from-java/

Не понятно как эти mixinы заменяют интерфейсы?
0
Iskin #
В Java интерфейсы — это хоть какой-то способ реализовать «множественное наследование». Ну, например, у меня есть класс CoolClass и я хочу от него сделать SuperCoolClass, но добавить возможность запускатся в потоке — классический вопрос множественного наследования. В итоге в Java мы наследуемся от CoolClass b реализуем интерфейс Runnable. В Ruby можно наследоваться от CoolClass и за’mixin’ить модуль Runnable (который в отличии от Java не только объявляет сигнатуры методов но и сам код).
0
Iskin #
Например, когда вы используете строку в качестве ключа хэша, Руби замораживает ее копию и в дальнейшем использует именно ее. Поэтому даже если строка изменится, на ключ это не повлияет.

Что я делаю не так?
>> a = 'one'
=> "one"
>> hash = {a => 1}
=> {"one"=>1}
>> a.frozen?
=> false
0
Iskin #
Понял:
>> hash.keys[0].frozen?
=> true
+1
Iskin #
Что вы сразу к Рельсам? Например, полезно и проще будет поиграться с Rake или Shoes. Так же начать веб можно с Sinatra — она очень аксентична и гораздо проще. Например, не надо будет сразу объяснять про MVC и ORM.
0
Iskin #
Вообще, см. Ruby is not Rails.
0
iv_s #
Sinatra это тоже MVC, ну если точнее то VC:). По-моему лучше рассмотреть какой-нибудь интересный пример, с применением всего изученного.
+3
bewhite #
А почему не рассказать про автоматическое тестирование, сетевые библиотеки, подробнее рассмотреть разнообразные итераторы? Куча тем осталась нераскрытой, а вы торопитесь в Rails.
0
iv_s #
Как то слабо про примеси рассказали. Например я часто их использую для стандартныйх классов:
class String
    def tab n
        "  "*n+self
    end
end


Теперь у любой строки можно вызывать метод tab:
puts "asdf".tab
0
antage #
То что вы написали — не примесь. Вы просто открыли существующий класс и добавили метод. Примесь, это если:
module StringExtension
  def tab(n)
    ("  " * n) + self
  end
end

class String; include StringExtension; end
или
String.send(:include, StringExtension)
0
dsCode #
модуль можно подмешать и extend'ом — для инстансов создастся virtual-класс, и методы модуля будут доступны конкретному инстансу, для класса же extend создаст мета-класс, где будет хранить методы класса.
0
dsCode #
да, отличие примеси от добавления своих слотов в том, что при include создается прокси-класс, который ссылается на модуль (в сам класс ничего не добавляется)
+2
MaxElc #
Итак: module.extend и .include, rake, БД, автотестирование, сетевые библиотеки, примеси и, конечно, пример со всем этим. ОК& :) Может еще есть предложения тем?
0
burgua #
а можешь в конце каждой капли дать ссылки на предыдущие?
0
MaxElc #
habrahabr.ru/blogs/starting_programming/ — здесь все капли
+1
Kane #
mixin по-русски называется «примесь»
0
MaxElc #
Спасибо, исправил!
0
tass #
Еще было бы очень наглядно писать результаты примеров (хотя бы то что выводится на консоль)
0
MaxElc #
Договорились!
0
ptiss #
ну руби и в чистом виде тоже интересен, хотя бы его метапрограмминг.
Нового ничего не узнал, но пишите вы здорово — читал с удовольствием, знаю теперь, что давать для быстрого въезда в тему.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.