Archive for August, 2008

My latest capistrano deploy.rb file using a deploy_via :copy

This is the approach I’ve been taking to my smaller sites that I just work on myself. I keep the site locally gitified on my computer and avoid posting to github.

It makes things super simple for those sole freelance projects, and as long as you keep your computer backed up you’re fine.

Just change lines 1,11,12,13,29, and 40.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
set :runner, "username"
set :use_sudo, false
 
# =============================================================================
# CUSTOM OPTIONS
# =============================================================================
set :user, "username"
set :application, "yourapp"
set :domain, "yourdoman.com"
 
role :web, domain
role :app, domain
role :db,  domain, :primary => true
 
# =============================================================================
# DATABASE OPTIONS
# =============================================================================
set :rails_env,       "production"
 
# =============================================================================
# DEPLOY TO
# =============================================================================
set :deploy_to, "/home/#{user}/apps/#{application}"
 
# =============================================================================
# REPOSITORY
# =============================================================================
set :scm, "git"
set :repository,  "/Users/scottmotte/Documents/code/rails/#{application}"
set :branch, "master"
set :deploy_via, :copy
set :copy_exclude, [".svn", ".git"]
 
# =============================================================================
# SSH OPTIONS
# =============================================================================
 
default_run_options[:pty] = true
ssh_options[:paranoid] = false
ssh_options[:keys] = %w(/Users/scottmotte/.ssh/id_rsa)
ssh_options[:port] = 1985
 
 
# =============================================================================
# RAKE TASKS & OTHER SERVER TASKS
# =============================================================================
 
# desc 'Fix attachment_fu'
# task :fix_attachment_fu, :roles => :app do
#   %w{attachments}.each do |share|
#     run "rm -rf #{release_path}/public/#{share}"
#     run "mkdir -p #{shared_path}/system/#{share}"
#     run "ln -nfs #{shared_path}/system/#{share} #{release_path}/public/#{share}"
#   end
# end
 
desc "Create symlink to public_html/#{domain}/public"
task :symlinkify do
  run "rm -rf /home/#{user}/public_html/#{domain}/public; ln -s #{current_path}/public /home/#{user}/public_html/#{domain}"
end
 
desc "Reload Apache"
task :reload_apache do
  sudo "/etc/init.d/apache2 reload"
end
 
# doesn't work yet, but would be good to add.
# desc 'Install all gems'
# task :rake_gems, :roles => :app do
#   run "cd #{release_path}"
#   run "rake gems:install"
# end
 
after "deploy", "deploy:migrations"
after "deploy:migrations", "deploy:cleanup"
after "deploy:cleanup", "symlinkify"
after "symlinkify", "reload_apache"

Configuring nginx to use www.domain.com instead of domain.com

I had a client who had their local server setup under domain.com instead of properly under local.domain.com or something similar. It caused a bit of a headache (you could view their new site from outside their local office, but in their local office the old website was still showing.)

Their local server guy (thanks Will) helped me solve the issue. Their website needed to use only www.

Here’s his notes:

Company XYZ Mortuary has two separate DNS systems

1. The public-facing DNS server that you control

2. The internal DNS server for Company XYZ’s private network that we control

Since both of these servers are authoritative for the CompanyXYZ.com domain, both need to be updated when IP addresses of public servers change.

The other issue we had was the web site removing the www from the URL, which caused computers on Company XYZ’ private network to access an internal server. Windows domain controllers reserve the IP address for the domain name for themselves, and modifying this behavior breaks functionality such as DFS and Exchange. For this reason, Microsoft and other OS vendors recommend using a TLD that will never be used on the Internet, such as .local.

And here is the adjusted nginx config file to use www & point appropriately.

The key line(s) are:
server_name company-xyz.com;
rewrite ^/(.*) http://www.company-xyz.com/$1 permanent;

upstream domain4 {
        server 127.0.0.1:3400;
        }

server {
            listen   80;
            server_name company-xyz.com;
            rewrite ^/(.*) http://www.company-xyz.com/$1 permanent;
           }

server {
            listen   80;
            server_name www.company-xyz.com;

            access_log /home/username/public_html/railsapp/shared/log/access.log;
            error_log /home/username/public_html/railsapp/shared/log/error.log;

            root   /home/username/public_html/railsapp/current/public/;
            index  index.html;

            location / {
                          proxy_set_header  X-Real-IP  $remote_addr;
                          proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
                          proxy_set_header Host $http_host;
                          proxy_redirect false;

                          if (-f $request_filename/index.html) {
                                           rewrite (.*) $1/index.html break;
                          }

                          if (-f $request_filename.html) {
                                           rewrite (.*) $1.html break;
                          }

                          if (!-f $request_filename) {
                                           proxy_pass http://domain4;
                                           break;
                          }
            }

}

server {

            listen   443;
            ssl on;
            ssl_certificate     /etc/ssl/certs/company-xyz.com.crt;
            ssl_certificate_key   /etc/ssl/private/companyxyz.key;       

            server_name  company-xyz.com;
            rewrite ^/(.*) http://www.company-xyz.com/$1 permanent;

           }

server {

            listen   443;
            ssl on;
            ssl_certificate     /etc/ssl/certs/company-xyz.com.crt;
            ssl_certificate_key   /etc/ssl/private/companyxyz.key;

            server_name www.company-xyz.com;

            access_log /home/username/public_html/railsapp/shared/log/access.log;
            error_log /home/username/public_html/railsapp/shared/log/error.log;

            root   /home/username/public_html/railsapp/current/public/;
            index  index.html;

            location / {
                          proxy_set_header X_FORWARDED_PROTO https;

                          proxy_set_header  X-Real-IP  $remote_addr;
                          proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
                          proxy_set_header Host $http_host;
                          proxy_redirect false;

                          if (-f $request_filename/index.html) {
                                           rewrite (.*) $1/index.html break;
                          }

                          if (-f $request_filename.html) {
                                           rewrite (.*) $1.html break;
                          }

                          if (!-f $request_filename) {
                                           proxy_pass http://domain4;
                                           break;
                          }
            }

}

Scheduled Tasks with Cron in a Rails app

One of my apps requires a daily task to run. It uses WWW::Mechanize (the gem) to log onto a website, insert a username and password, and save that information, come back to my app, and insert & save that into a form on my site.

I’m pretty impressed I was able to pull it off. It’s a great deal of detail to go into, but I used Recipe 75 in Advanced Rails Recipes and adjusted some of my old hpricot & mechanize scripts.

Best of all, it keeps me out of the crontab on the server. No hand editing. The capistrano cron rake task takes care of that.

UPDATED: Email validation/activation with rails, restful_authentication, and gmail smtp

This is updated from my old post on the subject. It’s a process cheat sheet for me.

I’m not gonna explain it too much. Here’s the code.

script/plugin install git://github.com/technoweenie/restful-authentication.git
script/generate authenticated user sessions --include-activation
1
2
3
#Add an observer to config/environment.rb
config.active_record.observers = :user_observer
#put it just before the very last 'end' on the page
1
2
3
4
5
#Add routes to these resources. In config/routes.rb, insert routes like:
    map.signup '/signup', :controller => 'users', :action => 'new'
    map.login  '/login',  :controller => 'sessions', :action => 'new'
    map.logout '/logout', :controller => 'sessions', :action => 'destroy'
    map.activate '/activate/:activation_code', :controller => 'users', :action => 'activate', :activation_code => nil
rake db:migrate
1
2
3
4
5
6
7
8
9
10
11
# create new file config/initializers/mail.rb
 
require "smtp_tls"
 
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:authentication => :plain,
:user_name => "you@gmail.com",
:password => 'yourpassword'}
1
2
3
4
# edit config/environments/production.rb & put these lines at the top
SITE_NAME = "Your Site"
SITE_URL = "www.yourdomain.com"
SITE_EMAIL = "admin@yourdomain.com"
1
2
3
4
# edit config/environments/development.rb & put these lines at the top
SITE_NAME = "ToolTime"
SITE_URL = "localhost:3000"
SITE_EMAIL = "scott@scottmotte.com"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Make app/models/user_mailer.rb look like the following:
class UserMailer < ActionMailer::Base
  def signup_notification(user)
    setup_email(user)
    @subject    += 'Please activate your new account'
 
    @body[:url]  = "http://#{SITE_URL}/activate/#{user.activation_code}"
 
  end
 
  def activation(user)
    setup_email(user)
    @subject    += 'Your account has been activated!'
    @body[:url]  = "http://#{SITE_URL}/"
  end
 
  protected
    def setup_email(user)
      @recipients  = "#{user.email}"
      @from        = "#{SITE_EMAIL}"
      @subject     = "#{SITE_NAME}"
      @sent_on     = Time.now
      @body[:user] = user
    end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# create file lib/smtp_tls.rb with following code in it
 
require "openssl"
require "net/smtp"
 
Net::SMTP.class_eval do
  private
  def do_start(helodomain, user, secret, authtype)
    raise IOError, 'SMTP session already started' if @started
    check_auth_args user, secret, authtype if user or secret
 
    sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
    @socket = Net::InternetMessageIO.new(sock)
    @socket.read_timeout = 60 #@read_timeout
 
    check_response(critical { recv_response() })
    do_helo(helodomain)
 
    if starttls
      raise 'openssl library not installed' unless defined?(OpenSSL)
      ssl = OpenSSL::SSL::SSLSocket.new(sock)
      ssl.sync_close = true
      ssl.connect
      @socket = Net::InternetMessageIO.new(ssl)
      @socket.read_timeout = 60 #@read_timeout
      do_helo(helodomain)
    end
 
    authenticate user, secret, authtype if user
    @started = true
  ensure
    unless @started
      # authentication failed, cancel connection.
      @socket.close if not @started and @socket and not @socket.closed?
      @socket = nil
    end
  end
 
  def do_helo(helodomain)
    begin
      if @esmtp
        ehlo helodomain
      else
        helo helodomain
      end
    rescue Net::ProtocolError
      if @esmtp
        @esmtp = false
        @error_occured = false
        retry
      end
      raise
    end
  end
 
  def starttls
    getok('STARTTLS') rescue return false
  return true
  end
 
  def quit
    begin
      getok('QUIT')
    rescue EOFError
    end
  end
end

Restart your server, visit http://yourapp.com/signup and give it a shot.

Once that works. Then put before_filter :login_required in your controllers where needed.

Autotest

Getting started tutorial

How to check for all your rake tasks

In terminal

rake -T

How to open files in terminal

open -a Firefox doc/plugins/rspec-rails/index.html

My Terminal bash settings

cd ~
mate .bashrc # or nano .bashrc
1
2
# then type in
export PS1='\[[~/\W]'

WP-Syntax

I’ve installed WP-Syntax on my blog. It will help brighten up my lines of code.

It was an easy install and pretty easy to use. Intead, of just using the pre html tag, I know do something like this:

pre lang=”ruby” line=”1″
#put some ruby code
Categories.find(:all, :conditions => ['cool_field = ?', 'awesome'])
pre

1
2
#put some ruby code
Categories.find(:all, :conditions => ['cool_field = ?', 'awesome'])

or html

pre lang=”html4strict” line=”1″>

Title me

That car was bright blue.

pre

1
2
3
<!-- comment -->
<h1>Title me</h1>
<p>That car was bright blue.</p>

Slicehost, nginx, mysql, php, wordpress setup

Detailed tutorial on nginx, mysql, php, wordpress setup. bonus: it includes setup instructions for the super cache plugin