cockpit 0.0.1.7 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +100 -199
- data/Rakefile +5 -4
- data/lib/cockpit/core/definition.rb +93 -0
- data/lib/cockpit/core/definitions.rb +55 -0
- data/lib/cockpit/core/include.rb +90 -0
- data/lib/cockpit/core/settings.rb +148 -0
- data/lib/cockpit/core/store.rb +51 -0
- data/lib/cockpit/many/include.rb +18 -0
- data/lib/cockpit/many/settings.rb +21 -0
- data/lib/cockpit/moneta/active_record.rb +120 -0
- data/lib/cockpit/moneta/simple_active_record.rb +93 -0
- data/lib/cockpit.rb +4 -10
- data/test/lib/database.rb +1 -2
- data/test/lib/user.rb +5 -3
- data/test/test_active_record.rb +80 -0
- data/test/test_helper.rb +5 -7
- data/test/test_mongo.rb +82 -0
- data/test/test_stores.rb +125 -0
- metadata +33 -22
- data/app/models/setting.rb +0 -59
- data/init.rb +0 -1
- data/lib/cockpit/cockpit.rb +0 -101
- data/lib/cockpit/configuration.rb +0 -218
- data/lib/cockpit/definition.rb +0 -55
- data/lib/cockpit/extensions.rb +0 -25
- data/lib/cockpit/helper.rb +0 -44
- data/lib/cockpit/store.rb +0 -103
- data/lib/cockpit/tree_hash.rb +0 -116
- data/rails/init.rb +0 -1
- data/test/test_settings.rb +0 -228
- data/test/test_settings_in_database.rb +0 -153
- data/test/test_settings_on_model.rb +0 -68
data/README.markdown
CHANGED
@@ -1,225 +1,126 @@
|
|
1
|
-
|
1
|
+
<h1>Cockpit <img src='http://imgur.com/oXAb6.png' width='16' height='15'/></h1>
|
2
2
|
|
3
|
-
|
3
|
+
> Super DRY Settings for Ruby, Rails, and Sinatra Apps. Thin layer above wycat's [Moneta](http://github.com/wycats/moneta) for pluggable backend support.
|
4
4
|
|
5
|
-
|
5
|
+
## How it works
|
6
6
|
|
7
|
-
|
7
|
+
You can define arbitrarily nested key/value pairs of any type, and customize them from an Admin panel or the terminal, and save them to the MySQL, MongoDB, Redis, or even a File.
|
8
8
|
|
9
|
-
|
10
|
-
S3.configure = ...
|
11
|
-
Authlogic.setup = ...
|
12
|
-
MyApp.settings = ....
|
13
|
-
|
14
|
-
to this:
|
9
|
+
You define settings like this:
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
settings = Cockpit "mongo" do
|
12
|
+
site do
|
13
|
+
title "My Site"
|
14
|
+
time_zone lambda { "Hawaii" }
|
15
|
+
feed do
|
16
|
+
per_page 10
|
17
|
+
formats %w(rss atom)
|
18
|
+
end
|
19
|
+
end
|
21
20
|
end
|
22
21
|
|
23
|
-
|
24
|
-
S3.configure = Settings(:s3)
|
25
|
-
Authlogic.setup = Settings(:authentication)
|
26
|
-
MyApp.settings = Settings(:app)
|
27
|
-
|
28
|
-
Which translates to 1, uniform, clean, configuration file.
|
29
|
-
|
30
|
-
## Install
|
22
|
+
That gives you [this data structure](http://gist.github.com/558480), which is accessed internally as a flat hash with keys like this:
|
31
23
|
|
32
|
-
|
33
|
-
|
34
|
-
##
|
24
|
+
["site.feed.formats", "site.time_zone", "site.feed.per_page", "site", "site.feed", "site.title"]
|
25
|
+
|
26
|
+
## Global and Instance Settings
|
35
27
|
|
36
|
-
|
28
|
+
By default you will have 1 set of global settings, accessible via `Cockpit::Settings.root` which is populated in this call:
|
37
29
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
t.string :configurable_type
|
43
|
-
t.integer :configurable_id
|
30
|
+
Cockpit "mongo" do
|
31
|
+
site do
|
32
|
+
author "Lance"
|
33
|
+
end
|
44
34
|
end
|
35
|
+
|
36
|
+
If you want to have settings encapsulated in an independent scope, you can just assign that to a variable:
|
45
37
|
|
46
|
-
|
47
|
-
|
48
|
-
Cockpit do
|
38
|
+
site_settings = Cockpit "mongo" do
|
49
39
|
site do
|
50
|
-
|
51
|
-
tagline "Developer Friendly, Client Ready Blog with Rails 3"
|
52
|
-
keywords "Rails 3, Heroku, JQuery, HTML 5, Blog Engine, CSS3"
|
53
|
-
copyright "© 2010 Viatropos. All rights reserved."
|
54
|
-
timezones :value => lambda { TimeZone.first }, :options => lambda { TimeZone.all }
|
55
|
-
date_format "%m %d, %Y"
|
56
|
-
time_format "%H"
|
57
|
-
week_starts_on "Monday", :options => ["Monday", "Sunday", "Friday"]
|
58
|
-
language "en-US", :options => ["en-US", "de"]
|
59
|
-
touch_enabled true
|
60
|
-
touch_as_subdomain false
|
61
|
-
google_analytics ""
|
62
|
-
teasers :title => "Teasers" do
|
63
|
-
disable false
|
64
|
-
left 1, :title => "Left Teaser"
|
65
|
-
right 2
|
66
|
-
center 3
|
67
|
-
end
|
68
|
-
main_quote 1
|
40
|
+
author "Lance"
|
69
41
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
42
|
+
end
|
43
|
+
|
44
|
+
## Associated Settings with Models
|
45
|
+
|
46
|
+
You can also associate settings with any object (plain Object, ActiveRecord, MongoMapper::Document, etc.):
|
47
|
+
|
48
|
+
class User < ActiveRecord::Base
|
49
|
+
include Cockpit
|
50
|
+
|
51
|
+
cockpit "mongo" do
|
52
|
+
preferences do
|
53
|
+
favorite_color "red"
|
78
54
|
end
|
79
|
-
|
80
|
-
|
81
|
-
height 295, :tip => "Large's height"
|
55
|
+
settings do
|
56
|
+
google_analytics "123123123"
|
82
57
|
end
|
83
58
|
end
|
84
|
-
authentication :title => "Authentication Settings" do
|
85
|
-
use_open_id true
|
86
|
-
use_oauth true
|
87
|
-
end
|
88
|
-
front_page do
|
89
|
-
slideshow_tag "slideshow"
|
90
|
-
slideshow_effect "fade"
|
91
|
-
end
|
92
|
-
page do
|
93
|
-
per_page 10
|
94
|
-
feed_per_page 10
|
95
|
-
end
|
96
|
-
people do
|
97
|
-
show_avatars true
|
98
|
-
default_avatar "/images/missing-person.png"
|
99
|
-
end
|
100
|
-
social do
|
101
|
-
facebook "http://facebook.com/viatropos"
|
102
|
-
twitter "http://twitter.com/viatropos"
|
103
|
-
end
|
104
|
-
s3 do
|
105
|
-
key "my_key"
|
106
|
-
secret "my_secret"
|
107
|
-
end
|
108
59
|
end
|
60
|
+
|
61
|
+
And access them like this:
|
109
62
|
|
110
|
-
|
63
|
+
user = User.new
|
64
|
+
user.cockpit["settings.google_analytics"] #=> "123123123"
|
65
|
+
user.cockpit["preferences.favorite_color"] = "green"
|
66
|
+
|
67
|
+
## Swappable Backend
|
111
68
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
69
|
+
Thanks to the work behind Moneta, there's a clear interface to key/value stores (and some people have added ActiveRecord support which I've included in this).
|
70
|
+
|
71
|
+
The current backends supported are these keys:
|
72
|
+
|
73
|
+
- mongodb (or 'mongo')
|
74
|
+
- redis
|
75
|
+
- active_record
|
76
|
+
- file
|
77
|
+
- memory
|
78
|
+
- yaml
|
79
|
+
|
80
|
+
It should be easy enough to wrap the rest of the Moneta adapters.
|
81
|
+
|
82
|
+
This is specified as the first DSL attribute:
|
83
|
+
|
84
|
+
Cockpit "redis" do
|
85
|
+
site do
|
86
|
+
author "Lance"
|
87
|
+
end
|
88
|
+
end
|
119
89
|
|
120
|
-
|
121
|
-
|
122
|
-
Settings.set("site.title" => "Martini") #=> {:site => {:title => {:value => "Martini"}}}
|
123
|
-
Settings("site.title" => "Martini") #=> {:site => {:title => {:value => "Martini"}}}
|
124
|
-
Settings["site.title"] = "Martini" #=> {:site => {:title => {:value => "Martini"}}}
|
125
|
-
Settings.site.title = "Martini" #=> {:site => {:title => {:value => "Martini"}}} # doesn't pass through store yet
|
126
|
-
|
127
|
-
### Key points
|
128
|
-
|
129
|
-
- Each node is any word you want
|
130
|
-
- You can nest them arbitrarily deep
|
131
|
-
- You can use Procs
|
132
|
-
- Values are type casted
|
133
|
-
- Settings can be defined in yaml or using the DSL.
|
134
|
-
- The preferred way to _get_ values is `Settings("path.to.value").value`
|
135
|
-
- You can add custom properties to each setting:
|
136
|
-
- `Settings("site.title").tooltip #=> "Set your title!"`
|
137
|
-
- You have multiple storage options:
|
138
|
-
- `Settings.store = :db`: Syncs setting to/from ActiveRecord
|
139
|
-
- `Settings.store = :memory`: Stores everything in a Hash (memoized, super fast)
|
140
|
-
- You can specify them on a per-model basis.
|
141
|
-
|
142
|
-
Example:
|
90
|
+
## Use Cases
|
143
91
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
92
|
+
This makes it really easy to edit random settings from an interface, such as an admin panel. Next goal is to add callbacks around save/destroy so you can run processes when settings are changed (such as changing your google_analytics, which would require re-rendering views if they were cached).
|
93
|
+
|
94
|
+
The goal is to make this [enormous configuration dsl work](http://gist.github.com/558432), so I can define an entire site in a DSL.
|
95
|
+
|
96
|
+
### Other API Notes
|
97
|
+
|
98
|
+
When you specify the DSL, that creates a flat tree of defaults, which aren't saved to the database. Then when you update the setting, it saves to the database, otherwise when the value is read and is null, it will use the default from the in-memory/dsl-defined tree.
|
99
|
+
|
100
|
+
You can also associate a hash with each setting definition. This is great for say options, defaults, titles and tooltips, etc. Here's an example:
|
101
|
+
|
102
|
+
Cockpit "active_record" do
|
103
|
+
site do
|
104
|
+
time_zones "MST", :options => Proc.new { TZInfo::Timezone.all.map(&:name) }
|
150
105
|
end
|
151
106
|
end
|
152
107
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
108
|
+
assert_equal TZInfo::Timezone.all.map(&:name), Cockpit::Settings["site.time_zones"][:options].call
|
109
|
+
|
110
|
+
And you can access the definition object directly:
|
111
|
+
|
112
|
+
Cockpit::Settings.definition("site.time_zones").attributes[:options]
|
113
|
+
|
114
|
+
## Customizations
|
115
|
+
|
116
|
+
Not yet implemented, just ideas.
|
117
|
+
|
118
|
+
If you want to extend the Relationship model and reference that in your child/parent classes, you can do that like so:
|
119
|
+
|
120
|
+
class Bookmark < ActsAsJoinable::Relationship; end
|
121
|
+
|
122
|
+
class User < ActiveRecord::Base
|
123
|
+
joins :posts, :as => :parent, :through => :bookmark
|
124
|
+
end
|
159
125
|
|
160
|
-
|
161
|
-
|
162
|
-
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).
|
163
|
-
|
164
|
-
Cockpit encapsulates the logic common to:
|
165
|
-
|
166
|
-
- Options
|
167
|
-
- Preferences
|
168
|
-
- Settings
|
169
|
-
- Configuration
|
170
|
-
- Properties and Attributes
|
171
|
-
- Key/Value stores
|
172
|
-
|
173
|
-
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.
|
174
|
-
|
175
|
-
## Todo
|
176
|
-
|
177
|
-
- Add ability to `freeze` certain branches of the tree (so plugins can use it and know `Settings.clear` won't remove it)
|
178
|
-
- Settings should be sorted by the way they were constructed
|
179
|
-
- Check type, so when it is saved it knows what to do.
|
180
|
-
- Store global declarations in memory
|
181
|
-
- Create "context" for each set of settings, giving it its own `tree`. Allows mimicking subclasses.
|
182
|
-
- `Settings` should be a collection of trees or `contexts`:
|
183
|
-
Settings
|
184
|
-
user
|
185
|
-
global
|
186
|
-
default
|
187
|
-
user_a
|
188
|
-
user_b
|
189
|
-
widget
|
190
|
-
global
|
191
|
-
default
|
192
|
-
widget_a
|
193
|
-
widget_b
|
194
|
-
text
|
195
|
-
default
|
196
|
-
widget_a
|
197
|
-
widget_b
|
198
|
-
social
|
199
|
-
default
|
200
|
-
widget_a
|
201
|
-
widget_b
|
202
|
-
Settings.for(:widget, :social) #=> default social widget settings.
|
203
|
-
|
204
|
-
This ended up being very similar to i18n:
|
205
|
-
|
206
|
-
- [http://guides.rubyonrails.org/i18n.html](http://guides.rubyonrails.org/i18n.html)
|
207
|
-
- [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)
|
208
|
-
|
209
|
-
I think the i18n gem should be broken down into two parts: Configuration (key/value store), and Translation.
|
210
|
-
|
211
|
-
#### End Goal
|
212
|
-
|
213
|
-
- 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.
|
214
|
-
- i18n and Cockpit build on top of that
|
215
|
-
|
216
|
-
### Alternatives
|
217
|
-
|
218
|
-
- [Preferences](http://github.com/pluginaweek/preferences)
|
219
|
-
- [SettingsGoo](http://rubygems.org/gems/settings-goo)
|
220
|
-
- [RailsSettings](http://github.com/Squeegy/rails-settings)
|
221
|
-
- [SimpleConfig](http://github.com/lukeredpath/simpleconfig)
|
222
|
-
- [Configatron](http://github.com/markbates/configatron)
|
223
|
-
- [RConfig](http://github.com/rahmal/rconfig)
|
224
|
-
- [Serenity](http://github.com/progressions/serenity)
|
225
|
-
- [ApplicationSettings](http://github.com/bradhaydon/application_settings)
|
126
|
+
<cite>copyright [@viatropos](http://viatropos.com) 2010</cite>
|
data/Rakefile
CHANGED
@@ -5,16 +5,17 @@ require 'rake/gempackagetask'
|
|
5
5
|
spec = Gem::Specification.new do |s|
|
6
6
|
s.name = "cockpit"
|
7
7
|
s.authors = ["Lance Pollard"]
|
8
|
-
s.version = "0.
|
9
|
-
s.summary = "
|
8
|
+
s.version = "0.1.1"
|
9
|
+
s.summary = "Super DRY Configuration Management for Ruby, Rails, and Sinatra Apps. With Pluggable NoSQL/SQL backends using Moneta"
|
10
10
|
s.homepage = "http://github.com/viatropos/cockpit"
|
11
11
|
s.email = "lancejpollard@gmail.com"
|
12
|
-
s.description = "Super DRY Configuration for Ruby, Rails, and Sinatra Apps"
|
12
|
+
s.description = "Super DRY Configuration for Ruby, Rails, and Sinatra Apps. With Pluggable NoSQL/SQL backends using Moneta"
|
13
13
|
s.has_rdoc = false
|
14
14
|
s.rubyforge_project = "cockpit"
|
15
15
|
s.platform = Gem::Platform::RUBY
|
16
|
-
s.files = %w(README.markdown Rakefile
|
16
|
+
s.files = %w(README.markdown Rakefile MIT-LICENSE) + Dir["{lib,test,app}/**/*"] - Dir["test/tmp"]
|
17
17
|
s.require_path = "lib"
|
18
|
+
s.add_dependency("moneta")
|
18
19
|
end
|
19
20
|
|
20
21
|
Rake::GemPackageTask.new(spec) do |pkg|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Cockpit
|
2
|
+
# This class defines default properties for a setting object, based on the DSL
|
3
|
+
class Definition
|
4
|
+
# keys is the nested keys associated with child values
|
5
|
+
attr_accessor :key, :value, :keys, :nested, :parent, :attributes, :type
|
6
|
+
|
7
|
+
def initialize(key, *args, &block)
|
8
|
+
process(key, *args, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def process(key, *args, &block)
|
12
|
+
self.key = key.to_s
|
13
|
+
if args.length >= 1
|
14
|
+
if args.last.is_a?(Hash)
|
15
|
+
self.attributes = args.pop
|
16
|
+
else
|
17
|
+
self.attributes = {}
|
18
|
+
end
|
19
|
+
else
|
20
|
+
self.attributes ||= {}
|
21
|
+
end
|
22
|
+
if block_given?
|
23
|
+
self.value ||= []
|
24
|
+
self.nested = true
|
25
|
+
instance_eval(&block)
|
26
|
+
else
|
27
|
+
self.value = *args.first
|
28
|
+
self.nested = false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](key)
|
33
|
+
if attributes.has_key?(key.to_sym)
|
34
|
+
attributes[key.to_sym]
|
35
|
+
elsif attributes.has_key?(key.to_s)
|
36
|
+
attributes[key.to_s]
|
37
|
+
else
|
38
|
+
method_missing(key)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def nested?
|
43
|
+
self.nested == true
|
44
|
+
end
|
45
|
+
|
46
|
+
def keys(separator = ".")
|
47
|
+
if nested?
|
48
|
+
value.inject({key => self}) do |hash, definition|
|
49
|
+
sub_definition = definition.keys.keys.inject({}) do |sub_hash, sub_key|
|
50
|
+
sub_hash["#{key}#{separator}#{sub_key}"] = definition.keys[sub_key]
|
51
|
+
sub_hash
|
52
|
+
end
|
53
|
+
hash.merge(sub_definition)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
{key => self}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def method_missing(method, *args, &block)
|
61
|
+
method = method.to_s.gsub("=", "").to_sym
|
62
|
+
if args.blank? && !block_given?
|
63
|
+
result = self.value.detect do |definition|
|
64
|
+
definition.key == method.to_s
|
65
|
+
end
|
66
|
+
result ? result.value : nil
|
67
|
+
else
|
68
|
+
old_value = self.value.detect { |definition| definition.key == method.to_s }
|
69
|
+
if old_value
|
70
|
+
old_value.process(method, *args, &block)
|
71
|
+
else
|
72
|
+
self.value << Cockpit::Definition.new(method, *args, &block)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class << self
|
78
|
+
# top-level declaration are the first keys in the chain
|
79
|
+
def define!(*args, &block)
|
80
|
+
@definitions = []
|
81
|
+
instance_eval(&block) if block_given?
|
82
|
+
definitions = @definitions
|
83
|
+
@definitions = nil
|
84
|
+
definitions
|
85
|
+
end
|
86
|
+
|
87
|
+
def method_missing(method, *args, &block)
|
88
|
+
method = method.to_s.gsub("=", "").to_sym
|
89
|
+
@definitions << Cockpit::Definition.new(method, *args, &block)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Cockpit
|
2
|
+
# settings have one direct definition and many child definitions
|
3
|
+
class Definitions < Hash
|
4
|
+
attr_accessor :name, :scope
|
5
|
+
|
6
|
+
def initialize(*args, &block)
|
7
|
+
define!(*args, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def define!(*args, &block)
|
11
|
+
options = args.extract_options!
|
12
|
+
options[:store] ||= args.first
|
13
|
+
options.each do |k, v|
|
14
|
+
send("#{k}=", v) if respond_to?("#{k}=")
|
15
|
+
end
|
16
|
+
raise ArgumentError.new("pass a :name to Cockpit::Setting.define!") if self.name.blank?
|
17
|
+
if block_given?
|
18
|
+
self << Cockpit::Definition.define!(&block)
|
19
|
+
end
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(value)
|
24
|
+
([value] + self.values).flatten.uniq.each do |definition|
|
25
|
+
self.merge!(definition.keys)
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def []=(key, value)
|
31
|
+
self << Cockpit::Definition.new(key, value) unless has_key?(key)
|
32
|
+
super(key.to_s, value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](key)
|
36
|
+
super(key.to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_hash
|
40
|
+
keys.inject({}) do |hash, key|
|
41
|
+
hash[key] = self[key].value
|
42
|
+
hash
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def method_missing(method, *args, &block)
|
47
|
+
if has_key?(method)
|
48
|
+
self[method]
|
49
|
+
else
|
50
|
+
super(method, *args, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Cockpit
|
2
|
+
def self.included(base)
|
3
|
+
base.send(:include, ObjectInclude)
|
4
|
+
if defined?(::ActiveRecord::Base) && base.ancestors.include?(::ActiveRecord::Base)
|
5
|
+
base.send(:include, ActiveRecordInclude)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module ActiveRecordInclude
|
10
|
+
def self.included(base)
|
11
|
+
base.class_eval do
|
12
|
+
def self.cockpit(*args, &block)
|
13
|
+
if block_given? || @cockpit.nil?
|
14
|
+
@cockpit = Cockpit::Settings.new(
|
15
|
+
:name => self.name.underscore.gsub(/[^a-z0-9]/, "_").squeeze("_"),
|
16
|
+
:store => :active_record,
|
17
|
+
&block
|
18
|
+
)
|
19
|
+
|
20
|
+
@cockpit.keys.each do |key|
|
21
|
+
next if key =~ /\./
|
22
|
+
|
23
|
+
define_method key do
|
24
|
+
send(:cockpit)[key]
|
25
|
+
end
|
26
|
+
|
27
|
+
define_method "#{key}?" do
|
28
|
+
!send(key).blank?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
else
|
33
|
+
@cockpit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def cockpit
|
38
|
+
unless @cockpit
|
39
|
+
@cockpit = Cockpit::Settings.new(
|
40
|
+
:name => self.class.cockpit.name,
|
41
|
+
:store => :active_record,
|
42
|
+
:record => self
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
@cockpit
|
47
|
+
end
|
48
|
+
|
49
|
+
def get(key)
|
50
|
+
cockpit[key]
|
51
|
+
end unless respond_to?(:get)
|
52
|
+
|
53
|
+
def set(*args)
|
54
|
+
if args.last.is_a?(Hash)
|
55
|
+
cockpit.set(args.last)
|
56
|
+
else
|
57
|
+
cockpit[args.first] = args.last
|
58
|
+
end
|
59
|
+
end unless respond_to?(:set)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module ObjectInclude
|
65
|
+
def self.included(base)
|
66
|
+
base.class_eval do
|
67
|
+
def self.cockpit(*args, &block)
|
68
|
+
if block_given?
|
69
|
+
@cockpit = Cockpit::Settings.new(
|
70
|
+
:name => self.name.underscore.gsub(/[^a-z0-9]/, "_").squeeze("_"),
|
71
|
+
:scope => "default",
|
72
|
+
:store => args.first || "memory",
|
73
|
+
&block
|
74
|
+
)
|
75
|
+
else
|
76
|
+
@cockpit
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def cockpit
|
81
|
+
unless @cockpit
|
82
|
+
@cockpit = self.class.cockpit.dup
|
83
|
+
end
|
84
|
+
|
85
|
+
@cockpit
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|