Shackles
About
Shackles allows multiple database environments and environment overrides to ActiveRecord, allowing least-privilege best practices.
Installation
Add gem 'shackles'
to your Gemfile (tested with Rails 2.3 and 3.2)
Usage
There are two major use cases for shackles. The first is for master/slave(/deploy) environments. Using a slave is as simple as adding a slave block (underneath your main environment block) in database.yml, then wrapping stuff you want to query the slave in Shackles.activate(:slave) blocks. You can extend this to a deploy environment so that migrations will run as a deploy user that permission to modify schema, while your normal app runs with lower privileges that cannot modify schema. This is defense-in-depth in practice, so that if you happen to have a SQL injection bug, it would be impossible to do something like dropping tables.
The other major use case is more defense-in-depth. By carefully setting up your environment, you can default to script/console sessions for regular users to use their own database user, and the slave.
Example database.yml file:
production:
adapter: postgresql
username: myapp
database: myapp
host: db-master
slave:
host: db-slave
deploy:
username: deploy
Using an initializer, you can achieve the default environment settings (in tandem with profile changes):
if ENV['RAILS_DATABASE_ENVIRONMENT']
Shackles.activate!(ENV['RAILS_DATABASE_ENVIRONMENT'].to_sym)
end
if ENV['RAILS_DATABASE_USER']
Shackles.apply_config!(:username => ENV['RAILS_DATABASE_USER'])
end
Additionally in Ruby 2.0+ you can include Shackles::HelperMethods and use several helpers to execute methods on specific environments:
class SomeModel
include Shackles::HelperMethods
def expensive_read_only
...
end
shackle_method :expensive_read_only, environment: :slave
def self.class_level_expensive_read_only
...
end
shackle_class_method :class_level_expensive_read_only, environment: :slave
# helpers for multiple methods are also available
shackle_methods :instance_method_foo, :instance_method_bar, environment: :slave
shackle_class_methods :class_method_foo, :class_method_bar, environment: :slave
end