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.