2013-05-30 17:41:29 -05:00
require 'cache'
2017-03-17 01:21:30 -05:00
require 'open3'
2017-12-21 15:29:11 -06:00
require_dependency 'route_format'
2013-08-23 01:21:52 -05:00
require_dependency 'plugin/instance'
2013-10-08 23:10:37 -05:00
require_dependency 'auth/default_current_user_provider'
2015-04-27 12:06:53 -05:00
require_dependency 'version'
2017-09-08 12:38:46 -05:00
require 'digest/sha1'
2013-05-30 17:41:29 -05:00
2015-07-14 13:52:35 -05:00
# Prevents errors with reloading dev with conditional includes
if Rails . env . development?
require_dependency 'file_store/s3_store'
require_dependency 'file_store/local_store'
end
2013-02-05 13:16:51 -06:00
module Discourse
2014-04-17 00:57:17 -05:00
require 'sidekiq/exception_handler'
2014-02-20 21:30:25 -06:00
class SidekiqExceptionHandler
extend Sidekiq :: ExceptionHandler
end
2017-03-17 01:21:30 -05:00
class Utils
def self . execute_command ( * command , failure_message : " " )
stdout , stderr , status = Open3 . capture3 ( * command )
if ! status . success?
failure_message = " #{ failure_message } \n " if ! failure_message . blank?
raise " #{ failure_message } #{ stderr } "
end
stdout
end
def self . pretty_logs ( logs )
logs . join ( " \n " . freeze )
end
end
2014-07-17 15:22:46 -05:00
# Log an exception.
#
2014-07-17 17:07:25 -05:00
# If your code is in a scheduled job, it is recommended to use the
# error_context() method in Jobs::Base to pass the job arguments and any
# other desired context.
2014-07-17 15:22:46 -05:00
# See app/jobs/base.rb for the error_context function.
2015-02-09 14:47:46 -06:00
def self . handle_job_exception ( ex , context = { } , parent_logger = nil )
2014-02-20 21:30:25 -06:00
context || = { }
parent_logger || = SidekiqExceptionHandler
cm = RailsMultisite :: ConnectionManagement
parent_logger . handle_exception ( ex , {
current_db : cm . current_db ,
current_hostname : cm . current_hostname
} . merge ( context ) )
end
2013-06-18 19:31:19 -05:00
# Expected less matches than what we got in a find
2015-03-22 20:16:21 -05:00
class TooManyMatches < StandardError ; end
2013-06-18 19:31:19 -05:00
2013-02-25 10:42:20 -06:00
# When they try to do something they should be logged in for
2015-03-22 20:16:21 -05:00
class NotLoggedIn < StandardError ; end
2013-02-05 13:16:51 -06:00
# When the input is somehow bad
2015-03-22 20:16:21 -05:00
class InvalidParameters < StandardError ; end
2013-02-05 13:16:51 -06:00
# When they don't have permission to do something
2015-09-18 02:14:10 -05:00
class InvalidAccess < StandardError
2018-02-09 18:09:54 -06:00
attr_reader :obj , :custom_message , :opts
2017-09-23 09:39:58 -05:00
def initialize ( msg = nil , obj = nil , opts = nil )
2015-09-18 02:14:10 -05:00
super ( msg )
2017-09-23 09:39:58 -05:00
2018-02-09 18:09:54 -06:00
@opts = opts || { }
@custom_message = opts [ :custom_message ] if @opts [ :custom_message ]
2015-09-18 02:14:10 -05:00
@obj = obj
end
end
2013-02-05 13:16:51 -06:00
# When something they want is not found
2017-09-26 11:58:15 -05:00
class NotFound < StandardError ; end
2013-02-05 13:16:51 -06:00
2013-06-04 17:34:53 -05:00
# When a setting is missing
2015-03-22 20:16:21 -05:00
class SiteSettingMissing < StandardError ; end
2013-06-04 17:34:53 -05:00
2013-11-05 12:04:47 -06:00
# When ImageMagick is missing
2015-03-22 20:16:21 -05:00
class ImageMagickMissing < StandardError ; end
2013-11-05 12:04:47 -06:00
2014-02-12 22:37:28 -06:00
# When read-only mode is enabled
2015-03-22 20:16:21 -05:00
class ReadOnly < StandardError ; end
2014-02-12 22:37:28 -06:00
2013-07-29 00:13:13 -05:00
# Cross site request forgery
2015-03-22 20:16:21 -05:00
class CSRF < StandardError ; end
2013-07-29 00:13:13 -05:00
2017-08-06 20:43:09 -05:00
class Deprecation < StandardError ; end
2013-12-23 17:50:36 -06:00
def self . filters
2015-07-27 01:46:50 -05:00
@filters || = [ :latest , :unread , :new , :read , :posted , :bookmarks ]
2013-12-23 17:50:36 -06:00
end
def self . anonymous_filters
2015-07-27 01:46:50 -05:00
@anonymous_filters || = [ :latest , :top , :categories ]
2013-12-23 17:50:36 -06:00
end
def self . top_menu_items
2014-01-14 11:48:57 -06:00
@top_menu_items || = Discourse . filters + [ :category , :categories , :top ]
2013-12-23 17:50:36 -06:00
end
def self . anonymous_top_menu_items
2014-01-14 11:48:57 -06:00
@anonymous_top_menu_items || = Discourse . anonymous_filters + [ :category , :categories , :top ]
2013-12-23 17:50:36 -06:00
end
2016-04-06 03:57:59 -05:00
PIXEL_RATIOS || = [ 1 , 1 . 5 , 2 , 3 ]
2015-05-29 02:57:54 -05:00
2015-05-25 10:59:00 -05:00
def self . avatar_sizes
2015-05-29 02:57:54 -05:00
# TODO: should cache these when we get a notification system for site settings
set = Set . new
SiteSetting . avatar_sizes . split ( " | " ) . map ( & :to_i ) . each do | size |
PIXEL_RATIOS . each do | pixel_ratio |
set << size * pixel_ratio
end
end
2015-05-26 00:41:50 -05:00
set
2015-05-25 10:59:00 -05:00
end
2013-08-01 00:59:57 -05:00
def self . activate_plugins!
2015-04-27 12:06:53 -05:00
all_plugins = Plugin :: Instance . find_all ( " #{ Rails . root } /plugins " )
2017-09-08 12:38:46 -05:00
if Rails . env . development?
plugin_hash = Digest :: SHA1 . hexdigest ( all_plugins . map { | p | p . path } . sort . join ( '|' ) )
hash_file = " #{ Rails . root } /tmp/plugin-hash "
2018-03-28 03:20:08 -05:00
old_hash = begin
File . read ( hash_file )
rescue Errno :: ENOENT
end
2017-09-08 12:38:46 -05:00
if old_hash && old_hash != plugin_hash
puts " WARNING: It looks like your discourse plugins have recently changed. "
puts " It is highly recommended to remove your `tmp` directory, otherwise "
puts " plugins might not work. "
puts
else
File . write ( hash_file , plugin_hash )
end
end
2015-04-27 12:06:53 -05:00
@plugins = [ ]
all_plugins . each do | p |
v = p . metadata . required_version || Discourse :: VERSION :: STRING
if Discourse . has_needed_version? ( Discourse :: VERSION :: STRING , v )
p . activate!
@plugins << p
else
STDERR . puts " Could not activate #{ p . metadata . name } , discourse does not meet required version ( #{ v } ) "
end
end
2013-08-01 00:59:57 -05:00
end
2015-02-04 15:23:39 -06:00
def self . disabled_plugin_names
2016-06-30 09:55:01 -05:00
plugins . select { | p | ! p . enabled? } . map ( & :name )
2015-02-04 15:23:39 -06:00
end
2013-08-01 00:59:57 -05:00
def self . plugins
2015-02-10 10:18:16 -06:00
@plugins || = [ ]
2013-08-01 00:59:57 -05:00
end
2018-05-08 00:24:58 -05:00
def self . hidden_plugins
@hidden_plugins || = [ ]
end
2018-05-08 18:52:21 -05:00
def self . visible_plugins
2018-05-08 00:24:58 -05:00
self . plugins - self . hidden_plugins
end
2017-01-12 14:43:09 -06:00
def self . plugin_themes
@plugin_themes || = plugins . map ( & :themes ) . flatten
end
2016-11-14 18:42:55 -06:00
def self . official_plugins
2017-07-27 20:20:09 -05:00
plugins . find_all { | p | p . metadata . official? }
2016-11-14 18:42:55 -06:00
end
def self . unofficial_plugins
2017-07-27 20:20:09 -05:00
plugins . find_all { | p | ! p . metadata . official? }
2016-11-14 18:42:55 -06:00
end
2014-01-14 19:07:42 -06:00
def self . assets_digest
@assets_digest || = begin
digest = Digest :: MD5 . hexdigest ( ActionView :: Base . assets_manifest . assets . values . sort . join )
channel = " /global/asset-version "
2015-05-03 21:21:00 -05:00
message = MessageBus . last_message ( channel )
2014-01-14 19:07:42 -06:00
unless message && message . data == digest
2015-05-03 21:21:00 -05:00
MessageBus . publish channel , digest
2014-01-14 19:07:42 -06:00
end
digest
end
end
2013-08-25 20:04:16 -05:00
def self . authenticators
# TODO: perhaps we don't need auth providers and authenticators maybe one object is enough
# NOTE: this bypasses the site settings and gives a list of everything, we need to register every middleware
# for the cases of multisite
# In future we may change it so we don't include them all for cases where we are not a multisite, but we would
# require a restart after site settings change
Users :: OmniauthCallbacksController :: BUILTIN_AUTH + auth_providers . map ( & :authenticator )
end
2013-08-01 00:59:57 -05:00
def self . auth_providers
2013-08-01 01:05:46 -05:00
providers = [ ]
2015-02-10 10:18:16 -06:00
plugins . each do | p |
next unless p . auth_providers
p . auth_providers . each do | prov |
providers << prov
2013-08-01 00:59:57 -05:00
end
end
providers
end
2013-05-30 17:41:29 -05:00
def self . cache
@cache || = Cache . new
end
2013-02-05 13:16:51 -06:00
# Get the current base URL for the current site
def self . current_hostname
2016-06-30 09:55:01 -05:00
SiteSetting . force_hostname . presence || RailsMultisite :: ConnectionManagement . current_hostname
2013-05-30 17:41:29 -05:00
end
2013-11-05 12:04:47 -06:00
def self . base_uri ( default_value = " " )
2016-06-30 09:55:01 -05:00
ActionController :: Base . config . relative_url_root . presence || default_value
2013-03-14 07:01:52 -05:00
end
2016-07-28 12:54:17 -05:00
def self . base_protocol
SiteSetting . force_https? ? " https " : " http "
end
2013-05-30 17:41:29 -05:00
def self . base_url_no_prefix
2016-07-28 12:54:17 -05:00
default_port = SiteSetting . force_https? ? 443 : 80
url = " #{ base_protocol } :// #{ current_hostname } "
2016-06-30 09:55:01 -05:00
url << " : #{ SiteSetting . port } " if SiteSetting . port . to_i > 0 && SiteSetting . port . to_i != default_port
url
2013-04-05 05:38:20 -05:00
end
2013-05-30 17:41:29 -05:00
def self . base_url
2015-09-21 13:28:20 -05:00
base_url_no_prefix + base_uri
2013-05-30 17:41:29 -05:00
end
2017-07-19 14:08:54 -05:00
def self . route_for ( uri )
2018-03-28 03:20:08 -05:00
unless uri . is_a? ( URI )
uri = begin
URI ( uri )
rescue URI :: InvalidURIError
end
end
2017-07-19 14:08:54 -05:00
return unless uri
path = uri . path || " "
2018-02-13 17:39:44 -06:00
if ! uri . host || ( uri . host == Discourse . current_hostname && path . start_with? ( Discourse . base_uri ) )
2017-07-19 14:08:54 -05:00
path . slice! ( Discourse . base_uri )
return Rails . application . routes . recognize_path ( path )
end
2017-07-20 15:01:16 -05:00
nil
rescue ActionController :: RoutingError
2017-07-19 14:08:54 -05:00
nil
end
2017-01-11 04:03:36 -06:00
READONLY_MODE_KEY_TTL || = 60
READONLY_MODE_KEY || = 'readonly_mode' . freeze
PG_READONLY_MODE_KEY || = 'readonly_mode:postgres' . freeze
2016-06-29 01:19:18 -05:00
USER_READONLY_MODE_KEY || = 'readonly_mode:user' . freeze
2017-01-11 04:03:36 -06:00
READONLY_KEYS || = [
2017-01-11 02:38:07 -06:00
READONLY_MODE_KEY ,
PG_READONLY_MODE_KEY ,
USER_READONLY_MODE_KEY
]
def self . enable_readonly_mode ( key = READONLY_MODE_KEY )
if key == USER_READONLY_MODE_KEY
$redis . set ( key , 1 )
2016-06-29 01:19:18 -05:00
else
2017-01-11 02:38:07 -06:00
$redis . setex ( key , READONLY_MODE_KEY_TTL , 1 )
keep_readonly_mode ( key )
2016-06-29 01:19:18 -05:00
end
2016-06-29 00:55:17 -05:00
2015-05-03 21:21:00 -05:00
MessageBus . publish ( readonly_channel , true )
2013-02-05 13:16:51 -06:00
true
end
2017-01-11 02:38:07 -06:00
def self . keep_readonly_mode ( key )
2015-02-11 14:50:17 -06:00
# extend the expiry by 1 minute every 30 seconds
2016-11-10 09:44:51 -06:00
unless Rails . env . test?
Thread . new do
2018-06-11 11:21:29 -05:00
while readonly_mode? ( key )
2017-01-11 02:38:07 -06:00
$redis . expire ( key , READONLY_MODE_KEY_TTL )
2016-11-10 09:44:51 -06:00
sleep 30 . seconds
end
2015-02-11 14:50:17 -06:00
end
end
end
2017-01-11 02:38:07 -06:00
def self . disable_readonly_mode ( key = READONLY_MODE_KEY )
2016-06-29 01:19:18 -05:00
$redis . del ( key )
2015-05-03 21:21:00 -05:00
MessageBus . publish ( readonly_channel , false )
2013-02-05 13:16:51 -06:00
true
end
2018-06-11 11:21:29 -05:00
def self . readonly_mode? ( keys = READONLY_KEYS )
recently_readonly? || $redis . mget ( * keys ) . compact . present?
2017-01-11 02:38:07 -06:00
end
def self . last_read_only
@last_read_only || = { }
end
def self . recently_readonly?
2017-01-11 04:03:36 -06:00
return false unless read_only = last_read_only [ $redis . namespace ]
2017-01-11 02:38:07 -06:00
read_only > 15 . seconds . ago
end
def self . received_readonly!
last_read_only [ $redis . namespace ] = Time . zone . now
end
def self . clear_readonly!
last_read_only [ $redis . namespace ] = nil
2013-02-05 13:16:51 -06:00
end
2017-08-15 21:38:30 -05:00
def self . request_refresh! ( user_ids : nil )
2014-02-20 23:52:11 -06:00
# Causes refresh on next click for all clients
#
2015-05-03 21:21:00 -05:00
# This is better than `MessageBus.publish "/file-change", ["refresh"]` because
2014-02-20 23:52:11 -06:00
# it spreads the refreshes out over a time period
2017-08-15 21:38:30 -05:00
if user_ids
2017-08-15 23:06:47 -05:00
MessageBus . publish ( " /refresh_client " , 'clobber' , user_ids : user_ids )
2017-08-15 21:38:30 -05:00
else
MessageBus . publish ( '/global/asset-version' , 'clobber' )
end
2014-02-20 23:52:11 -06:00
end
2017-10-03 22:22:23 -05:00
def self . ensure_version_file_loaded
unless @version_file_loaded
version_file = " #{ Rails . root } /config/version.rb "
require version_file if File . exists? ( version_file )
@version_file_loaded = true
end
end
2013-08-02 16:25:57 -05:00
2017-10-03 22:22:23 -05:00
def self . git_version
ensure_version_file_loaded
$git_version || =
begin
git_cmd = 'git rev-parse HEAD'
self . try_git ( git_cmd , Discourse :: VERSION :: STRING )
end
2013-02-18 00:39:54 -06:00
end
2014-09-09 16:04:10 -05:00
def self . git_branch
2017-10-03 22:22:23 -05:00
ensure_version_file_loaded
$git_branch || =
begin
git_cmd = 'git rev-parse --abbrev-ref HEAD'
self . try_git ( git_cmd , 'unknown' )
end
2017-08-28 11:24:56 -05:00
end
def self . full_version
2017-10-03 22:22:23 -05:00
ensure_version_file_loaded
$full_version || =
begin
git_cmd = 'git describe --dirty --match "v[0-9]*"'
self . try_git ( git_cmd , 'unknown' )
end
2017-08-28 11:24:56 -05:00
end
2017-10-03 22:22:23 -05:00
def self . try_git ( git_cmd , default_value )
2017-08-28 11:24:56 -05:00
version_value = false
2014-09-09 16:04:10 -05:00
2017-10-03 22:22:23 -05:00
begin
version_value = ` #{ git_cmd } ` . strip
rescue
version_value = default_value
2014-09-09 16:04:10 -05:00
end
2017-08-28 11:24:56 -05:00
if version_value . empty?
version_value = default_value
end
version_value
2014-09-09 16:04:10 -05:00
end
2013-09-06 02:28:37 -05:00
# Either returns the site_contact_username user or the first admin.
def self . site_contact_user
2014-05-06 08:41:59 -05:00
user = User . find_by ( username_lower : SiteSetting . site_contact_username . downcase ) if SiteSetting . site_contact_username . present?
2015-11-24 13:37:33 -06:00
user || = ( system_user || User . admins . real . order ( :id ) . first )
2013-05-30 17:41:29 -05:00
end
2013-02-05 13:16:51 -06:00
2015-05-06 18:00:13 -05:00
SYSTEM_USER_ID || = - 1
2014-06-24 19:45:20 -05:00
2013-09-06 02:28:37 -05:00
def self . system_user
2016-04-25 16:03:17 -05:00
@system_user || = User . find_by ( id : SYSTEM_USER_ID )
2013-09-06 02:28:37 -05:00
end
2013-07-31 16:26:34 -05:00
def self . store
2017-10-06 00:20:01 -05:00
if SiteSetting . Upload . enable_s3_uploads
2013-07-31 16:26:34 -05:00
@s3_store_loaded || = require 'file_store/s3_store'
2013-11-05 12:04:47 -06:00
FileStore :: S3Store . new
2013-07-31 16:26:34 -05:00
else
@local_store_loaded || = require 'file_store/local_store'
2013-11-05 12:04:47 -06:00
FileStore :: LocalStore . new
2013-07-31 16:26:34 -05:00
end
end
2013-10-08 23:10:37 -05:00
def self . current_user_provider
@current_user_provider || Auth :: DefaultCurrentUserProvider
end
def self . current_user_provider = ( val )
@current_user_provider = val
end
2013-11-05 12:04:47 -06:00
def self . asset_host
Rails . configuration . action_controller . asset_host
end
2014-02-12 22:37:28 -06:00
def self . readonly_channel
2014-02-19 11:21:41 -06:00
" /site/read-only "
2013-02-05 13:16:51 -06:00
end
2014-02-12 22:37:28 -06:00
2014-03-27 21:48:14 -05:00
# all forking servers must call this
# after fork, otherwise Discourse will be
# in a bad state
def self . after_fork
2015-05-05 18:53:10 -05:00
# note: all this reconnecting may no longer be needed per https://github.com/redis/redis-rb/pull/414
2015-05-03 21:21:00 -05:00
MessageBus . after_fork
2014-03-27 21:48:14 -05:00
SiteSetting . after_fork
2018-04-20 00:01:17 -05:00
$redis . _client . reconnect
2014-03-27 21:48:14 -05:00
Rails . cache . reconnect
2014-05-07 17:05:28 -05:00
Logster . store . redis . reconnect
2014-04-22 20:01:17 -05:00
# shuts down all connections in the pool
2017-07-27 20:20:09 -05:00
Sidekiq . redis_pool . shutdown { | c | nil }
2014-04-22 20:01:17 -05:00
# re-establish
Sidekiq . redis = sidekiq_redis_config
2014-08-11 02:51:55 -05:00
start_connection_reaper
2016-07-16 00:11:34 -05:00
# in case v8 was initialized we want to make sure it is nil
PrettyText . reset_context
2016-11-01 21:34:20 -05:00
2016-11-02 00:59:58 -05:00
Tilt :: ES6ModuleTranspilerTemplate . reset_context if defined? Tilt :: ES6ModuleTranspilerTemplate
2016-11-01 21:34:20 -05:00
JsLocaleHelper . reset_context if defined? JsLocaleHelper
2014-05-07 17:05:28 -05:00
nil
2014-04-22 20:01:17 -05:00
end
2017-11-30 23:23:21 -06:00
# report a warning maintaining backtrack for logster
def self . warn_exception ( e , message : " " , env : nil )
if Rails . logger . respond_to? :add_with_opts
2018-01-04 16:54:28 -06:00
env || = { }
env [ :current_db ] || = RailsMultisite :: ConnectionManagement . current_db
2017-11-30 23:23:21 -06:00
# logster
Rails . logger . add_with_opts (
:: Logger :: Severity :: WARN ,
" #{ message } : #{ e } " ,
" discourse-exception " ,
backtrace : e . backtrace . join ( " \n " ) ,
env : env
)
else
# no logster ... fallback
Rails . logger . warn ( " #{ message } #{ e } " )
end
rescue
STDERR . puts " Failed to report exception #{ e } #{ message } "
end
2015-02-16 16:58:23 -06:00
def self . start_connection_reaper
return if GlobalSetting . connection_reaper_age < 1 ||
GlobalSetting . connection_reaper_interval < 1
2014-08-11 02:51:55 -05:00
# this helps keep connection counts in check
Thread . new do
while true
2015-02-16 16:58:23 -06:00
begin
sleep GlobalSetting . connection_reaper_interval
2015-10-16 19:29:16 -05:00
reap_connections ( GlobalSetting . connection_reaper_age , GlobalSetting . connection_reaper_max_age )
2015-02-16 16:58:23 -06:00
rescue = > e
2017-11-30 23:23:21 -06:00
Discourse . warn_exception ( e , message : " Error reaping connections " )
2014-08-11 02:51:55 -05:00
end
end
end
end
2015-10-16 19:29:16 -05:00
def self . reap_connections ( idle , max_age )
2015-02-16 16:58:23 -06:00
pools = [ ]
2017-07-27 20:20:09 -05:00
ObjectSpace . each_object ( ActiveRecord :: ConnectionAdapters :: ConnectionPool ) { | pool | pools << pool }
2015-02-16 16:58:23 -06:00
pools . each do | pool |
2018-06-14 00:54:48 -05:00
# reap recovers connections that were aborted
# eg a thread died or a dev forgot to check it in
pool . reap
2015-10-16 19:29:16 -05:00
pool . drain ( idle . seconds , max_age . seconds )
2015-02-16 16:58:23 -06:00
end
end
2016-12-04 21:46:34 -06:00
SIDEKIQ_NAMESPACE || = 'sidekiq' . freeze
2014-04-22 20:01:17 -05:00
def self . sidekiq_redis_config
2015-06-25 01:51:48 -05:00
conf = GlobalSetting . redis_config . dup
2016-12-04 21:46:34 -06:00
conf [ :namespace ] = SIDEKIQ_NAMESPACE
2015-06-25 01:51:48 -05:00
conf
2014-03-27 21:48:14 -05:00
end
2014-07-29 09:40:02 -05:00
def self . static_doc_topic_ids
[ SiteSetting . tos_topic_id , SiteSetting . guidelines_topic_id , SiteSetting . privacy_topic_id ]
end
2017-02-17 11:09:53 -06:00
cattr_accessor :last_ar_cache_reset
def self . reset_active_record_cache_if_needed ( e )
last_cache_reset = Discourse . last_ar_cache_reset
2017-07-27 20:20:09 -05:00
if e && e . message =~ / UndefinedColumn / && ( last_cache_reset . nil? || last_cache_reset < 30 . seconds . ago )
2018-01-18 15:32:15 -06:00
Rails . logger . warn " Clearing Active Record cache, this can happen if schema changed while site is running or in a multisite various databases are running different schemas. Consider running rake multisite:migrate. "
2017-02-17 11:09:53 -06:00
Discourse . last_ar_cache_reset = Time . zone . now
Discourse . reset_active_record_cache
end
end
def self . reset_active_record_cache
ActiveRecord :: Base . connection . query_cache . clear
2017-08-17 05:27:35 -05:00
( ActiveRecord :: Base . connection . tables - %w[ schema_migrations versions ] ) . each do | table |
2017-02-17 11:09:53 -06:00
table . classify . constantize . reset_column_information rescue nil
end
nil
end
2017-11-15 15:39:11 -06:00
def self . running_in_rack?
ENV [ " DISCOURSE_RUNNING_IN_RACK " ] == " 1 "
end
2013-02-05 13:16:51 -06:00
end