Jul 07

Есть такая фича в Rubygems, как установка гем-ов в директорию пользователя ~/.gem, если gem install запускается без sudo и, соответственно, у пользователя нет разрешения установить что-либо в дефолтную директорию.

В Rubygems до версии 1.3.6 это решалось просто - автоматически. Т.е. если вы вместо sudo gem install пишете gem install - вам выдается warning, но гем успешно устанавливается:

[fxposter@kitty ~]$ gem install haml
WARNING:  Installing to ~/.gem since /usr/lib/ruby/gems/1.8 and
/usr/bin aren't both writable.
Successfully installed haml-3.0.13
1 gem installed
Installing ri documentation for haml-3.0.13...
Installing RDoc documentation for haml-3.0.13...

Вроде бы всё замечательно. Но с версии 1.3.6 это дефолтное поведение изменилось, и при обновлении с помощью gem update --system об этом честно пишется:

–user-install is no longer the default. If you really liked it, see Gem::ConfigFile to learn how to set it by default. (This change was made in 1.3.6)

Но кто реально читают все эти тексты? Да и не все обновляют Rubygems таким образом. В нашем случае произошла смена сервера. Причем на старом сервере стояла старая версия Ruby Enterprise Edition и дефолтные Rubygems 1.3.5. На новом же серваке наши бравые админы опять всё “покомпилили” и вместе с REE у нас там обновился и Rubygems - теперь там стояла версия 1.3.7. И… сначала отвалился деплой, т.к. по умолчанию rake gems:install у нас там не включен. Запустили rake gems:install - не работает. Запустили gem install - не работает. Всё просто замечательно.

Разбирались долго. Сначала думали на админов, которые в рельсах плохо шарят и думали, что они там “понастраивали”. Кстати, понастраивали они действительно знатно - скомпилированный REE почему-то стоит поверх стандартного Ruby 1.8.5 (у нас там CentOS 5, в ближайшем времени переезжаем на Amazon EC2 на Ubuntu). Но это неважно. После долгого разговора с админом и ковыряния серваков я чисто случайно обратил внимание на разные версии Rubygems. Заподозрил неладное. Обновил их на старом серваке на 1.3.7 и… получил сообщение, написанное выше. Отлично. Теперь осталось восстановить поведение старых Rubygems.

В итоге всё оказалось довольно просто. В файл ~/.gemrc нужно дописать следующую строку: "install: --user-install". После чего мой .gemrc стал выглядеть так:

[waysgo@web-waysgo ~]$ cat .gemrc
---
:benchmark: false
:update_sources: true
:sources:
- http://rubygems.org/
- http://gems.github.com/
:bulk_threshold: 1000
:verbose: true
:backtrace: false
install: --user-install

written by fxposter \\ tags: ,

May 10

Недавно писал проект на Rails 2.3 + MongoDB + MongoMapper. Не могу сказать, что все было хорошо - для того, чтобы существующие плагины для рельсов заработали с MongoMapper-ом пришлось немного повозиться, но в итоге все закончилось хорошо. :)

А мой сегодняшний рассказ пойдет о некоторых особенностях MongoDB и MongoMapper-а, с которыми вы, скорее всего столкнетесь, если будете использовать эти библиотеки.

Для начала поговорим немного о MongoDB. Что это такое?

MongoDB — документо-ориентированная система управления базами данных (СУБД) с открытым исходным кодом, не требующая описания схемы таблиц. Написана на языке C++. СУБД управляет наборами JSON-подобных документов, хранимых в двоичном виде в формате BSON.

Если попробовать вкратце охарактеризовать эту базу данных, то получится что-то вроде этого: аналог реляционной СУБД без join-ов и транзакций, зато с поддержкой структур данных (массивов, хешей).

MongoMapper - это “ORM” для MongoDB, написанный на руби. “ORM” в кавычках потому что сложно себе представить ORM для нереляционной базы данных. Я бы скорее назвал это высокоуровневой оберткой над API, которое предоставляет MongoDB, с поддержкой ассоциаций между записями и много чем еще.

Теперь, собственно, о “особенностях” MongoDB и MongoMapper-а.

Вложенные документы

Вложенные документы в понятии MongoMapper-а - это когда одни обьекты хранят внутри себя другие. Для примера, возьмем следующую модель:

class User
  include MongoMapper::Document

  key :login
  key :password
  key :salt

  many :posts
  many :addresses
end

class Post
  include MongoMapper::Document
  key :title, String
  key :body, String
  timestamps!
end

class Address
  include MongoMapper::EmbeddedDocument
  key :type, String
  key :country_id, ObjectId
  key :city_id, ObjectId
  key :city_address, String
end

Чтобы понимать, что тут происходит нужно, во-первых знать Ruby :) и во вторых - прочесть пост John Nunemaker (даже не знаю, как правильно перевести :)) о MongoMapper-е. Вот еще один, кстати, тоже интересный.

Таким образом имеем модель пользователя, которая хранить набор постов, написанных этим пользователем и набор адресов пользователя (домашний, рабочий, etc).

Особенности работы со встроенными документами легче показать на примере:

user = User.first
user.posts

Последняя комманда вернет “scope”/”relation”, а не просто массив элементов (люди, знакомые с named_scope в ActiveRecord версии < 3 поймут).
Соответственно, можно дродолжить эту комманду, например, так:

user.posts.all(:conditions => { :created_at => { "$gt" => (Date.today - 10.days) } })

В то время как

user.addresses

вернет массив и всю дополнительную фильтрацию прийдется производить с помощью Ruby.

Манипуляции со вложенными документами

У EmbeddedDocument нет понятия “id”, т.е. отличать обьекты Address один от другого можно только по их индексу в массиве user.addresses либо вводя “свои” идентификаторы. Но даже в этом случае выбирать соответствующий обьект прийдется “вручную”:

user.addresses.detect { |address| address.identifier == params[:address_identifier] }

PS. Для тех, кто в танке: detect - это аналог select { |address| … }.first.

Соответственно - вложенные обьекты легко добавлять, выбирать всю связь полностью, но с ними становится “сложно” работать, как с отдельными обьектами. В частности - неудобно обновлять из-за проблем с выборкой отдельного элемента и неудобно удалять.

С удалением вообще интересная история. Удалить обьект имея только ссылку на него - нельзя, нужна еще и ссылка на массив, в котором он хранится:

user.addresses.first.delete/destroy

не работает, т.к. таких методов у вложенных обьектов нет. Нужно делать так:

user.addresses.delete_at(address_index)

если вы знаете индекс в массиве адресов, либо

user.addresses.delete_if { |address| address.identifier == address_identifier }

если знаете идентификатор или удаляете по какому-либо другому полю.

Несложно, но проблемы на первых порах с этим бывают.

“идентификаторы” обьектов MongoDB

Каждому обьекту в бд MongoDB присваивает свой идентификатор. Как он по умолчанию строится можно посмотреть здесь. Обычно эти идентификаторы совсем не мешают, но иногда хочется просто засунуть вместо них какие-нибудь данные.

Например, вместо такой модели:

class Tag
  include MongoMapper::Document
  key :name, String
end

иметь такую

class Tag
  include MongoMapper::Document
  # id-шник явно никогда не прописывается
end

И создавать обьект так:

Tag.create(:_id => "ruby on rails")

MongoDB это позволяет делать. Более того - драйвер Ruby для MongoDB это тоже может делать. Проблема в том, что это не умеет делать MongoMapper. Совершенно. Как обойти эту проблему я на данный момент не знаю (я в итоге сделал модель первого типа и забил), есди кто знает решение - напишите, будет интересно.

И напоследок поговорим о

Has And Belongs To Many в MongoDB

… или habtm, знакомый многим по ActiveRecord.

Приведу пример из моего проекта: есть домены, есть их модераторы, связть many-to-many. Для каждого домена хочется видеть список модераторов и для каждого модератора хочется видеть список доменов, которые он модерирует.

Честно говоря, задача поставила меня в тупик. Т.е. как все мы решаем эти задачи с помощью RDBMS? Правильно - связующей таблицей. Так же можно было поступить и здесь, но… Как-то это было некрасиво, на мой взгляд, использовать этот подход вв документоориентированной базе данных.

Первое решение, которое мне пришло в голову - хранить массив id-шников доменов для каждого пользователя и хранить массив id-шников пользователей для каждого домена и синхронизировать их.

Последние два слова очень смущали - синхронизация вносила излишнюю сложность. Т.е. реализовать-то её можно, но очень не хотелось.

Собственно, обратился за помощью к знакомым и вышел на одного человека (ник - Necromant, сайта его я не знаю :) ), который помог решить мне эту проблему.

Итак, решение - хранить только один список id-шников (или в юзерах или в доменах) и сделать по нему индекс, соответственно, вывод как доменов по юзеру так и юзеров по домену будет достаточно быстрый.

Я хранил массив юзеров для домена, т.к. если бы я сделал иначе, то при удалении домена пришлось бы пройтись по всем юзерам и удалить все связи на домен, а в выбранном варианте этого делать не нужно (а удаление юзеров - это пока что вообще не существующее событие для моего проекта):

class Domain
  include MongoMapper::Document
  # ...
  key :moderator_ids, Array, :index => true
  def moderators
    User.find moderator_ids
  end
end

class User
  include MongoMapper::Document
  # ...
  def moderator_of?(domain)
    domain.moderator_ids.include? id.to_s
  end

  def moderated_domains
    Domain.all :moderator_ids => id.to_s
  end
end

Еще хочу рассказать о том, как подружить MongoMapper и Clearance, но пост вроде и так не маленький получился, так что ждите еще один пост о MongoMapper-е в ближайшие дни.

PS. Совсем писать разучился… :(

PPS. Если вам понравился этот пост, проголосуйте за него на Habrahabr-е. :)

written by fxposter \\ tags: , , ,

May 02

Настало время возродить этот блог. За более чем полугодие на нем не появилось ни одной статьи и я наконец-то намерен прекратить этот “застой”. Да, по сравнению с временами, которые были раньше у меня стало существенно меньше времени, чтобы писать что-то большое и серьезное - работа, университет (если быть точнее - то написание диплома) и еще один “секретный сайд проект” о котором, возможно, я когда-нибудь всем расскажу.

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

Ну, во-первых, я теперь не пишу на PHP. Вообще не пишу. И не очень сильно слежу за последними веяниями моды, связанными с этим языком. Да, я все равно иногда появляюсь в группе симфонистов (кому нужен доступ туда - пишите, там новичков любят и на многие нубские вопросы отвечают :) ). Да, я иногда посматриваю на фреймворки, которые сейчас являются популярными в сфере PHP - всякие Yii, ZF, etc. Посматриваю исключительно для того, чтобы быть в курсе происходящего. Учить их и писать на них в общем-то не очень хочется.

На что я променял PHP? Естественно на Ruby. Я не восхищаюсь этим языком, не превозношу его над остальными, просто на работе я пишу на нем (кстати, я работаю над стартапом для бизнесов - WaysGo, запуск будет, я надеюсь, очень скоро) и ни на чем другом желания писать особо не возникает - с точки зрения веб-разработки Ruby меня полностью устраивает. Этот язык удобен и достаточно экспрессивен. А Ruby On Rails - отличное средство для тех, кто хочет писать веб-приложения. Ну, в общем, вы меня поняли. ;)

Кстати говоря, я немного соврал насчет того, что я пишу на Ruby на работе. С начала этой недели я пишу iPhone-приложение на Objective-C и, в связи с этим, шустренько осваиваю Mac, Xcode, сам Objective-C и всякую прочую фигню, которая нужна для разработки под iPhone. Определенного мнения по поводу мака я пока сказать не могу, а вот Objective-C с первого подхода отличается похожестью на руби с точки зрения вызовов методов у объектов и открытых классов, но в то же время отличается несколько некрасивым синтаксисом (квадратные скобки, рррррр). С другой стороны, к синтаксису Ruby у меня тоже изначально было плохое отношение. Это отношение, кстати, не изменилось - я по прежнему считаю, что Ruby - непонятный язык для новичка, и если код на, например, Python можно просто читать, то для того, чтобы почитать код на Ruby придется сначала посмотреть туториалы, или что-то в этом роде, потому что синтаксис кроме выразительности отличается еще и неочевидностью. После пары дней работы с Ruby проблемы отпадают сами собой, так что этот аспект меня уже не очень беспокоит. Собственно, я надеюсь, что рано или поздно я перестану замечать синтаксис Objective-C и буду просто писать код, который делает что мне нужно.

Вот, собственно, и все на сегодня. Вроде ничего не забыл. До скорого! :)

written by fxposter \\ tags: , , , , ,

Sep 04

26-го сентября, в субботу, в Киеве будет проведен BarCamp на тему языка Ruby и фреймворка Ruby On Rails. Место проведения зависит от количества желающих посетить данное мероприятие и на данный момент уточняется. Если не произойдет ничего особенного - я туда скорее всего поеду. Буду рад увидеть там читателей своего блога. ;)

Заходим, смотрим, регистрируемся.

written by fxposter \\ tags: , ,

May 09

Еще одно, интересное на мой взгляд, видео. На этот раз - с RailsConf 2009. Выступает Роберт Мартин, известный в узких кругах под ником Uncle Bob (если вы еще не читаете блог его компании - начинайте, там пишут интересные вещи). Как можно догадаться из названия - доклад о том, что “убило” в своё время Smalltalk и чего нужно опасаться тем людям, которые программируют на Ruby. Доклад интересный, смешной, Боб, оказывается, отлично умеет повеселить публику.

Вердикт - смотреть обязательно, хотя бы для поднятия настроения. :)

written by fxposter \\ tags: ,

Feb 09

Смотрим, учимся и наслаждаемся.

PS. Несмотря на название - полезно отнюдь не только для “рубистов” (интересно, как адептов ruby называют на русском?).
PPS. Пора дизайн блога менять - видео не влазит. В FF3 смотреть можно, но если у вас с этим проблемы - идем на сайт RubyConf 2008 и смотрим видео там.

written by fxposter \\ tags: ,

Jun 21

Думаю, все знакомы с сайтом, предоставляющим возможность сравнить реализации различных языков программирования по скорости и потреблению памяти - Computer Language Benchmarks Game.

Зашел я туда сегодня… Почему - уже не помню, и обнаружил такую вот картину, которая меня довольно сильно удивила:

Shootout: Ruby

Руби на последнем месте… Раньше всё вроде было немного по-другому. Радует, что 1.9.0 будет “немного” быстрее. :)

written by fxposter \\ tags: ,

Jun 11

Нет, я не собираюсь перечислять нововведения в новой версии ROR, это уже сделали другие, а я лишь представлю вам их наработки. Встречаем очередную бесплатную книгу по рельсам - “Ruby on Rails 2.1 - What’s new”. В ней, как можно догадаться из названия, описываются (главные?) нововведения в последней версии фреймворка.

Почитать о книге и скачать её можно здесь.

PS. Сам книгу посмотрел… Могу сказать, что тем, кто рельсами не занимается, она будет довольно непонятной. Прочтите что-нибудь другое, например, Agile Development With Rails.

written by fxposter \\ tags: ,

May 27

Доступна для свободного скачивания книга “Road To Ruby”, предназначенная преимущественно для тех, кто имеет опыт программирования на C#, Java и C++. Подробности и ссылку для скачивания можно получить здесь (для скачивания нужно будет зарегистрироваться на сайте internet.com).

Кто не хочет регистрироваться - может скачать книгу у меня.

written by fxposter \\ tags: ,

Mar 18

Собственно, побывал я на первой своей конференции, впечатления остались, в целом, положительные, но обо всём попорядку…
Continue reading »

written by fxposter \\ tags: ,