http://blog.gipoo.net/assets/2011/2/17/Screen_shot_2011-02-17_at_11.07.50_PM.png
RailsWayConf 2010, Berlin
Recently I’ve attended my first big ruby conference, which took place in Berlin, Germany. And here are some comments on it.
Interesting presentations
The first presentation I’d like to comment on is “Command your domain” by Lourens Naude. The key ideas are: “Maintenance cost is multiple times more than development cost”. Application should be domain driven, not data driven. Application becomes a separate framework with it’s language, features and specialties. Mostly talked about ruby in enterprise context, probably Lourens use to spend some time on java side :)
The next great presentation was “Rails in the Cloud – Experiences from running on EC2” by Jonathan Weiss. Speaker was talking about his experience in different amazon architectures. He told about possibilities which Amazon brings, pointing some really great tips like: using chef for EC2 instance configuration, load balancing architectures, handling emails on EC2 and so on. Slides.
The next useful presentation was “Enterprise Rails Hosting” by Julian Ficher. The key idea that you must know your needs. You don’t always need a cloud and you don’t always need a dedicated server. Everything depends on your needs. As of practical stuff, he shared some experience about network file systems (iSCSI + GFS). As well as a way to determine where to run such application as sphinx, memcache, database and so on in your infrastructure.
Fun and geeky presentation from Jan Krutisch “10 fun projects to improve your coding skills”. It was an unordinary one. Jan suggested 10 projects with a scope of technologies for improving your coding skills. Was interesting, I’d like to implement some of them. The build lights is really fun. Slides
And of course the panel “Beer on Rails”. During the talk, people discussed different topics like “If there are ruby vacations of the market?”, “Does ruby have to go enterprise or there are enough of startups?”, “What’s a future of Rails? Would it survive?” and many other topics.
Some other
“Small is sexy – We need Components in Rails” – The guy was trying to introduce a cell mechanism, which actually in nothing more that partials. When you go deeper in using cell, you face a problem, that cell does not work very well with complicated logic, nesting and does not scale well. In fact the guy said “I was drinking yesterday, so I did not managed to prepare well”.
“Rails 3: Dashing to the Finish” by Yehuda Katz. Nothing new. Presentation is done on the basis of Rails 3 Release Notes.
“Productive rails development with RubyMine” Russian team from Intellij IDEA were trying to advertise their IDE, which costs €599 per license for company. :)
“Evolutionary programming with Ruby” The speech was for very unclear targeted. People not familiar with GA, didn’t get the idea. But people familiar with that, were disappointed with simplicity of example. The topic is really interested, but needed a deeper explanation and more realistic examples.
Handling bounced emails in Ruby on Rails
Problem
It’s quite often you need to know if your user is received your email. Especially when you have registration with activation or similar. Ruby on Rails doesn’t provide such a feature out of the box.
You can find the solution on the Recepie #70 Rails Recepies, but it doesn’t provide server side settings. And I also made some code modifications.
Solution
The solution is quite simple. You need to filter somehow all bounced emails and forward it to ruby application, so that I can link it using message-id and parse the bounce reason.
As a mail user I use postfix, so all configurations are done with postfix.
Mail server setup
There are two approaches to handle bounced emails:- Create a special user `bounce`, which would receive all bounced mails and forward it to Rails;
- Change transport layer for all bounced mails, so that the goes directly to rails
So let’s start.
Approach #1. Create `bounce` user
This method is much simpler. And everything you need is:
Say who would receive bounced mails (bounce_notice_recipient). Setup notifies (notify_classes) and make it possible to redirect mails to file (allow_mail_to_commands).
#/etc/postfix/main.cf bounce_notice_recipient = bounces # User to be notified about bounces error_notice_recipient = bounces # User to be notified about any other delivery errors notify_classes = resource, software, bounce # Add 'bounce' to notify classes, so that when bounce occur, email is sent allow_mail_to_commands = alias, forward, include # Add 'include' so that emails could be redirected to file
After that all bounced mail would go to `bounce` user and we need somehow inform it to forward mail to rails(bounce_receiver.rb). This could be done in two ways:
1. Create a user `bounce`, and in his home page (/home/bounce) create a new file .forward:"|/usr/bin/ruby /path/to/script/bounce_receiver.rb"2. Make an alias for `bounce` user:
bounce: "|/usr/bin/ruby /path/to/script/bounce_receiver.rb"And to make it work, run following:
$ sudo /usr/bin/newaliases
(~) ’|’ the line says that stream will goes to external script.
(~~) this is possible only with `include` option set in ‘allow_mail_to_commands’.
One of disadvantages of such approach is access rights. I would need to assign some extra permissions for `bounce` user, which is not very secure.
Approach #2. Override transport layer for bounced messages
This approach is more `true`. Here are the steps:
1. Add a new transport layer /etc/postfix/master.cf
rails_transport unix - n n - - pipe
flags=Xhq user=deploy argv=/usr/bin/ruby /path/to/script/bounce_receiver.rb
(~) The flag (X) says that application would take care about this message and no further work from mail-server is needed.
2. Add a transport map by creating a file /etc/postfix/transportbounce@yourdomain.com rails_transport:Make it work:
$ sudo postmap /etc/postfix/transportAnd write it to postfix config (/etc/postfix/main.cf):
transport_maps = hash:/etc/postfix/transport
$ sudo /etc/init.d/postfix reload
Restart postfix and we’re actually done with server. Next steps would concern application.
Application layer (MailReceiver)
The code if from Recepie #70 Rails Recepies with some modifications.
First we need a model Delivery:$ ruby script/generate model DelvieryCreate migration:
#db/migrate/xxx_create_deliveries.rb
class CreateDeliveries < ActiveRecord::Migration
def self.up
create_table :deliveries do |t|
t.column :message_id, :string
t.column :recipient, :string
t.column :user_id, :integer
t.column :message, :text
t.column :status, :string
t.timestamps
end
end
def self.down
drop_table :deliveries
end
end
And some adjustments to class:
#app/model/delivery.rb
class Delivery < ActiveRecord::Base
def failure?
self.status == 'Failure'
end
def sent?
self.status == 'Sent'
end
end
Then we need an entry point for all bounced mails.
# app/models/bounce_receiver.rb
class BounceReceiver < ActionMailer::Base
class BouncedDelivery
attr_accessor :status_info, :original_message_id
attr_accessor :attr_names
attr_accessor :message
def self.from_email(email)
returning(bounce = self.new) do
plain_part = email.parts.detect do |part|
part.content_type == 'text/plain'
end
bounce.message = plain_part.body.strip
status_part = email.parts.detect do |part|
part.content_type == "message/delivery-status"
end
statuses = status_part.body.split(/\n/)
bounce.status_info = statuses.inject({}) do |hash, line|
key, value = line.split(/:/)
hash[key] = value.strip rescue nil
hash
end
original_message_part = email.parts.detect do |part|
part.content_type == "message/rfc822" or
part.content_type == "text/rfc822-headers"
end
parsed_msg = TMail::Mail.parse(original_message_part.body)
bounce.original_message_id = parsed_msg.message_id
end
end
def status
case status_info['Status']
when /^5/
'Failure'
when /^4/
'Temporary Failure'
when /^2/
'Success'
end
end
end
def receive(email)
return unless email.content_type == "multipart/report"
bounce = BouncedDelivery.from_email(email)
msg = Delivery.find_by_message_id(bounce.original_message_id)
msg.update_attributes(
:status=>bounce.status,
:message=>bounce.message
)
end
end
BounceReceiver receives standard IO stream from postfix server. Parse it accoding to RFC-822. Finds the Delivery model by message-id and updates the status.
And the last config file is a delegator to rails.#/path/to/script/bounce_receiver.rb +x #!/bin/sh RAILS_ENV=production /opt/applications/555_bg/current/script/runner 'BounceReceiver.receive(STDIN.read)'Now when I send emails, I do following:
mail = UserNotifier.deliver_activation_mail(user) Delivery.create(:message_id=>mail.message_id, :recipient => user.email, :status => 'Sent')
All Delivery records have a message-id, which is a unique message identificator in postifx and rails.
To see all undelivered messages just type the following:
>> Delivery.find(:all, :conditions=>{:status => 'Failure'})
Useful links
- http://en.wikipedia.org/wiki/Bounce_message
- http://www.postfix.org/postconf.5.html#allow_mail_to_commands
- http://www.networkjack.info/blog/2007/11/22/managing-bulk-email-delivery-bounce-handling-with-postfix-and-php/
- http://jasonseifer.com/2009/04/24/receving-email-with-rails
- http://github.com/kovyrin/bounces-handler/
Additions
In my case /script/runner didn’t found ruby. That’s why I made the following fix:# /script/runner #!/usr/bin/env /usr/local/bin/ruby
Получение уведомления о недоставленных письмах в Ruby on Rails
Проблема
Довольно часто при отсылки email сообщений нужно узнать, отправлено ли письмо или нет, и в случае если письмо не отправлено, то желательно знать причину. В Ruby on Rails нет такой функциональности out of the box.
Решение это проблемы в рецепте #70 Rails Recepies .
Описание решения
В качестве почтового серера будем рассматривать postfix сервер. Решение заключается в том, чтобы фильтровать все письма со статусом bounced и перенаправлять их в руби приложение. И соответсвенно там уже парсить и получать причину отказа.
Подготовка mail-server`а
Существует два подхода перенаправления bounced писем.- Метод “forward” – создание пользователя `bounce`, которому будут приходить все недоставленные письма и соответсвенно перенаправление этих писем в приложение;
- Метод “Transport layer” – Переопредление транспортного уровня для bounced email`ов;
Подробнее к каждому методу.
Метод “forward”
Более простой метод. Для того чтобы это заработало нужны следующие настройки.
Нужно сказать какой пользователь будет получать `bounced` почту(bounce_notice_recipient). Также установить уровень оповещения (notify_classes). И сказать что почта может уходить не только пользователю но и в файл (allow_mail_to_commands).#/etc/postfix/main.cf bounce_notice_recipient = bounces # User to be notified about bounces error_notice_recipient = bounces # User to be notified about any other delivery errors notify_classes = resource, software, bounce # Add 'bounce' to notify classes, so that when bounce occur, email is sent allow_mail_to_commands = alias, forward, include # Add 'include' so that emails could be redirected to file
Далее необходимо перенаправить всю почту от пользователя в файл приемщик (bounce_receiver.rb). Это можно также сделать двумя способами:
1. Создать пользователя ‘bounce’, и в домашней папке у него (/home/bounce) добавить файл .forward со след содержанием:"|/usr/bin/ruby /path/to/script/bounce_receiver.rb"2. Второй вариант – добавить алиас ‘bounce’ такого вида:
bounce: "|/usr/bin/ruby /path/to/script/bounce_receiver.rb"И для закрепления выполнить:
$ sudo /usr/bin/newaliases
(~) ’|’ прямая линия говорит о том что переправлять поток нужно в внешний скрипт.
(~~) такого рода перенаправление будет возможно только если установлено ‘allow_mail_to_commands’ содержит ‘include’ директиву.
Один из недостатков этого подхода в том, что требуется настраивать права для запуска приложения, что может повлечь некоторые security issues.
Метод “Transport layer”
Этот метод мне кажеться более предпочтительным и прозрачным. Ниже шаги по настройке:
1. Добавить новый транспортный уровень в конец файла /etc/postfix/master.cf
rails_transport unix - n n - - pipe
flags=Xhq user=deploy argv=/usr/bin/ruby /path/to/script/bounce_receiver.rb
(~) Флаг (X) обозначает что внешнее приложение является финальной инстанцией в процессе доставки почты.
2. Добавить транспортную карту (transport map) путем создания файла /etc/postfix/transportbounce@yourdomain.com rails_transport:Активируем его:
$ sudo postmap /etc/postfix/transportИ пропишем в настройках postfix (/etc/postfix/main.cf):
transport_maps = hash:/etc/postfix/transport
$ sudo /etc/init.d/postfix reloadПерезапускаем postfix и с настройкой готово. Далее можно переходить к настройке приложения.
Уровень приложения (MailReceiver)
Код MailReceiver`а взят и немного модифицирован из рецепта #70 Rails Recepies.
Для начала создадим модель Delivery:$ ruby script/generate model DelvieryМиграция:
#db/migrate/xxx_create_deliveries.rb
class CreateDeliveries < ActiveRecord::Migration
def self.up
create_table :deliveries do |t|
t.column :message_id, :string
t.column :recipient, :string
t.column :user_id, :integer
t.column :message, :text
t.column :status, :string
t.timestamps
end
end
def self.down
drop_table :deliveries
end
end
Сам класс:
#app/model/delivery.rb
class Delivery < ActiveRecord::Base
belongs_to :user
after_save :kill_user_if_failed
def kill_user_if_failed
self.user.die! if self.user and failure?
end
def failure?
self.status == 'Failure'
end
def sent?
self.status == 'Sent'
end
end
Далее нам нужен entry point для всех писем перенаправляемых на наш приемщик BounceReceiver.
# app/models/bounce_receiver.rb
class BounceReceiver < ActionMailer::Base
class BouncedDelivery
attr_accessor :status_info, :original_message_id
attr_accessor :attr_names
attr_accessor :message
def self.from_email(email)
returning(bounce = self.new) do
plain_part = email.parts.detect do |part|
part.content_type == 'text/plain'
end
bounce.message = plain_part.body.strip
status_part = email.parts.detect do |part|
part.content_type == "message/delivery-status"
end
statuses = status_part.body.split(/\n/)
bounce.status_info = statuses.inject({}) do |hash, line|
key, value = line.split(/:/)
hash[key] = value.strip rescue nil
hash
end
original_message_part = email.parts.detect do |part|
part.content_type == "message/rfc822" or
part.content_type == "text/rfc822-headers"
end
parsed_msg = TMail::Mail.parse(original_message_part.body)
bounce.original_message_id = parsed_msg.message_id
end
end
def status
case status_info['Status']
when /^5/
'Failure'
when /^4/
'Temporary Failure'
when /^2/
'Success'
end
end
end
def receive(email)
return unless email.content_type == "multipart/report"
bounce = BouncedDelivery.from_email(email)
msg = Delivery.find_by_message_id(bounce.original_message_id)
msg.update_attributes(
:status=>bounce.status,
:message=>bounce.message
)
end
end
BounceReceiver принимает стандартный IO в виде сообщения. И парсит его в соответсвии со стандартом RFC-822. Далее находит модель Delivery по original_message_id и обновляет его статус.
И последнее из настроек. Нужно создать исполняемый файл, который будет делегировать запрос в BounceReceiver#/path/to/script/bounce_receiver.rb +x #!/bin/sh RAILS_ENV=production /opt/applications/555_bg/current/script/runner 'BounceReceiver.receive(STDIN.read)'Теперь при отсылки emailа, нужно создать запись Delivery.
mail = UserNotifier.deliver_activation_mail(user) Delivery.create(:message_id=>mail.message_id, :recipient => user.email, :status => 'Sent')
Запись Delivery сохраняется вместе с message-id, который является уникальным идентификатором сообщения в системе.
В случае если письмо по каким-то причинам не может быть доставлено, оно будет перенаправлено пользователю `bounces` и далее от него перенаправлено нашему BounceReceiver`у. С помощью message-id он найдет соответсвующую запись Delivery и обновит его статус.
Для отображения всех undelivered email`ов можно запустить след команду:
>> Delivery.find(:all, :conditions=>{:status => 'Failure'})
Ссылки по теме
- http://en.wikipedia.org/wiki/Bounce_message
- http://www.postfix.org/postconf.5.html#allow_mail_to_commands
- http://www.networkjack.info/blog/2007/11/22/managing-bulk-email-delivery-bounce-handling-with-postfix-and-php/
- http://jasonseifer.com/2009/04/24/receving-email-with-rails
- http://github.com/kovyrin/bounces-handler/
Дополнения
В моем случае /script/runner не находил интерпретатор ruby. Поэтому пришлось дописать следующее:# /script/runner #!/usr/bin/env /usr/local/bin/ruby
Detecting fragmented tables
$ mysql -u root -p
USE information_schema;
SELECT concat("OPTIMIZE TABLE ", table_schema,".",table_name,";") FROM tables WHERE DATA_FREE > 0;
nginx upload module with paperclip
Nginx upload module did a really great performance boost on big images upload. Here are some articles on how to run it and setup:
- http://matthewhutchinson.net/2010/1/6/nginx-upload-module-with-paperclip-on-rails
- http://gist.github.com/303921
- http://www.grid.net.ru/nginx/upload.en.html
Checking web-server
The approach I found to detect currently running web server provided by newRelic.
Using NewRelic::Control.instance.dispatcher this technique you can detect whenever you are running server or any rake task or smth else.
Setting up the redis
Show manual on how to make redis work with your rails application.
Server setup
First download and install redis server:wget http://redis.googlecode.com/files/redis-1.2.6.tar.gz gzip -d redis-1.2.6.tar.gz tar -xvf redis-1.2.6.tar cd redis-1.2.6 make sudo cp redis-server redis-cli redis-benchmark /usr/local/bin
Then create redis.conf file here /etc/redis.conf. Configuration file could be downloaded from here
Create init.d script for that.sudo nano /etc/init.d/redis sudo chmod +x redis sudo /etc/init.d/redis start sudo /etc/init.d/redis stop sudo /etc/init.d/redis restartMake redis start on server restart.
$ sudo crontab -e << @reboot /etc/init.d/redis startInstall redis-store and ezmobius-redis gems:
sudo gem sources -a http://gems.github.com sudo gem install ezmobius-redis sudo gem install redis-storeYou might also have want some patch to redis-store gem. Add the following lines to /usr/lib/ruby/gems/1.8/gems/redis-store-0.3.7/lib/cache/rails/redis_store.rb
require 'rubygems' require 'activesupport'
And that is it with server setup.
Rails setup
config/environment.rb
require "redis-store" Rails::Initializer.run do |config| config.gem "redis-store", :source => "http://gemcutter.org", :lib => "redis-store" config.cache_store = :redis_store end
So that’s it. Now on you server use can use Rails.cache.read, Rails.cache.write and by this you would be working with Redis store.
Usefull links
Setting up medium ruby server
In this article I’m going to share some techniques of configuring typical Ruby/RoR server. I’ll skip all steps on installing OS, so lets just assume that we have Ubuntu 8.04 LTS Server 64bit installed out of the box with SSH account.
Plan
- Setting up server
- Installing ruby/rails
- Installing necessary gems
- Setting up application
- Moving data
1. Setting up server
- Users set up
- Setting up SSH
- Setting up public keys
- Set up time/timezone
1.1 Users set up
First I need to create a user for deploying and accessing server, lets call it ‘deploy’ user.sudo adduser --home /home/deploy deployIt’s a good practice to select complicated password for you accounts, here is a great password generator I use.
Then add the following line deploy ALL=(ALL) ALL after root ALL=(ALL) ALL to suoders list.
sudo nano /etc/suodersRemove all unneeded users. A list of all users you can find at /etc/passwd. User can be deleted by:
$ sudo deluser usernameSet up user password policies. User policies are changed via chage command. Here is an example:
sudo chage -l deploy sudo change deploy
For a detailed description of commands refer to the following manual on user management.
1.2 Setting up SSH
All ssh setting are found at /etc/ssh/sshd_config. Basically what I need to change are port, protocol, root access, public key auth.#/etc/ssh/sshd_config ... Port <b>567</b> ... Protocol <b>2</b> ... PermitRootLogin <b>no</b> ... PubkeyAuthentication <b>yes</b> ...Once you made any changes to ssh, you need to reboot ssh deamon:
$ /etc/init.d/ssh restart $ exit Connection to xx.xx.xx.xx closed. $ ssh deploy@xx.xx.xx.xx -p 567
1.3 Setting up public keys
Every time I deploy or access my server, I need to enter long unmemorable password. To avoid that I set up public keys. This is done simply, first you need to generate public key on your site by:home$ ssh-keygen home$ cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqLHM9juBojORQIrIcvo0.......And then append your ssh-rsa key to authorized_keys (Create ~/.ssh/authorized_keys if file doesn’t exists).
cat id_rsa.pub > ~/.ssh/authorized_keys
Here is a manual for generating ssh keys.
1.3 Setting up time/timezone
There are actually several ways how to set up time on your server. First one is to set up timezone, which is described here
The other way, which I prefer more, is to change system time:$ hwclock --set --date="YYYY-MM-DD HH:MM:SS" $ hwclock -s
That’s it with server configuration.
2. Installing ruby/rails
- Installing development libraries
- Installing RubyEE
- Install mysql, sphinx, postfix, etc
sudo apt-get update sudo apt-get install libssl-dev -y sudo apt-get install build-essential -yand some others
sudo apt-get -y install git-core -y sudo apt-get -y install curl wget -y sudo apt-get install libcurl-ocaml-dev -y sudo apt-get install mysql-server -y sudo apt-get install libmysqlclient15-dev -y sudo apt-get install libsqlite3-dev make -y sudo apt-get install libjpeg-progs sudo apt-get install libmagick9-dev sudo apt-get install imagemagickSetting up sphinx:
wget http://www.sphinxsearch.com/downloads/sphinx-0.9.9.tar.gz gzip -d sphinx-0.9.9.tar.gz & tar -xvf sphinx-0.9.9.tar cd sphinx-0.9.9 ./configure make sudo make installOnce this is done, go and download rubyEE, which comes together with nginx or apache, whatever you need. And run the following command:
mkdir ~/sources
cd ~/sources
wget http://rubyforge.org/frs/download.php/68720/ruby-enterprise_1.8.7-2010.01_amd64.deb
sudo dpkg -i ruby-enterprise_1.8.7-2010.01_amd64.deb
sudo /usr/local/bin/passenger-install-apache2-module
To get mails sent, I also need a postfix installed. This is simple as
sudo apt-get install postfix
This would install ruby,rails and nginx.
3. Installing gems
Here’s just a small list of gems, required by regular rails project.
sudo gem sources --add http://gems.github.com
sudo gem install backup \
archive-tar-minitar \
aws-s3 \
chronic \
curb \
fastthread \
javan-whenever \
mime-types \
mysql \
net-scp \
net-sftp \
net-ssh \
rack \
ruby-hmac \
rubyzip
sudo gem install rmagick -v 2.10.0
sudo gem install rails -v 2.3.5
sudo gem install javan-whenever
sudo gem install daemons
4. Setting up application
First I need to create a folder where my application would be stored. Usually I create it in /opt/applications.
$ mkdir -p /opt/applications/app_name $ cd /opt/applications/app_name/shared
To deploy source code you need to add your public key to github (in case of deploying from github). For more details on public keys and github access this manual.
Before first deploy I prefer setting up MySql and shared folder.
It is a good practice to create a separate MySQL user for you application. This is done simple by:
$ mysql --user=root database mysql> CREATE USER 'deploy'@'localhost' IDENTIFIED BY 'some_pass'; mysql> GRANT ALL PRIVILEGES ON *.* TO 'deploy'@'localhost';
Setting up shared folder. Usually there are log, pids and system folder. With all basic configuration files.
$ cd /opt/application/app_name/shared $ mkdir log $ mkdir pids $ mkdir system $ mkdir sphinx $ tocuh database.yml <- production databases username and password
Once you have MySQL installed, it’s time to think about nginx configuration. Here is a typical nginx configuration for my applications (/opt/nginx/conf/nginx.onf).
Another useful thing for nginx is a init.d deamon file.
Add nginx to startup on reboot:
$ sudo crontab -e @reboot /etc/init.d/nginx start
Next, I should set up postfix.
And MySQL config with some optimisations.
So, once, we are done, it’s time for deploy script.
And now on you local machine just type:$ cap production deploy
4. Moving data
First create a mysql dump:$ mysqldump --complete-insert --skip-extended-insert --user=deploy --password="xxxx" app_name > backup.sql
And then rsync all data from old server to the new one.
$ rsync -az folder/ -e 'ssh -p 987' deploy@domain.com:/opt/applications/app_name/shared/ $ mysql -u deploy -p app_name < backup.sql
And we are actually done. Now just restart server and application is ready for running.
$ sudo /etc/init.d/nginx restart
FTP autologin
$ nano ~/.netrc machine my_ftp_server login teacher password secret1 $ ftp my_ftp_server 230 User teacher logged in
Most used git commands
$ git statusPushing local changes to the remote repository
$ git add . $ git commit -a -m 'Message' $ git pull $ git pushSee all local branches
$ git branchMake a remote branch
$ git push origin master:v1.1Checkout the remote branch
$ git checkout -b v1.1 origin/v1.1Delete the remote branch
$ git push origin :v1.1Change local branch
$ git checkout v1.1Merge with remote branch
$ git merge v1.1
Seed produciton data to test environment
Usually when calling rake test you testing database is purged. In many cases this behaviour is normal. But what about then you data is a part of code and you have connection to data from code. Here is a rake task, which makes it possible to copy some data from development/production database and not using fixtures:
# lib/tasks/test.rake
DATA_TABLES = [:categories]
namespace :db do
namespace :test do
task :seed_data => :environment do
config = Rails::Configuration.new
dev_db = config.database_configuration['development']["database"]
test_db = config.database_configuration['test']["database"]
DATA_TABLES.each do |table|
ActiveRecord::Base.connection.execute("DELETE FROM #{test_db}.#{table.to_s};")
ActiveRecord::Base.connection.execute("INSERT #{test_db}.#{table.to_s} SELECT * FROM #{dev_db}.#{table.to_s};")
end
end
desc 'Check for pending migrations and load the test schema'
task :prepare => 'db:abort_if_pending_migrations' do
if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke
Rake::Task["db:test:seed_data"].invoke
end
end
end
end
And that is it. So next time when you call rake test table categories would be copied right from the development database and you wouldn’t have to create fixtures.