cockpit 0.0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ module Cockpit
2
+ # Represents the definition of a preference for a particular model
3
+ class Definition
4
+ # The data type for the content stored in this preference type
5
+ attr_reader :type
6
+
7
+ def initialize(name, *args) #:nodoc:
8
+ options = args.extract_options!
9
+
10
+ @type = args.first ? args.first.to_sym : :boolean
11
+
12
+ # Create a column that will be responsible for typecasting
13
+ @column = ActiveRecord::ConnectionAdapters::Column.new(name.to_s, options[:default], @type == :any ? nil : @type.to_s)
14
+
15
+ @group_defaults = (options[:group_defaults] || {}).inject({}) do |defaults, (group, default)|
16
+ defaults[group.is_a?(Symbol) ? group.to_s : group] = type_cast(default)
17
+ defaults
18
+ end
19
+ end
20
+
21
+ # The name of the preference
22
+ def name
23
+ @column.name
24
+ end
25
+
26
+ # The default value to use for the preference in case none have been
27
+ # previously defined
28
+ def default_value(group = nil)
29
+ @group_defaults.include?(group) ? @group_defaults[group] : @column.default
30
+ end
31
+
32
+ # Determines whether column backing this preference stores numberic values
33
+ def number?
34
+ @column.number?
35
+ end
36
+
37
+ # Typecasts the value based on the type of preference that was defined.
38
+ # This uses ActiveRecord's typecast functionality so the same rules for
39
+ # typecasting a model's columns apply here.
40
+ def type_cast(value)
41
+ @type == :any ? value : @column.type_cast(value)
42
+ end
43
+
44
+ # Typecasts the value to true/false depending on the type of preference
45
+ def query(value)
46
+ if !(value = type_cast(value))
47
+ false
48
+ elsif number?
49
+ !value.zero?
50
+ else
51
+ !value.blank?
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,25 @@
1
+ class Hash
2
+ def recursively_symbolize_keys!
3
+ self.symbolize_keys!
4
+ self.values.each do |v|
5
+ if v.is_a? Hash
6
+ v.recursively_symbolize_keys!
7
+ elsif v.is_a? Array
8
+ v.recursively_symbolize_keys!
9
+ end
10
+ end
11
+ self
12
+ end
13
+ end
14
+
15
+ class Array
16
+ def recursively_symbolize_keys!
17
+ self.each do |item|
18
+ if item.is_a? Hash
19
+ item.recursively_symbolize_keys!
20
+ elsif item.is_a? Array
21
+ item.recursively_symbolize_keys!
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ module Cockpit::Helper
2
+
3
+ # always returns either an array or a string
4
+ def c(*args)
5
+ options = args.extract_options!
6
+ result = args.collect {|i| Settings.get(i).value }
7
+ result = result.pop if result.length == 1
8
+ result.blank? ? nil : result.to_s
9
+ end
10
+
11
+ def setting_tag(tag)
12
+
13
+ end
14
+
15
+ def settings_tag(key, &block)
16
+ Settings(key).each_setting do |key, attributes, value|
17
+
18
+ end
19
+ end
20
+
21
+ def setting_value(value)
22
+ result = case value
23
+ when Proc
24
+ value.call
25
+ when Cockpit::TreeHash
26
+ value
27
+ else
28
+ value
29
+ end
30
+ end
31
+
32
+ def setting_options(attributes)
33
+ return {} unless (attributes.is_a?(Hash) && attributes[:options])
34
+ options = case attributes[:options]
35
+ when Proc
36
+ attributes[:options].call
37
+ else
38
+ attributes[:options]
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ ActionView::Base.send(:include, Cockpit::Helper) if defined?(ActionView)
@@ -0,0 +1,103 @@
1
+ module Cockpit
2
+ module Store
3
+ class Base
4
+ attr_accessor :configurable
5
+
6
+ def initialize(*args)
7
+ options = args.extract_options!
8
+ options.each do |k, v|
9
+ self.send("#{k.to_s}=", v) if self.respond_to?("#{k.to_s}=")
10
+ end
11
+ @configuration = args.first
12
+ end
13
+
14
+ def configuration
15
+ @configuration
16
+ end
17
+
18
+ def tree=(value)
19
+ @tree = value if value.is_a?(TreeHash)
20
+ @tree
21
+ end
22
+
23
+ def tree
24
+ @tree ||= TreeHash.new
25
+ end
26
+
27
+ def clear(options = {})
28
+ tree.each do |k, v|
29
+ tree.delete(k) unless (options[:except] && options[:except].include?(k.to_sym))
30
+ end
31
+ end
32
+
33
+ def get(path)
34
+ tree.get(path)
35
+ end
36
+ alias_method :[], :get
37
+
38
+ def get!(path)
39
+ result = get(path)
40
+ raise "'#{path.to_s}' was not in Config" if result.blank?
41
+ result
42
+ end
43
+
44
+ def set(value)
45
+ return unless value.is_a?(Hash)
46
+ value.each { |k,v| set_one(k, v) }
47
+ end
48
+
49
+ def set_one(key, value)
50
+ tree.set(key, value)
51
+ end
52
+
53
+ def set!(value)
54
+ result = set(value)
55
+ raise "'#{path.to_s}' was not set in Config" if result.blank?
56
+ result
57
+ end
58
+
59
+ def []=(key, value)
60
+ set_one(key => value)
61
+ end
62
+ end
63
+
64
+ class Memory < Base
65
+
66
+ end
67
+
68
+ class Database < Base
69
+
70
+ def clear(options = {})
71
+ configuration.setting_class.all.collect(&:destroy) if options[:hard] == true
72
+ super(options)
73
+ end
74
+
75
+ def find_or_create(key)
76
+ result = configuration.setting_class.find(key.to_s) rescue nil
77
+ result ||= configuration.setting_class.create(:key => key.to_s)
78
+ end
79
+
80
+ def set_one_with_database(key, value)
81
+ setting = find_or_create(key)
82
+ cast_as = (setting.cast_as || Cockpit.get_type(value)).to_s
83
+ attributes = {:value => value, :cast_as => cast_as}
84
+ attributes[:configurable] = self.configurable if self.configurable
85
+ setting.update_attributes(attributes)
86
+ set_one_without_database(key, Cockpit.type_cast(value, cast_as))
87
+ end
88
+ alias_method_chain :set_one, :database
89
+
90
+ def get(key)
91
+ result = super(key)
92
+ if result.blank? && setting = find_or_create(key)
93
+ set(key => setting.value)
94
+ result = super(key)
95
+ else
96
+
97
+ end
98
+ result
99
+ end
100
+
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,113 @@
1
+ # http://www.daniel-azuma.com/blog/view/z3bqa0t01uugg1/implementing_dsl_blocks
2
+ module Cockpit
3
+ class TreeHash < Hash
4
+
5
+ def initialize
6
+ block = Proc.new {|h,k| h[k] = TreeHash.new(&block)}
7
+ super &block
8
+ end
9
+
10
+ def value_type
11
+ has_key?(:type) ? self[:type] : :string
12
+ end
13
+
14
+ def each_setting(&block)
15
+ dup.each do |k, v|
16
+ atrib = Hash.new
17
+ if v.has_key?(:value)
18
+ value = clone(v[:value])
19
+ atrib = clone(v)
20
+ else
21
+ v.each do |k, sub_v|
22
+ atrib[k] = clone(v[k]) unless sub_v.is_a?(TreeHash)
23
+ end
24
+ value = clone(v)
25
+ end
26
+ yield(k.to_s, atrib, value) if block_given?
27
+ end
28
+ end
29
+
30
+ def dup
31
+ result = super
32
+ result.each do |k,v|
33
+ result[k] = v.dup if v.is_a?(TreeHash)
34
+ end
35
+ result
36
+ end
37
+
38
+ def children
39
+ hash = {}
40
+ each do |k, v|
41
+ hash[k] = v if v.is_a?(Hash)
42
+ end
43
+ hash
44
+ end
45
+
46
+ def get(key)
47
+ traverse(key)
48
+ end
49
+
50
+ def get_attribute(key, attribute)
51
+ get(key)[attribute.to_sym]
52
+ end
53
+
54
+ def set(key, value)
55
+ traverse(key, value)
56
+ end
57
+
58
+ def set_attribute(key, value)
59
+ traverse(key, value, false)
60
+ end
61
+
62
+ def set_attributes(hash)
63
+ hash.each { |k, v| set_attribute(k, v) }
64
+ self
65
+ end
66
+
67
+ def to_hash
68
+ hash = {}
69
+ self.each do |k, v|
70
+ hash[k] = v.is_a?(TreeHash) ? v.to_hash : v
71
+ end
72
+ hash
73
+ end
74
+
75
+ protected
76
+ def traverse(path, value = nil, as_node = true)
77
+ path = path.to_s.split('.')
78
+ child = path.pop.to_sym
79
+ parent = path.inject(self) { |h,k| h[k.to_sym] }
80
+ unless value.nil?
81
+ if as_node
82
+ if parent[child].has_key?(:type)
83
+ parent[child][:value] = Cockpit.type_cast(value, parent[child][:type])
84
+ else
85
+ parent[child][:value] = value
86
+ parent[child][:type] = Cockpit.get_type(value)
87
+ end
88
+ else
89
+ parent[child] = value
90
+ end
91
+ end
92
+ parent[child]
93
+ end
94
+
95
+ def method_missing(meth, *args, &block)
96
+ options = args.extract_options!
97
+ meth = meth.to_s.gsub("=", "").to_sym
98
+ if args.empty?
99
+ return self[meth] if self.has_key?(meth)
100
+ found = get(meth).set_attributes(options)
101
+ found = found.instance_eval(&block) if block_given?
102
+ found
103
+ else
104
+ get(meth).set_attributes({:type => Cockpit.get_type(args.first)}.merge(options).merge(:value => args.first))
105
+ end
106
+ end
107
+
108
+ private
109
+ def clone(object)
110
+ (object.respond_to?(:dup) ? object.dup : object) rescue object
111
+ end
112
+ end
113
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'cockpit'
@@ -0,0 +1,22 @@
1
+ begin
2
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
3
+ rescue ArgumentError
4
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
5
+ end
6
+
7
+ ActiveRecord::Base.configurations = true
8
+
9
+ ActiveRecord::Schema.define(:version => 1) do
10
+
11
+ create_table :settings, :force => true do |t|
12
+ t.string :key
13
+ t.string :value
14
+ t.string :cast_as
15
+ t.string :configurable_type
16
+ t.integer :configurable_id
17
+ end
18
+
19
+ create_table "users", :force => true do |t|
20
+ end
21
+
22
+ end
data/test/lib/user.rb ADDED
@@ -0,0 +1,8 @@
1
+ class User < ActiveRecord::Base
2
+ acts_as_configurable :settings do
3
+ name "Lance", :title => "First Name", :options => ["Lance", "Viatropos"]
4
+ favorite do
5
+ color "red"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,90 @@
1
+ require "rubygems"
2
+ require "ruby-debug"
3
+ gem 'test-unit'
4
+ require "test/unit"
5
+ require 'active_support'
6
+ require 'active_support/test_case'
7
+ require 'active_record'
8
+ require 'active_record/fixtures'
9
+ require 'shoulda'
10
+ require 'shoulda/active_record'
11
+
12
+ require File.dirname(__FILE__) + '/lib/database'
13
+
14
+ require File.expand_path(File.join(File.dirname(__FILE__), '/../lib/cockpit'))
15
+
16
+ require File.dirname(__FILE__) + '/lib/user'
17
+
18
+ ActiveRecord::Base.class_eval do
19
+ def self.detonate
20
+ all.map(&:destroy)
21
+ end
22
+ end
23
+
24
+ ActiveSupport::TestCase.class_eval do
25
+
26
+ def load_settings
27
+ Settings do
28
+ asset :title => "Asset (and related) Settings" do
29
+ thumb do
30
+ width 100, :tip => "Thumb's width"
31
+ height 100, :tip => "Thumb's height"
32
+ end
33
+ medium do
34
+ width 600, :tip => "Thumb's width"
35
+ height 250, :tip => "Thumb's height"
36
+ end
37
+ large do
38
+ width 600, :tip => "Large's width"
39
+ height 295, :tip => "Large's height"
40
+ end
41
+ end
42
+ authentication :title => "Authentication Settings" do
43
+ use_open_id true
44
+ use_oauth true
45
+ end
46
+ front_page do
47
+ slideshow_tag "slideshow"
48
+ slideshow_effect "fade"
49
+ end
50
+ page do
51
+ per_page 10
52
+ feed_per_page 10
53
+ end
54
+ people do
55
+ show_avatars true
56
+ default_avatar "/images/missing-person.png"
57
+ end
58
+ site do
59
+ title "Martini"
60
+ tagline "Developer Friendly, Client Ready Blog with Rails 3"
61
+ keywords "Rails 3, Heroku, JQuery, HTML 5, Blog Engine, CSS3"
62
+ copyright "© 2010 Viatropos. All rights reserved."
63
+ timezones :value => lambda { TimeZone.first }, :options => lambda { TimeZone.all }
64
+ date_format "%m %d, %Y"
65
+ time_format "%H"
66
+ week_starts_on "Monday", :options => ["Monday", "Sunday", "Friday"]
67
+ language "en-US", :options => ["en-US", "de"]
68
+ touch_enabled true
69
+ touch_as_subdomain false
70
+ google_analytics ""
71
+ teasers :title => "Teasers" do
72
+ disable false
73
+ left 1, :title => "Left Teaser"
74
+ right 2
75
+ center 3
76
+ end
77
+ main_quote 1
78
+ end
79
+ social do
80
+ facebook "http://facebook.com/viatropos"
81
+ twitter "http://twitter.com/viatropos"
82
+ email "lancejpollard@gmail.com"
83
+ end
84
+ s3 do
85
+ key "my_key"
86
+ secret "my_secret"
87
+ end
88
+ end
89
+ end
90
+ end