РИТ++ 12-14 апреля, Москва

Опубликовано 01.04.2010

Итак, 12-14 апреля я буду читать доклад о Ruby on Rails на конференции РИТ++ 2010. Программа конференции уже официально сформирована, поэтому я могу заявить об этом официально.

Доклад будет предназначен для людей, не знакомых с Rails или знакомых очень поверхностно. Больших откровений и срывания покровов я не планирую делать (во всяком случае в самом докладе, про вопросы и общение в кулуарах ничего обещать не могу ;), однако если вы (или кто-то из ваших знакомых) хотели бы узнать о том, чем хороши рельсы и где их можно использовать, то мой доклад – именно для вас. Приходите, будет интересно. Примерные тезисы доклада можно посмотреть здесь (блин, знал бы что их опубликуют – оформил бы поприличнее).

Однако, конференция – это не единственное ради чего я поеду в столицу, есть еще масса интересных дел, которым я хотел бы посвятить часть времени. Например, мне бы хотелось пообщаться с коллегами-рельсовиками и/или прийти в гости в какую-нибудь компанию, где активно занимаются разработкой проектов на Ruby on Rails. Уверен, у нас найдутся общие темы для общения.

Я буду в Москве с утра 9 апреля и до вечера 14 апреля. Если у вас есть желание встретиться или пригласить меня в гости – пишите письма или стучитесь в Jabber/GTalk rene.dekart@gmail.com

Расширение ассоциаций в Rails

Опубликовано 12.10.2009

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

Давайте немного поговорим об ассоциациях. Если вы программируете на Rails дольше 15 минут, то уже наверняка знаете, что рельсы поддерживают несколько типов ассоциаций – belongs_to, has_one, has_many и has_and_belongs_to_many. Каждый тип ассоциаций решает определенную задачу, у каждого свой API. Но есть у них одна общая замечательная особенность – ассоциации можно расширять.

Допустим, у нас есть проект “Доска объявлений”, где пользователь может опубликовать до 5 объявлений каждое стоимостью 10 долларов. Давайте взглянем на модель пользователя, содержащую несколько методов для проверки возможности публикации объявлений:

class User < ActiveRecord::Base
  has_many :classifieds

  def can_publish_classified?
    has_free_classified_slots? and enough_money_to_publish_classified?
  end

  def has_free_classified_slots?
    self.classifieds.size <= 5
  end

  def enough_money_to_publish_classified?
    self.balance >= 10
  end
end

Вот так выглядят вызовы методов проверок:

@user.can_publish_classified?
@user.has_free_classified_slots?
@user.enough_money_to_publish_classified?

Обратите внимание на постоянно повторяющееся слово classified в названиях методов. Было бы гораздо удобнее, если бы методы вызывались в контексте того, к чему они непосредственно относятся, а именно – в контексте ассоциации classifieds. Именно для таких случаев в Rails реализована возможность расширения ассоциаций, а точнее – прокси-классов, которые эти ассоциации обслуживают. Делается это следующим образом:

class User < ActiveRecord::Base
  has_many :classifieds do
    def can_publish?
      free_slots? and enough_money_to_publish?
    end

    def free_slots?
      self.size <= 5
    end

    def enough_money_to_publish?
      proxy_owner.balance >= 10
    end
  end
end

Теперь проверки возможности публикации объявлений выглядят у нас так:

@user.classifieds.can_publish?
@user.classifieds.free_slots?
@user.classifieds.enough_money_to_publish?

Существенно лучше, не так ли? Обратите внимание на метод proxy_owner, который вызывается в методе enough_money_to_publish?. Его описание, а так же описание других полезных методов прокси-класса ассоциации можно найти в документации (см. Association extensions):

  • proxy_owner – объект, частью которого является данная ассоциация (в нашем случае экземпляр класса User)
  • proxy_reflection – объект класса ActiveRecord::Reflection::AssociationReflection, хранящий в себе сведения об ассоциации
  • proxy_target – объект или коллекция объектов, которые загружаются ассоциацией (в нашем случае это коллекция объявлений, привязанных к пользователю); загрузка этих данных выполняется вызовом метода load_target

Однако давайте допустим, что авторами объявлений у нас могут стать не только пользователи, но еще и компании (модель Company). Есть два пути добавления методов в ассоциацию второй модели – copy-paste и вынос методов в отдельный модуль. Я думаю, нет сомнений, что второй путь более правильный.

Выносим код расширения в отдельный модуль:

module ClassifiedExtension
  def can_publish?
    free_slots? and enough_money_to_publish?
  end

  def free_slots?
    self.size <= 5
  end

  def enough_money_to_publish?
    proxy_owner.balance >= 10
  end
end

А затем расширяем им ассоциации:

class User < ActiveRecord::Base
  has_many :classifieds, :extend => ClassifiedExtension
end

class Company < ActiveRecord::Base
  has_many :classifieds, :extend => ClassifiedExtension
end

Как видите, расширять ассоциации совсем не сложно. Однако, как ни странно, далеко не все разработчики, даже опытные, используют эту возможность. А зря :)