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.

Rails Tip: See ActiveRecord’s SQL Statements log output under Rails Console (rails console)

A good trick for prototyping/testing ActiveRecord is to have the SQL statement output to your console as you work. It could be achieved so with:

$ rails c
Loading development environment (Rails 3.0.10)
ruby-1.8.7-p352 :001 > ActiveRecord::Base.logger = Logger.new(STDOUT)

Whenever your code relates to AR, the SQL statement will be piped directly to STDOUT:


ruby-1.8.7-p352 :002 > Person.find 1
  Person Load (0.1ms)  SELECT "people".* FROM "people" WHERE "people"."id" = 1 LIMIT 1
 => #<Person id: 1, name: "one", age: 23, created_at...

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.

TextMate Tip #2: Quickly switch to Ruby/RoR language format

One useful shortcut that help switching to Ruby/RoR format quickly:

Shift Ctrl Alt/Option R

a popup will show up and you can chooses your Language by the number key 1- 5

TextMate Tip #1: Refactoring code with Column Selection

Column Selection and Edit is one unique feature that TextMate offer. This function comes handy if you are to make amendment to a group of selection that share same syntax.

Holding down Alt/Option key and you should see your pointer turns to Crosshair icon. Then select the text with your mouse. You can see that when you type in, the new text will be repetitively inserted into all lines.

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.

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.

Tip: Shorthand for RubyOnRails raw method

<%= raw something %>

can be short-handed with <%== %> to

<%== something %>

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>

A take on Spree + deface

Firstly, kudos to Brian Quinn aka BDQ for the great ‘Deface‘ integration that leverage the Spree hook to a new level [1]. The secret lies in the power of the Deface library that allow targeting an html element with CSS selector and replace, remove, insert_before, insert_after, insert_top and insert_bottom new markup to that target. This mechanism shares resemblance to jQuery selector and manipulator.

Pros:

  • Flexible to target element that is not pre-defined with hooks
  • Able to target even ruby code
  • Memory safe as all overrides are applied on template once and included in the Rails compiled template
  • Easy to pick up as long as you are proficient with CSS selector rules
  • 99% percent backward-compatible with old hooks

Cons:

  • Bleeding edge technology is not always stable
  • 1% hook-related stuff might breaks

So the question is how well this feature work? Does it work with all CSS selector rules? Does it support the new CSS3 selectors? How inheritances works with multiple overrides on same target? Well, I hold no answer, so why don’t we venture into the code and work it out?

The Setup

At the time of this writing, BDQ has not yet merged the branch with master, so we have to manually pull deface branch to test it. Deface is now merged with master branch of spree.

  1. Fetch the upstream spree from GitHub
    git clone git://github.com/spree/spree.git
  2. Fetch and track BDQ’s deface branch.This step is not required any longer.
    cd spree
    git remote add bdq git://github.com/BDQ/deface.git
    git fetch bdq
    git checkout -b deface --track bdq/master
  3. Install all Gem dependencies and generate sandbox
    bundle install
    rake sandbox

Next, we need to a placeholder for hook. I’d recommend to create a new file called 'spree_site_hook.rb' in your sandbox’s lib. Then we need to include that file in our SpreeSite engine, so append require 'spree_site_hooks' into the first line of 'lib/spree_site.rb'

# lib/spree_site.rb
require 'spree_site_hooks'

module SpreeSite
class Engine < Rails::Engine
def self.activate
# Add your custom site logic here
end

def load_tasks
end

config.to_prepare &method(:activate).to_proc
end
end

There isn’t convention for the hook placeholder or how it should be loaded. You don’t have to initialize the hook file in your Engine file, in fact you can place file 'spree_site_hook.rb' in folder 'config/initializers'.

Alternatively, you can create folder app/deface and chuck in files with _decorator naming (for example app/deface/site_decorator.rb. To do so, you should tell the Engine (lib/spree_site.rb) to include those decorators files.

# lib/spree_site.rb

module SpreeSite
class Engine < Rails::Engine
def self.activate
Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c|
  Rails.env.production? ? require(c) : load(c)
end
end

def load_tasks
end

config.to_prepare &method(:activate).to_proc
end
end

How does it work?

Says I want to change the line ‘Powered by Spree’ in the footer partial with ‘Hello world’. Let’s look into how the old partial looks like:

<div class="left">
    <%= hook :footer_left do %>
    <p>
      <%= t("powered_by") %> <a href="http://spreecommerce.com/">Spree</a>
    </p>
    <% end %>
  </div>

and this is the new partial:

  <div class="left" data-hook="footer_left">
    <p>
      <%= t("powered_by") %> <a href="http://spreecommerce.com/">Spree</a>
    </p>
  </div>

You can see that ruby hook is deprecated and replaced with html5 attribute tag data-hook. The change is very marginal but creates much differences.

Below is how you replace the footer text with old hook:

class SiteHooks < Spree::ThemeSupport::HookListener
  remove :footer_left
  replace :footer_left, :text => '<p>Hello world</p>'
end

The new way is:

# lib/spree_site_hook.rb
Deface::Override.new(:virtual_path => "shared/_footer",
                     :name => "site_footer_left",
                     :replace => "[data-hook='footer_left'] > p:first-child",
                     :text => "Hello world",
                     :disabled => false)

This example above is very simple yet this hinders a problem with the old hooks that is you could not target deeper than pre-defined hook. With the old way, we could not target the p tag that enclose the “Powered by Spree” text, thus we have remove existing content within that hook and replace it with new content. Whilst the new Deface way allows you to use CSS rule "[data-hook='footer_left'] > p:first-child" to explicitly just target the p tag. This example is very trivial but it demonstrates the inefficiency of the old hook system. Just imagine you have to work with a big complex template, it’d be an mission impossible with hooks. I often find the old hook way is too broad/narrow in term of hook scope, that usually makes me resolve to replacing a full view file with new file using hook (no DRY at all).

How to use it?

By creating new Deface::Override object with sets of params. Taking above example:

# lib/spree_site_hook.rb
Deface::Override.new(:virtual_path => "shared/_footer",
                     :name => "site_footer_left",
                     :replace => "[data-hook='footer_left'] > p:first-child",
                     :text => "Hello world",
                     :disabled => false)

The parameters are 3 parts: Target, Action and Source.

Target
:virtual_path is the partial where Deface should apply the override.

Action
:replace is one of 4 actions provided by Deface, ie :remove, :replace, :insert_after, :insert_before

Source
In the example, :text source is used to replace a matching element with text. You can replace matching element with :partial and :template too.

Optional
:name is an optional unique name so you can identify the object and modify it later
:disabled is a flag to disable/enable the object if something breaks, useful for debugging

Once the object initialization is finished, Deface targets the shared/_footer partial and try to match any element with [data-hook='footer_left'] > p:first-child CSS rule, then replace the first p tag of footer_left hook with Hello world text.

You can find more details on BDQ GitHub page at https://github.com/bdq/deface.

Advanced

Deface can also target ruby code thanks to Nokogiri support out-of-the box. Deface will temporarily convert ERB files with ruby code to pseudo HTML that can queried by Nokogiri.

<%= some ruby code %>

becomes

<code erb-loud> some ruby code </code>

and

<% other ruby code %>

becomes

<code erb-silent> other ruby code </code>

This feature is truly a killer feature of Deface. For example, if I want to replace link_to_cart helper from 'shared/_store_menu' partial with my own customized advanced_link_to_cart helper, how could I do that? Well, you could use CSS to match li.cart-indicator and replace it with a new partial that contains the helper. That’s not nifty enough, isn’t it? You can achieve that like this:

Deface::Override.new(:virtual_path => "shared/_store_menu",
                     :name => "remove_link_to_cart_helper",
                     :replace => "code[erb-loud]:contains('link_to_cart')",
                     :text => "<%= advanced_link_to_cart %>" )

The above code tells Deface to look for any instances of link_to_cart helper and replace it with advanced_link_to_cart helper. So you are probably wondering how things work at the background. First, deface will escape all ERB template to pseudo markup

The example markup is

<li><%= link_to t("home") , root_path %></li>
<li class="cart-indicator"><%= link_to_cart %></li>

becomes

<li><code erb-loud> link_to t(&quot;home&quot;) , root_path </code></li>
<li class="cart-indicator"><code erb-loud> link_to_cart </code></li>

Deface then queries our selector code[erb-loud]:contains('link_to_cart') and replace matchings with advanced_link_to_cart

BDQ also create a web-app test harness for you to test out Deface @ http://deface.heroku.com/. This tool comes very handy when you on complicated rules.

Issues

Update: BDQ has resolved below issue in the recent merge.
So far as I am aware, Deface parser has few problems with few odd ERB + Ruby combination. For example:

<tr <%= 'style="color:red;"' if product.deleted? %> id="<%= dom_id product %>" data-hook="admin_products_index_rows">

could not be parsed correctly. However worry not, BDQ is aware of it. For now, I’d recommend you not to mix up Ruby and ERB too much. So the above markup can be converted to:

<tr style="<%= "color:red" if product.deleted? %>" id="<%= dom_id product %>" data-hook="admin_products_index_rows">

Conclusion

I have to say Deface is a great addition to Spree stack, another step closer to a truly modular templating. Give it a try and remember to report issues to BDQ and most importantly please send a thank you to BDQ if you like it.

[1] https://github.com/spree/spree/commit/87c97bf7314eae0ca01a043bb44fa0812d578766

Follow

Get every new post delivered to your Inbox.

Join 60 other followers