Cockpit
Super DRY Settings for Ruby, Rails, and Sinatra Apps.
I am going to use this in all future gems that need configurable variables. Reason being, every gem uses some configurable variables, and you end up writing the same code over and over again.
This will make it so if say 10 gems have custom settings, you can change this:
Paperclip.config = ...
S3.configure = ...
Authlogic.setup = ...
MyApp.settings = ....
to this:
Settings do
paperclip ...
s3 ...
authentication ...
app ...
end
Paperclip.config = Settings(:paperclip)
S3.configure = Settings(:s3)
Authlogic.setup = Settings(:authentication)
MyApp.settings = Settings(:app)
Which translates to 1, uniform, clean, configuration file.
Install
sudo gem install cockpit
Usage
Migration
create_table :settings, :force => true do |t|
t.string :key
t.string :value
t.string :cast_as
t.string :configurable_type
t.integer :configurable_id
end
Setup (config/initializers/settings.rb
)
Cockpit do
site do
title "Martini", :tooltip => "Set your title!"
tagline "Developer Friendly, Client Ready Blog with Rails 3"
keywords "Rails 3, Heroku, JQuery, HTML 5, Blog Engine, CSS3"
copyright "© 2010 Viatropos. All rights reserved."
timezones :value => lambda { TimeZone.first }, :options => lambda { TimeZone.all }
date_format "%m %d, %Y"
time_format "%H"
week_starts_on "Monday", :options => ["Monday", "Sunday", "Friday"]
language "en-US", :options => ["en-US", "de"]
touch_enabled true
touch_as_subdomain false
google_analytics ""
:title => "Teasers" do
disable false
left 1, :title => "Left Teaser"
right 2
center 3
end
main_quote 1
end
asset :title => "Asset (and related) Settings" do
thumb do
width 100, :tip => "Thumb's width"
height 100, :tip => "Thumb's height"
end
medium do
width 600, :tip => "Thumb's width"
height 250, :tip => "Thumb's height"
end
large do
width 600, :tip => "Large's width"
height 295, :tip => "Large's height"
end
end
authentication :title => "Authentication Settings" do
use_open_id true
use_oauth true
end
front_page do
"slideshow"
"fade"
end
page do
per_page 10
feed_per_page 10
end
people do
show_avatars true
default_avatar "/images/missing-person.png"
end
do
facebook "http://facebook.com/viatropos"
twitter "http://twitter.com/viatropos"
end
s3 do
key "my_key"
secret "my_secret"
end
end
Get
Settings.get("site.title").value #=> "Martini"
Settings.get("site.title.value") #=> "Martini"
Settings("site.title").value #=> "Martini"
Settings("site.title.value") #=> "Martini"
Settings["site.title"].value #=> "Martini"
Settings["site.title.value"] #=> "Martini"
Settings.site.title.value #=> "Martini" # doesn't pass through store yet
Set
Settings.set("site.title" => "Martini") #=> {:site => {:title => {:value => "Martini"}}}
Settings("site.title" => "Martini") #=> {:site => {:title => {:value => "Martini"}}}
Settings["site.title"] = "Martini" #=> {:site => {:title => {:value => "Martini"}}}
Settings.site.title = "Martini" #=> {:site => {:title => {:value => "Martini"}}} # doesn't pass through store yet
Key points
- Each node is any word you want
- You can nest them arbitrarily deep
- You can use Procs
- Values are type casted
- Settings can be defined in yaml or using the DSL.
- The preferred way to get values is
Settings("path.to.value").value
- You can add custom properties to each setting:
Settings("site.title").tooltip #=> "Set your title!"
- You have multiple storage options:
Settings.store = :db
: Syncs setting to/from ActiveRecordSettings.store = :memory
: Stores everything in a Hash (memoized, super fast)
- You can specify them on a per-model basis.
Example:
class User < ActiveRecord::Base
acts_as_configurable :settings do
name "Lance", :title => "First Name", :options => ["Lance", "viatropos"]
favorite do
color "red"
end
end
end
User.new.settings #=> <#Settings @tree={
:favorite => {
:color => {:type=>:string, :value=>"red"}
},
:name => {:type=>:string, :title=>"First Name", :value=>"Lance", :options=>["Lance", "Viatropos"]}
}/>
Why
There's no standard yet for organizing random properties in Rails apps. And settings should be able to be modified through an interface (think Admin panel).
Cockpit encapsulates the logic common to:
- Options
- Preferences
- Settings
- Configuration
- Properties and Attributes
- Key/Value stores
Sometimes you need a global store, sometimes that global store needs to be customizable by the user, sometimes each user has their own set of configurations. This handles all of those cases.
Todo
- Add ability to
freeze
certain branches of the tree (so plugins can use it and knowSettings.clear
won't remove it) - Settings should be sorted by the way they were constructed
- Check type, so when it is saved it knows what to do.
- Store global declarations in memory
- Create "context" for each set of settings, giving it its own
tree
. Allows mimicking subclasses. Settings
should be a collection of trees orcontexts
: Settings user global default user_a user_b widget global default widget_a widget_b text default widget_a widget_b social default widget_a widget_b Settings.for(:widget, :social) #=> default social widget settings.
This ended up being very similar to i18n:
I think the i18n gem should be broken down into two parts: Configuration (key/value store), and Translation.
End Goal
- Base key-value functionality gem, which allows you to store arbitrary key values in any database (similar to moneta). Should store settings in MongoDB by default.
- i18n and Cockpit build on top of that