Deploy Spree 1.0 on Heroku

I am in the process of moving my digital house to the new Octopress-based ruby-journal.com, there are many things to clean up and migrate but I guess for now I will do double-post on this site and the new site.

Please head to http://ruby-journal.com/spree-1-0-stable-deployment-on-heroku/ if you want to read my tutorial on how to deploy Spree 1.0 on Heroku

Specify precision and scale attributes for decimal number with scaffold/model/migration generators in Rails 3.2.0

By using the short hand  :decimal{precision, scale}, rails generator lets you generate number migration with decimal and scale. You could use {precision, scale} or {precison-scale} or {precision.scale} shorhand:

rails g scaffold Product title:string price:decimal{8,2}

the above would generate a migration of:

t.decimal :price, :precision => 8, :scale => 2

NOTE: sadly, this feature is so broken as of the release of 3.2.0 and is addressed in the bug #4602.

Fix Postgres permission denied issue on OSX Lion

The good thing is that Apple decides to bundle PostgresSQL 8.4 with OSX Lion which cause conflict if you install Postgres 9.x using brew or Macport. You will likely bump into following error:

    $ psql -d postgres -U postgres
    psql: could not connect to server: Permission denied
    Is the server running locally and accepting
    connections on Unix domain socket "/var/pgsql_socket/.s.PGSQL.5432"? 

So what’s going wrong here? Because the stock postgresql has higher precedence in PATH.

To fix this, simply make /usr/local/bin highest precendence for $PATH.

Local scope (recommended):
Edit ~/.bash_profile

export PATH=/usr/local/bin:$PATH

System-wide scope:
Edit your /etc/paths and move your /usr/local/bin to the top.

$ sudo nano /etc/paths
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

Ruby Tip: Reference to previous line

Travis Winters showed me a nifty trick to reference to previous line in rib by using the symbol _ (aka underscore). Look at the example below:

$ irb
ruby-1.9.2-p290 :002 > "hello world"
ruby-1.9.2-p290 :002 > @hello = _
ruby-1.9.2-p290 :002 > puts @hello
hello world
 => nil

How to create new Report extension in Spree 0.70.x

In this tutorial, I will show how to create new Report as extension for Spree 0.70.x backend from scratch. The idea is to generate a new extension with our new Report logic and register it by having the function to be merged with the constant AVAILABLE_REPORTS of the Admin::ReportsController. No rocket-science here, it’s pure basic RoR coding, and I hope I could show you a thing or two onto how to write your own extension for Spree.

Prerequisites

It is required that Spree 0.70.x is installed on your computer.

$ gem install spree --version=0.70.2

Generate new extension

Thanks to Spree generator, we could generate a new extension for Spree easily by using spree command. Let’s create our cool FooReport:

$ spree extension FooReport

Spree generates a skeleton mountable engine with name ‘spree_foo_report’:

      create  spree_foo_report
      create  spree_foo_report/app
      create  spree_foo_report/app/assets/javascripts/admin/spree_foo_report.js
      create  spree_foo_report/app/assets/javascripts/store/spree_foo_report.js
      create  spree_foo_report/app/assets/stylesheets/admin/spree_foo_report.css
      create  spree_foo_report/app/assets/stylesheets/store/spree_foo_report.css
      create  spree_foo_report/app/controllers
      create  spree_foo_report/app/helpers
      create  spree_foo_report/app/models
      create  spree_foo_report/app/views
      create  spree_foo_report/app/overrides
      create  spree_foo_report/config
      create  spree_foo_report/db
      create  spree_foo_report/lib
      create  spree_foo_report/lib/spree_foo_report.rb
      create  spree_foo_report/lib/spree_foo_report/engine.rb
      create  spree_foo_report/lib/generators/spree_foo_report/install/install_generator.rb
      create  spree_foo_report/script
      create  spree_foo_report/script/rails
      create  spree_foo_report/spec
      create  spree_foo_report/LICENSE
      create  spree_foo_report/Rakefile
      create  spree_foo_report/README.md
      create  spree_foo_report/.gitignore
      create  spree_foo_report/spree_foo_report.gemspec
      create  spree_foo_report/Versionfile
      create  spree_foo_report/config/routes.rb
      create  spree_foo_report/spec/spec_helper.rb
      create  spree_foo_report/.rspec
      append  Gemfile

Customize extension

gemspec

Thanks to greatness of mountable engine brought by Rails 3.1, now we could have our Spree extension deployed as gem and easily distributed with everyone. We need to provide details for our gem. Change the file spree_foo_report.gemspec:

# encoding: UTF-8
Gem::Specification.new do |s|
  s.platform    = Gem::Platform::RUBY
  s.name        = 'spree_foo_report'
  s.version     = '0.1'
  s.summary     = 'The Foo Man Chew Report'
  s.description = 'All the secret of Universe'
  s.required_ruby_version = '>= 1.8.7'

  s.author            = 'Foo Man Chew'
  s.email             = 'foo@man-chew.com'
  s.homepage          = 'http://github.com/foo-man-chew/spree_foo_report'

  s.files         = `git ls-files`.split("\n")
  s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
  s.require_path = 'lib'
  s.requirements << 'none'

  s.add_dependency 'spree_core', '>= 0.70.2'
  s.add_development_dependency 'rspec-rails'
end

If you are writing a public extension, the best practice is to have your GitHub URL as the homepage.

Versionfile

In the scope of this tutorial, our extension is written for Spree 0.70.x. However if future version is released which might break our current codebase, we could easily separate our code into Git branches or taggings. So Versionfile comes in handy, it designate the compatibility with different version of Spree . Read more at http://spreecommerce.com/documentation/extensions.html#versionfile. Change our Versionfile to:

# This file is used to designate compatibility with different versions of Spree
# Please see http://spreecommerce.com/documentation/extensions.html#versionfile for details

"0.70.x" => { :branch => "0-70-stable"}

README

README.mdfile is used to describe your extension. Now we add some installation and testing details:

Spree Foo Report
================

Fooo foo fooo fooo .. M..AN..CHEW!!!

Installation
------------

Append to your Gemfile:

    gem 'spree_foo_report', :git => 'git://github.com/foo-man-chew/spree_foo_report.git'

Testing
-------

Be sure to add the rspec-rails gem to your Gemfile and then create a dummy test app for the specs to run against.

    $ bundle exec rake test app
    $ bundle exec rspec spec

Copyright (c) 2011 Foo Man Chew, released under the New BSD License

ReportsController Decorator

We extends the Spree Core ReportsController by adding a new file in app/controllers/admin/reports_controller_decorator.rb:

Admin::ReportsController.class_eval do
  AVAILABLE_REPORTS.merge({:foo_report => {:name => I18n.t(:foo_name), :description => I18n.t(:foo_description)}})

  def foo_report
    # Add your logic here
  end
end

By having our new foo_report added in AVAILABLE_REPORTS, this ensure our new report to be listed on admin/reports index page. Yet, we are not done, we need to add in translation string for foo_name and foo_description by adding those 2 into config/locales/en.yml:

---
en:
  foo_name: The Foo Report
  foo_description: The You Know All Report

View

But what’s good for a report with no visual presentation? Let’s add in our View at app/views/admin/reports/foo_report.html.erb. Please note that the view name should be the same as the function name.

<h1>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</h1>

Routing

We’re nearly there, the last thing is to configure the routing. Edit config/routes.rb:

Rails.application.routes.draw do
  match '/admin/reports/foo_report' => 'admin/reports#foo_report', :via => [:get, :post]
end

Testing

There are 2 ways to try out our work, first is to build our gem and have it included in sandbox’s Gemfile OR we could commit the whole extension to GH and configure our Gemfile to use git path.

How to install REE or MRI 1.9.3 via RVM on OSX 10.7

Okay, if you happen to get errors when compiling REE or Ruby 1.9.3 on OSX 10.7, there is a high chance that you are using XCode 4.2 or 4.2.1.

gcc -g -O2  -pipe -fno-common    -DRUBY_EXPORT  -L.    main.o dmydln.o libruby-static.a -L/opt/local/lib -Wl,-rpath,/Users/mojo/.rvm/rubies/ree-1.8.7-2011.03/lib -L/Users/mojo/.rvm/rubies/ree-1.8.7-2011.03/lib -lsystem_allocator -ldl -lobjc   -o miniruby
ld: warning: directory not found for option '-L/opt/local/lib'
./ext/purelib.rb:2: [BUG] Segmentation fault
ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin11.1.0], MBARI 0x6770, Ruby Enterprise Edition 2011.03

make: *** [.rbconfig.time] Abort trap: 6

The solution is to set your CC back to the old gcc-4.2 aka non-llvm-gcc-4.2

$ export CC=/usr/bin/gcc-4.2

and force re-install REE:

$ rvm install ree --force

What’s wrong here? Xcode 4.2 on OSX Lion 10.7 default CC to llvm-gcc-4.2 which causes error in compiling REE under RVM. Worse, Xcode 4.2.1 completely removed gcc-4.2.

So what are workarounds out there?

1. Using osx-gcc-installer, please see this post

2. Compile GCC component for Xcode 4.2.x. I found an article on Caius Theory site which can be found here. Basically, what Caius did is to compile 4.1 developer tools to get gcc-4.2:

# Copied from http://caiustheory.com/install-gcc-421-apple-build-56663-with-xcode-42#comment-79836
# Grab and unpack the tarball
$ mkdir ~/tmp && cd ~/tmp
$ curl -O http://opensource.apple.com/tarballs/gcc/gcc-5666.3.tar.gz
$ tar zxf gcc-5666.3.tar.gz
$ cd gcc-5666.3

# Setup some stuff it requires
$ mkdir -p build/obj build/dst build/sym
# And then build it. You should go make a cup of tea or five whilst this runs.
$ gnumake install RC_OS=macos RC_ARCHS='i386 x86_64' TARGETS='i386 x86_64' \
  SRCROOT=`pwd` OBJROOT=`pwd`/build/obj DSTROOT=`pwd`/build/dst SYMROOT=`pwd`/build/sym

# And finally install it
$ sudo ditto build/dst /

Spree: LESS is dead. Long live SCSS/SASS!

I still remember one of the key feature of Spree when it first came out to the market is Compass/Blueprint + LESS. LESS is a dynamic CSS syntax language that offers powerful functions like mixin, netsing, variable, interpolation, inheritances, etc that make CSS easier to write and manage. Yet LESS seems unpopular amongst developers, it could be explained that because it was quite new at the time and considered hard for noob users. In fact, Spree’s made LESS optional in opt for a all-in-one screen.css file for quite a while. Since the move to Rails 3 last year, LESS was commented out and tweaks that were made on the screen.css are not populated back to LESS stylesheets, this discrepancies makes LESS less ideal for Spree theming.

When I was working on de-coupling spree_blue_theme assets from the spree core 0.70.x, I had a conversation with BDQ regarding of moving LESS stylesheets to spree_blue_theme. BDQ had not objection of moving it to spree_blue_theme and leave LESS stylesheets optional as is. However he suggested that rebase all changes from the main screen.css to LESS stylesheets then compile all of them to css and having screen.css act as the sprocket asset pipeline. To me that is a very good idea because it mitigate the incompatibility for theme migration as well as leverage the use of rails 3.1′s sprocket. Think again, that solution seems a bit more daunting than converting all LESS to SCSS/SASS. First is the poor performance of sprocket in development mode. Second is why settle for something worse? Hence, I suggested to him that we should give SCSS/SASS a try because it has close resemblance with LESS syntax (the SCSS is – not the SASS) and is supported out of the box on Rails 3.1 that would help resolve locating asset path problem. Hence this is the reason of this post, that is guiding you through my journey from LESS wonderland to SASS-y land.

You can check my commit for spree_blue_theme here.

Rebase

With the help of the al-mighty git log -p, I could track down all changes that has been made to the screen.css then merge these changes back to equivalent LESS stylesheets.

Relocation

I moved all LESS stylesheets from app/stylesheets to app/assets/stylesheets/store and renamed all filenames extension to .scss. I did not opt for the SASS syntax because of its lacking curl brackets that is native to CSS and LESS syntax.

Conversion

Assisting Tool

SASS provides you very powerful and nifty tool to validate stylesheets on-the-fly.

sass --watch app/assets/stylesheets/store:screen.css

The --watch would keep track of all changes you made to files that contribute to the final compiled screen.css. In case you make mistakes in your stylesheets, the output will tell you instantly what goes wrong and at what line of which file.

Importing

The screen.less is used as a meta-file to consolidate all LESS ‘partials’. Similarly, SASS does the same regarding the filename prefix _ and use same @import syntax, however you do not specify the under score in import line. For example:

@import '_form';

would become

@import 'form';

Variables

Variable is again another plus that LESS offers, so do SASS. To convert LESS variable to SASS, simple replace the @ sign with $ sign. For example:

@white: #FFF;

would become:

$white: #FFF;

as the same goes to the argument of mixin, for example:

.message_box (@bg_color: #ccddff, @color: #556699, @border_color: #99aacc)

would become

@mixin message_box ($bg_color: #ccddff, $color: #556699, $border_color: #99aacc)

Mixins

Mixins in SCSS/SASS are defined with prefix @mixin and invoked with @include rather than then dot notation of LESS. I think SCSS/SASS is more visible as CSS coders tend to mistake the dot with CSS class selector.

So something like:

.message_box (@bg_color: #ccddff, @color: #556699, @border_color: #99aacc) { ... }

.flash.errors {
  .message_box(#F4B4B4, #000, #000);
}

would now be

@mixin message_box ($bg_color: #ccddff, $color: #556699, $border_color: #99aacc) { .. }

.flash.errors {
  @include message_box(#F4B4B4, #000, #000);
}

Nesting Rules

Nesting in both are the same. So I leave the nesting intact.

Operator & Function

Same.

Assets Location

Assets pipeline is new to Rails 3.1. Rails provides asset_path helper to assist the job, however I could not rename filename to scss.erb to use it, it just won't work. Thanks to sass-rails, I could use image-url helper to locate the asset easily:

background: #FFF url('../images/body-back.png') top left repeat-x;

to

background: #FFF image-url('body-back.png') top left repeat-x;

Enhancement

SASS provides @extends directive to inherits rules of a set that helps reduce code duplication. One example is the .errorExplanation of messages partial that has duplicated rules of .flash and .flash.errors can be refactored to:

.formError, .errorExplanation {
  @extend .flash;
  @extend .flash.errors;
  p { margin: 0px; }
  ul { margin-bottom: 0px; }
  h2 {
    font-weight: bold;
    font-size: 1.0em;
    margin: 0px;
  }
}

Conclusion

In conclusion, SASS suffice all the features of LESS with minor modification to the syntax. Additionally, thanks to the tight support of Rails 3.1 for SASS, there seems to be little configuration. Though virtually as same as SASS, LESS seems to die out due to lacking of a strong user base of RoR.

Howto fix rake 0.9.2 to work with Ruby 1.9.2 under RVM

The recent version bump of rake seems to do no good for Ruby developer. And when combined with the monster Ruby 1.9.2, you can imagine you could pull your hair out trying to figure out why this or that doesn’t work. So if you happen to install Ruby 1.9.2 using RVM and bumped into below errors:

/Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/version.rb:4: warning: already initialized constant MAJOR /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/version.rb:5: warning: already initialized constant MINOR /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/version.rb:6: warning: already initialized constant BUILD /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/version.rb:3: warning: already initialized constant NUMBERS /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/version.rb:9: warning: already initialized constant VERSION /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake.rb:26: warning: already initialized constant RAKEVERSION /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/early_time.rb:17: warning: already initialized constant EARLY /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/alt_system.rb:32: warning: already initialized constant WINDOWS /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/application.rb:28: warning: already initialized constant DEFAULT_RAKEFILES WARNING: Possible conflict with Rake extension: String#ext already exists WARNING: Possible conflict with Rake extension: String#pathmap already exists /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/task_arguments.rb:73: warning: already initialized constant EMPTY_TASK_ARGS /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/invocation_chain.rb:49: warning: already initialized constant EMPTY /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_utils.rb:10: warning: already initialized constant RUBY /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_utils.rb:84: warning: already initialized constant LN_SUPPORTED /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/dsl_definition.rb:143: warning: already initialized constant Commands /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_list.rb:44: warning: already initialized constant ARRAY_METHODS /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_list.rb:47: warning: already initialized constant MUST_DEFINE /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_list.rb:51: warning: already initialized constant MUST_NOT_DEFINE /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_list.rb:55: warning: already initialized constant SPECIAL_RETURN /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_list.rb:61: warning: already initialized constant DELEGATING_METHODS /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_list.rb:364: warning: already initialized constant DEFAULT_IGNORE_PATTERNS /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake/file_list.rb:370: warning: already initialized constant DEFAULT_IGNORE_PROCS /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake.rb:64: warning: already initialized constant FileList /Users/devuser/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2/lib/rake.rb:65: warning: already initialized constant RakeFileUtils rake aborted! stack level too deep

Here is how you could fix without reverting to version 0.8.7:

1. Please verify the rake installation with gem list | grep 'rake'. You will likely to see rake (0.9.2 ruby). I have no idea what ruby means, but it seems a duplication of rake that is installed @global.
2. Remove the duplicated version with gem uninstall rake. Please say Y for both 2 questions. Verify again with gem list | grep 'rake' and this time the output is rake (0.9.2). That’s it, this has fixed the issue. I speculate this is a bug with RVM.

Some people dislike 0.9.2 and wants to revert to 0.8.7 yet they could not do gem uninstall because rake is kept within no-touch @global zone. So here is how you can manually remove rake 0.9.2 from @global:

1. We need to figure out where rake resides with gem list -d rake. The output will likely be:

 
*** LOCAL GEMS ***

rake (0.9.2)
    Author: Jim Weirich
    Rubyforge: http://rubyforge.org/projects/rake
    Homepage: http://rake.rubyforge.org
    Installed at: /Users/mojo/.rvm/gems/ruby-1.9.2-p290@global

    Ruby based make-like utility.

2. The “Installed at” is the location, now we can remove it manually:

rm /Users/mojo/.rvm/gems/ruby-1.9.2-p290\@global/bin/rake
rm /Users/mojo/.rvm/gems/ruby-1.9.2-p290\@global/cache/rake-0.9.2.gem
rm -rf /Users/mojo/.rvm/gems/ruby-1.9.2-p290\@global/doc/rake-0.9.2/
rm -rf /Users/mojo/.rvm/gems/ruby-1.9.2-p290\@global/gems/rake-0.9.2/
rm /Users/mojo/.rvm/gems/ruby-1.9.2-p290\@global/specifications/rake-0.9.2.gemspec

3. Install rake 0.8.7 with gem install rake -v=0.8.7

Hope this helps.

Deploy Spree 0.70.x with Heroku Cedar stack

UPDATE: This tutorial is updated with details related to S3 superclass mismatch bug.

Heroku’s just released the public beta of their major new Celadon Cedar stack, introducing many new technologies like Process Model, new HTTP stack, default Ruby 1.9.2. One feature that I like is allowing you to choose different webserver than the Thin webserver, for example Unicorn which is recommended for Rails 3.1.x. And another feature is new Process Model that is similar to Foreman where you can control process worker via Procfile.

In this post, I am going to test Spree 0.70.x deployment on this new stack.

Setup

I opted for the upstream of Spree because I want to test how ready version 0.70.x on Heroku. I generate new Rails 3.1.3 and append Spree gem into Gemfile, and because Heroku only supports PostgreSQL, I also specify the database adapter:

$ rails new sandbox -d postgresql

The content of Gemfile:

gem 'rails', '3.1.3'
gem 'unicorn'
gem 'spree', '0.70.4'

group :production do
  gem 'pg'
end

Then I set up Unicorn to use 4 workers processes, according to Michael’s blog, this is the optimal configuration. You can scale up to more Dynos should the app needs more processing power.

# config/unicorn.rb

worker_processes 4 # amount of unicorn workers to spin up
timeout 30         # restarts workers that hang for 30 seconds

and also remember to add in Cedar’s new Rails.root/Procfile

web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb

Heroku now also needs the Gemfile.lock committed so please bundle up first

bundle install

Next is to create initial commit and push to heroku

git init
git add -A
git commit -m "Initial commit"
heroku create --stack cedar
git push heroku master

If you ever bump into issues where Bundler fails to locate gems, the best workaround is to cache the bundle:

bundle cache
git add -A
git commit -m 'Bundle cache'

and try to push again:

git push heroku master

The successful reports:

...
       Injecting rails_log_stdout
       Injecting rails3_serve_static_assets
-----> Preparing app for Rails asset pipeline
       Running: rake assets:precompile
       rake aborted!
       could not connect to server: Connection refused
       Is the server running on host "127.0.0.1" and accepting
       TCP/IP connections on port 5432?

       Tasks: TOP => environment
       (See full trace by running task with --trace)
       Precompiling assets failed, enabling runtime asset compilation
       Injecting rails31_enable_runtime_asset_compilation
       Please see this article for troubleshooting help:
       http://devcenter.heroku.com/articles/rails31_heroku_cedar#troubleshooting
-----> Discovering process types
       Procfile declares types      -> web
       Default types for Ruby/Rails -> console, rake, worker
-----> Compiled slug size is 44.0MB
-----> Launching... done, v41
       http://falling-mist-496.herokuapp.com deployed to Heroku

Asset Pipeline

We could see from the output that there is a problem with Heroku and asset pipeline. It seems to me that Heroku fail to precompile assets during slug compilation. It make some sense though because Spree requires access to DB to complete this task and yet before you push to Heroku the environment config are not present for boot. To workaround this issue, the only way I could think of is to locally precompile assets:

Precompiling assets locally
First, we need to set up our DEVELOPMENT DB in config/database.yml. Then we precompile our assets locally:

bundle exec rake assets:precompile RAILS_ENV=development

What will happen next is Sprocket will compile our assets and place them in public/assets folder. What Heroku really care is the public/assets/manifest.yml. This file contains all MD5 checksum of our assets and Heroku will check the existence of the file to tell if we compile our assets locally or not. Okay, next is to commit and push to Heroku:

git add -A public/assets
git commit -m 'Added precompiled assets'
git push heroku master

and we should see output:

....
-----> Preparing app for Rails asset pipeline
       Detected manifest.yml, assuming assets were compiled locally
...

You could read more on Rails 3.1 on Heroku

Bootstrapping

First, we install the generator to do basic setup:

heroku run rails g spree:site

However, there is an issue with PostgreSQL:

/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.1.1/lib/active_record/connection_adapters/postgresql_adapter.rb:958:in `async_exec': PGError: ERROR:  relation "activators" does not exist (ActiveRecord::StatementInvalid)
:             SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
              FROM pg_attribute a LEFT JOIN pg_attrdef d
                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
             WHERE a.attrelid = '"activators"'::regclass
               AND a.attnum > 0 AND NOT a.attisdropped
             ORDER BY a.attnum
	from /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.1.1/lib/active_record/connection_adapters/postgresql_adapter.rb:958:in `exec_no_cache'

Again, it is very bad to see how broken things are in bootstrap step, above issue could be easily resolved by 2 ways:

1. Reset the remote Database

One command to do the fix job:

heroku run rake db:reset

We don’t have to worry about db:migrate and db:seed steps because db:reset has actually invoked those two for us already.

2. Push local DB to remote DB

We can do DB migration on local development environment first then push to production environment. To do so, please make sure you set up a database in your local PostgresSQL server and configure the DB settings for development in config/database.yml. Then we carry out:

bundle exec rake db:migrate
bundle exec rake db:seed

After the above step, we have a working development DB. Now we need to push this DB to the remote heroku. To do so, we need to have taps gem pre-installed:

gem install taps

After the gem is installed, we can push with command:

heroku db:push

Next we create admin user by:

heroku run rake db:admin:create

Once you’ve create your own admin user, we can test our spree. Yet first we need to restart heroku processees:

heroku restart

Then we test the site by opening the app URL with command:

heroku open

You should see a empty products page w/o any errors.

Load Sample Data

Because Heroku is disk-less therefore rake spree_sample:load CLI will NOT work. A little tweak for paperclip to use Cloud Storage service like Amazon S3 is required.

I have consolidated all forks of RSpace’s spree-heroku extension to work with upstream spree, ie. as an mountable engine. I am in talk with Spree core team regarding of moving this extension to official spree github tree so everyone don’t have to look around and confused with bunch of forks.

Alright, let’s set up Amazon S3 for Spree, please make sure you have S3 Account and create a US Standard region bucket for this app. Again, please make sure your bucket is US Standard region or else you will bump into this error:

The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.

Firstly, append this line into your Gemfile:

gem 'spree_heroku', :git => 'git://github.com/joneslee85/spree-heroku.git', :branch => '0-70-stable'

then

bundle install

Next, we create a new file under config/s3.yml and modify the key in accordance to your S3 account

development:
  bucket: your_app_dev
  access_key_id: your_access_key
  secret_access_key: secret_access_key

test:
  bucket: your_app_test
  access_key_id: your_access_key
  secret_access_key: secret_access_key

production:
  bucket: your_app_prod
  access_key_id: your_access_key
  secret_access_key: secret_access_key

Now you’re ready to push again

git add -A
git commit -m 'Added spree_heroku'
git push heroku master

and load up sample data

heroku run rake spree_sample:load

if nothing goes wrong, you would get to this:

...
-- Processing image: ror_baseball_jersey_red.png
-- Processing image: ror_baseball_jersey_back_red.png
-- Processing image: ror_baseball_jersey_red.png
-- Processing image: ror_baseball_jersey_back_red.png
-- Processing image: ror_baseball_jersey_red.png
-- Processing image: ror_baseball_jersey_back_red.png
loading ruby    /app/vendor/bundle/ruby/1.9.1/bundler/gems/spree-eaeac1e20a8a/sample/lib/tasks/../../db/sample/taxons.rb
loading ruby    /app/vendor/bundle/ruby/1.9.1/bundler/gems/spree-eaeac1e20a8a/sample/lib/tasks/../../db/sample/update_memberships.rb

should any problems occurs, you can reset the db:

heroku run rake db:reset

Okay, yet you are not done yet, though w/o getting errors, you won’t get the app running. Checking the logs with

heroku logs

shows that there is some problem with Calculator::PriceBucket calculator:

2011-08-18T03:55:33+00:00 app[web.1]: Error registering calculator Calculator::PriceBucket
2011-08-18T03:55:34+00:00 app[web.1]: Error registering calculator Calculator::PriceBucket
2011-08-18T03:55:34+00:00 app[web.1]: Error registering calculator Calculator::PriceBucket
2011-08-18T03:55:34+00:00 app[web.1]: E, [2011-08-18T03:55:34.699856 #2054] ERROR -- : superclass mismatch for class PriceBucket (TypeError)
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/bundler/gems/spree-eaeac1e20a8a/core/app/models/calculator/price_bucket.rb:1:in `'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:237:in `require'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:237:in `block in require'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:225:in `load_dependency'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:237:in `require'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:344:in `require_or_load'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:298:in `depend_on'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc1/lib/active_support/dependencies.rb:214:in `require_dependency'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:416:in `block (2 levels) in eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:415:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:415:in `block in eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:413:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:413:in `eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/application/railties.rb:8:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/application/railties.rb:8:in `all'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/engine.rb:411:in `eager_load!'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/application/finisher.rb:51:in `block in '
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:25:in `instance_exec'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:25:in `run'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:50:in `block in run_initializers'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:49:in `each'
2011-08-18T03:55:34+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/railties-3.1.0.rc6/lib/rails/initializable.rb:49:in `run_initializers'
2011-08-18T03:55:34+00:00 app[web.1]: E, [2011-08-18T03:55:34.732186 #1] ERROR -- : reaped # worker=2

Shocking, I did not have any idea what could possibly go wrong. Thanks to our hero Ryan (@ryanbigg), he figured out that the keyword Bucket is reserved in S3, thus our PriceBucket is so in the warzone. The 0-70-stable branch has addressed this issue which can be found here. You need to use the 0-70-stable branch or backport that commit locally to your app.

For your Gemfile:

gem 'spree', :git => 'git://github.com/spree/spree.git', :branch => '0-70-stable'

and bravo, then checkout your site with

heroku apps:open

you would see this when browsing:

Disable SSL in Production (optional)

You can disable SSL in Production by creating file RAILS_ROOT/app/models/app_configuration_decorator.rb with content:

AppConfiguration.class_eval do
  preference :allow_ssl_in_production, :boolean, :default => false
end

and please make sure you commit and push to heroku

Add spree-blue-theme

To add spree-blue-theme, simply append this line into the end of your Gemfile:

gem 'spree_blue_theme', :git => 'git://github.com/spree/spree_blue_theme.git'

Then bundle update and precompile assets:

bundle install
bundle exec rake assets:precompile
git add -A
git commit -m 'Added spree-blue-theme'
git push heroku master

Issues

1. First time push to heroku returns with errors with PG regarding ‘activators’ table – Solution: run rake db:reset
2. Non-US Standard S3 bucket won’t work
3. Get superclass mismatch error with Calculator::PriceBucket if loading additional extensions

Conclusion

I am relieved that in the end I could get Spree and its sample data up and running on Heroku Cedar stack though the journey is very painstaking.

Heroku Cedar stack seems potential hosting solution for Spree for its newly introduced technologies. At the time of the writing, there are issues outstanding. However I believe those issues will be polished by the time Spree 0.70 is released.

Rails logging makes easy with itslog + axe

Should errors occurred and to be isolated, Rails developer tend to spend hours digest the Rails log files to work out the issues. However this task could become a real nightmare because filtering through megabytes of text to search for specific thing you suspect that go wrong might be very tedious and strenuous. (I know it ’cause I’ve been through cases where log file get up to 60MByes). IMHO, life is too short to be small [1] ineffective, so I google-ed around and found this nice piece of gem ‘itslog‘ that could ‘nail the ass’.

itslog is a log formatter designed to aid rails development/debugging. The formatting will prepend all log statements with a colored header and additional information about the statement.

[timestamp] [rails namespace]: [normal log]

Example, default Rails log:

Taxon Load (4.1ms)  SELECT "taxons".* FROM "taxons" WHERE "taxons"."parent_id" IS NULL ORDER BY "lft"

turns to

19:17:29 active_record: Taxon Load (4.1ms)  SELECT "taxons".* FROM "taxons" WHERE "taxons"."parent_id" IS NULL ORDER BY "lft"

The log is now more readable and with the help of ‘cat’ you could extract specific info, for example:

cat log/development.log | grep 'active_record'

Furthermore, you can use ‘tail’ to track specific details from log file:

tail -f  log/development.log | grep 'active_record'

Yet even easier, the same author wrote ‘axe‘ which is a wrapper of ‘tail’ to help you generate ‘tail -f’ easily.

For example:

$ axe
> tail -f log/development.log

$ axe active_record
> tail -f log/development.log | grep active_record

$ axe active_record action_view INSERT
> tail -f log/development.log | grep "active_record\|action_view\|INSERT"

$ axe -e test
> tail -f log/test.log

$ axe -e test active_record action_view INSERT
> tail -f log/test.log | grep "active_record\|action_view\|INSERT"

$ axe -n 50
> tail -n 50 log/development.log

$ axe -n 50 INSERT
> tail -n 50 log/development.log | grep INSERT

I hope you find ‘itslog’ useful and if you like this post, please subscribe to my blog, I love you :)

[1] <humor>oops, I get mixed up with the protein drink advertising</humor>

Follow

Get every new post delivered to your Inbox.

Join 60 other followers