вівторок, 18 вересня 2007 р.

Качина типізація

Латентна, неявна або качина типізація (калька з англ. Duck typing) - вид динамічної типізації, яка використовується в деяких мовах програмування (Smalltalk, Python. Ruby, Boo), коли значення змінної визначає, те що вона може робити. Тобто є об'єкт, який реалізує інтерфейс, якщо він містить всі методи цього інтерфейсу, незалежно від зв'язків в ієрархії наслідування.
Англійська назва посилається на "качиний тест" (duck test) - "If a bird looks like a duck, swims like a duck and quacks like a duck, then it's probably a duck." ("Якщо птах виглядає як качка, плаває як качка і крякає як качка, це ймовірно і є качка").
Качина типізації піклується тільки про ті аспекти об'єкта, які використовуються, замість типу об'єкту безпосередньо.Наприклад, у мові без качиної типізації, можна створити функцію яка бере об'єкт Duck (Качка) і викликає його методи swim (плавати) quack (крякати). У мові з качиною типізацією, еквівалентна функція може взяти об'єкт будь-якого типу і викликати методи "плавати" і "крякати". Якщо об'єкт немає методів до яких звертаються, функція сигналізує run-time error (помилку виконання) .
Качина типізація - це фундаментальна частина мови програмування Ruby. Можна відмітити Ruby, не оголошує види змінних чи методів - все є тільки деяким видом об'єкту. Об'єкти Ruby (на відміну від об'єктів у деяких інших об'єктно-орієнтованих мовах) можуть бути змінені індивідуально. Ви завжди можете додавати методи до об'єкта. В Ruby поведінка і можливості об'єкту можуть відрізнятися від тих, що забезпечуються його класом.
В Ruby ми залежимо менше від типу (або класу) об'єкту і більше від його можливостей (методів). Відтепер, качина типізація має на увазі тип об'єкту, як те що з ним можна зробити, а не те, чим є об'єкт. Качина типізація посилається на тенденцію Ruby менше турбуватися про клас об'єкта, і більше концентруватися на його методах.
Якщо об'єкт плаває як качка і крякає як качка, то інтерпретатор Ruby щасливий розглядати його, ніби то це качка.
Розглянемо наступний приклад:
# Check whether the object defines the to_str method
puts ('A string'.respond_to? :to_str) # => true
puts (Exception.new.respond_to? :to_str) # => true
puts (4.respond_to? :to_str) # => false

Вищеописаний приклад - це найпростіший приклад філософії "качиної типізації"в Ruby, якщо об'єкт крякає як качка (або діє як рядок(String)), розглядати його як качку (або рядок(String)). При першій-ліпшій можливості, ви повинні розглядати об'єкт згідно методів, які він визначає, замість класів з яких він успадковується або модулів, у які він включений.
Шоб краще зрозуміти качину типізацію введемо наступні три класи - Duck, Goose і DuckRecording.

class Duck
def quack
'Quack!'
end
def swim
'Paddle paddle paddle...'
end
end

class Goose
def honk
'Honk!'
end
def swim
'Splash splash splash...'
end
end

class DuckRecording
def quack
play
end
def play
'Quack!'
end
end

def make_it_quack(duck)
duck.quack
end

puts make_it_quack(Duck.new) # => Quack!
puts make_it_quack(DuckRecording.new) # => Quack!

def make_it_swim(duck)
duck.swim
end

puts make_it_swim(Duck.new) # => Paddle paddle paddle...
puts make_it_swim(Goose.new) # => Splash splash splash...

Метод який вказує Качці (Duck) крякати, працює і для DuckRecording завдяки качиній типізації. Як і метод, який вказує Качці (Duck) плавати, буде працювати і для Гуски (Goose).

Використанні ресурси:
http://en.wikipedia.org/wiki/Duck_typing
http://rubylearning.com/satishtalim/duck_typing.html
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/100511

четвер, 13 вересня 2007 р.

Apache 2 + Ruby 1.8 + mod_ruby + eruby

В цій статі ми розглянемо встановлення і настройку Web-сервера Apache 2, Ruby 1.8 (mod_ruby + eruby) для використання на локальній машині під ОС Linux (Ubuntu/Debian).

Інсталяція:
Для початку необхідно встановити всі необхідні пакети:

$ sudo apt-get install ruby
$ sudo apt-get install apache2
$ sudo apt-get install libapache2-mod-ruby eruby


Я буду використовувати конфігурацію Apache "по замовчуванню".
Після інсталяції mod_ruby, автоматично буде створений файл /etc/apache2/mods-available/ruby.load з таким вмістом:
LoadModule ruby_module /usr/lib/apache2/modules/mod_ruby.so

Настройка:
В дистрибутивах Ubuntu/Debian(можливо і в інших) конфігураційний файл httpd.conf розбитий на велику кількість менших файлів у директорії /etc/apache2.
Для конфігурації mod_ruby створимо файл /etc/apache2/mods-available/ruby.conf. Також необхідно створити посилання на нього до каталогу /etc/apache2/mods-enabled, щоб він завантажувався автоматично.
sudo ln -s /etc/apache2/mods-available/ruby.conf /etc/apache2/mods-enabled/ruby.conf

З докладним описом процесу інсталяції і настройки можна ознайомитися тут .

Я буду використовувати зв'язку eRuby + mod_ruby.
eruby - це виконання eRuby(Embedded Ruby), що служить для включення коду Ruby у HTML та інші документи. Має розширення *.rhtml. Код включається за допомогою пари роздільників "<%" і "%>". Коли мене запитують про eRuby, я завжди відповідаю що це типу PHP, тільки для Ruby :). Докладніше про eRuby тут i тут .
Додамо наступні рядки до /etc/apache2/mods-available/ruby.conf.
<IfModule mod_ruby.c>
RubyRequire apache/eruby-run

# Обробляти файлами з директорій /eruby як файли eRuby
<Location /eruby>
SetHandler ruby-object
RubyHandler Apache::ERubyRun.instance
</Location>

# Обробляти *.rhtml файли як eRuby файли
<Files *.rhtml>
SetHandler ruby-object
RubyHandler Apache::ERubyRun.instance
</Files>
</IfModule>

<Directory /var/www/eruby>
AllowOverride All
Options ExecCGI
Order allow,deny
Allow from all
</Directory>

AddType text/html .rhtml


Існує одна проблема, коли ви здійснюєте запит до директорії(наприклад, http://localhost/eruby), mod_ruby викликає помилку (щось типу /usr/lib/ruby/1.8/apache/eruby-run.rb:101:in `compile_file': Is a directory - /var/www/eruby/ (Errno::EISDIR). Щоб виправити це додамо наступний рядок до ruby.conf .
RedirectMatch ^/eruby/?$ /eruby/index.rhtml


Перезапустимо веб-сервер командою:
$ sudo /etc/init.d/apache2 restart


Створимо файл /var/www/eruby/index.rhtml:
<%= Time.now %>

Перейдемо у Web-броузері за адресою http://localhost/eruby і побачимо поточну дату і час.
Вуаля, готово!

Відображення помилок mod_ruby:
mod_ruby не терпить невдачу з попередженнями(warnings) і повідомлення про попередження не записуються до log-файлу (наприклад, /var/log/apache2/error.log чи будь-який інший), тільки повідомлення про помилки записуються до цього файлу. Так що забудьте про попередження під mod_ruby. Можливо є якийсь діагностичний рівень, на якому це можна встановити, але мені не відомо про це :(.
Коли Ruby зазнає синтаксичну або іншу помилку він повертає "Internal Server Error (500)" і повідомлення про помилку записується до log-файлу Apache. Щоб постійно слідкувати за помилками доводиться читати цей файл або використовувати утиліту tail:
$ sudo tail -f /var/log/apache2/error.log

Постійно доводиться переключатися у консоль, шо не дуже зручно у процесі написання коду.
Для виведення помилок на Web-сторінку використовується опція ErrorDocument Apache.
Створимо файл /var/www/eruby/errors/error_500.rhtml:
<%
r = Apache.request
r.content_type = "text/plain"
r.send_http_header
print "Ruby Error: ",r.prev.uri,"\n\n"
print r.prev.error_message
%>


І додамо у /etc/apache2/mods-available/ruby.conf наступний рядок.
ErrorDocument 500 /eruby/errors/error_500.rhtml


Перезапустимо Apache:
$ sudo /etc/init.d/apache2 restart


Тепер при виникненні помилки(але не попередження) фактичне повідомлення про невдачу буде показане не сторінці. Рекомендую використовувати даний метод лише під час розробки і відналагодження.

понеділок, 20 серпня 2007 р.

Ruby лучший друг программиста

Ruby лучший друг программиста. Саме так написано на головній сторінці нового російськомовного порталу по Ruby. Приємно, що на пострадянському просторі збільшується цікавість до цієї мови програмування. Сайт вартий уваги хоча б тому, що він один із не багатьох. На даний момент портал ще досить сирий. Подивимось, що з цього виросте.

субота, 18 серпня 2007 р.

methodphitamine <^_^>


http://jicksta.com/articles/2007/08/04/the-methodphitamine

Джей Філіпс (Jay Phillips) у свому блозі описав прості і ефективні можливості мови Ruby. Я в захваті. А-а-а-а! Відсипте дві!

rbtorstat - простий ґенератор веб-сторінки rTorrent

rbtorstat (r - ruby, rb - ruby, b - bit, btor - bittorrent, tor - torrent, stat - status) - простий ґенератор веб-сторінки, написаний (мною) на мові програмування Ruby, який показує інформацію статусу про rTorrent - консольний bittorrent клієнт для Unix/Linux систем. Це дає змогу швидко переглядати список torrent-ів і їх статус у веб-браузері, без необхідності коннектитись на віддалену машину (наприклад по ssh, далі screen + rTorrent).

Скрипт зчитує збережені дані про сесію rtorrent, створює звіт про статус torrent файлів і виводить код HTML на stdout, який далі можна перенаправити у файл і зробити його публічно доступним на веб-сервер. rbtorstat не підтримую віддалене адміністрування rTorrent. Але завдяки тому що у версії 0.7.5 була додана можливість віддаленого адміністрування використовуючи засоби xmlrpc (докладніше про це можна почитати тут) це не складе особливих труднощів. rbtorstat, як видно із заголовку переслідує зовсім інші цілі.

Початковий код rbtorstat базується на основі rtorstat, написаному Джорджом Нотарасом(
George Notaras) на мові програмування Python.

Залежності:
  • Ruby;
  • bencode - бібліотека Ruby для перетворення даних Bencode (що використовується в протоколі BitTorrent) у послідовну форму;
  • rTorrent - консольний BitTorrent-клієнт для Linux, написаний на мові програмування C++, сконфігурований для зберігання даних про сесії (про це докладніше далі);
Конфігурація rTorrent:
rbtorstat повинен зчитувати дані
сесії torrent, тому rTorrent потрібно налаштувати, так щоб зберігати цю інформацію у певну директорію. Далі наведений конфігураційний файл (.rtorrent.rc) :
port_range=6900-6999
download_rate = 64
upload_rate = 16
session = /opt/rtorrent
# Keeps session data files even if torrent has finished
session_on_completion = yes
session_lock = yes
# Save session data every 5 mins, with a 4 min offset
schedule = session_save,240,300,session_save=

Остання директива (
schedule) вказує rTorrent щоб зберігати дані сесії кожні 5 хвилин з відгалуженням 4 хвилини. Це означає, що дані сесії будуть збережені, наприклад, о 00:04, 00:09, і т.д.

Використання:

Для виконання скрипта достатньо вказати у вихідному файлі коректний шлях до директорії із даними сесії rTorrent. На виході буде отриманий код HTML, який можна перенаправити у файл, наприклад так:
$ ruby rbtorstat.rb > /var/www/index.txt

Крім того його можна прописати до cron:
*/5 * * * * root ruby /path/to/rbtorstat.rb /opt/rtorrent/ > /var/www/index.html

Скрипт буде запускатись кожні 5 хвилин.
Результат можна переглянути на веб-сервері.
Завантажити:
Наразі чорновий варіант скрипта можна завантажити за адресою:
http://anton.linux.googlepages.com/rbtorstat.rb

Реальний приклад:
http://info.opoz.homeip.net/rbtorstat.html

Плани на майбутнє:
Скрипт написаний виключно у приватних цілях і підтримуватись у майбутньому не буде.

Програмування:
Для того щоб обробляти torrent-файли або файли сесії rTorrent (які є тими ж *.torrent) у Ruby потрібно проінсталювати бібліотеку bencode:
$ gem install bencode

Приклад скрипта для завантаження torrent-файлу у хеш(Hash):
require 'rubygems'
require 'bencode'
t_dict = BEncode.load_file(torrent)

Дізнатись докладніше про специфікація протоколу Bittorrent тут .
Наприклад:
t_dict['info']['name'] # назва файлу
t_dict['info']['length'] # довжина файлу у байтах

Крім того rTorrent має декілька специфічних директив:
t_dict['rtorrent']['total_uploaded'] # кількість вивантажених байт
t_dict['rtorrent']['complete'] # статус закачки (завантажився чи ні?)
Просто і плоско до бєзобразія :). Успіхів.

Підбірка літератури по Ruby (on Rails)

Моя підбірка літератури по мові програмування Ruby, а також Ruby on Rails.
Поповнюється по мірі надходження нових книг.

Більшість книг є англомовними

Я сам не пірат, поважаю авторське право. Ті книги, які продаються на Заході (читайте: amazon.com) коштують $20 . В нашій країні не перекладені оригінали (можливо, навіть копії?) продаються по ціні мінімум X*2 раза більшії. Хоча особисто я поки не зустірчав їх у вільному продажу.
Тому користуємось тим що є. Це ж тільки для ознайомлення :)

пʼятниця, 3 серпня 2007 р.

WxRuby Beta

А тим часом тихо і непомітно вийшла перша бета версія бібліотеки WxRuby2
Судячи з changelogу виправлено величезне число багів і покращена роботу з пам'яттю. І це дійсно так і є. Тепер програми не падають просто так :).
Крім того додано класи для Advanced User Interface (AUI). М-м-м... смачна штука!!! Варто якось спробувати.

Побудова власно пакету gem

Цією статтею я хочу розпочати невеликий цикл записів про створення і підтримку власного коду Ruby. В більшій мірі я це пишу для себе, як шпаргалку :). Можливо це комусь і стане у нагоді.

В цій статті я хочу поділитись досвідом побудови інсталяційного пакету gem для Ruby. Якщо Вам здається, що Ви маєте бібліотеку, яка, можливо, була б корисна для інших людей, Ви можете легко упакувати її як gem і розповсюджувати її через Інтернет (для Ruby існує спеціалізований сайт для сторонніх бібліотек - http://rubyforge.org/).
Процес перетворення Вашого коду у пакет gem складається з двох частин. Не хвилюйтесь, Вам не доведеться змінювати ваш код, щоб пристосувати його до gem. По-перше, Вам необхідно змінити(створити) структуру каталогів, яка б відповідала потребам gem. Далі для прикладу я буду використовувати свій власний проект під назвою uaenv - http://rubyforge.org/projects/uaenv/.
Структура каталогу організована, як вказано нижче:
  • /uaenv/lib : цей каталог містить весь код Ruby, який має безпосереднє відношення до проекту.
  • /uaenv/pkg : каталог, у який власне і буде згенерований gem.
  • /uaenv/tests : каталог, у якому буде знаходитись код для тестів.
  • /uaenv/README : цей файл повинен містити опис проекту, ліцензію, і взагалі будь-яку інформацію за бажанням.

Це мінімум для того щоб створити gem.
Складніші проекти можуть містити наступні директорії:
  • /uaenv/bin : каталог для будь-яких виконуваних скриптів, які є частиною проекту.
  • /uaenv/doc : додаткова документація по проекту.

Це буде корисним, якщо існує необхідність додати нові інструменти командного рядка при встановленні в систему.
RubyGems підтримує зберігання не тільки джерельних кодів Ruby. Коли користувач встановлює пакет gem на комп'ютері, gem намагається скомпілювати додатковий код як частину установки. Перевагою використання gem таким чином є, те що не-Ruby код буде пов'язаний з фактичними бібліотеками, які встановленні на комп'ютері. Наприклад, це трапляється коли Ви встановлюєте RMagick. Якщо інсталятор не знайшов належних бібліотек(ImageMagick), він потерпить невдачу. Крім того до пакету можна додати заздалегідь скомпільовані файли, щоб уникнути проблеми неможливості компілювати код на комп'ютері призначення.
Як тільки Ви влаштували свій код у коректну структуру каталогів, Ви можете зосередитись на іншій частині процесу побудови gem - специфікації gem. Це декларація, яка надає всю необхідну інформацію щоб побудувати gem. Ви можете створити специфікацію gem, як автономний файл, але простіше якщо Ви будете використовувати стандартні засоби, створите Rakefile. Це значно спростить процес побудови.
Нижче наведений приклад файлу Rakefile, який повинен міститись у кореневому каталозі проекту uaenv.
require 'rubygems'
Gem::manage_gems
require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "uaenv"
s.version = "0.0.3"
s.author = "Anton Maminov"
s.email = "anton.linux @nospam@ google.com"
s.homepage = "http://rubyforge.org/projects/uaenv"
s.rubyforge_project = "uaenv"
s.summary = "Simple processing of Ukrainian strings"
s.files = FileList['lib/**/*.rb','test/*'].to_a
s.require_path = "lib"
s.autorequire = "uaenv"
s.test_files = Dir.glob('tests/*.rb')
s.has_rdoc = true
s.extra_rdoc_files = ["README"]
end

Rake::GemPackageTask.new(spec) do |pkg|
pkg.need_zip = true
end

task :default => "pkg/#{spec.name}-#{spec.version}.gem" do
puts "generated latest version"

Це хороший приклад стандартного Rakefile для gem. Тут Ви можете бачити, що він включає RubyGems і додає деякі завдання для rake. Головна специфікація забезпечує всю інформацію про gem. Останнє завдання подає простого помічника який дозволяє запустити rake у каталозі і автоматично побудувати пакет gem і файл zip. Кожна лінія специфікації має спеціальне призначення. За більш детальнішою інформацією зверніться на офіційний сайт Rubygems - http://docs.rubygems.org/.

четвер, 2 серпня 2007 р.

Короткий огляд wxRuby.

В одному зі своїх попередніх дописів я обіцяв провести аналіз декількох графічних тулкітів для Ruby. Я цього не зробив і мабуть вже не зроблю... Так-так, я лінивий. Мене тільки вистачило на поверхневий огляд тулкітів... І вибір мій впав на wxRuby(wxruby.rubyforge.org). Чому? Хз... Просто чомусь відразу сподобався він мені.

wxRuby2 поточна версія, яка перебуваю у розробці. Базується на wxWidgets 2.8 API, і перебуває на стадії beta.

Декілька переваг:
  • досить непогана реалізація для Ruby;
  • легкість встановлення (rubygems + декілька сторонніх бібліотек)
  • кросплатфомність (Linux, MacOSX, Windows);
  • look and feel: використовує віджети Windows на Win32, Aqua на OS X і GTK2 на Linux. + стандартний набір віджетів, яких вистачить для написання графічних програм будь-якого рівня складності.
  • інтернаціоналізація (підтримка unicode стрічок по замовчуванню);
Плюс багато приємних дрібниць, таких як кросплатформна підтримка рідних гарячих клавіш і пунктів головного меню, підтримка трею. Мабуть всі ці переваги можна віднести до wxWidgets.
Крім того на оффсайті WxRuby є документація по API і декілька практичних посібників по використанню.

З недоліків, можу зазначити: нестабільність (часті RunTimeErrors і різного баги при виконанні), дивний вигляд інтерфейсу на ОС Windows.
Висновок. Як на мене, то бібліотека поки що не придатна для використання у стабільних проектах, але для власних домашніх цілей згодиться.

P.S. Я використовував WxRuby для однієї своєї невеликої програми для роботи з базою даних SQLite, конкретніше телефонна книга м. Тернополя, з можливістю пошуку і відображення результатів. Швидко і зручно.

Вирішення проблеми з rubygems під Ubuntu Linux 7.04

Вчора виникла необхідність встановити декілька пакетів з репозиторію rubygems. Раніше я вже писав про проблеми з цим. Після виконання $ sudo gem install <назва пакету>, через декілька хвилин система вішалась намертво. Неприємні враження :(.
Був проведений простий аналіз роботи. Ось як це відбувалось.
$ sudo gem install unicode

Дослідження трафіку утилітою iptraf показало що gem з'єднується з IP адресою 205.234.109.18(rubyforge.org) і завантажує приблизно 870 пакетів (125500 байт). Після закриття з'єднання починалось найцікавіше. Розмір swap(розділ підкачки) починав активно збільшуватись. Саме час відкинутись на спинку крісла і чекати завершення роботи :). В моєму випадку - піти перекурити. О жах! Свап 600 метрів. Комп практично не реагує на маніпуляції. Допомогла тільки перезавантаження системи.
Перевірив версія gem:
$ gem -v
0.9.0
Тоді як на оффсайті (http://rubyforge.org/projects/rubygems/) доступна 0.9.4. Завантажую і встановлюю її.
$ wget http://rubyforge.org/frs/download.php/20989/rubygems-0.9.4.tgz
$ tar xzvf rubygems-0.9.4.tgz
$ cd rubygems-0.9.4
$ sudo ruby setup.rb
Далі використовую інсталятор rubygem для встановлення себе самого.
$ sudo gem install rubygems-update
Ииии... чудо. Воно таки встановилося і працює:
$ gem -v
0.9.4


Хто винен? Висновки робіть самі...
P.S. І все одно воно працює повільно :(, але хоча б працює...