5 липня 2009

Ruby for System Administration → Bash quoting

Інколи використовую Ruby для задач системного адміністрування. Ruby з широким набором бібліотек чудово для цього підходить.
Однією з поширених задач є виклик зовнішніх команд Linux, передачі їм різноманітних параметрів, отримання і обробки результатів. І тут ви обов'язково стикнетеся з маскуванням символів в bash.

Маскування(quoting) в bash використовується для відміни спеціального значення для команд інтерпрeтатора визначених символів або слів.
Маскування можна використовувати для скасування специфічної обробки спеціальних символів, для запобігання розпізнавання зарезервованих слів, а також для запобігання підстановки параметрів.
Метасимвол - це символ, який розділяє слова, якщо він не замаскований. Є одним з наступних символів:
| & ; ( ) < > пробіл табуляція

Існує три механізми маскування:
символ маскування (escape character), одинарні лапки і подвійні лапки.

Незамаскована зворотна коса риска (\) є символом маскування(escape character). Вона вимагає використовувати наступний за нею символ(за винятком переведення рядка) буквально.

Усі символи в одиночних лапках (') використовуються буквально. Символ одиночної лапки(апостроф) не повинен вказуватися між лапках, навіть якщо його випереджає зворотна коса.

Символи в подвійних лапках(") використовуються буквально, за виключенням $, !, ` і \
Зворотна коса має спеціальне значення тільки якщо після неї йде один з спеціальних символів.

З точки зору програмування, маскування з допомогою одинарних лапок є найпростішим (але не дозволяє маскувати символ одинарних лапок). Тоді як з допомогою зворотної косої - найскладніший.
Ми не шукаємо простих шляхів :)

Напишемо просто функцію, яка отримуватиме cтроку і повертатиме масковану строку, придатну для використання в bash.
Зауважу, код буде працювати тільки в версіями Ruby вище 1.9, оскільки в Regexp використовується нова можливість Oniguruma - іменовані групи(named groups).
#!/usr/bin/env ruby1.9
# -*- encoding: utf8 -*-

def quote_for_bash(text)
text = text.dup
text.gsub!( /(?<backslash>\\)/, '\\\\\k<backslash>' )
text.gsub!( /(?<space>\s)/, '\\\\\k<space>' )
text.gsub!( /(?<double-quotes>")/, '\\\\\k<double-quotes>' )
text.gsub!( /(?<single-quotes>')/, '\\\\\k<single-quotes>' )
text.gsub!( /(?<apostrophe>`)/, '\\\\\k<apostrophe>' )
text.gsub!( /(?<brackets>[()<>])/, '\\\\\k<brackets>' )
text.gsub!( /(?<semicolon>;)/, '\\\\\k<semicolon>' )
text.gsub!( /(?<exclamation-mark>!)/, '\\\\\k<exclamation-mark>')
text.gsub!( /(?<ampersand>&)/, '\\\\\k<ampersand>' )
text.gsub!( /(?<dollar-sign>\$)/, '\\\\\k<dollar-sign>' )
text.gsub!( /(?<number-sign>#)/, '\\\\\k<number-sign>' )
text.gsub!( /(?<vertical>\|)/, '\\\\\k<vertical>' )
return text
end


Задамо текст, який, наприклад, в майбутньому будемо використовувати як параметр для якоїсь команди:
text = %{#<b>$a;say "hello" & |Profit!|  -  (`1$)</b>}


Початковий текст матиме такий вигляд:
puts text
#<b>$a;say "hello" & |Profit!| - (`1$)</b>


Маскований текст буде таким:
puts quote_for_bash(text)
\#\<b\>\$a\;say\ \"hello\"\ \&\ \|Profit\!\|\ \ -\ \ \(\`2\$\)\</b\>

29 червня 2009

Redcar - аналог Textmate для Linux


Ті з вас хто хоч раз бачив Railscasts, думаю в курсі про Mac OS'ісовський текстовий редактор TextMate - Великий фетиш і Ідол всіх програмістів на Ruby під Mac. Особисто я був у захваті від побаченого. Але нажаль розробники TextMate не подумали про нещасних Linux користувачів, які готові віддати свої 57$. Так ось, схоже у нас тепер з'явився аналог - Redcar, автором якого є Daniel B. Lucraft'а.
Ось що говорить сам автор про свій проект:
Redcar - текстовим редактором для програмістів з відкритим вихідним кодом для Gnome(Linux). Він призначена для забезпечення сумісності з Textmate bundles(робота триває), і написаний майже повністю на Ruby, і з використанням Vala для швидкодії. Redcar все ще перебуває в зародковому стані...

Зараз функціонує вже 75% всіх можливостей TextMate.
Детальна (і що головне, робоча) інструкція як зібрати його під Ubuntu 9.04 лежить у git'і.
Дуже хочеться що б проект продовжував розвиватися.

22 червня 2009

Merb tutorial in Ukrainian


На сайті молодої україномовної соціальної IT спільноти Розробка.com з'явився туторіал по Merb. Підручник охоплює як створювати прості програми за допомогою Merb, haml і DataMapper.

14 червня 2009

URL Encoding/Decoding with Ruby 1.9

Encode and decode a string for a URL with Ruby 1.9 (according to RFC 3986 and RFC 3629).

# URL encoded text:
enc_str = "http://uk.wikipedia.org/wiki/%D0%84%D0%B2%D1%80%D0%BE%D0%BF%D0%B0"

# Decode:
require 'cgi'
dec_str = CGI::unescape(enc_str)
=> http://uk.wikipedia.org/wiki/Європа
dec_str.encoding
=> #<Encoding:UTF-8>


# URL decoded text:
dec_str = "Декодований текст"

# Encode:
require 'cgi'
enc_str = CGI::escape(dec_str)
=> "%D0%94%D0%B5%D0%BA%D0%BE%D0%B4%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B9+%D1%82%D0%B5%D0%BA%D1%81%D1%82"
enc_str.encoding
=> #<Encoding:US-ASCII>

Ruby 1.9 and R-Bus

Намагаючись завести зв'язку Ruby 1.9 + D-Bus, натрапив на проект R-Bus.
З офіційної сторінки бубліотеки:
R-Bus is a native implementation of the D-Bus protocol, with these goals in mind:Ruby + standard library is the only dependencies, a rubyish API, approach and way of doing things, complete client functionality and a comprehensive test suite.

Останньою новиною проетку є Ruby 1.9.1 support in trunk датована 5 квітня 2009 року.
Як виявилось пізніше версія з транку не працює з Ruby 1.9.1. Тому доведеться накладати патч. Перевірено, він сумісний не тільки з версією 1.9.1 Ruby, а й попередніми. Приємно, що автором патчу є наш земляк Sergey Yanovitsky, за що йому велике Дякую.

Install Ruby 1.9.1 from sources:
$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p129.tar.gz
$ tar xzvf ruby-1.9.1-p129.tar.gz
$ mkdir ruby1.9
$ cd /home/mama/ruby-1.9.1-p129
$ ./configure --prefix=/home/mama/ruby1.9
$ make
$ make install
$ PATH=/home/mama/ruby1.9/bin:$PATH
$ ruby -v
ruby 1.9.1p129 (2009-05-12 revision 23412) [i686-linux]

Get the R-Bus code from Subversion with:
$ svn checkout svn://rubyforge.org/var/svn/rbus/trunk rbus
$ cd rbus

Get Ruby 1.9 and Rspec 1.2.2 support patch
wget http://rubyforge.org/tracker/download.php/3277/12588/25318/4487/ruby-1.9_rspec-1.2.2_support.diff

Apply patch:
$ patch -p0 < ruby-1.9_rspec-1.2.2_support.diff

Install rbus:
$ ruby setup.rb

Example:
require 'rbus'
session_bus = RBus.session_bus
rb_player = session_bus.get_object('org.kde.amarok', '/Player')
uri = rb_player.GetMetadata()

Посилання:
R-Bus API Docs, Tutorial and more

13 червня 2009

Ruby D-Bus

Цей запис описує створення простого D-Bus клієнта до аудіопрогравача Amarok2 за допомогою Ruby.

Що таке D-Bus і принцип його роботи
D-Bus - це система міжпроцесної взаємодії, яка надає додаткам декілька шин для передачі повідомлень.
D-Bus є частиноє проекту freedesktop.org. Вона володіє високою швидкодією, інтегрується з багатьма робочими середовищами. D-Bus доступна для GLib, Java, Mono, Qt, Python, Ruby і є прозорою для мережі.
D-Bus надає системі декілька шин:
  1. Системна шина. Створюється під час старту демона. З її допомогою відбувається спілкування різноманітних демонів, вона практично недоступна для додатків користувача.
  2. Сесійна шина. Створюється для користувача, авторизованого у системі. Для кожної такої шини запускається окрема копія демона, через неї спілкуються додатки, з якими працює користувач.

Кожне повідомлення D-Bus має свого відправника і одержувача, а їхні адреси називаються шляхами об'єктів, оскільки D-Bus вважає, що кожен додаток складається із набору об'єктів, а повідомлення пересилається не між додатками, а між об'єктами цих самих додатків.
Кожен об'єкт може підтримувати один або більше інтерфейсів, які представленні у вигляді іменованих груп методів і сигналів.
D-Bus також передбачає концепцію сервісів. Сервіс - унікальне місцезнаходження додатка на шині. При запуску додаток реєструє один або декілька сервісів, якими воно буде володіти до тих пір поки самостійно не звільнить. Ніякий інший додаток, що претендує на той же сервіс, зайняти його не зможе. Іменується сервіси аналогічно інтерфейсам.
У кожного об'єкта своє унікальне ім'я, яке виглядає як шлях в файловій системі. Наприклад, /org/kde/amarok/Player

Інсталяція ruby-dbus
В ubuntu бібліотеку можна встановити з репозиторія за допомогою команди:
$ sudo apt-get install libdbus-ruby1.8

Це все! Тепер можемо перейти до використання...

Зауважу, ви можете використовувати консольну утиліту qdbus.
Якщо в консолі набрати qdbus org.kde.amarok, ви побачите приблизно таке:
~$ qdbus org.kde.amarok
/
/AudioOutputs
/AudioOutputs/0
/Collection
/KBookmarkManager
/KBookmarkManager/amarok
/KBookmarkManager/kfilePlaces
/KDebug
/KIO
/KIO/Scheduler
/MainApplication
/Player
/TrackList
/amarok
/amarok/MainWindow
/internal
/internal/PhononXine


org.kde.amarok - це назва сервісу.
В результаті виконання команди маємо список об'єктів цього сервісу. У об'єктів в свою чергу є методи.

Використання бібліотеки в Ruby

Для того що використовувати бібліотеку ви повинні включити її у вашу програму за допомогою
require 'dbus'

Приєдняння до шини
bus = DBus::SystemBus.instance
bus = DBus::SessionBus.instance

відповідно, або до системної шини, або до сесійної.

Виклик методу
Наприклад ми хочемо отримати доступ до об'єкта клієнта аудіопрогравача Amarok через шину сесій. Amarok надає сервіс з іменем org.kde.amarok
rb_service = bus.service("org.kde.amarok")

Тепер у нас є маніпулятор для сервісу і ми знаємно, що він експортує об'єкт /Player. Отримати доступ до цього об'єкта можна за допомогою методу object:
rb_player = rb_service.object("/Player")

Нагадаю, об'єкти D-Bus мають інтерфейси, а інтерфейси в свою чергу мають методи. Далі отримуємо доступ до цих методів:
rb_player_iface = rb_playes["org.freedesktop.MediaPlayer"]
puts rb_player_iface.PositionGet

Замість висновків
Знайти докладну інформацію про D-Bus API конкретних програм буває непросто, але Google завжди буде відданим супутником у ваших дослідженнях.

На сьогодні це все, малята :)

Використанні посисання

Історія команд в irb

Якщо ви бажаєте, щоб історія введених команд зберігалася після закриття irb обов'язково додайте наступні рядки до ~/.irbrc
require 'irb/ext/save-history'
IRB.conf[:SAVE_HISTORY] = 100
IRB.conf[:HISTORY_FILE] = "#{ENV['HOME']}/.irb_history"

Крім того загляньте сюди: Автодоповнення в irb

8 червня 2009

Відновлення GRUB у Ubuntu після встановлення Windows

Це допис ніяким боком не стосується основної теми блогу.
Більше того, про відновлення GRUB в інтернетах не писав мабуть тільки лінивий. Тому я не буду стояти осторонь і додам свої 5 копійок :).
Між іншим Ubuntu Linux перейшов на використання завантажувача Grub2.
На моїй машині в дуалбуті постійно мешкають Windows(зараз Windows 7) і Linux(зараз kubuntu 9.04). Сьогодні встановав Windows 7 build 7100. Як на мене система досить непогана і на перший погляд працює значно стабільніше свого попередника. Хоча я більше надаю перевагу Linux. Але це тема іншої історії.
Так от встановлювач Windows перезаписує завантажувальний розділ жорсткого диску, і встановлює туди свій завантажувач. Як тепер відновити grub, щоб можна було при старті машини звично обирати ОС для завантаження?
Розписую покрокову інстукцію для свої системи:

1. Завантажуємо графічний інсталятор kubuntu 9.04 і відкриваємо консоль.
2. Дивимось таблицю розділів:
ubuntu@ubuntu:~$ sudo fdisk -l /dev/sda

Disk /dev/sda: 250.0 GB, 250059350016 bytes
255 heads, 63 sectors/track, 30401 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x64920a32

Device Boot Start End Blocks Id System
/dev/sda1 * 1 2550 20482843+ 7 HPFS/NTFS
/dev/sda2 5101 30401 203230282+ f W95 Ext'd (LBA)
/dev/sda5 5101 11534 51681073+ 7 HPFS/NTFS
/dev/sda6 11535 18036 52227283+ 7 HPFS/NTFS
/dev/sda7 18037 19252 9767488+ 83 Linux
/dev/sda8 19253 19374 979933+ 82 Linux swap / Solaris
/dev/sda9 19375 30389 88477956 83 Linux
/dev/sda10 30390 30401 96358+ 83 Linux

де:
sda1 - розділ з windows.
sda5, sda6 - розділи де зберігається всякий хламу для windows.
sda7 - кореневий розділ ubuntu(fs ext4).
sda8 - розділ підкачки(swap) для linux.
sda10 - розділ boot.
sda9 - розділ файлосмітник для linux.

3. Далі необхідно примонтувати кореневий розділ ubuntu. Наприклад в /mnt/root.
ubuntu@ubuntu:~$ sudo mkdir /mnt/root/
ubuntu@ubuntu:~$ sudo mount -t ext4 /dev/sda7 /mnt/root/
ubuntu@ubuntu:~$ sudo mount /dev/sda10 /mnt/root/boot/
ubuntu@ubuntu:~$ sudo mount -t proc none /mnt/root/proc/
ubuntu@ubuntu:~$ sudo mount -o bind /dev/ /mnt/root/dev


4. Змінюємо кореневий розділ за допомогою chroot:
ubuntu@ubuntu:~$ sudo chroot /mnt/root/ /bin/bash
root@ubuntu:/#


5. Далі переходимо до командного рядка grub і встановлюємо завантажувач на /dev/sda:
root@ubuntu:/# grub
grub> root (hd0,9)
grub> setup (hd0)
Checking if "/boot/grub/stage1" exists... no
Checking if "/grub/stage1" exists... yes
Checking if "/grub/stage2" exists... yes
Checking if "/grub/e2fs_stage1_5" exists... yes
Running "embed /grub/e2fs_stage1_5 (hd0)"... 17 sectors are embedded.
succeeded
Running "install /grub/stage1 (hd0) (hd0)1+17 p (hd0,9)/grub/stage2 /grub/menu
.lst"... succeeded
Done.

grub>


6. Якщо все пройшло без помилок, то можна перевантажувати комп'ютер.

7. ???

8. PROFIT

11 травня 2009

Rubystein: Ruby meets Wolfenstein

На RailsConf 2009 хлопці з Phusion представили Rubystein, клон Wolfenstein написаний на Ruby.
Нагадаю, Hongli Lai і Ninh Bui є розробниками Phusion Passenger™ і Ruby Enterprise Edition.

Отримати вихідні коди на Github, і пограти самостійно.

Джерело новини

Curb (Ruby + cURL) + вКонтакте. Частина 4



Сьогодні продовжимо копатися у тенетах...
На операційному столі і досі лежить пацієнт вКонтакте. Хтось запевняє, що він живіший за всіх живих Операцією керує доктор Ruby. Ця людина не потребує представлення. І сьогодні його асистентом є відомий у вузьких колах майстер ножа і скальпеля, хірург - cURL. Прошу любіть і жаловать! :) Бурні овації в залі.

cURL - це програма командного рядка для передачі файлів з синтаксом URL, яка підтримує FTP, FTPS, HTTP, HTTPS, TFTP, SCP, SFTP, Telnet, DICT, Файли: URL і LDAP. cURL підтримує сертифікати HTTPS, методи HTTP POST, HTTP PUT, завантаження на FTP, Kerberos, завантаження через форми HTTP, проксі, файли cookie, перевірку автентичності «ім'я користувача + пароль» (базову, дайджест, NTLM і Negotiate для HTTP, а також kerberos4 для FTP), поновлення передачі файлів, тунелювання через проксі HTTP та багато інших функцій. cURL - це вільне програмне забезпечення, що розповсюджується під ліцензією MIT.

Головна мета використання cURL полягає в тому, щоб автоматизувати передачу файлів або послідовність таких операцій. Наприклад, це хороший засіб для моделювання дій користувача у веб-оглядачі.

Libcurl - це API інтерфейс, який можна включити в свої програми; cURL діє як автономна обгортка для бібліотеки libcurl. libcurl використовується, щоб забезпечити можливість передачі URL численним додаткам (як відкритим, так і комерційних). Для libcurl є більше 30 різних прив'язок до мов програмування. В тому числі і для Ruby: Curb та curl-multi.

Сьогодні розглянемо роботу з libcurl на Ruby 1.8, за допомогою Curb. З сторінки проекту "Curb is a work-in-progress, and currently only supports libcurl‘s ‘easy’ mode". Вже по традиції, на прикладі сайту вКонтакте.
Але для початку потрібно встановити необхідні бібліотеки.
$ sudo apt-get install libcurl4-gnutls-dev
$ sudo gem1.8 install curb


Сьогодні наша задача полягатиме у написанні простого парсера на Рубі, який перевіряє нову інформацію з особистої сторінки вКонтакте: кількість повідомлень, запрошень у групи, нових фотографіях і відео, на яких вас відзначили, коментарі в замітках, запрошень на зустрічі, відповідей на пропозиції та питання, анонімних думок. Для того щоб скрипт працював у вас, достатньо змінити рядок
body = getdata(mid = '********', email = 'user@example.com', pass = '*********')

де mid - ваш числовий ID наприклад 12227146 (http://vkontakte.ru/id12227146), email - ваш логін, і pass - ваш пароль.
Наразі скрипт праює тільки з версією Ruby 1.8. Скомпілювати Curb для 1.9 мені не вдалось.

# -*- coding: utf-8 -*-

# ruby1.8
require 'rubygems'
require 'curb'
require 'md5'
require 'iconv'

def trymatch(str, regex)
if str.match(regex) == nil
return '0'
end
str.match(regex)[2]
end

def filter(data, label, tool)
# <li id='myfriends'><a href='/friend.php?24072'>My Friends</a></li>
# <li><a href='/mail.php?id=32576819&24072'>My Messages (<b>1</b>)</a></li>
regex = /<a href=\'\/#{tool}.php?([\w\d\s\?\'&=>]*)\'>[\w\s]*\(<b>(\d+)<\/b>\)<\/a>/m
rez = trymatch(data, regex)
label + ": " + rez
end

def getdata(mid, email, pass)
user = {'mid' => mid, 'email' => email, 'pass' => MD5.md5(pass)}
curl = Curl::Easy.new("http://vkontakte.ru/id#{mid}")
curl.headers = {"User-Agent" => "Opera/10.00 (X11; Linux i686; U; uk) Presto/2.2.1",
"Cookie" => "remixlang=3; remixchk=5; remixmid=#{user['mid']}; remixemail=#{user['email']}; remixpass=#{user['pass']}; remixautobookmark=8",
"Referer" => "http://vkontakte.ru/"}
curl.follow_location = true
curl.perform
#return '0'
body = curl.unescape curl.body_str
coder = Iconv.new("UTF-8","WINDOWS-1251")
body = coder.iconv(body)
return body
end

body = getdata(mid = '********', email = 'user@example.com', pass = '*********')

puts filter(body,'Friends','friend')
puts filter(body,'Photos','photos')
puts filter(body,'Videos','video')
puts filter(body,'Messages','mail')
puts filter(body,'Notes','notes')
puts filter(body,'Groups','groups')
puts filter(body,'Events','events')
puts filter(body,'Matches','matches')
puts filter(body,'Opinions','opinions')
puts filter(body,'Questions','questions')
puts filter(body,'Opinions','opinions')


Список посилань: