cockpit 0.0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.markdown +175 -0
- data/Rakefile +78 -0
- data/init.rb +1 -0
- data/lib/cockpit.rb +22 -0
- data/lib/cockpit/cockpit.rb +94 -0
- data/lib/cockpit/configuration.rb +218 -0
- data/lib/cockpit/definition.rb +55 -0
- data/lib/cockpit/extensions.rb +25 -0
- data/lib/cockpit/helper.rb +44 -0
- data/lib/cockpit/store.rb +103 -0
- data/lib/cockpit/tree_hash.rb +113 -0
- data/rails/init.rb +1 -0
- data/test/lib/database.rb +22 -0
- data/test/lib/user.rb +8 -0
- data/test/test_helper.rb +90 -0
- data/test/test_settings.rb +228 -0
- data/test/test_settings_in_database.rb +153 -0
- data/test/test_settings_on_model.rb +68 -0
- metadata +86 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Lance Pollard (lancejpollard@gmail.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# Cockpit
|
2
|
+
|
3
|
+
<q>Super DRY Settings for Ruby, Rails, and Sinatra Apps.</q>
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
sudo gem install cockpit
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
### Migration
|
12
|
+
|
13
|
+
create_table :settings, :force => true do |t|
|
14
|
+
t.string :key
|
15
|
+
t.string :value
|
16
|
+
t.string :cast_as
|
17
|
+
t.string :configurable_type
|
18
|
+
t.integer :configurable_id
|
19
|
+
end
|
20
|
+
|
21
|
+
### Setup (`config/initializers/settings.rb`)
|
22
|
+
|
23
|
+
Cockpit do
|
24
|
+
site do
|
25
|
+
title "Martini", :tooltip => "Set your title!"
|
26
|
+
tagline "Developer Friendly, Client Ready Blog with Rails 3"
|
27
|
+
keywords "Rails 3, Heroku, JQuery, HTML 5, Blog Engine, CSS3"
|
28
|
+
copyright "© 2010 Viatropos. All rights reserved."
|
29
|
+
timezones :value => lambda { TimeZone.first }, :options => lambda { TimeZone.all }
|
30
|
+
date_format "%m %d, %Y"
|
31
|
+
time_format "%H"
|
32
|
+
week_starts_on "Monday", :options => ["Monday", "Sunday", "Friday"]
|
33
|
+
language "en-US", :options => ["en-US", "de"]
|
34
|
+
touch_enabled true
|
35
|
+
touch_as_subdomain false
|
36
|
+
google_analytics ""
|
37
|
+
teasers :title => "Teasers" do
|
38
|
+
disable false
|
39
|
+
left 1, :title => "Left Teaser"
|
40
|
+
right 2
|
41
|
+
center 3
|
42
|
+
end
|
43
|
+
main_quote 1
|
44
|
+
end
|
45
|
+
asset :title => "Asset (and related) Settings" do
|
46
|
+
thumb do
|
47
|
+
width 100, :tip => "Thumb's width"
|
48
|
+
height 100, :tip => "Thumb's height"
|
49
|
+
end
|
50
|
+
medium do
|
51
|
+
width 600, :tip => "Thumb's width"
|
52
|
+
height 250, :tip => "Thumb's height"
|
53
|
+
end
|
54
|
+
large do
|
55
|
+
width 600, :tip => "Large's width"
|
56
|
+
height 295, :tip => "Large's height"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
authentication :title => "Authentication Settings" do
|
60
|
+
use_open_id true
|
61
|
+
use_oauth true
|
62
|
+
end
|
63
|
+
front_page do
|
64
|
+
slideshow_tag "slideshow"
|
65
|
+
slideshow_effect "fade"
|
66
|
+
end
|
67
|
+
page do
|
68
|
+
per_page 10
|
69
|
+
feed_per_page 10
|
70
|
+
end
|
71
|
+
people do
|
72
|
+
show_avatars true
|
73
|
+
default_avatar "/images/missing-person.png"
|
74
|
+
end
|
75
|
+
social do
|
76
|
+
facebook "http://facebook.com/viatropos"
|
77
|
+
twitter "http://twitter.com/viatropos"
|
78
|
+
end
|
79
|
+
s3 do
|
80
|
+
key "my_key"
|
81
|
+
secret "my_secret"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
#### Get
|
86
|
+
|
87
|
+
Settings.get("site.title").value #=> "Martini"
|
88
|
+
Settings.get("site.title.value") #=> "Martini"
|
89
|
+
Settings("site.title").value #=> "Martini"
|
90
|
+
Settings("site.title.value") #=> "Martini"
|
91
|
+
Settings["site.title"].value #=> "Martini"
|
92
|
+
Settings["site.title.value"] #=> "Martini"
|
93
|
+
Settings.site.title.value #=> "Martini" # doesn't pass through store yet
|
94
|
+
|
95
|
+
#### Set
|
96
|
+
|
97
|
+
Settings.set("site.title" => "Martini") #=> {:site => {:title => {:value => "Martini"}}}
|
98
|
+
Settings("site.title" => "Martini") #=> {:site => {:title => {:value => "Martini"}}}
|
99
|
+
Settings["site.title"] = "Martini" #=> {:site => {:title => {:value => "Martini"}}}
|
100
|
+
Settings.site.title = "Martini" #=> {:site => {:title => {:value => "Martini"}}} # doesn't pass through store yet
|
101
|
+
|
102
|
+
### Key points
|
103
|
+
|
104
|
+
- Each node is any word you want
|
105
|
+
- You can nest them arbitrarily deep
|
106
|
+
- You can use Procs
|
107
|
+
- Values are type casted
|
108
|
+
- Settings can be defined in yaml or using the DSL.
|
109
|
+
- The preferred way to _get_ values is `Settings("path.to.value").value`
|
110
|
+
- You can add custom properties to each setting:
|
111
|
+
- `Settings("site.title").tooltip #=> "Set your title!"`
|
112
|
+
- You have multiple storage options:
|
113
|
+
- `Settings.store = :db`: Syncs setting to/from ActiveRecord
|
114
|
+
- `Settings.store = :memory`: Stores everything in a Hash (memoized, super fast)
|
115
|
+
- You can specify them on a per-model basis.
|
116
|
+
|
117
|
+
Example:
|
118
|
+
|
119
|
+
class User < ActiveRecord::Base
|
120
|
+
acts_as_configurable :settings do
|
121
|
+
name "Lance", :title => "First Name", :options => ["Lance", "viatropos"]
|
122
|
+
favorite do
|
123
|
+
color "red"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
User.new.settings #=> <#Settings @tree={
|
129
|
+
:favorite => {
|
130
|
+
:color => {:type=>:string, :value=>"red"}
|
131
|
+
},
|
132
|
+
:name => {:type=>:string, :title=>"First Name", :value=>"Lance", :options=>["Lance", "Viatropos"]}
|
133
|
+
}/>
|
134
|
+
|
135
|
+
### Why
|
136
|
+
|
137
|
+
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).
|
138
|
+
|
139
|
+
Cockpit encapsulates the logic common to:
|
140
|
+
|
141
|
+
- Options
|
142
|
+
- Preferences
|
143
|
+
- Settings
|
144
|
+
- Configuration
|
145
|
+
- Properties and Attributes
|
146
|
+
- Key/Value stores
|
147
|
+
|
148
|
+
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.
|
149
|
+
|
150
|
+
## Todo
|
151
|
+
|
152
|
+
- Settings should be sorted by the way they were constructed
|
153
|
+
- Check type, so when it is saved it knows what to do.
|
154
|
+
|
155
|
+
This ended up being very similar to i18n:
|
156
|
+
|
157
|
+
- [http://guides.rubyonrails.org/i18n.html](http://guides.rubyonrails.org/i18n.html)
|
158
|
+
- [I asked about this on the i18n lighthouse](http://i18n.lighthouseapp.com/projects/14947/tickets/21-abstract-out-configuration-functionality-from-i18n-into-separate-gem#ticket-21-1)
|
159
|
+
|
160
|
+
I think the i18n gem should be broken down into two parts: Configuration (key/value store), and Translation.
|
161
|
+
|
162
|
+
#### End Goal
|
163
|
+
|
164
|
+
- Base key-value functionality gem, which allows you to store arbitrary key values in any database (similar to moneta).
|
165
|
+
- i18n and Cockpit build on top of that
|
166
|
+
|
167
|
+
### Alternatives
|
168
|
+
|
169
|
+
- [Preferences](http://github.com/pluginaweek/preferences)
|
170
|
+
- [SettingsGoo](http://rubygems.org/gems/settings-goo)
|
171
|
+
- [RailsSettings](http://github.com/Squeegy/rails-settings)
|
172
|
+
- [SimpleConfig](http://github.com/lukeredpath/simpleconfig)
|
173
|
+
- [Configatron](http://github.com/markbates/configatron)
|
174
|
+
- [RConfig](http://github.com/rahmal/rconfig)
|
175
|
+
- [Serenity](http://github.com/progressions/serenity)
|
data/Rakefile
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require "rake/rdoctask"
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
|
5
|
+
spec = Gem::Specification.new do |s|
|
6
|
+
s.name = "cockpit"
|
7
|
+
s.authors = ["Lance Pollard"]
|
8
|
+
s.version = "0.0.1.5"
|
9
|
+
s.summary = "Cockpit: Super DRY Configuration for Ruby, Rails, and Sinatra Apps"
|
10
|
+
s.homepage = "http://github.com/viatropos/cockpit"
|
11
|
+
s.email = "lancejpollard@gmail.com"
|
12
|
+
s.description = "Super DRY Configuration for Ruby, Rails, and Sinatra Apps"
|
13
|
+
s.has_rdoc = false
|
14
|
+
s.rubyforge_project = "cockpit"
|
15
|
+
s.platform = Gem::Platform::RUBY
|
16
|
+
s.files = %w(README.markdown Rakefile init.rb MIT-LICENSE) + Dir["{lib,rails,test}/**/*"] - Dir["test/tmp"]
|
17
|
+
s.require_path = "lib"
|
18
|
+
end
|
19
|
+
|
20
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
21
|
+
pkg.gem_spec = spec
|
22
|
+
pkg.package_dir = "pkg"
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'run unit tests'
|
26
|
+
task :test do
|
27
|
+
Dir["test/**/*"].each do |file|
|
28
|
+
next unless File.basename(file) =~ /test_/
|
29
|
+
next unless File.extname(file) == ".rb"
|
30
|
+
system "ruby #{file}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Create .gemspec file (useful for github)"
|
35
|
+
task :gemspec do
|
36
|
+
File.open("pkg/#{spec.name}.gemspec", "w") do |f|
|
37
|
+
f.puts spec.to_ruby
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Build the gem into the current directory"
|
42
|
+
task :gem => :gemspec do
|
43
|
+
`gem build pkg/#{spec.name}.gemspec`
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Publish gem to rubygems"
|
47
|
+
task :publish => [:package] do
|
48
|
+
%x[gem push pkg/#{spec.name}-#{spec.version}.gem]
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Print a list of the files to be put into the gem"
|
52
|
+
task :manifest do
|
53
|
+
File.open("Manifest", "w") do |f|
|
54
|
+
spec.files.each do |file|
|
55
|
+
f.puts file
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Install the gem locally"
|
61
|
+
task :install => [:package] do
|
62
|
+
File.mkdir("pkg") unless File.exists?("pkg")
|
63
|
+
command = "gem install pkg/#{spec.name}-#{spec.version} --no-ri --no-rdoc"
|
64
|
+
command = "sudo #{command}" if ENV["SUDO"] == true
|
65
|
+
sh %{#{command}}
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Generate the rdoc"
|
69
|
+
Rake::RDocTask.new do |rdoc|
|
70
|
+
files = ["README.markdown", "lib/**/*.rb"]
|
71
|
+
rdoc.rdoc_files.add(files)
|
72
|
+
rdoc.main = "README.markdown"
|
73
|
+
rdoc.title = spec.summary
|
74
|
+
end
|
75
|
+
|
76
|
+
task :yank do
|
77
|
+
`gem yank #{spec.name} -v #{spec.version}`
|
78
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
File.dirname(__FILE__) + "/rails/init.rb"
|
data/lib/cockpit.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
this = File.dirname(__FILE__)
|
6
|
+
Dir["#{this}/cockpit/*"].each { |c| require c }
|
7
|
+
|
8
|
+
class Settings
|
9
|
+
include Cockpit::Configuration
|
10
|
+
end
|
11
|
+
|
12
|
+
def Settings(*args, &block)
|
13
|
+
Settings.configure(*args, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def Cockpit(*args, &block)
|
17
|
+
Settings(*args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveRecord::Base.send(:include, Cockpit) if defined?(ActiveRecord::Base)
|
21
|
+
|
22
|
+
require "#{this}/../app/models/setting.rb"
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Cockpit
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def acts_as_configurable(*args, &block)
|
9
|
+
options = args.extract_options!
|
10
|
+
settings_name = (args.shift || "settings").to_s
|
11
|
+
clazz_name = self.to_s.downcase.split("::").last
|
12
|
+
|
13
|
+
class_inheritable_accessor settings_name
|
14
|
+
has_many settings_name, :class_name => "Setting", :as => :configurable
|
15
|
+
|
16
|
+
Settings { send(clazz_name, &block) }
|
17
|
+
|
18
|
+
self.send("#{settings_name}=", ::Settings.for(clazz_name))
|
19
|
+
|
20
|
+
define_method settings_name do |*value|
|
21
|
+
unless @settings
|
22
|
+
@settings = self.class.send(settings_name).dup
|
23
|
+
@settings.configurable = self
|
24
|
+
end
|
25
|
+
|
26
|
+
unless value.empty?
|
27
|
+
@settings[value.first]
|
28
|
+
else
|
29
|
+
@settings
|
30
|
+
end
|
31
|
+
# model-dependent settings.
|
32
|
+
# requires refactoring the Settings module
|
33
|
+
# so none of it uses class methods...
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def acts_as_settable
|
39
|
+
belongs_to :configurable, :polymorphic => true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.get_type(object)
|
44
|
+
result = case object
|
45
|
+
when Fixnum
|
46
|
+
:integer
|
47
|
+
when Array
|
48
|
+
:array
|
49
|
+
when Float # decimal
|
50
|
+
:float
|
51
|
+
when String
|
52
|
+
:string
|
53
|
+
when Proc
|
54
|
+
:proc
|
55
|
+
when TrueClass
|
56
|
+
:boolean
|
57
|
+
when FalseClass
|
58
|
+
:boolean
|
59
|
+
when DateTime
|
60
|
+
:datetime
|
61
|
+
when Time
|
62
|
+
:time
|
63
|
+
else
|
64
|
+
:string
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.type_cast(object, type)
|
69
|
+
return object if type.nil?
|
70
|
+
result = case type.to_sym
|
71
|
+
when :integer
|
72
|
+
object.to_i
|
73
|
+
when :array
|
74
|
+
object.to_a
|
75
|
+
when :float, :decimal # decimal
|
76
|
+
object.to_f
|
77
|
+
when :string, :text
|
78
|
+
object.to_s
|
79
|
+
when :boolean
|
80
|
+
if object == "true" || object == true || object == "1" || object == 1 || object == "t"
|
81
|
+
true
|
82
|
+
else
|
83
|
+
false
|
84
|
+
end
|
85
|
+
when :datetime, :timestamp
|
86
|
+
object.is_a?(String) ? DateTime.parse(object) : object
|
87
|
+
when :time
|
88
|
+
object.is_a?(String) ? Time.parse(object) : object
|
89
|
+
else
|
90
|
+
object.to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
module Cockpit
|
2
|
+
module Configuration
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
base.class_eval do
|
7
|
+
include InstanceMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
|
13
|
+
attr_accessor :setting_class, :configurable
|
14
|
+
|
15
|
+
def setting_class
|
16
|
+
@setting_class ||= ::Setting
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(*args, &block)
|
20
|
+
store.tree = args.extract_options!
|
21
|
+
value = args
|
22
|
+
build(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def build(&block)
|
26
|
+
tree.instance_eval(&block) if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
def configurable=(object)
|
30
|
+
@configurable = object
|
31
|
+
store.configurable = object
|
32
|
+
end
|
33
|
+
|
34
|
+
# something like this
|
35
|
+
# Settings.for(@user.id).set(:allow_email => params[:allow_email])
|
36
|
+
# or
|
37
|
+
# @user.settings(:allow_email => params[:allow_email])
|
38
|
+
def for(configurable_id)
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def each_setting(&block)
|
43
|
+
tree.each_setting(&block)
|
44
|
+
end
|
45
|
+
|
46
|
+
def tree
|
47
|
+
store.tree
|
48
|
+
end
|
49
|
+
|
50
|
+
def defaults
|
51
|
+
store.defaults
|
52
|
+
end
|
53
|
+
|
54
|
+
def store
|
55
|
+
self.store = :memory if @store.nil?
|
56
|
+
@store
|
57
|
+
end
|
58
|
+
|
59
|
+
def empty?
|
60
|
+
tree.empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
def store=(value)
|
64
|
+
options = {:configurable => configurable}
|
65
|
+
options[:tree] = @store.tree unless @store.nil?
|
66
|
+
@store = case value
|
67
|
+
when :memory
|
68
|
+
Cockpit::Store::Memory.new(self, options)
|
69
|
+
when :db
|
70
|
+
Cockpit::Store::Database.new(self, options)
|
71
|
+
else
|
72
|
+
value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def get(path)
|
77
|
+
store.get(path)
|
78
|
+
end
|
79
|
+
alias_method :[], :get
|
80
|
+
|
81
|
+
def get!(path)
|
82
|
+
store.get!(path)
|
83
|
+
end
|
84
|
+
|
85
|
+
def set(value)
|
86
|
+
store.set(value)
|
87
|
+
end
|
88
|
+
|
89
|
+
def set!(value)
|
90
|
+
store.set!(value)
|
91
|
+
end
|
92
|
+
|
93
|
+
def []=(key, value)
|
94
|
+
store.set(key => value)
|
95
|
+
end
|
96
|
+
|
97
|
+
def clear(options = {})
|
98
|
+
store.clear(options)
|
99
|
+
end
|
100
|
+
|
101
|
+
def inspect
|
102
|
+
"<##{self.class.to_s} @tree=#{tree.inspect}/>"
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_yaml
|
106
|
+
to_hash.to_yaml
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_hash
|
110
|
+
store.tree.to_hash
|
111
|
+
end
|
112
|
+
|
113
|
+
def method_missing(meth, *args, &block)
|
114
|
+
if args.empty?
|
115
|
+
store.get(meth)
|
116
|
+
else
|
117
|
+
store.set(meth, args.first)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
module ClassMethods
|
124
|
+
|
125
|
+
def setting_class
|
126
|
+
global.setting_class
|
127
|
+
end
|
128
|
+
|
129
|
+
def global(&block)
|
130
|
+
@global ||= new
|
131
|
+
@global.build(&block)
|
132
|
+
@global
|
133
|
+
end
|
134
|
+
|
135
|
+
def configure(*args, &block)
|
136
|
+
options = args.extract_options!
|
137
|
+
path = args.first
|
138
|
+
if path && !path.is_a?(Hash)
|
139
|
+
global[path]
|
140
|
+
elsif !options.empty?
|
141
|
+
global.set(options)
|
142
|
+
else
|
143
|
+
global(&block)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# something like this
|
148
|
+
# Settings.for(@user.id).set(:allow_email => params[:allow_email])
|
149
|
+
# or
|
150
|
+
# @user.settings(:allow_email => params[:allow_email])
|
151
|
+
def for(name)
|
152
|
+
Settings.new(get(name).dup)
|
153
|
+
end
|
154
|
+
|
155
|
+
def tree
|
156
|
+
global.tree
|
157
|
+
end
|
158
|
+
|
159
|
+
def defaults
|
160
|
+
global.defaults
|
161
|
+
end
|
162
|
+
|
163
|
+
def store
|
164
|
+
global.store
|
165
|
+
end
|
166
|
+
|
167
|
+
def store=(value)
|
168
|
+
global.store = value
|
169
|
+
end
|
170
|
+
|
171
|
+
def get(path)
|
172
|
+
global.get(path)
|
173
|
+
end
|
174
|
+
alias_method :[], :get
|
175
|
+
|
176
|
+
def get!(path)
|
177
|
+
global.get!(path)
|
178
|
+
end
|
179
|
+
|
180
|
+
def set(value)
|
181
|
+
global.set(value)
|
182
|
+
end
|
183
|
+
|
184
|
+
def set!(value)
|
185
|
+
global.set!(value)
|
186
|
+
end
|
187
|
+
|
188
|
+
def []=(key, value)
|
189
|
+
global.set(key => value)
|
190
|
+
end
|
191
|
+
|
192
|
+
def clear(options = {})
|
193
|
+
global.clear(options)
|
194
|
+
end
|
195
|
+
|
196
|
+
def empty?
|
197
|
+
global.empty?
|
198
|
+
end
|
199
|
+
|
200
|
+
def inspect
|
201
|
+
global.inspect
|
202
|
+
end
|
203
|
+
|
204
|
+
def to_yaml
|
205
|
+
global.to_yaml
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_hash
|
209
|
+
global.to_hash
|
210
|
+
end
|
211
|
+
|
212
|
+
def method_missing(meth, *args, &block)
|
213
|
+
global.method_missing(meth, *args, &block)
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|