Posts Tagged ‘rails’

Set Cookie in HTML Body for Rails 2.3.2

Tuesday, May 26th, 2009

I recently upgraded an app to rails 2.3 and on many pages on my local machine the top of the HTML body included something like this:


Set-Cookie: _myapp_session_id=BAh7B...; path=/; HttpOnly

I couldn’t figure out what was happening until I found this forum post which explained that the problem was related to passenger 2.0.6. To fix the problem I just needed to update the version of passenger I was running.

To update passenger (on a mac) was pretty simple. Just run the following 2 commands:


sudo gem install passenger
sudo passenger-install-apache2-module

Depending on how you set up your local apache instance, you may also need to update the config. I needed to update /etc/apache2/httpd.conf to


LoadModule passenger_module /Library/Ruby/Gems/1.8/gems/passenger-2.1.2/ext/apache2/mod_passenger.so
PassengerRoot /Library/Ruby/Gems/1.8/gems/passenger-2.1.2
3
Tags: , ,
Posted in tech |

Upgrading from Rails 2.2.2 to Rails 2.3.2

Monday, May 4th, 2009

I recently upgraded one of my apps from Rails 2.2.2 to Rails 2.3.2. It was actually a really easy upgrade, but there were few little gotchas I had to watch out for, so I thought I’d share my experience.

Upgrading Rails

I imagine there is probably a better way to do this, but since I change the version of rails so infrequently I’m not sure. (If there is a better way, please let me know). My flow is as follows:

Install the new version of the rails gems:


sudo gem install rails

Branch your app


git branch newrails
git co newrails

Remove the old version of frozen gems


git rm -rf vendor/rails

Update your config/environment.rb file to the new version of rails:


RAILS_GEM_VERSION = '2.3.2' unless defined? RAILS_GEM_VERSION

freeze the new gems


rake rails:freeze:gems

Please note, that if instead of installing the new gems and freezing them from your local gems, it looks as though you need to be careful if using rake rails:freeze:gems. You can read more about it in the official post or in this post which I found more understandable.

Testing Your App

The first and most obvious error you will run into is this:


uninitialized constant ApplicationController

This is because as of Rails 2.3 application.rb is now application_controller.rb. To fix this just do


git mv app/controllers/application.rb app/controllers/application_controller.rb

The next error I got was


undefined method `relative_url_root' for #

I narrowed this down to a stylesheet_link_tag call. I got around this by updating the asset_packager plugin, and updating HAML to 2.0.9. By the way, if you’re not using asset packager, you should be.

That’s it! It overall was a very easy upgrade that took much less time to test than my rails 2.1 to rails 2.2.2 upgrade.

Here are some other rails 2.3 gotchas to watch out for courtesy of thoughtbot.

3
Tags: , ,
Posted in tech |

Setting a Capistrano Variable from the Command Line

Friday, March 27th, 2009

It took me a little while to find a solution for this, so I thought I’d post it.

I was cleaning some deployment dirs and wanted, just for this instance, to only leave 1 release as opposed to the 5 releases that capistrano leaves by default. Keep in mind this was across about 6 apps and 2 stages for each.

Option 1: Add the following to the deploy.rb files:


set :keep_releases, 1

That would require changing them all back afterwards.

Options 2: Set the capistrano variable from the command line:


cap deploy:cleanup -s keep_releases=1
0
Tags: ,
Posted in tech |

Domain Name Registration API Plugin for Rails

Thursday, February 26th, 2009

If you’ve ever had an app where you want to allow users to purchase a domain name, you’ve probably felt the pain of trying to interface to a registrar.  Although some have APIs, my search found that most were hard to interface to or poorly documented.  Many even required signing up as a partner (and paying a big fee) before you could even get documenation.

After much searching and experimenting I decided to go with Register.com’s XML api for my app. They offered the best API, and the easiest signup path.

I bundled the main part of the interface into a rails plugin.  The plugin is stored on github: http://github.com/geoffevason/register-api/tree/master

To install the plugin do this:


script/plugin install git://github.com/geoffevason/register-api.git

The plugin is of little value unless you have spoken to register.com and have received their API documentation. You need to register as a partner (it’s free) and have the IP of your dev machine whitelisted for testing.

Most of the info on use is in the readme in the plugin. You can call any of the Register.com API methods by calling Register::API.


# A call to the API looks like this
# Register::Api::Call(params)

# Example to check if the domain name google.com is available
Register::Api::Check( :tld => 'com', :sld => 'google' )

The plugin also contains a few helper methods and classes. If all you want to do is let people search for an available domain, and purchase it, then everything you need is in these helpers. Some important logic remains in my controllers, but if you have any questions, let me know. geoff [a] evason.name

2
Tags: , , ,
Posted in tech |

Upgrading an app from Rails 2.1 to Rails 2.2.2

Thursday, January 15th, 2009

Here is a summary of a few of the problems I encountered while trying to upgrade an app from rails 2.1 to rails 2.2.2

Getting my dev code to run

First, to update the rails code:

 > rake rails:freeze:gems

The first time I tried to run the app I got this error:

vendor/rails/activesupport/lib/active_support/dependencies.rb:445:in `load_missing_constant': uninitialized constant Inflector (NameError)
Accessing the inflector changes, so as described here you need update APP/config/initializers/inflector.rb so it looks like this:
ActiveSupport::Inflector.inflections do |inflect|
  ...
end

In my case, I was also using the ActiveMerchant plugin which needed updating for the same reason

script/plugin install git://github.com/Shopify/active_merchant.git --force

According to gusg.us, HAML needs to be at 2.0.4.   I didn’t see any problems in my testing but I didn’t want to try my luck so I updated HAML too.

sudo gem update haml

update my environment.rb gem requirement

config.gem "haml", :version => "2.0.6"
Then run
rake gems:unpack:dependencies

Production

While deploying to a staging server I got this error:

initializer.rb:514:in `send': undefined method `cache_template_loading=' for ActionView::Base:Class (NoMethodError)

As described here, to fix this, just update your APP/config/environments/production.rb (and staging.rb) file by removing the following:

config.action_view.cache_template_loading

Overall, it wasn’t too painful an upgrade…

I didn’t run rake rails:update. That may have fixed some of these things on it’s own, but my understanding was that that wasn’t needed anymore…

7
Tags: ,
Posted in tech |

Simple Currency Conversion Rate API Consumption For Ruby / Rails

Monday, October 27th, 2008

I had the need to consume some exchange rate data for an internal project so I began looking for an about. My searching found no api for Google Finance, Yahoo Finance, XE, or Oanda. :-(

Fortunately I found the currency converter from Xavier Media and their simple currency exchange rate xml API, which includes historical data too. :-D

I put together an absolutely minimal lib to get the data I need.  I just needed the AU/US rate.  The xml provides all data as base to EUR, but with some simple math I can find the rate I need with reasonable accuracy.  In this case ‘accuracy’ is based on spot checking it against the yahoo rates.

I thought it worth sharing in case others are looking for something similar.

UPDATE: I changed the xml string below to better handle single digit date months. Xavier needs ‘01′ instead of ‘1′

require "cgi"
require "uri"
require "net/https"
require "rexml/document"

module XavierMedia
  # Returns the exchange rate (AUD/USD) on the given date.
  def self.exchange_rate_on(date)
    url = URI.parse("http://api.finance.xaviermedia.com/api/#{date.year}/#{date.strftime("%m")}/#{date.strftime("%d")}.xml")

    resp = Net::HTTP.get(url)
    xml  = REXML::Document.new(resp)

    us_to_eur = 1.0
    au_to_eur = 1.0
    xml.elements.each("//exchange_rates/fx") { |el|
      if el.elements[1].text == "USD"
        us_to_eur = el.elements[2].text.to_f rescue 1.0
      end
      if el.elements[1].text == "AUD"
        au_to_eur = el.elements[2].text.to_f rescue 1.0
      end
    }

    return us_to_eur/au_to_eur
  end
end

 

5
Tags: , , ,
Posted in Uncategorized |

Rails Fragment Caching Slowness With Regex Expiry

Sunday, October 26th, 2008

Fragment caching in rails works great.  We use it a lot for MomentVille and easily get most of our responses in under 150ms because it dramatically reduces teh number of queries we need to run for our most common actions.

I did notice some slowness recently though, and was quite confused.  The slowness only occurred on our production servers. Our dev, test, and pre-production servers were all still fast.  I tried using new relic rpm service to help pinpoint the problem, and while it does a great job in helping you track things, it didn’t help me narrow down the problem.

Ultimately I discovered that the problem had to do with how we clear the cache. For some actions we have to expire multiple fragments, so we used regex expiration.  Unfortunately, that is very slow.  It seems that regex expiry compares your regex to each fragment stored, even if you think your regex looks like it’s targeting a directory.

Alexander Dymo had a post about regex expiry of fragment caches in rails that outlined a solution for him.  It helped guide me to a solution that works well for us.  OUr fragment caches are actually structured around the data as opposed to the actions, so related fragments are stored within sub directories.  When we need to clear a bunch at once, we just want to wipe out the whole directory.  So, I created a file called fragment_dir_expiration.rb and put it into my /config/initializers folder.  It looks like this:

# For rails 2.0 and lower
module ActionController
  module Caching
    module Fragments

      #dir is the cache path relative to the cache root
      def expire_fragment_dir(dir, options = nil)
        return unless perform_caching
        self.class.benchmark("Expired fragments in dir : #{dir}") do
          ActionController::Base.cache_store.delete_fragment_dir(dir, options)
        end
      end

      class UnthreadedFileStore

        def delete_fragment_dir(dir, options = nil)
          path = @cache_path + dir
          return unless File.exist?(path) #it's ok to not have the cache dir
          search_dir(path) do |f|
            begin
              File.delete(f)
            rescue SystemCallError => e
              # If there's no cache, then there's nothing to complain about
            end
          end
        end

      end
    end
  end
end

When you want to call this you can call it from an observer with a call like this

class WidgetSweeper < ActionController::Caching::Sweeper
  observe Widget
  def after_save(widget)
    # Expire all the fragments for the updated widget
    expire_fragment_dir("/widget/#{widget.id}/")
  end
end

UPDATE: For rails 2.1 and above use the following:

module ActionController
  module Caching
    module Fragments

      #dir is the cache path relative to the cache root
      def expire_fragment_dir(dir, options = nil)
        return unless perform_caching
        self.class.benchmark("Expired fragments in dir : #{dir}") do
          ActionController::Base.cache_store.delete_fragment_dir(dir, options)
        end
      end

    end
  end
end

module ActiveSupport
  module Cache
    class FileStore

      def delete_fragment_dir(dir, options = nil)
        path = @cache_path + dir
        return unless File.exist?(path) #it's ok to not have the cache dir
        search_dir(path) do |f|
          begin
            File.delete(f)
          rescue SystemCallError => e
            # If there's no cache, then there's nothing to complain about
          end
        end
      end
    end
  end
end

 

1

My .bash_profile for git & Rails

Tuesday, August 19th, 2008

I’ve converted all of my projects from svn to git.  If you haven’t done so already, and can do it, I would recommend not waiting anymore.  It does make things better.  

I used to really like the visual representations available on svn (such as tortoise svn) and rarely used the command line for repository related things.  The way git works (in particular, not having to have a special ’svn folder within each folder) makes managing files way easier.  The best way to learn is to check out the git peepcode. I borrowed some of the aliases below from the screencast.

Now that I’m on the command line more, I’ve created a .bash_profile to make my life easier.  It is full of nice goodies that I collected.  I figured that I’d share it with the world in case others find it helpful.  It includes:

  • Updating the OSX Leopard terminal title bar (and potentially the tabs) to include the working directory
  • Updating the command prompt to include the current directory and the current git branch (if you’re in a git repo)
  • Lots of aliases to save myself keystrokes.

# Get the name of the current git branc
function parse_git_branch {
  git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
}

# Update the command prompt to be <user>:<current_directory>(git_branch) >
# Note that the git branch is given a special color
function set_my_prompt {
  PS1="\u:\w\e[1;34m\$(parse_git_branch)\e[m > "
}

# Update the title for the terminal window to be the full working dir
function set_term_title
{
    local title="$1"
    if [[ -z "$title" ]]; then
        title="root"
    fi

    local tmpdir=~/Library/Caches/${FUNCNAME}_temp
    local cmdfile="$tmpdir/$title"

    # Set window title
    #echo -n -e "\e]0;${title}\a"
    echo -n -e "\e]0;${PWD#*/}\a"

   # Set tab title
   # This works by creating a process with the name of the working dir.  
   # So, the tab name doesn't stick if you start running a mongrel server :-(
    if [[ -n ${CURRENT_TAB_TITLE_PID:+1} ]]; then
        kill $CURRENT_TAB_TITLE_PID
    fi
    mkdir -p $tmpdir
    ln /bin/sleep "$cmdfile"
    "$cmdfile" 10 &
    CURRENT_TAB_TITLE_PID=$(jobs -x echo %+)
    disown %+
    kill -STOP $CURRENT_TAB_TITLE_PID
    command rm -f "$cmdfile"
}

set_my_prompt
PROMPT_COMMAND='set_term_title "${PWD##*/}"'

# Some aliases I find useful
alias gclb="git checkout -b"
alias gb="git branch"
alias gba="git branch -a"
alias gs="git status"
alias gca="git commit -a"
alias gcm="git commit -m"
alias gk="gitk --all &"
alias ss="script/server"
alias ssp="script/server -p"
alias sr="script/runner"

Big credit goes Christopher Stawarz to the the tab & title thing: l

3

Railscasts Does It Again : Site Wide Announcements

Tuesday, May 27th, 2008

A note to all rails developers, new and old.  If you’re not following Ryan Bates’ Railcasts, you should be.

I follow a variey of rails blogs and lean on a number of resources quite regularly, but the Railscasts are consistently the most useful.  There are now over 100 railscast, each one a roughly 5 minute screencast outlining the solution to some problem.

A recent cast showed how to create a site wide announcement that each user could mark as read individually.  This is a great, non-intrusive way to communicate notices with users.

The screencast details how to do it.  I was able to implement this on a site in a very short period of time.  I made some modifications which make it work better within my site.  I do have one suggestion to improve it overall.  To track whether a message had been read/should be shown Ryan uses the session.  Sessions expire in the near future, and if using a db store, should be wiped daily.  If your users don’t visit daily, you will want to create a message that hangs around for a week or 2.  In this case, a session variable won’t work.  Instead, you can store the info in a cookie and set a delayed expire time on it.  (By default, cookies expire with the session in rails).

Before reading how to store this info in a cookie you should watch the screencast.  Once you’ve implemented everything like Ryan’s demo, there are just 3 small changes to use cookies and hence have a longer memory.

1. In your controller, set the cookie:


def hide_announcement
  cookies[:announcement_hide_time] ={ :value => Time.now.to_s , :expires => 2.weeks.from_now }
end

2. In your helper method, read the value from the cookie.


def current_announcements
  @announcements ||= Announcement.current_announcements(cookies[:announcement_hide_time])
end

3. In the announcement controller you need to parse the time since it is stored as a string in the cookie


def self.current_announcements(hide_time)
  with_scope :find => { :conditions => "starts_at <= now() AND ends_at >= now()" } do
    if hide_time
      time = Time.parse(hide_time)
      find(:all, :conditions => ["updated_at >= ?", time])
    else
      find(:all)
    end
  end
end
1
Tags: , ,
Posted in Uncategorized |

Cross Site Reference Forgery

Monday, May 5th, 2008

I just read a great article on Cross Site Reference Forgery, specifically related to how Rails 2.0 handles it. I think it is a must read for all rails developers. It gives a very clear description of the potential vulnerability, which I think is important to understand

I recently upgraded some apps to Rails 2.0 from Rails 1.2.4 (more on that in another post) and this was one of the main reasons.

0
Tags: , ,
Posted in Uncategorized |