Первая книга про Ruby на русском

Илья • 18 сентября 2007 г.

Очень порадовало наткнуться на Озоне на первую книгу про Ruby на русском языке.

Перевести почему-то решили первое издание The Ruby Way Хэла Фултона, выпущенного аж в далёком 2001 году, не смотря на то что ещё в прошлом году вышло второе. С такой скоростью мы вряд-ли увидим первые книги по рельсам на русском языке в ближайшем будущем…

The Ruby Way будет весьма интересна людям, которые уже знакомы с программированием и основами Руби. Новичкам же придётся подождать пока на русский переведут Programming Ruby или Ruby Cook Book.

По старой доброй русской традиции, издатель русской версии The Ruby Way до неузнаваемости изуродовал её оригинальную обложку. Им чем-то не понравилось деревце из которого растёт цветная капуста, изображённое на обложке в оригинале, поэтому пришлось изворачиваться и рисовать соплевидный плевок грязно-бардового цвета и прикреплять клипартовый рубин на чёрном фоне. Этот шедевр вообще сложно описать словами, лучше взгляните на него сами:

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

Update: как мне помогли выяснить мои любезные читатели, эта книга является переводом второго издания The Ruby Way, а не первого, как я считал ранее. Хороший повод всё-таки купить эту книжку…

Один маленький нюанс :counter_cache

Илья • 18 июля 2007 г.

Если вы хотите использовать :counter_cache для своих ассоциаций, не забудьте что поле, в котором будет хранится количество связей, должно иметь дефолтовое значение 0, а не NULL, как это иногда бывает по-стандарту.

class AddCounterCaches < ActiveRecord::Migration
  def self.up
    add_column :posts, :comments_counter, :integer, :default => 0
  end

  def self.down
    remove_column :posts, :comments_size
  end
end

Сегодня в порыве продуктивно-оптимизационной истерии нашел удобный способ отображать проект в Textmate Drawer.

Превратил такой, “стандартный” вариант:

В такой:

Суть такая, создаем новый проект, из Finder перекидываем содержимое app/ в Drawer. Это то, что мы используем чаще всего. Следом создаем в Drawer новую группу – rails root, куда засовываем всё остальное.

Кстати, вы знали что по запросам в Гугле продуктивность популярней чем порно? vs.

require 'rubygems'
require_gem 'activerecord'

p ActiveRecord

Всё ок:

Warning: require_gem is obsolete.  Use gem instead.
ActiveRecord

Теперь попробуем то же самое, но вместо require_gem используем метод gem:

require 'rubygems'
gem 'activerecord'

p ActiveRecord

То ничего не работает:

uninitialized constant ActiveRecord::Base (NameError)

Кто может объяснить сей феномен? Использую Ruby 1.8.5, Rubygems 0.9.4.

Часто бывает, что вы создаёте новый рельсовый проект, несколько моделей для начала и даже пару миграций, но, ничего не спасёт вас от того, чтобы создать базу данных самостоятельно. Теперь же, достаточно только написать rake db:create и всё готово! А если вы хотите наоборот удалить базу данных, то rake db:drop ваш помощник. Но самое интересное начинается тогда, когда вы хотите полностью пересоздать базу данных и перезапустить для неё миграции — rake db:reset и вуаля!

Итак, три новых рейка для баз данных:

rake db:create
rake db:drop
rake db:reset

Ну и, разумеется, вы можете указывать над какой именно базой данных будут производится действия, указывая переменную RAILS_ENV перед запуском рейка:

RAILS_ENV=test rake db:create

Так же, когда вы используете rake db:reset, вы можете указать до какой версии базу нужно мигрировать:

rake db:reset VERSION=15

Уже несколько лет работаю на Маке в редакторе TextMate и до сих пор не знал о существовании такой замечательной незаменимой штукенции как TextMate Footnotes. Надеюсь и вы не знали :)

TextMate Footnotes это плугин для рельсов и его нужно устанавливать отдельно для каждого вашего веб-приложения, после чего, когда вы зайдёте в своё веб-приложение через браузер, вы увидите внизу страницы небольшое серое меню с потрясающе удобными ссылками: Controller, View, Layout, Stylesheets, Javascripts. Первые три ссылки откроют для вас прямо в ТекстМейте вьюшку, контроллер или лейаут данного действия, которое вы смотрите в браузере. Это потрясающе удобно! А ссылки stylesheets и javascripts выведут на страницу маленький аккуратный списочек используемых на данной странице стилей и скриптов.

Более того, используя Footnotes, можно просмотреть содержание сессии, куков, params и последние 200 строк логов.

Как всё это выглядит, можно посмотреть на фликре.

В общем, Footnotes позволяют экономить очень много времени и тем самым реально увеличивают продуктивность.

Кстати, идея эта, судя по всему, была взята из фреймворка Seaside, только на сиасайде она ещё круче в несколько раз.

А теперь, заходим в своё супер-крутое веб-приложение, которое не сегодня, завтра изменит весь мир, и устанавливаем плугин TextMate Footnotes:

script/plugin install -x http://macromates.com/svn/Bundles/trunk/Bundles/Rails.tmbundle/Support/plugins/footnotes

Перезапускаем веб-сервер (ну на всякий случай) и смотрим что из этого получилось в браузере :)

Маленький Безобидный Трюк

Дима • 13 мая 2007 г.

Если хочется чтобы

@group = Group.find(params[:group_id])

не вызывало исключения в случае если группа не найдена, то можно сделать так:

@group = Group.find_by_id(params[:group_id])

Мне кажется что во многих случаях это явно лучше чем rescue nil.

Регэкспы для валидаций

Илья • 3 мая 2007 г.

Каждый раз пишу валидации и каждый раз приходится искать в инете регэкспы или писать самому (ну нет…). Спасибо Рику Олсену за наше счастливое детство — теперь для меня эта проблема навсегда снята.

Я откопал в его Мефисто маленький файлик format.rb, в котором описаны все, нужные для валидаций, регэкспы. Теперь этот файлик кочует из одного моего проекта в другой.

Итак, копируем файл в папку lib/ и используем его во время написания валидаций:

Ляпота? :)

Представим что у нас есть модель User со следующими валидациями:

class User < ActiveRecord::Base 

  validates_presence_of :username
  validates_presence_of :email
  validates_presence_of :password 
  validates_length_of :password, :in => 6..12

  attr_accessor :password 

end

Самая обычная модель с самыми обычными валидациями. Вот только, если мы не заполним поле пароль, у нас сработают сразу две ошибки: пароль не может быть пустым и пароль слишком короткий. Абсурд: конечно пароль короткий, ведь его нет!

Решается всё элементарно: добавляем флаг :allow_nil => :true для валидации длины пароля:

class User < ActiveRecord::Base 

  validates_presence_of :username
  validates_presence_of :email
  validates_presence_of :password 
  validates_length_of :password, :in => 6..12, :allow_nil => true

  attr_accessor :password 

end

Теперь, если пользователь не введёт пароль, то сработает только валидация присутствия, а валидация на длину будет молчать как партизан.

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

Update: Поправка опечатки.

Belongs_to :counter_cache

Илья • 24 апреля 2007 г.

Как всё-таки иногда полезно почитать чужой код. Особенно, если его писал Рик Олсен ;)

Ковыряясь в Beast, наткнулся на интересную штуку, которую раньше не замечал:

class Topic < ActiveRecord::Base
  belongs_to :forum, :counter_cache => true
  …

Флаг counter_cache сделан для автоматического сохранения в базе количество ассоциированных объектов. Будет их автоматически наращивать и уменьшать при добавлении и удалении соответственно.

Для того чтобы вся эта красота работала, в таблице бд модели нужно добавить поле с именем типа «#{table_name}_count». Или, если вы хотите собственное название, достаточно просто его прописать вместо true:

class Topic < ActiveRecord::Base
  belongs_to :forum, :counter_cache => :super_puper_counter
  …

Делается это всё для быстроты. Чтобы каждый раз, когда вам нужно узнать сколько у форумов топиков (к примеру), вам не нужно было бы делать SQL-запрос, считая все топики.

Как приятно узнать что-то новое.

Update: добавил информацию о том, как нужно назвать поле в базе данных

Simple Emptiness

Дима • 31 марта 2007 г.

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

Для строк:

"" | "default"                         # "default"
"      \n" | "default"                 # "default"
"  blah  " | "default"                 # "  blah  "

Для хешей:

{} | {:default => :value}              # {:default => :value}

Для других объектов:

false | "something"                    # "something"
nil | "something"                      # "something"
true | false                           # true
{:pi => 3.1416} | {:pi => 3.14159265}  # {:pi => 3.1416}

Для массивов работать не будет, у них уже есть метод |, который объединяет два массива удаляя дубликаты.

Также, ещё один недостаток это то, что данный способ не предполагает lazy evaluation, то есть следующий код вызовет и some_method() и redundant_method() при любом условии:

value = some_method() | redundant_method()

А вообще, всё это было сделано для того, чтобы сократить количество строк при следующем действии:

@plan = params[:plan_name].to_s.strip.empty? ? "Not given." : params[:plan_name]

До:

@plan = params[:plan_name] | "Not given."

Ну а потенциально может пригодиться и в множестве других случаев.

Скачать файл с кодом можно здесь: simple_emptiness.rb

У кого есть предложения по поводу того как это можно ещё использовать или улучшить?

Update: по поводу замечания насчет возможного использования метода blank? (из ActiveSupport) вместо цепочки to_s.strip отмечу, что код писался с расчетом на возможное использование и в pure ruby проектах, поэтому решено было зависимости сократить по минимуму.

Ура! Убрали эту уродскую точку с запятой.

Ещё несколько дней назад, при создании такого рестового роута:

map.resources :users, :collection => { :filter => :get }

Генерировался такой урл:

#GET /users;filter

А теперь, в Edge Rails генерируется такой:

#GET /users/filter

Учтите, что в Rail 1.2 урлы по прежнему с точкой запятой, а в Rails 2.0 они окончательно перестанут работать.

Вместо того, чтобы забивать ApplicationHelper сотнями различных методов, только для того, чтобы они были доступны во всех “вьюшках”, лучше создайте несколько отдельных хелперов и вынесите туда часть методов. Так вы упорядочите свой код, сделаете его более читабельным и удобным.

А подключить эти хелперы в своё приложение можно вот так:

class ApplicationController < ActionController::Base

  helper "javascript" # Подключаем JavascriptHelper
  helper "images"    # Подключаем ImagesHelper

end

Или, если вы используете edge Rails, то вот так:

class ApplicationController < ActionController::Base

  helper :all # Подключаем абсолютно все хелперы из папки app/helpers

end

Кстати, второй вариант теперь стандартный для всех новых edge Rails приложений.

Новое в рейлс: аннотации кода

Илья • 27 февраля 2007 г.

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

Например в коде у нас вот что:
class Friendship < ActiveRecord::Base

  # TODO: Ability to make friends with your eyes closed.
  def make_friends
    #code here
  end

  # FIXME: Right now if your friend is Britney you just can't lose her...
  def lose_friends
    #code here
  end

  # OPTIMIZE: Contact Hannibal to optimize this method
  def eat_friend
    #code here
  end

end

А потом запускаем rake notes и получаем список всех заметок со всего приложения:

$ rake notes
app/models/friendship.rb:
 * [3] [TODO] Ability to make friends with your eyes closed.
 * [8] [FIXME] Right now if your friend is Britney you just can't lose her...
 * [13] [OPTIMIZE] Contact Hannibal to optimize this method

Ещё один changeset от DHH. И опять связанный сугубо с организацией файлов и папок рейлс-проектов.

Теперь в edge rails все ваши настройки, которые раньшы подключались в конец файла environment.rb, нужно запихивать в папку config/initializers/. Получается что-то вроде этого:

config/initializers/mailer.rb:
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.default_charset = "utf-8"
config/initializers/exceptions_notifier.rb:
ExceptionNotifier.exception_recipients = ["your@emailaddress.com"]

Впрочем многие уже давно примерно так и делают.

Кстати, можете подписаться на этот RSS feed и следить за всеми changeset’ами самостоятельно :)