Factory girl: extract_factories

19 мая 2009, Вторник

Все наверное слышали о фабиках от Thoughtbots, а кто не слышал услыште.

Ставим все очень просто:

 gem install thoughtbot-factory_girl --source http://gems.github.com

А дальше курим доку и радуемся жизни.

О птичках, наваял тут скриптец для построения фабрик, который достаточно скопировать себе в директорию lib/tasks/

Использовать:

rake db:extract_fixtures

Генерит туповато но иногда полезно, особенно когда большой список аттрибутов у модели.

shoulda: running concrete context

5 мая 2009, Вторник

Многие Rails разработчики уже используют shoulda - замечтельный  BDD фреймворк от команды thoughtbot, как альтернативу rspec, например.

Сам фреймворк - это надстройка над UnitTests.

При разработке с  UnitTests часто возникает необходимость запустить отдельный тест и посмотреть результаты. Но как быть с shoulda - ведь названия тестов здесь это обычные текстовые предложения, которые формируются с учетом контекста теста и контекстов в котором тест находится. Например:

require File.dirname(__FILE__) + '/../test_helper'
require 'shoulda'

class ArticleTest < Test::Unit::TestCase
  fixtures :all
  context "On create" do
    should "create article if all params ok" do
      article = Article.new( :title => "shoulda rocks", :body => "Try it")
    end
    should "require title" do
      article = Article.new(:body => "That article wouldn't be published")
    end
    should "require body" do
      article = Article.new(:title => "Where is my body?")
    end
  end
end

В таком случае shoulda сгенерит следующие тесты:

test: On create should create article if all params ok
test: On create should require title
test: On create should require body

Пока у нас не много тестов все ок. Но если у нас большое количество контекстов, например, On Create, On  Update, On Destroy и т.д.? И мы хотим выполнить определенный контекст для того чтобы понять где именно проблема.

Для этого воспользуемся возможностью UnitTests запускать отдельные тесты с ключем -n

Test::Unit automatic runner.
Usage: test/unit/shop_test.rb [options] [-- untouched arguments]

    -r, --runner=RUNNER          Use the given RUNNER
                                 (c[onsole], f[ox], g[tk], g[tk]2, t[k])
    -n, --name=NAME              Runs tests matching NAME.
                                 (patterns may be used).
    -t, --testcase=TESTCASE      Runs tests in TestCases matching
                                 TESTCASE. (patterns may be used).
    -I, --load-path=DIR[:DIR...] Appends directory list to $LOAD_PATH.
    -v, --verbose=[LEVEL]        Set the output level (default
                                 is verbose). (s[ilent],
                                 p[rogress], n[ormal],v[erbose])
        --                       Stop processing options so that the
                                 remaining options will be passed to the
                                 test.
    -h, --help                   Display this help.
Deprecated options:
        --console                Console runner (use --runner).
        --gtk                    GTK runner (use --runner).
        --fox                    Fox runner (use --runner).

Как показывает нам справка, параметром ключа -n может быть любое регулярное выражение. Например, команда:

ruby test/unit/article_test.rb --name="/test:(.*)On create(.*)/"

запустит на выполнение только тесты из контекста On create.

Вот так вот.

rake gems:install

3 мая 2009, Воскресенье

В версию rails 2 была добавлена интересная возможность установки зависимых gem библиотек используя только 1 команду. Для этого Вам необходимо вписать в environment.rb следующие строки, например для установки will_paginate:

    config.gem 'mislav-will_paginate', :version => '~> 2.3.8',
      :lib => 'will_paginate',
      :source => 'http://gems.github.com'

После этого в командной строке нужно набрать rake gems:install. Но не все так просто.

Если у Вас есть rake task в котором Вы, например включаете  environment.rb то Вы получите интересное сообщение, примерно такое:

sudo rake gems:install
(in /home/cthulhu/development/blog)
Missing these required gems:
  mislav-will_paginate  ~> 2.3.8

You're running:
  ruby 1.8.6.111 at /usr/bin/ruby1.8
  rubygems 1.3.2 at /home/cthulhu/.gem/ruby/1.8, /usr/lib/ruby/gems/1.8

Run `rake gems:install` to install the missing gems.

ИМХО это еще хуже чем драйвера для вашего CD на CD.

Существует пара хаков против такого поведения.

Плохой, ИМХО, - подхачить rail. Для этого в файле rails/lib/gems.rake в блоке:

namespace :gems do
  task :base do
    $gems_rake_task = true
    require 'rubygems'
    require 'rubygems/gem_runner'
    begin
      Rake::Task[:environment].invoke
    rescue
    end
  end
  ...
end

Нужно заменить:

 Rake::Task[:environment].invoke

на

begin
  Rake::Task[:environment].invoke
 rescue
end

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

Второй вариант, более преемлемый - дописать в  environment.rb небольшой хак вида:

if $rails_gem_installer
  class Rails::Initializer
    def load_application_initializers; end
  end
  config.gem 'mislav-will_paginate',
    :version => '~> 2.3.8', :lib => 'will_paginate',
    :source => 'http://gems.github.com'
end

Переменная $rails_gem_installer будет установлена когда Вы будете вызывать рейк таски из семейства установки гемов.

Так вот.

Может кто знает более правильные способы решения этой проблемы?

TDD mantra RED/GREEN/REFACTOR in tha Rails

30 апреля 2009, Четверг

Все слышали о TDD мантре красный, зеленый, реорганизация. Чтобы все было именно так, в рельсах нужно 2 вещи autotest и плагин redgreen.

Создаем в корне прожекта файлик .autotest и пишем в него 1 строчку require ‘redgreen/autotest’. Далее стартуем как обычно:

autotest -rails

И вуаля, консоль с тестами красно/зеленая.

MySQL MyISAM + rails tests = bugs

29 апреля 2009, Среда

Пришлось мне на днях потюнить MySQL. Решил попробовать разные конфигурации на предмет максимальной производительности по моим задачам. Нарыл ряд статей по  улучшению производительности. Лучше всего ИМХО написано было сдесь.

Пред история. Сразу скажу что запросы у меня простые и короткие. Пара селектов по индесированым полям + инсерт + апдейт. Ничего сложного. Но! В один прекрасный момент СУБД просто брала и начинала жрать все ресурсы процессора как локально так и на продакшин сервере. Однодневный инвестигейт показал интересную вещь. После инсерта происходит перестрой индексов данной таблицы, что само по себе требует ресурсов процессорного времени и это время прямо пропорционально количеству записей в таблице. Таким образом, если мне нужно много циклов записей/селектов подряд то время каждого цикла начинает расти. И у меня оно дошло до 1го цикла в секунду. Это при том что мне нужно это сделать 300к-500к раз. Посчитав примерно во что мне это обойдется вышло что весь цикл будет длиться с недельку. Это естественно ни в какие рамки не лезет.

Посему начал рыть. Сначала в сторону тюнинга СУБД. В целом добился прироста производительности гдето на 10%. Но проблема как обычно была не в СУБД. Т.е. как-бы в СУБД но не в СУБД. В общем это не самое интересное.

Самое интересно это то, что вовремя тюнинга я настроил все базы на использования движка MyISAM. И тут начались спец-эффекты. Упало ряд тестов. Анализ кода ничего не показал. Анализ фикстур, откаты изменений (я работаю в команде посему начал грешить на последние комиты) все это ни к чему не привело.

Начал думать (гуглом). Как оказалось тесты в рельсах работают (в основном) в режиме транзакций. Т.е. каждый тестовый случаю выполняется как транзакция, которая потом (после выполнения тестового случая) откатывается. Теперь о MyISAM, как извесно этот движек не поддерживает транзакций, поэтому ряд тестов модифицируют данные необратимо. С этим и были связаны глюки. Кроме того, глюки будут также если у вас есть модифицирующие хранимые процедуры.

Вердикт. Для тех кто использует MySQL в тестовых версиях используйте движек InnoDB, иначе могут выпасть очень даже интересные глюки. Или используйте PostgreSQL.

acts_as_random_id

27 апреля 2009, Понедельник

Ребята из дружественного мне Hash Train написали отличный плагин для генерирования поля id для ваших рельсовых объектов. Как известно rails генерирует поле id простым автоинкриментом. Таким образом, существует возможность проверить сколько объектов всего в БД. Иногда это секретная информация.

Использую плагин acts_as_random_id  можно подсунуть любое значение поля id. Естественно на Вас ложиться вся ответственность за уникальность.

Детали на github.

BTW, если есть идеи по улучшениям и доработкам плагина, велком.

Rails tip: LoadError exception

9 февраля 2009, Понедельник

Разрабатывая приложения на Rails программисты используют разнообразные gem библиотеки. При переносе или деплое приложения эти библиотеки необходимо либо включать в приложение (фризить) либо ставить на сервере. Но что если тот или иной gem использовался только для тестирования и в рабочей продакт версии уже не нужен. В целом если у Вас vps и туева хуча места это не важно. Но если вы теснитесь на shared розделяя считаные метры под несколько проектов нужно что-то думать.

Итак, каким образом происходит включение тех или иных гемов? Чаще всего при помощи оператора require. А что просиходит когда require не находит нужного файла? Генерируется исключение LoadError. Таким образом, перехватывая это исключение мы можем продолжать работать.

Пример кода, в котором я использую populator + faker для создания тестовых данных:

begin
  require 'faker'
  require 'populator'

  require 'rake'
  require 'rake/testtask'
  require 'rake/rdoctask'
  require 'rake/rdoctask'

  require 'rubygems'

  require(File.join(File.dirname(__FILE__), '../../config/environment.rb'))

  namespace :db do

    desc "Populates a fake data to database"
    task( :populate ) do
      User.populate(10) do |user|
        user.login = Faker::Internet.user_name
        user.first_name = Faker::Name.first_name
        user.last_name = Faker::Name.last_name
        user.email = Faker::Internet.email
        user.admin = [true, false]
      end
    end
  end
rescue LoadError
end

Используя такой трюк у нас нет необходимости устанавливать на продакт сервере не нужные библиотеки.

Ruby & RubyOnRails weblogs

7 февраля 2009, Суббота

Очередная порция блогов. На этот раз рубисты и рубисты-рельсовики.

Пользуемся на здоровье:

Ruby:

http://feeds2.feedburner.com/RubyInside
http://feeds2.feedburner.com/Rubyflow
http://on-ruby.blogspot.com/feeds/posts/default?alt=rss
http://www.igvita.com/feed/
http://www.abletech.co.nz/feed/
http://feeds.feedburner.com/PaulDixExplainsNothing
http://feeds.feedburner.com/momoro
http://feeds2.feedburner.com/DrNic
http://www.juixe.com/techknow/index.php/feed/
http://feedproxy.google.com/buckblog
http://feeds.floehopper.org/floehopper-blog
http://talklikeaduck.denhaven2.com/xml/rss20/feed.xml
http://feeds.feedburner.com/tagaholic
http://phvalues.wordpress.com/feed/
http://brainspl.at/xml/rss20/feed.xml
http://blog.mmediasys.com/feed/

Rails:

http://carlopecchia.eu/blog/feed/
http://feeds.feedburner.com/VigetExtend
http://www.culann.com/feed
http://feeds.feedburner.com/TechnicalPickles
http://feeds.feedburner.com/AtlanticDominionSolutions
http://feeds.feedburner.com/jystewart/iLiN
http://feeds.feedburner.com/ZachInglis
http://www.fngtps.com/feed/index.rss
http://feeds2.feedburner.com/railscasts
http://feeds.feedburner.com/RidingRails
http://hivelogic.com/feed
http://www.rubyrailways.com/feed/
http://feeds.feedburner.com/obie
http://blog.kineticweb.com/xml/rss20/feed.xml
http://feeds.feedburner.com/rubyonrailspodcast

Rails + Populator + Faker

29 ноября 2008, Суббота

Часто в процессе разработки сайта на Rails нужно быстро создать много данных, например для тестирования пажинатора.

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

Чтобы сэкономить время рекомендую воспользоваться библиотекой populator.

Ставим:

  gem install populator 

Используем. Например, нам нужно добавить в базу 3000 записей о пользователях

  User.populate(3000) do |user|
    user.first_name = “Tony”
    user.last_name = “Stark”
  end

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

Для этого есть еще одна чудная библиотечка faker. Ставим:

  gem install faker 

Теперь перепишем наш пример, добавив пару дополнительных полей и оформив в rake задачу, чтобы удобно было запускать, например в production:

  require ‘populator’
  require ‘faker’

  require ‘rake’
  require ‘rake/testtask’
  require ‘rake/rdoctask’

  require ‘rubygems’
  require(File.join(File.dirname(__FILE__), ‘../../config/environment.rb’))

  namespace :populate do
     desc “Add test data”
     task(:users) do
       User.populate(3000) do |user|
         user.first_name = Faker::Name.first_name # генерирует Имя
         user.last_name = Faker::Name.last_name   # Фамилию
         user.email = Faker::Internet.email       # мыло
         user.login = Faker::Internet.user_name   # логин
         user.phone = Faker::PhoneNumber.phone_number # номер телефона
         user.about = Faker::Lorem.paragraphs( 5 ) # О себе – 5 параграфов
       end
    end
  end

Теперь у нас в базе в любой конфигурации может быть 3000 пользователей с совершенно разными данными. Очень удобно. Сокращает время, и появляются дополнительные возможности тестирования.

Graphicoolus - сервис построения графиков

27 ноября 2008, Четверг

Недавно был открыт простенький сервис построения графиков и диаграмм - Graphicoolus.

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

Создание осуществляется в несколько простых интуитивно понятных шагов, после выполнения которых получаем код для встраивания графика в страницу Вашего сайта.

И на последок пример графика созданного с помощью данного сервиса:

Кому интересно испытать сервис в работе кликаем сюда.

Блог работает на WordPress.
Подписка RSS: все записи, комментарии.