четвер, 23 липня 2009 р.

Matz Has a Mac!

Ця фотографія підняла мені настрій :)

Для тих хто не в курсі, це Юкіхіро Мацумото(Yukihiro Matsumoto) також відомий як Matz, творець мови програмування Ruby.

>>> Подробиці

Ubuntu on Rails


Недавно на просторах Launchpad.net була створена команда Ubuntu on Rails.

Ціль команди полягає у підтриманні пакетів для розробки на Ruby/Rails. Першочергово це актуальні версії rubygems і passenger.
В команді беруть участь хлопці з Brightbox, які досі підтримують пакети passenger, nginx-passenger, ruby-ee для Ubuntu і Debian.

>>> Подробиці

вівторок, 21 липня 2009 р.

Mechanize + Cookie + вКонтакте. Частина 6



Куки(від англ. Cookie - печиво) - фрагмент даних, який створюється веб-сервером і зберігається на комп'ютері користувача у вигляді файлу, який веб-клієнт(зазвичай веб-браузер) кожен раз пересилає веб-серверу у HTTP-запиті при спробі відкрити сторінку відповідного сайту. Застосовується для збереження даних на стороні користувача, на практиці зазвичай використовується для:

  • автентифікації користувача;

  • зберігання персональних даних і налаштувань користувача;

  • відслідковування стану сесії доступу користувача;

  • ведення статистики про користувачів.



Бібліотека WWW::Mechanize надає зручний інтерфей для роботи з куками - WWW::Mechanize::CookieJar. Цей клас використовується для керування куками, які були повернуті з якого-небудь конкретного сайту. Крім того дозволяє зберігати куки до файлу, з можливістю використовувати його пізніше(наприклад, для автентифікації користувача на сайті без необхідності вводу логіна і пароля). Доступні наступні формати для зберігання/завантаження куки: :yaml <- YAML структура і :cookiestxt <- Mozilla у форматі cookies.txt.


Повернемося до попередньої статті і додамо можливість авторизації за допомогою пересилання куки вебсерверу.
Авторизація здійснюється через надсилання куки 'remixpassword'. Значення 'remixpassword', встановлюється в результаті успішної авторизації, і може бути використане для продовження ідентифікатора сесії('sid').
Цей метод можна використати і у UserAPI при запиті до http://login.userapi.com/auth?login=auto&site=2.

Тут я не буду приводити повний лістинг. А лише доповню код з попередньої статті.
Для початку потрібно авторизуватися на сайті ВКонтакте і записати куки у файл на диску. Для цього створимо новий метод save_cookie().
def save_cookie(cookie_file)
@a.cookie_jar.save_as(cookie_file, format = :cookiestxt)
end

Якщо авторизація пройшла успішно, записуємо куки у файл cookie.txt

if login_force(id, email, pass)
puts "Successfully Authentication"
save_cookie('cookie.txt')
else
puts "Authentication Error"
end


Далі ми будемо авторизовуватись за допомогою цією куки. Створимо метод login_auto()

def login_auto(uid, cookie_file)
@a.cookie_jar.load(cookie_file, format = :cookiestxt)
login_page = @a.get('http://vkontakte.ru/')
return /http:\/\/vkontakte\.ru\/id(\d+)/ === login_page.uri.to_s ? ($1 == uid ? true : false) : false
end


Надалі у нас нема необхідності використовувати метод login_force(). Замість нього використовуємо login_auto()

if login_auto(id, 'cookie.txt')
puts "Successfully Authentication"
else
puts "Authentication Error"
end


Якщо комусь цікаво про плюси авторизації за допомогою куки(в т.ч. і на сайті ВКонтакте), залишайте коментарі.

вівторок, 14 липня 2009 р.

розробка.com


Розробка — молодий соціальний сайт для розробників, побудований по моделі Хабрахабри. Сайт орієнтований на українську аудиторію розробників. Наразі проет ще досить малоактивний, але маю надію в майбутньому до нього буде залучатися все більше і більше людей.
Це перша стаття на Розробці опублікована мною, проба пера так сказати. Прошу не судити строго. Можливо комусь вона буде цікавою. Ця стаття була написана мною у цьому блозі ще у вересні 2007 року. Хоча і досі не втратила своєї актуальності.

пʼятницю, 10 липня 2009 р.

Mechanize + Nokogiri + вКонтакте. Частина 5


Ніхто не помітив зовнішніх змін. Положення тіла пацієнта вКонтакте на операційному столі залишалось незмінним. Але мушу повідомити неприємну новину: ідентифіковано мутації. Тому всі попередні дослідження [1][2][3][4] вважаються недійсними. Нічого... не треба падати духом.
Їжачки плакали і кололись, але продовжували кохатися з кактусами! ©

Nokogiri - це парсер HTML, XML, SAX. Серед його численних можливостей можна виділити можливість використання XPath(XML Path Language) і CSS3 селекторів для пошуку.
Крім того Nokogiri має сумісний з Hpricot синтаксис XPath та CSS.
На даний час Nokogiri найшвидший парсер XML для Ruby.
Mechanize для синтаксичного розбору html використовує саме Nokogiri.

Приклад використання:

Підключення необхідних бібліотек
require 'nokogiri'
require 'open-uri'

Завантаження HTML сторінки, використовуючи open-uri, який йде з Ruby
doc = Nokogiri::HTML(open('http://www.google.com/search?q=ruby-ua'))

Пошук вузлів за допомогою CSS3 селекторів
doc.css('h3.r a.l').each{|link| puts link.content}

Пошук вузлів за допомогою XPath
doc.xpath('//h3/a[@class="l"]').each {|link| puts link.content}


Автор рекомендує використовувати XPath селектори для вищої швидкодії - пошук CSS швидкий ніж у Hpricot, але не такий швидкий.
Nokogiri::XML::Node#xpath(*paths) - функція пошук вузлів(nodes) для XPath шляхів(paths). Шлях повинен бути одним або декількома XPath запитами(queries).
Цікавоє можливістю є задання власних функцій XPath із використанням регекспів(regexp). Для цього потрібно створити класс і застосувати функцію # яку хочете визначити. Наприклад:

node.xpath('.//tr[mess(., "mess\d+")]', Class.new {
def mess node_set, regex
node_set.find_all { |node| node['id'] =~ /#{regex}/ }
end
}.new)


Така функція знайде всі вузли виду:
<tr id="mess127">[...]</tr>
<tr id="mess128">[...]</tr>
<tr id="mess129">[...]</tr>


вКонтакте!?
У зв'язку зі змінами на сайті vkontakte.ru мої попередні дописи про вконтакте втратили свою актуальність. Не знаю скільки часу буде працездатним цей код. Але суть в іншому: показати принципи роботи з Mechanize і Nokogiri на реальному прикладі.
Що ж робить нищеприведений код?

  • Авторизація через надсилання email-адреси та пароля користувача; (login_force)

  • Отримання останніх приватних повідомлень; (get_messages)


# -*- encoding: utf-8 -*-
require 'mechanize' # recommended 0.9.2

@a = WWW::Mechanize.new{|agent|
agent.user_agent_alias = 'Linux Konqueror'
agent.follow_meta_refresh = true
}

# Authorisation by submitting email and password (Login)
#
def login_force(uid, email, pass)
login_page = @a.get('http://vkontakte.ru/login.php')
login_form = login_page.form_with(:name => "login")
login_form.email = email
login_form.pass = pass
user_page = login_form.submit
return /http:\/\/vkontakte\.ru\/id(\d+)/ === user_page.uri.to_s ? ($1 == uid ? true : false) : false
end

# Getting private messages list for current user
#
# Structure of each entry of resulting array
# [0] - message from
# [1] - message date
# [2] - message title
# [3] - message_body
#
def get_messages
url = 'http://vkontakte.ru/mail.php'
messages_page = @a.get(url)
node = Nokogiri::HTML(messages_page.body)
message = node.xpath('.//tr[mess(., "mess\d+")]', Class.new {
def mess node_set, regex
node_set.find_all { |node| node['id'] =~ /#{regex}/ }
end
}.new)
message_from = message.xpath('//td[@class="messageFrom"]/div[@class="name"]/a').map {|link| /\/id(\d+)/.match(link['href'])[1]}
message_date = message.xpath('//td[@class="messageFrom"]/div[@class="date"]').map {|link| link.content}
message_title = message.xpath('//a[@class="new messageSubject"]/span').map {|link| link.content}
message_body = message.xpath('//a[@class="new messageBody"]').map {|link| link.content}
messages = Array.new
messages = [message_from, message_date, message_title, message_body].transpose
return messages
end

id = "12345678"
email = "username@example.com"
pass = "secret"

if login_force(id, email, pass)
puts "Successfully Authentication"
get_messages.each {|message|
puts "[#{message[1]}]: #{message[0]}"
puts message[2]
puts message[3]
puts "==="
}
else
puts "Authentication Error"
end

Blogger: Створи власний безкоштовний блог?

Весь час перебування в Blogger мені не вистачало однієї корисною фічі з ЖЖ. І нарешті цей прикрий пробіл закритий.
За посиланням можна прочитати як створювати розширювані короткі описи повідомлень, такий собі аналог <lj-cut> з ЖЖ.
Доведеться трохи повозитися з шаблоном. Але найважливіше воно працює :)
Не встиг я потішитись обновці, як на і-мейл прийшов лист від Blogger.
А-а-а-а-а!!!!!11 OMG! Хто б міг подумати? Я спамер!!! На жаль або на щастя в Україні немає безпосередньої відповідальності за поширення спаму.
І тепер замість блогу красується заглушка з багатозначним надписом:

Я пам'ятайте, якщо ви читаєте цей пост, значить цей блог не містить потенційного спаму :). Перевірено Великим Ґуґло'м.

Якщо комусь цікаво, під катом текст листа від Blogger.


Вітаємо!

Ваш блог на: http://ruby-ua.blogspot.com/ ідентифіковано як блог із потенційним спамом. Для вирішення цієї проблеми надішліть запит на перегляд, заповнивши форму на http://www.blogger.com/unlock-blog.g?lockedBlogID=4152168096498110483

Якщо ваш блог не буде переглянуто, його буде видалено через 20 днів, а впродовж цього періоду вашим читачам відображатиметься сторінка попередження. Після отримання запиту ми переглянемо ваш блог і розблокуємо його протягом двох робочих днів. Після того як ваш буде розглянуто та буде виявлено, що він не є спамом, блог буде розблоковано, а повідомлення на вашій панелі керування Blogger більше не відображатиметься. Якщо цей блог не належить вам, нічого робити не потрібно. Це не вплине на будь-які інші ваші блоги.

Ми шукаємо спам, використовуючи автоматичний класифікатор. Автоматичне виявлення спаму є неточним, і час від часу блоги, схожі на ваш, позначаються неправильно. Ми просимо вибачення за цю помилку. Однак за допомогою такої системи ми можемо забезпечити більше місця для зберігання, вищу пропускну здатність і більше технічних ресурсів для таких авторів блогів, як ви, а не для спамерів. Для отримання додаткової інформації див. довідку Blogger: http://help.blogger.com/bin/answer.py?answer=42577

Дякуємо за розуміння та допомогу в боротьбі зі спамом.

Із повагою,

команда Blogger

P.S. Ще одне нагадування: якщо ви не надішлете запит на перегляд вашого блогу, через 20 днів його буде видалено. Перейдіть за цим посиланням, щоб надіслати запит на перегляд: http://www.blogger.com/unlock-blog.g?lockedBlogID=4152168096498110483


Мені от дійсно цікаво: хто і як буде перевіряти rubукраїномовний блог на спам відповідність умовам обслуговування Blogger?

неділю, 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\>