Showing posts with label Rails. Show all posts
Showing posts with label Rails. Show all posts

Tuesday, 15 November 2011

Rails 3, Draper, Mongo and will_paginate

You can get mongomapper, will paginate and draper to work together like the demos on (https://github.com/mislav/will_paginate) if you access the models in the controller like below

@models = ModelDecorator.
decorate(Model.paginate(:page => params[:page]))

Friday, 14 October 2011

Continuous Integration - Headless JS on CentOS

This was a bit of a pain to get working so I thought I would layout the steps to get Continuous Integration (CI) working on CentOS 5.5 with cruise control, rspec and cucumber when you have javascript scenarios you want to run without a real browser.

Before Cruise Control is set up:

1) Clone the latest version of CC from: http://cruisecontrolrb.thoughtworks.com/documentation to your server. (read the guide, update your database.yml etc...)

2) CC can run standalone so start the server. and add your project(s)

3) If you have apache running you will have to set up a virtual host and set it to run as a proxy to the CI e.g. my configuration is:


ServerAdmin EMAIL_ADDRESS
ServerName DOMAIN_NAME
ErrorLog PATH_TO_ERROR_LOG
CustomLog PATH_TO_CUSTOM_LOG
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]


Thanks TomJ.

4) vim lib/tasks/custom_cc.rake

desc 'Custom curise task for RSpec'
task :cruise do
ENV['RAILS_ENV'] = 'test'

if File.exists?(Dir.pwd + "/config/database.yml")
if Dir[Dir.pwd + "/db/migrate/*.rb"].empty?
raise "No migration scripts found in db/migrate/ but database.yml exists, " +
"CruiseControl won't be able to build the latest test database. Build aborted."
end

# perform standard Rails database cleanup/preparation tasks if they are defined in project
# this is necessary because there is no up-to-date development database on a continuous integration box
if Rake.application.lookup('db:test:purge')
CruiseControl::invoke_rake_task 'db:test:purge'
end

if Rake.application.lookup('db:migrate')
CruiseControl::reconnect
CruiseControl::invoke_rake_task 'db:migrate'
end
end

CruiseControl::invoke_rake_task 'spec'
CruiseControl::invoke_rake_task 'cucumber:ok'
end

After Cruise Control is set up

If all is going well then you should be able to start the builder for that project and it should be running the rspec and cucumber tests suite. Now you hit the requirement of writing the scenarios that require javascript and these are currently failing (not unexpected since we don't have a X11 system or browser installed). There are a couple of ways to get these tests running the method I chose was:

1. Add capybara-webkit and headless to the Gemfile:

group :test, :cucumber do
....
gem 'headless'
gem 'capybara-webkit'
....
end

2. Update the features/support/env.rb. Require headless and start it. Also set the capybara js driver to webkit.

require 'cucumber/rails'
require 'headless'

headless = Headless.new
headless.start

Capybara.javascript_driver = :webkit
Capybara.default_selector = :css
Capybara.server_boot_timeout = 50

ActionController::Base.allow_rescue = false

begin
DatabaseCleaner.strategy = :transaction
rescue NameError
raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
end

3. Now on the CI server we need to install some packages for the capybara-webkit gem (I hope you have permission)

sudo yum install -y Xvfb
sudo yum install -y qt4 qt4-devel

4. Almost there. Start the screen or capybara will give you an error like "webkit_server: cannot connect to X server"

Xvfb :1 -screen 0 1024x768x16 -nolisten inet6 &

It should now be running in the background, you can check ps or jobs

5. Kill the builder and reboot the CI app and then push the earlier changes. When you start the builder again all the tests should now run and if you're lucky they may even pass.

Thursday, 19 May 2011

Using virtual attributes in ruby model.to_xml

If you have virtual attributes that you want to include in your to_xml call then you can override the to_xml on the model and pass the virtual attributes as methods. For example

class ModelName < ActiveRecord::Base
has_one :model_name_2

# real attributes a, b
attr_accessor :c, :d
delegate e, :to => :model_name_2
end

Now an instance (mn) of type ModelName calling to_xml will output

mn.to_xml

115true

Now to override the to_xml to include attributes c, d and e.

class ModelName < ActiveRecord::Base
has_one :model_name_2

class ModelName < ActiveRecord::Base
has_one :model_name_2
# real attributes a, b
attr_accessor :c, :d
delegate e, :to => :model_name_2

def to_xml
super(:methods => [:c, :d, e])
end
end

Now when to_xml is called on the model c, d and e will also be included.

mn.to_xml

115trueAnExamplefrom another model

Wednesday, 18 May 2011

5 useful Rails Migrations commands

So we all know Rails migrations are extremely powerful, they allow you to write exactly how you want to structure your data, without getting down to the SQL. It's also really convenient to have a database schema checked into your revision control. It's ridiculously straight forward.. until you start moving data around your database (assuming your not some kind of terminator sent from the future with enough hindsight to never need to refactor your database).

As a general rule of thumb, we should not be moving data around in the migration structure. Here's why:

  • Its hard to write migrations and remember the order that you removed tables
  • Its really hard to read
  • If a migration breaks half way because of the data then we have run half a migration and it fails when we try it again.
  • If we have models we need to keep them and mark them deprecated until the data can be moved, we may remove a model and break our migrations

Therefore its essential to take a database dump of your data. With large data sets this does not seem practical to move around which is why we need to write and up and down data migration scripts. These can easily be placed in rake tasks.

Anyway here are 5 useful commands to get you out of trouble if your migrations go wrong:

rake db:migrate VERSION=20080901210000
rake db:rollback
rake db:rollback STEP=3
rake db:migrate:redo STEP=3
rake db:migrate:down VERSION=20080906120000

Sinatra/ActiveRecord Delegate Method Missing Error

I was doing delegation through multiple models earlier

class Model1 < ActiveRecord::Base
#model relationships

delegate :name, :to => :model2, :prefix => true
end

class Model2 < ActiveRecord::Base
#model relationships

delegate :name, :to => :model3, :prefix => false
end

class Model3 < ActiveRecord::Base
#model relationships
#where we want to access the attribute
end

Sinatra was throwing an error unknown method 'name' for Model2. This is strange since I had already told it to delegate this method to Model3. The problem was that even setting the :prefix to false in Model2 was causing method missing to be thrown. To fix all that was required was to remove the middle prefix making Model2

class Model2 < ActiveRecord::Base
#model relationships

delegate :name, :to => :model3
end

This then fixed the problem. When :prefix is assigned true it will prepend the model name to the accessor, you can specify a different prefix though using a different symbol.

class Model1 < ActiveRecord::Base
#model relationships

delegate :name, :to => :model2, :prefix => :user
end

This will allow you to access the name attribute of Model3 through model1.user_name