Class: TentSteak
- Inherits:
-
Object
- Object
- TentSteak
- Defined in:
- lib/tent_steak.rb
Overview
Application-level support for TentSteak.
Constant Summary collapse
- VERSION =
"0.3.0"- MEM_LOG =
"<mem_log>"- BASE_FEATURES =
Default TentSteak features.
[ :form, :html, :table ]
- ALL_FEATURES =
Array of all available TentSteak features.
BASE_FEATURES + [ :auth, :debug ]
- @@logger =
nil- @@injected =
[]
- @@features =
[]
Class Method Summary collapse
-
.add_local_paths(base_dir, lib_dirs) ⇒ Object
Load local dependencies by prepending to $LOAD_PATH.
-
.bootstrap(app_module, *features) ⇒ Object
Initialize TentSteak for a Camping application.
-
.controllers ⇒ Object
Retrieves controller helper modules for all loaded features.
-
.init_db(config_file, log_file = nil, model_hash = {}) ⇒ Object
Set up the database environment.
-
.inject_features(*features) ⇒ Object
Inject named TentSteak features into your Camping application.
-
.loaded_features ⇒ Object
Array of all currently loaded TentSteak features.
-
.logger ⇒ Object
Retrieves TentSteak logger.
-
.method_missing(sym, *args, &block) ⇒ Object
Check for feature query methods, e.g.
-
.run_app(app_module, app_base = "app") ⇒ Object
Runs the camping app.
-
.set_logging(log_file, log_level = Logger::INFO) ⇒ Object
Assign a TentSteak library logging file.
-
.view_inject(*helpers) ⇒ Object
Inject one or more external helper modules into the Application View.
-
.view_method_defined?(meth_sym) ⇒ Boolean
Determine if a method is defined in the Camping View context.
Class Method Details
.add_local_paths(base_dir, lib_dirs) ⇒ Object
Load local dependencies by prepending to $LOAD_PATH. Assumes each library has a subdirectory of ‘lib’ to add to the path, as is the case for most gems. Thus, calling
add_local_paths("extdir", %w{lib1 lib2})
would add the following to $LOAD_PATH:
"extdir/lib1/lib"
"extdir/lib2/lib"
Useful for using gem packages that haven’t been installed yet. For example:
> mkdir extdir
> cd extdir
> gem unpack mygem
TentSteak.add_local_paths("extdir", %w{mygem})
409 410 411 412 413 414 415 |
# File 'lib/tent_steak.rb', line 409 def self.add_local_paths(base_dir, lib_dirs) # Reverse order so libs appear (prepended) in $LOAD_PATH in the same order # that they appear in lib_dirs. lib_dirs.reverse.each do |library| $:.unshift File.(File.join(base_dir, library, "lib")) end end |
.bootstrap(app_module, *features) ⇒ Object
Initialize TentSteak for a Camping application. app_module is the root module for your Camping application, but like <tt>Camping.goes<tt/> it can also be expressed in symbol form. Thus, these invocations are functionally equivalent:
TentSteak.bootstrap MyApp
TentSteak.bootstrap :MyApp
For convenience, you can list any TentSteak features you want after the app module. If no features are given, #bootstrap implicitly loads the :base feature set.
# Implicitly load :base feature set (i.e. :form, :html, :table).
TentSteak.bootstrap :MyApp
# Explicitly load :base feature set.
TentSteak.bootstrap :MyApp, :base
# Explicitly load :html and :form features (i.e. omit :table from :base).
TentSteak.bootstrap :MyApp, :html, :form
To skip default features, pass in :none for the feature.
TentSteak.bootstrap :MyApp, :none
See #view_inject for details on TentSteak features.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/tent_steak.rb', line 177 def self.bootstrap(app_module, *features) @@rootmod = _to_mod(app_module) @@features = [] # Add :base if no features given. Ignore :none. features = BASE_FEATURES if features.empty? # features = [] if features.uniq == [:none] inject_features(*features) # Must log after feature injection because TSLogger.mem_log checks for # the presence of the :debug feature. logger.progname = app_module logger << "#{"="*75}\n" logger.info "Bootstrapping application #{app_module} with features: #{features.join(', ')}" end |
.controllers ⇒ Object
Retrieves controller helper modules for all loaded features. Useful for injecting feature support into controllers:
class MyController < R '/my_controller'
include *TentSteak.controllers
def get
...
end
end
134 135 136 137 138 |
# File 'lib/tent_steak.rb', line 134 def self.controllers loaded_features.map do |feature| TentSteakFeatures.const_get("#{feature.to_s.titleize}Controller") end end |
.init_db(config_file, log_file = nil, model_hash = {}) ⇒ Object
Set up the database environment. Loads ActiveRecord-style YAML database config info and optionally assigns an ActiveRecord log file. Pass in a block to globally tweak the configuration; pass config fragments inside model_hash to tweak the configuration per model base class.
The database info should reside in the “dbconfig” YAML block:
dbconfig:
adapter: mysql
database: my_app_db
host: localhost
username: myuser
password: mypass
By default, init_db will call establish_connection on ActiveRecord::Base with the settings in config_file:
TentSteak.init_db("dbconfig.yml")
Optionally assigns an ActiveRecord logging file if log_file is given. Passing a log_file of true will hook AR logging into the TentSteak logger; passing in a String filename will log to that file. Pass nil to disable AR logging.
TentSteak.init_db("dbconfig.yml", true)
TentSteak.init_db("dbconfig.yml", "my_app.log")
You can use model_hash to pass in additional custom config settings per model object. After initializing ActiveRecord::Base, init_db will call establish_connection on each model class in the hash. This is typically useful if you have more than one database to connect models to. The example below will connect SomeTable and AnotherTable to my_app_db via ActiveRecord::Base, and WayOverThereTable to other_db via OtherModelBase.
module MyApp::Models
# Alternate anchor point for other_db.
class OtherModelBase < Base; end
class SomeTable < Base; end
class AnotherTable < Base; end
class WayOverThereTable < OtherModelBase; end
end
if __FILE__ == $0
model_hash = { MyApp::Models::OtherModelBase => { "database" => "other_db" } }
TentSteak.init_db("dbconfig.yml", "my_app.log", model_hash)
TentSteak.run_app(MyApp)
end
To post-process the config file before connecting with it – for example adding or decrypting the password – pass in a block to init_db. The config block parameter will contain the hash loaded from the YAML config file.
model_hash = { MyApp::Models::OtherModelBase => { "database" => "other_db" } }
TentSteak.init_db("dbconfig.yml", "my_app.log", model_hash) do |config|
config[:my_prop] = "my_custom_value"
end
This example will add :my_prop to the config for ActiveRecord::Base and OtherModelBase, and set the database to “other_db” for OtherModelBase. Note that block config is applied first; any config values passed into model_hash will override those set in the config block.
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/tent_steak.rb', line 372 def self.init_db(config_file, log_file = nil, model_hash = {}) logger.info "Loading database configuration from config file '#{config_file}'" config = YAML.load_file(config_file)["dbconfig"] if block_given? yield config end ActiveRecord::Base.logger = (log_file == true) ? TentSteak.logger : Logger.new(log_file) # Always assign to Base; also assign configs in model_hash, if any. ActiveRecord::Base.establish_connection config unless model_hash.has_key? ActiveRecord::Base if model_hash.any? model_hash.each do |model_class, model_config| model_class.establish_connection(config.merge(model_config)) end end end |
.inject_features(*features) ⇒ Object
Inject named TentSteak features into your Camping application. Can be invoked indirectly by passing features into #bootstrap. Cannot be called before bootstrap.
Each feature is represented as a symbol, e.g. :html, :table. Injection consists of calling require on the feature source file, then calling view_inject on the feature’s view module.
The following features are distributed with TentSteak:
-
:auth- User authentication helpers -
:debug- Debug log helpers -
:form- HTML form helpers -
:html- HTML generation helpers -
:table- HTML table helpers -
:base- Feature set of :form, :html, and :table -
:all- Feature set of all built-in features
Bundled feature source files reside in the TentSteak distribution, in tent_steak-x.x.x/lib/tent_steak/[feature].rb. Applications can install their own feature files in ./tent_steak/[feature].rb, relative to $LOAD_PATH. Thus the source file for the :html built-in feature is at tent_steak-x.x.x/lib/tent_steak/html.rb, and an application’s :wowza feature would be at ./tent_steak/wowza.rb. NOTE: Custom features with identical names to built-in features (e.g. :html) are completely ignored.
The feature code should all reside in the ::TentSteakFeatures module, with view methods in a [Feature]View module and controller methods in a [Feature]Controller module. For example:
module TentSteakFeatures
module WowzaView
# view methods...
end
module WowzaController
# controller methods...
end
end
In the example above, calling TentSteak.inject_features :wowza would require the ./tent_steak/wowza.rb file, then call TentSteak.view_inject "TentSteakFeatures::WowzaView". The same happens for TentSteak.bootstrap :MyApp, :wowza. Any code outside those special modules will be loaded by the require, but will not be automatically injected into your Camping application.
The feature’s controller modules can be included explicitly, or in bulk with <tt>TentSteak.controllers<tt/>, as below, respectively:
class MyController < R '/my_controller'
include TentSteakFeatures::WowzaController # explicitly
include *TentSteak.controllers # all injected feature controllers
end
Features are only injected once; subsequent injections of loaded features are safely ignored. Thus, if your custom feature is dependent on other TentSteak features, you can add a (recursive) call to inject_features at the top of your feature source file:
TentSteak.inject_features :html, :form
module TentSteakFeatures
...
end
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/tent_steak.rb', line 259 def self.inject_features(*features) raise "Must call TentSteak.bootstrap before inject_features" if @@rootmod.nil? # Expand feature sets into actual features. real_features = features.map do |feature| case feature when :none then [] when :all then ALL_FEATURES when :base then BASE_FEATURES else feature end end.flatten.uniq # Load feature source files. # # Check tent_steak library path, then local path for feature file. # This allows local per-app feature extensions. Be nice and don't throw # exceptions if features or source files not found (but DO log an error). real_features.each do |feature| next if @@features.include? feature # Only inject a feature once. paths = _feature_paths(feature) source_file = paths.find { |file| File.exist? file } logger.error "TentSteak Feature #{feature.inspect} not found at #{paths.inspect}!" unless source_file if source_file && File.exist?(source_file) require source_file @@features << feature else real_features.delete feature end end # Found all valid features, so inject them into the Markaby view context. view_inject real_features.map { |feature| "TentSteakFeatures::#{feature.to_s.titleize}View" } end |
.loaded_features ⇒ Object
Array of all currently loaded TentSteak features.
107 108 109 |
# File 'lib/tent_steak.rb', line 107 def self.loaded_features @@features end |
.logger ⇒ Object
Retrieves TentSteak logger. Defaults to TSLogger.
102 103 104 |
# File 'lib/tent_steak.rb', line 102 def self.logger @@logger ||= TSLogger.new end |
.method_missing(sym, *args, &block) ⇒ Object
Check for feature query methods, e.g. html? returns true if the :html feature is activated.
113 114 115 116 117 118 119 120 121 122 |
# File 'lib/tent_steak.rb', line 113 def self.method_missing(sym, *args, &block) meth_name = sym.to_s[/(\w+)\?/, 1] # NOTE: loaded_features might contain user-contributed features, so # ALL_FEATURES might not list all possible valid features. if meth_name && (ALL_FEATURES + loaded_features).include?(meth_name.to_sym) loaded_features.include? meth_name.to_sym else super end end |
.run_app(app_module, app_base = "app") ⇒ Object
Runs the camping app. app_module is whatever you call Camping.goes with. app_base is the base directory to mount the application on, e.g. host.com/app_base (ignored in CGI mode).
Supports webrick, mongrel, and CGI modes. Pass in “webrick” as the first command-line parameter to run in a webrick instance, “mongrel” to run in Mongrel, and nothing to run in CGI mode (the default).
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/tent_steak.rb', line 425 def self.run_app(app_module, app_base = "app") case ARGV.first when "mongrel" puts "Running Mongrel in FCGI mode" require "mongrel/camping" app_module::Models::Base.threaded_connections = false server = Mongrel::Camping::start("0.0.0.0", 3301, "/#{app_base}/", app_module) puts "#{app_module} app is running at http://localhost:3301/#{app_base}/" server.run.join when "webrick" puts "Running in WEBrick mode" require 'webrick/httpserver' require 'camping/webrick' s = WEBrick::HTTPServer.new(:BindAddress => "0.0.0.0", :Port => 3301) s.mount("/#{app_base}", WEBrick::CampingHandler, app_module) trap(:INT) { s.shutdown } s.start when "fcgi" # FIXME: Add FCGI runner. when "dreamhost" # FIXME: Add Dreamhost FCGI runner. else # Default to CGI. puts app_module.run end end |
.set_logging(log_file, log_level = Logger::INFO) ⇒ Object
Assign a TentSteak library logging file. Turn off logging if log_file is nil.
92 93 94 95 96 97 98 99 |
# File 'lib/tent_steak.rb', line 92 def self.set_logging(log_file, log_level = Logger::INFO) if log_file.nil? @@logger = nil else @@logger = TSLogger.new(log_file) @@logger.level = log_level end end |
.view_inject(*helpers) ⇒ Object
Inject one or more external helper modules into the Application View.
TentSteak.view_inject MyViewHelper, MyOtherViewHelper
144 145 146 147 148 149 |
# File 'lib/tent_steak.rb', line 144 def self.view_inject(*helpers) raise "Must call TentSteak.bootstrap before view_inject" if @@rootmod.nil? logger.info "Injecting View helpers: #{helpers.join(', ')}" @@rootmod.module_eval "class Mab < Markaby::Builder; include #{helpers.join(', ')}; end" end |
.view_method_defined?(meth_sym) ⇒ Boolean
Determine if a method is defined in the Camping View context. Only meaningful after #bootstrap is called.
456 457 458 |
# File 'lib/tent_steak.rb', line 456 def self.view_method_defined?(meth_sym) @@rootmod ? @@rootmod::Mab.method_defined?(meth_sym) : false end |