2013-02-05 14:16:51 -05:00
module SiteSettingExtension
module Types
String = 1
Time = 2
Fixnum = 3
Float = 4
Bool = 5
Null = 6
end
def mutex
@mutex || = Mutex . new
end
def current
@@containers || = { }
@@containers [ RailsMultisite :: ConnectionManagement . current_db ] || = { }
end
def defaults
@defaults || = { }
end
def setting ( name , default = nil , type = nil )
mutex . synchronize do
self . defaults [ name ] = default
current_value = current . has_key? ( name ) ? current [ name ] : default
setup_methods ( name , current_value )
end
end
# just like a setting, except that it is available in javascript via DiscourseSession
2013-02-07 00:46:57 +00:00
def client_setting ( name , default = nil , type = nil )
setting ( name , default , type )
2013-02-05 14:16:51 -05:00
@@client_settings || = [ ]
@@client_settings << name
end
def client_settings
@@client_settings
end
def client_settings_json
Rails . cache . fetch ( SiteSettingExtension . client_settings_cache_key , expires_in : 30 . minutes ) do
MultiJson . dump ( Hash [ * @@client_settings . map { | n | [ n , self . send ( n ) ] } . flatten ] )
end
end
# Retrieve all settings
def all_settings
@defaults . map do | s , v |
{ setting : s ,
description : description ( s ) ,
default : v ,
value : send ( s ) . to_s }
end
end
def description ( setting )
I18n . t ( " site_settings. #{ setting } " )
end
# table is not in the db yet, initial migration, etc
def table_exists?
@table_exists = ActiveRecord :: Base . connection . table_exists? 'site_settings' if @table_exists == nil
@table_exists
end
def self . client_settings_cache_key
" client_settings_json "
end
# refresh all the site settings
def refresh!
return unless table_exists?
mutex . synchronize do
ensure_listen_for_changes
old = current
changes = [ ]
deletions = [ ]
all_settings = SiteSetting . select ( [ :name , :value , :data_type ] )
new_hash = Hash [ * ( all_settings . map { | s | [ s . name . intern , convert ( s . value , s . data_type ) ] } . to_a . flatten ) ]
# add defaults
new_hash = defaults . merge ( new_hash )
new_hash . each do | name , value |
changes << [ name , value ] if ! old . has_key? ( name ) || old [ name ] != value
end
old . each do | name , value |
deletions << [ name , value ] unless new_hash . has_key? ( name )
end
if deletions . length > 0 || changes . length > 0
@current = new_hash
changes . each do | name , val |
setup_methods name , val
end
deletions . each do | name , val |
setup_methods name , defaults [ name ]
end
end
$redis . del ( SiteSettingExtension . client_settings_cache_key )
end
end
def ensure_listen_for_changes
unless @subscribed
pid = process_id
MessageBus . subscribe ( " /site_settings " ) do | msg |
message = msg . data
if message [ " process " ] != pid
begin
# picks a db
MessageBus . on_connect . call ( msg . site_id )
SiteSetting . refresh!
ensure
MessageBus . on_disconnect . call ( msg . site_id )
end
end
end
@subscribed = true
end
end
def process_id
@@process_id || = SecureRandom . uuid
end
def remove_override! ( name )
return unless table_exists?
SiteSetting . where ( :name = > name ) . destroy_all
end
def add_override! ( name , val )
return unless table_exists?
setting = SiteSetting . where ( :name = > name ) . first
type = get_data_type ( defaults [ name ] )
if type == Types :: Bool && val != true && val != false
val = ( val == " t " || val == " true " )
end
if type == Types :: Fixnum && ! ( Fixnum === val )
val = val . to_i
end
if setting
setting . value = val
setting . data_type = type
setting . save
else
SiteSetting . create! ( :name = > name , :value = > val , :data_type = > type )
end
MessageBus . publish ( '/site_settings' , { process : process_id } )
end
protected
def get_data_type ( val )
return Types :: Null if val . nil?
if String === val
Types :: String
elsif Fixnum === val
Types :: Fixnum
elsif TrueClass === val || FalseClass === val
Types :: Bool
else
raise ArgumentError . new :val
end
end
def convert ( value , type )
case type
when Types :: Fixnum
value . to_i
when Types :: String
value
when Types :: Bool
value == " t "
when Types :: Null
nil
end
end
def setup_methods ( name , current_value )
# trivial multi db support, we can optimize this later
db = RailsMultisite :: ConnectionManagement . current_db
@@containers || = { }
@@containers [ db ] || = { }
@@containers [ db ] [ name ] = current_value
setter = ( " #{ name } = " ) . sub ( " ? " , " " )
eval " define_singleton_method : #{ name } do
c = @@containers [ RailsMultisite :: ConnectionManagement . current_db ]
c = c [ name ] if c
c
end
define_singleton_method : #{setter} do |val|
add_override! ( : #{name}, val)
refresh!
end
"
end
def method_missing ( method , * args , & block )
as_question = method . to_s . gsub ( / \ ?$ / , '' )
if respond_to? ( as_question )
return send ( as_question , * args , & block )
end
super ( method , * args , & block )
end
end