Browse files

Added configuration class to provide better DSL

  • Loading branch information...
1 parent bd9c974 commit 74c09d03c2663de79cf9e8dd1bf9dcb4695c651d @ledermann committed Mar 6, 2013
Showing with 187 additions and 28 deletions.
  1. +30 −2 README.md
  2. +4 −10 lib/rails-settings.rb
  3. +1 −8 lib/rails-settings/base.rb
  4. +32 −0 lib/rails-settings/configuration.rb
  5. +108 −0 spec/configuration_spec.rb
  6. +12 −8 spec/spec_helper.rb
View
32 README.md
@@ -17,10 +17,38 @@ Ruby 1.8.7, 1.9.3 or 2.0.0
### Define Settings for a model with default values
+Without defaults:
+
+```ruby
+class User < ActiveRecord::Base
+ has_settings :dashboard, :calendar
+end
+```
+
+With defaults:
+
```ruby
class User < ActiveRecord::Base
- has_settings :dashboard => { :theme => 'blue', :view => 'monthly', :filter => false },
- :calendar => { :scope => 'company'}
+ has_settings do |s|
+ s.key :dashboard, :defaults => { :theme => 'blue', :view => 'monthly', :filter => false }
+ s.key :calendar, :defaults => { :scope => 'company'}
+ end
+end
+```
+
+With customized object, e.g. for validation:
+
+```ruby
+class Project < ActiveRecord::Base
+ has_settings :info, :class_name => 'ProjectSettingObject'
+end
+
+class ProjectSettingObject < RailsSettings::SettingObject
+ validate do
+ unless self.owner_name.present? && self.owner_name.is_a?(String)
+ errors.add(:base, "Owner name is missing")
+ end
+ end
end
```
View
14 lib/rails-settings.rb
@@ -1,20 +1,14 @@
require 'app/models/setting_object'
+require 'rails-settings/configuration'
require 'rails-settings/base'
require 'rails-settings/scopes'
ActiveRecord::Base.class_eval do
- def self.has_settings(keys)
- class_attribute :default_settings
- self.default_settings = {}
-
- keys.each_pair do |key, defaults|
- raise ArgumentError unless key.is_a?(Symbol)
- raise ArgumentError unless defaults.nil? || defaults.is_a?(Hash)
-
- self.default_settings[key] = defaults.stringify_keys.freeze
- end
+ def self.has_settings(*args, &block)
+ RailsSettings::Configuration.new(*args.unshift(self), &block)
include RailsSettings::Base unless self.include?(RailsSettings::Base)
include RailsSettings::Scopes unless self.include?(RailsSettings::Scopes)
end
end
+
View
9 lib/rails-settings/base.rb
@@ -2,18 +2,11 @@ module RailsSettings
module Base
def self.included(base)
base.class_eval do
- # Use a custom SettingObject class if there is any
- setting_object_class_name = begin
- "#{base.name}SettingObject" if Module.const_get("#{base.name}SettingObject")
- rescue NameError
- 'RailsSettings::SettingObject'
- end
-
has_many :setting_objects,
:as => :target,
:autosave => true,
:dependent => :delete_all,
- :class_name => setting_object_class_name
+ :class_name => self.setting_object_class_name
def settings(var)
raise ArgumentError unless var.is_a?(Symbol)
View
32 lib/rails-settings/configuration.rb
@@ -0,0 +1,32 @@
+module RailsSettings
+ class Configuration
+ def initialize(*args, &block)
+ options = args.extract_options!
+ klass = args.shift
+ keys = args
+
+ raise ArgumentError unless klass
+
+ @klass = klass
+ @klass.class_attribute :default_settings, :setting_object_class_name
+ @klass.default_settings = {}
+ @klass.setting_object_class_name = options[:class_name] || 'RailsSettings::SettingObject'
+
+ if block_given?
+ yield(self)
+ else
+ keys.each do |k|
+ key(k)
+ end
+ end
+
+ raise ArgumentError.new('has_settings: No keys defined') if @klass.default_settings.blank?
+ end
+
+ def key(name, options={})
+ raise ArgumentError.new("has_settings: Symbol expected, but got a #{name.class}") unless name.is_a?(Symbol)
+ raise ArgumentError.new("has_settings: Option :defaults expected, but got #{options.keys.join(', ')}") unless options.blank? || (options.keys == [:defaults])
+ @klass.default_settings[name] = (options[:defaults] || {}).stringify_keys.freeze
+ end
+ end
+end
View
108 spec/configuration_spec.rb
@@ -0,0 +1,108 @@
+require 'spec_helper'
+
+module RailsSettings
+ class Dummy
+ end
+
+ describe Configuration, 'successful' do
+ it "should define single key" do
+ Configuration.new(Dummy, :dashboard)
+
+ Dummy.default_settings.should == { :dashboard => {} }
+ Dummy.setting_object_class_name.should == 'RailsSettings::SettingObject'
+ end
+
+ it "should define multiple keys" do
+ Configuration.new(Dummy, :dashboard, :calendar)
+
+ Dummy.default_settings.should == { :dashboard => {}, :calendar => {} }
+ Dummy.setting_object_class_name.should == 'RailsSettings::SettingObject'
+ end
+
+ it "should define single key with class_name" do
+ Configuration.new(Dummy, :dashboard, :class_name => 'MyClass')
+ Dummy.default_settings.should == { :dashboard => {} }
+ Dummy.setting_object_class_name.should == 'MyClass'
+ end
+
+ it "should define multiple keys with class_name" do
+ Configuration.new(Dummy, :dashboard, :calendar, :class_name => 'MyClass')
+
+ Dummy.default_settings.should == { :dashboard => {}, :calendar => {} }
+ Dummy.setting_object_class_name.should == 'MyClass'
+ end
+
+ it "should define using block" do
+ Configuration.new(Dummy) do |c|
+ c.key :dashboard
+ c.key :calendar
+ end
+
+ Dummy.default_settings.should == { :dashboard => {}, :calendar => {} }
+ Dummy.setting_object_class_name.should == 'RailsSettings::SettingObject'
+ end
+
+ it "should define using block with defaults" do
+ Configuration.new(Dummy) do |c|
+ c.key :dashboard, :defaults => { :theme => 'red' }
+ c.key :calendar, :defaults => { :scope => 'all' }
+ end
+
+ Dummy.default_settings.should == { :dashboard => { 'theme' => 'red' }, :calendar => { 'scope' => 'all'} }
+ Dummy.setting_object_class_name.should == 'RailsSettings::SettingObject'
+ end
+
+ it "should define using block and class_name" do
+ Configuration.new(Dummy, :class_name => 'MyClass') do |c|
+ c.key :dashboard
+ c.key :calendar
+ end
+
+ Dummy.default_settings.should == { :dashboard => {}, :calendar => {} }
+ Dummy.setting_object_class_name.should == 'MyClass'
+ end
+ end
+
+ describe Configuration, 'failure' do
+ it "should fail without args" do
+ expect {
+ Configuration.new
+ }.to raise_error(ArgumentError)
+ end
+
+ it "should fail without keys" do
+ expect {
+ Configuration.new(Dummy)
+ }.to raise_error(ArgumentError)
+ end
+
+ it "should fail without keys in block" do
+ expect {
+ Configuration.new(Dummy) do |c|
+ end
+ }.to raise_error(ArgumentError)
+ end
+
+ it "should fail with keys not being symbols" do
+ expect {
+ Configuration.new(Dummy, 42, "string")
+ }.to raise_error(ArgumentError)
+ end
+
+ it "should fail with keys not being symbols" do
+ expect {
+ Configuration.new(Dummy) do |c|
+ c.key 42, "string"
+ end
+ }.to raise_error(ArgumentError)
+ end
+
+ it "should fail with unknown option" do
+ expect {
+ Configuration.new(Dummy) do |c|
+ c.key :dashboard, :foo => {}
+ end
+ }.to raise_error(ArgumentError)
+ end
+ end
+end
View
20 spec/spec_helper.rb
@@ -27,16 +27,24 @@
ActiveRecord::Migration.verbose = false
class User < ActiveRecord::Base
- has_settings :dashboard => { :theme => 'blue', :view => 'monthly', :filter => false },
- :calendar => { :scope => 'company'}
+ has_settings do |s|
+ s.key :dashboard, :defaults => { :theme => 'blue', :view => 'monthly', :filter => false }
+ s.key :calendar, :defaults => { :scope => 'company'}
+ end
end
class GuestUser < User
- has_settings :dashboard => { :theme => 'red', :view => 'monthly', :filter => false }
+ has_settings do |s|
+ s.key :dashboard, :defaults => { :theme => 'red', :view => 'monthly', :filter => false }
+ end
end
class Account < ActiveRecord::Base
- has_settings :portal => {}
+ has_settings :portal
+end
+
+class Project < ActiveRecord::Base
+ has_settings :info, :class_name => 'ProjectSettingObject'
end
class ProjectSettingObject < RailsSettings::SettingObject
@@ -47,10 +55,6 @@ class ProjectSettingObject < RailsSettings::SettingObject
end
end
-class Project < ActiveRecord::Base
- has_settings :info => {}
-end
-
def setup_db
ActiveRecord::Schema.define(:version => 1) do
create_table :settings do |t|

0 comments on commit 74c09d0

Please sign in to comment.