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

Дима • 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 проектах, поэтому решено было зависимости сократить по минимуму.

Вместо того, чтобы забивать 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 приложений.

Совет для маководов

Илья • 9 марта 2007 г.

Обалденный совет, позволяющий отключить функцию Safe Sleep у Мака и тем самым сократить время ухода в режим сна в несколько раз.

Прикол в том, что каждый раз когда вы “усыпляете” Мак, он копирует всё содержимое оперативной памяти в файл, чтобы если у вас вдруг во время сна кончилась батарейка, то он мог спокойно выключиться, а потом, когда вы подключите его к сети и нажмёте кнопку ВКЛ, быстренько восстановить содержимое оперативки из этого файла. Это и называется режим Safe Sleep, то есть когда содержимое оперативной памяти сохраняется на винт.

На деле, у меня лично не было ни разу случая, когда садилась бы батарейка и мак был вынужден уходить в Safe Sleep. Более того, даже если это случится и у меня режим Safe Sleep будет выключен (как сейчас), то пусть у меня всё сбросится — мне не страшно загрузить всё заново. Зато каждодневное использование Мака стало гораздо более приятней, потому что засыпает он теперь за три секунды, вместо привычных 10-20. И ещё 2 гига на винте сэкономил, что всегда приятно :)

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

Илья • 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

Наткнулся на очень удобный баш скрипт (для мак ос и юниксов, разумеется), который делает вот что:

  1. Создаёт чистое рельсовое приложение.
  2. Импортирует его в свн репозитарий.
  3. Удаляет и ставит игнор на логи, кэш, сессии и прочее в папке tmp.
  4. Копирует database.yml в database.example и ставит на него игнор.

Очень удобно! Качать тут.

  1. Учитывай что великая любовь и великие достижения требуют великого риска.
  2. Когда проиграл, вынеси из этого урок.
  3. Уважай себя, уважай других и неси ответственность за все свои действия.
  4. Помни, что иногда не получить то, что хочешь, это удивительный подарок судьбы.
  5. Знай правила, чтобы правильно их нарушать.
  6. Не давай маленькой ссоре разрушить великую дружбу.
  7. Когда понимаешь что сделал ошибку, не медли ни секунды чтобы её исправить.
  8. Проводи немного времени наедине с собой каждый день.
  9. Будь открыт для изменений, но не отпускай свои ценности.
  10. Помни, что иногда тишина—это лучший ответ.
  11. Веди добрую, достойную жизнь. Тогда в старости, когда оглянешься назад, ты сможешь насладиться ей ещё раз.
  12. Любящая атмосфера в твоём доме это фундамент для твоей жизни.
  13. В разногласиях с теми кого любишь, смотри только на текущую ситуацию. Не поднимай то, что было в прошлом.
  14. Делись своим знанием. Это путь к достижению бессмертия.
  15. Будь вежлив с Землей.
  16. Раз в год, бывай там где никогда раньше небыл.
  17. Помни, что лучшие отношения те, в которых ваша любовь друг к другу превосходит вашу потребность друг в друге.
  18. Оценивай свой успех по тому, что тебе пришлось отдать чтобы его получить.
  19. Относись к любви и приготовлению пищи с безудержной энергией.

Updated: Пермалинк на этот пост был сломанный. Теперь всё работает.

В связи с выходом новых рельс, нужно было обновить ruby gems до версии 0.9.x.

После обновления гемов, при попытке установить новые рельсы, вылетела ошибка:


ERROR:  While executing gem ... (NoMethodError)
    undefined method `refresh' for #<Hash:0x14fbfd0>

Ошибка проявляется как на Мак ОС, так и на линуксе и даже солярисе.

Решается очень просто: стираем файл source_cache в системной папке ruby/gems/1.8

Updated: ребята с Riding Rails уже написали об этой проблеме.

Тем, кто пользуется Mac OS и Locomotive посвящается этот небольшой трюк.

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

Это очень удобно, если у вас в системе нету ни Руби, ни рельсов, зато они есть под локомотивом. Вы и не заметите разницу: просто включаете терминал и вот у вас и руби и рельсы и все гемы с рмагиком :)

Собственно вот здесь прекрасно описано как это сделать.

Единственное замечание, я в .locomotive.environment.bash в конце ещё clear добавил, чтобы консоль не заспамливать.

Используем символ как Proc

Илья • 29 января 2007 г.

Просто замечательный пример элегантности Руби:

Вместо:
@people.collect{|human| human.name }
Делаем так:
@people.collect(&:name)

Dave Thomas подробно описал как это работает.

Ловим exception внутри выражений

Илья • 28 января 2007 г.

Век живи, век учись.

Не так:
if @user.name
  @user.name.capitalize
else
  "name not set"
end
И не так:
@user.name? ? @user.name.capitalize : "name not set"
И даже не так:
begin
  @user.name.capitalize
rescue
  "name not set"
end
А вот так:
@user.name.capitalize rescue "name not set"

Красота…

Update: Привёл немного не подходящий пример в прошлый раз: @user.name обычно не возвращает исключение, поэтому этот метод тут не поможет.

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

Наблюдаем за ActiveRecord

Илья • 26 января 2007 г.

Небольшая, но приятная находка, которая позволяет следить за тем что делает ActiveRecord прямо из script/console.

$ script/console
>> ActiveRecord::Base.logger = Logger.new(STDOUT)
=> #<Logger:0x2814134 ...>
>> user = User.find :first
  User Load (0.000346)   SELECT * FROM users LIMIT 1
=> #<User:0x2665478 ...>

По-поему очень удобно.