Ruby GC params matters

Posted by balepc
on Thursday, February 17

http://blog.gipoo.net/assets/2011/2/17/Screen_shot_2011-02-17_at_11.07.50_PM.png

RailsWayConf 2010, Berlin

Posted by balepc
on Tuesday, June 08

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.

http://davidebenini.it/2009/08/29/faking-an-ip-with-webrat/

Posted by balepc
on Friday, June 04

Handling bounced emails in Ruby on Rails

Posted by balepc
on Wednesday, May 26

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:
  1. Create a special user `bounce`, which would receive all bounced mails and forward it to Rails;
  2. 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/transport
bounce@yourdomain.com rails_transport:
Make it work:
$ sudo postmap /etc/postfix/transport
And 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 Delviery
Create 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

  1. http://en.wikipedia.org/wiki/Bounce_message
  2. http://www.postfix.org/postconf.5.html#allow_mail_to_commands
  3. http://www.networkjack.info/blog/2007/11/22/managing-bulk-email-delivery-bounce-handling-with-postfix-and-php/
  4. http://jasonseifer.com/2009/04/24/receving-email-with-rails
  5. 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

Posted by balepc
on Wednesday, May 26

Проблема

Довольно часто при отсылки email сообщений нужно узнать, отправлено ли письмо или нет, и в случае если письмо не отправлено, то желательно знать причину. В Ruby on Rails нет такой функциональности out of the box.

Решение это проблемы в рецепте #70 Rails Recepies .

Описание решения

В качестве почтового серера будем рассматривать postfix сервер. Решение заключается в том, чтобы фильтровать все письма со статусом bounced и перенаправлять их в руби приложение. И соответсвенно там уже парсить и получать причину отказа.

Подготовка mail-server`а

Существует два подхода перенаправления bounced писем.
  1. Метод “forward” – создание пользователя `bounce`, которому будут приходить все недоставленные письма и соответсвенно перенаправление этих писем в приложение;
  2. Метод “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/transport
bounce@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'})

Ссылки по теме

  1. http://en.wikipedia.org/wiki/Bounce_message
  2. http://www.postfix.org/postconf.5.html#allow_mail_to_commands
  3. http://www.networkjack.info/blog/2007/11/22/managing-bulk-email-delivery-bounce-handling-with-postfix-and-php/
  4. http://jasonseifer.com/2009/04/24/receving-email-with-rails
  5. http://github.com/kovyrin/bounces-handler/

Дополнения

В моем случае /script/runner не находил интерпретатор ruby. Поэтому пришлось дописать следующее:
# /script/runner

#!/usr/bin/env /usr/local/bin/ruby

Know version of your ubuntu server

Posted by balepc
on Thursday, April 29
 $ cat /etc/issue

Installing Rails 3

Posted by balepc
on Wednesday, April 14

http://adamfortuna.com/2010/02/06/getting-rails-3-beta-setup/

Detecting fragmented tables

Posted by balepc
on Wednesday, April 07
  $ 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

Posted by balepc
on Friday, April 02

Nginx upload module did a really great performance boost on big images upload. Here are some articles on how to run it and setup:

  1. http://matthewhutchinson.net/2010/1/6/nginx-upload-module-with-paperclip-on-rails
  2. http://gist.github.com/303921
  3. http://www.grid.net.ru/nginx/upload.en.html

Checking web-server

Posted by balepc
on Friday, March 26

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

Posted by balepc
on Friday, March 26

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 restart
Make redis start on server restart.
  $ sudo crontab -e
  << @reboot /etc/init.d/redis start
Install redis-store and ezmobius-redis gems:
   sudo gem sources -a http://gems.github.com
   sudo gem install ezmobius-redis
   sudo gem install redis-store
You 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

  1. http://blog.grayproductions.net/articles/setting_up_the_redis_server
  2. http://code.google.com/p/redis/
  3. http://www.engineyard.com/blog/2009/key-value-stores-for-ruby-part-4-to-redis-or-not-to-redis/
  4. http://robots.thoughtbot.com/post/443934722/redis-data-cheeseburgers

Setting up medium ruby server

Posted by balepc
on Tuesday, March 23

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

  1. Setting up server
  2. Installing ruby/rails
  3. Installing necessary gems
  4. Setting up application
  5. 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 deploy  
It’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/suoders
Remove all unneeded users. A list of all users you can find at /etc/passwd. User can be deleted by:
  $ sudo deluser username
Set 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
Before installing ruby, I need to install some dependencies:
   sudo apt-get update
   sudo apt-get install libssl-dev -y
   sudo apt-get install build-essential -y
and 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 imagemagick
Setting 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 install
Once 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

Posted by balepc
on Tuesday, March 23
  $ nano ~/.netrc
   machine my_ftp_server
   login teacher
   password secret1
  $ ftp my_ftp_server
  230 User teacher logged in

Most used git commands

Posted by balepc
on Tuesday, March 23
See local changes
  $ git status
Pushing local changes to the remote repository
  $ git add .
  $ git commit -a -m 'Message'
  $ git pull
  $ git push
See all local branches
  $ git branch
Make a remote branch
  $ git push origin master:v1.1
Checkout the remote branch
  $ git checkout -b v1.1 origin/v1.1
Delete the remote branch
  $ git push origin :v1.1
Change local branch
  $ git checkout v1.1
Merge with remote branch
  $ git merge v1.1

Seed produciton data to test environment

Posted by balepc
on Thursday, March 18

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.