Class: Kiss
- Defined in:
- lib/kiss.rb,
lib/kiss/form.rb,
lib/kiss/bench.rb,
lib/kiss/debug.rb,
lib/kiss/login.rb,
lib/kiss/model.rb,
lib/kiss/action.rb,
lib/kiss/format.rb,
lib/kiss/mailer.rb,
lib/kiss/request.rb,
lib/kiss/iterator.rb,
lib/kiss/template.rb,
lib/kiss/form/field.rb,
lib/kiss/static_file.rb,
lib/kiss/sequel_session.rb,
lib/kiss/exception_report.rb,
lib/kiss/accessors/request.rb,
lib/kiss/accessors/template.rb,
lib/kiss/ext/sequel_database.rb,
lib/kiss/accessors/controller.rb,
lib/kiss/ext/sequel_mysql_dataset.rb
Overview
Kiss - An MVC web application framework for Ruby, built on:
-
Erubis template engine
-
Sequel database ORM library
-
Rack web server abstraction
Defined Under Namespace
Modules: Bench, ControllerAccessors, DatabaseAccessors, Debug, KissAccessors, RequestAccessors, SequelDatabase, SequelMySQLDataset, TemplateMethods Classes: Action, ExceptionReport, FileNotFoundError, Form, Format, Iterator, Login, Mailer, Model, ModelCache, Request, SequelSession, StaticFile, Template
Constant Summary collapse
- MIME_TYPES =
These supplement the MIME types defined by Rack.
{ 'rhtml' => 'text/html' }
- @@default_action =
'index'
'Kiss'
- @@default_project_dir =
'.'
Class Method Summary collapse
-
.absolute_path(filename) ⇒ Object
Converts passed-in filename to absolute path if it does not start with ‘/’.
-
.context_class ⇒ Object
Finds the class defined by the file path of the execution context.
-
.mime_type(extension) ⇒ Object
Returns MIME type corresponding to passed-in extension.
- .rack(config = {}) ⇒ Object
-
.register_class_path(klass, path) ⇒ Object
Register a class by its file path, to enable context_class to work.
- .run(config = {}) ⇒ Object
Instance Method Summary collapse
-
#app_url(options = {}) ⇒ Object
Returns URL/URI of app root (corresponding to top level of action_dir).
-
#call(env) ⇒ Object
Creates new controller instance to handle Rack request.
-
#database ⇒ Object
(also: #db)
Acquires and returns a database connection object from the connection pool, opening a new connection if the pool is empty.
- #debug(obj, *args) ⇒ Object
-
#directory_exists?(dir) ⇒ Boolean
Returns true if specified path is a directory.
-
#evolution_file(index) ⇒ Object
Returns an array of evolution filenames (relative to project dir) matching evolution number specified by index.
-
#file_cache(path = nil, return_changed_state = false) ⇒ Object
TODO: Move file and directory caching to new class(es) TODO: File cache should store hashes of file info, keyed by path, instead of using separate hashes (cache time, contents, etc).
-
#initialize(options = {}) ⇒ Kiss
constructor
Creates a new application controller instance, and also configures the application from config file options and any passed-in options.
-
#last_evolution_file_number ⇒ Object
Gets the number of the last file in the evolution dir, or 0 if the directory does not exist.
- #load_db_class_extensions(db_class) ⇒ Object
- #login ⇒ Object
-
#models ⇒ Object
(also: #dbm)
Kiss Model cache, used to invoke and store Kiss database models.
- #new_database_connection(database_config) ⇒ Object
-
#new_email(options = {}) ⇒ Object
Returns new Kiss::Mailer object using specified options.
- #rack(config = nil) ⇒ Object
- #return_database(db) ⇒ Object
-
#run(options = nil) ⇒ Object
Runs Kiss application found at project_dir (default: ‘..’), with config read from config files plus additional options if passed in.
- #send_email(options = {}) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ Kiss
Creates a new application controller instance, and also configures the application from config file options and any passed-in options.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/kiss.rb', line 124 def initialize( = {}) # init config @_config = { :layout => '/_layout', :file_cache_reload => true } @_lib_dirs = ['lib'] @_gem_dirs = ['gems'] @_require = [] @_authenticate_exclude = ['/login', '/logout'] @_mailer_config = {} @_mailer_override = {} @_exception_handlers = {} @_exception_mailer_config = {} # store for cached files and directories @_file_cache = {} @_directory_cache = {} @_file_cache_time = {} # If options is string, then it specifies an environment # (else it should be a hash of config options) = { :environment => } if .is_a?(String) # project dir # all other files and directories are relative to the project dir Dir.chdir([:project_dir] || [:root_dir] || @@default_project_dir) # save current path to force return there in case an action changes directory @_project_dir = Dir.pwd # directory containing the config files @_config_dir = [:config_dir] || 'config' # get environment name from options or config/environment @_environment = [:environment] || if File.file?(env_file = @_config_dir + '/environment') File.read(env_file).sub(/\s+\Z/, '') end # read common (shared) config merge_config_file(@_config_dir + '/common.yml') # read environment config merge_config_file(@_config_dir + "/environments/#{@_environment}.yml") if @_environment # merge options passed in to override config files merge_config( ) # set app instance variables from config data and defaults @_action_dir = @_config[:action_dir] || 'actions' @_template_dir = (@_config[:template_dir] ? @_config[:template_dir] : @_action_dir) @_model_dir = @_config[:model_dir] || 'models' @_evolution_dir = @_config[:evolution_dir] || 'evolutions' @_asset_dir = @_public_dir = @_config[:asset_dir] || @_config[:public_dir] || 'public_html' @_email_template_dir = @_config[:email_template_dir] || 'email_templates' @_upload_dir = @_config[:upload_dir] || 'uploads' @_cookie_name = @_config[:cookie_name] || @@default_cookie_name @_default_action = @_config[:default_action] || @@default_action # exception log @_exception_log_file = @_config[:exception_log] ? ::File.open(@_config[:exception_log], 'a') : nil # authenticate all actions? @_authenticate_all = @_config[:authenticate_all] # don't require authentication on exception actions @_authenticate_exclude << @_config.exception_action if @_config.exception_action @_authenticate_exclude << @_config.file_not_found_action if @_config.file_not_found_action # app host: default hostname of application @_app_host = @_config[:app_host] # app uri: default URI of application @_app_uri = @_config[:app_uri] # asset host: hostname of static assets @_asset_host = @_config[:asset_host] # public_uri: URI of requests to serve from public_dir @_asset_uri = @_config[:asset_uri] || @_config[:public_uri] || nil @_rack_file = Rack::File.new(@_asset_dir) if @_asset_uri # add lib dirs to load path $LOAD_PATH.unshift(*( @_lib_dirs.flatten.select {|dir| File.directory?(dir) } )) # add gem dirs to rubygems search path Gem.path.unshift(*( @_gem_dirs.flatten.select {|dir| File.directory?(dir) } )) # require specified libs @_require.flatten.each {|lib| require lib } # session class @_session_class = @_config[:session_class] @_session_class = @_session_class.to_const if @_session_class && !@_session_class.is_a?(Class) # database @_database_config = @_config[:database] @_database_pool = [] self end |
Class Method Details
.absolute_path(filename) ⇒ Object
Converts passed-in filename to absolute path if it does not start with ‘/’.
95 96 97 |
# File 'lib/kiss.rb', line 95 def absolute_path(filename) ( filename[0,1] == '/' ) ? filename : "#{Dir.pwd}/#{filename}" end |
.context_class ⇒ Object
Finds the class defined by the file path of the execution context.
112 113 114 115 116 117 118 |
# File 'lib/kiss.rb', line 112 def context_class caller.each do |frame| if klass = @_classes[frame.sub(/\:.*/, '')] return klass end end end |
.mime_type(extension) ⇒ Object
Returns MIME type corresponding to passed-in extension.
100 101 102 103 104 |
# File 'lib/kiss.rb', line 100 def mime_type(extension) extension = extension.to_s rack_mime_types = Rack::Mime::MIME_TYPES rescue Rack::File::MIME_TYPES rack_mime_types[extension] || rack_mime_types['.' + extension] || Kiss::MIME_TYPES[extension] end |
.rack(config = {}) ⇒ Object
86 87 88 |
# File 'lib/kiss.rb', line 86 def rack(config = {}) self.new(config).rack end |
.register_class_path(klass, path) ⇒ Object
Register a class by its file path, to enable context_class to work.
107 108 109 |
# File 'lib/kiss.rb', line 107 def register_class_path(klass, path) @_classes[path] = klass end |
.run(config = {}) ⇒ Object
90 91 92 |
# File 'lib/kiss.rb', line 90 def run(config = {}) self.new(config).run end |
Instance Method Details
#app_url(options = {}) ⇒ Object
Returns URL/URI of app root (corresponding to top level of action_dir). Part of Kiss class to be available to kiss irb.
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
# File 'lib/kiss.rb', line 517 def app_url( = {}) # cache return values by unique options input @_app_url_cache ||= {} @_app_url_cache[.inspect] ||= begin url_settings = { :protocol => @_protocol || 'http', :host => @_app_host, :uri => @_app_uri }.merge() raise 'host missing' unless url_settings[:host] "#{url_settings[:protocol]}://#{url_settings[:host]}#{url_settings[:uri]}" end end |
#call(env) ⇒ Object
Creates new controller instance to handle Rack request.
342 343 344 |
# File 'lib/kiss.rb', line 342 def call(env) Kiss::Request.new(env, self, @_config).call(env) end |
#database ⇒ Object Also known as: db
Acquires and returns a database connection object from the connection pool, opening a new connection if the pool is empty.
354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/kiss.rb', line 354 def database @_database_pool.shift || begin raise 'database config missing' unless @_database_config # open database connection db = new_database_connection(@_database_config) # create model cache for this database connection db.kiss_controller = self db.kiss_model_cache = Kiss::ModelCache.new(db, @_model_dir) db end end |
#debug(obj, *args) ⇒ Object
537 538 539 |
# File 'lib/kiss.rb', line 537 def debug(obj, *args) gdebug obj end |
#directory_exists?(dir) ⇒ Boolean
Returns true if specified path is a directory. Always check filesystem if file_cache_reload option is set; otherwise, cache result.
437 438 439 440 441 442 443 |
# File 'lib/kiss.rb', line 437 def directory_exists?(dir) @_config[:file_cache_reload] ? File.directory?(dir) : ( @_directory_cache.has_key?(dir) ? @_directory_cache[dir] : @_directory_cache[dir] = File.directory?(dir) ) end |
#evolution_file(index) ⇒ Object
Returns an array of evolution filenames (relative to project dir) matching evolution number specified by index.
423 424 425 426 427 428 429 430 431 432 |
# File 'lib/kiss.rb', line 423 def evolution_file(index) # find files matching ev_dir/.*next_version_number files = Dir.glob("#{@_evolution_dir}/*#{index}[^0-9]*"). # make sure we have a match for ev_dir/0*next_version_number select {|f| f =~ /\/0*#{index}[_\.][^\/]*\Z/ } raise "multiple evolution files for evolution number #{index}" if files.size > 1 files[0] end |
#file_cache(path = nil, return_changed_state = false) ⇒ Object
TODO: Move file and directory caching to new class(es) TODO: File cache should store hashes of file info, keyed by path, instead of using separate hashes (cache time, contents, etc)
TODO: FIX BUG: The file cache keeps old classes after they are removed from the action/model class hierarchies. Will the class hierarchies force reload these paths, or get the old cached versions? Answer: They reload the classes because they only cache the source text in the file cache. They cache the classes in the hierarchy.
TODO: Need a generic class for Action/Template/Model class hierarchy caches.
Given a file path, caches or returns the file’s contents or the return value of the passed block applied to the file’s contents. If file is not found, the file’s contents are nil.
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 |
# File 'lib/kiss.rb', line 460 def file_cache(path = nil, return_changed_state = false) return @_file_cache unless path cache_changed = false if @_file_cache.has_key?(path) # we've loaded this path before if @_config[:file_cache_reload] # check to see if there's been a change that needs to be reloaded if !File.file?(path) if @_file_cache_time[path] # file cached as existing but has been removed; update cache to show no file cache_changed = true contents = nil end elsif !@_file_cache_time[path] || @_file_cache_time[path] < File.mtime(path) || ( File.symlink?(path) && (@_file_cache_time[path] < File.lstat(path).mtime) ) # cache shows file missing, or file has been modified since cached cache_changed = true contents = File.read(path) end end else # haven't loaded this path yet cache_changed = true if !File.file?(path) # nil path, of file doesn't exist contents = nil else # file exists; mark cache time and read file contents = File.read(path) end end if cache_changed @_file_cache_time[path] = contents ? Time.now : nil @_file_cache[path] = block_given? ? yield(contents) : contents end return_changed_state ? [@_file_cache[path], cache_changed] : @_file_cache[path] end |
#last_evolution_file_number ⇒ Object
Gets the number of the last file in the evolution dir, or 0 if the directory does not exist.
407 408 409 410 411 412 413 414 415 416 417 418 419 |
# File 'lib/kiss.rb', line 407 def last_evolution_file_number version = 0 if directory_exists?(@_evolution_dir) digits = 1 while ( entries = Dir.glob("#{@_evolution_dir}/#{'[0-9]' * digits}*") ).size > 0 version = entries.sort.last.sub(/.*\//, '').sub(/\D.*/, '').to_i digits += 1 end end version end |
#load_db_class_extensions(db_class) ⇒ Object
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/kiss.rb', line 370 def load_db_class_extensions(db_class) @_db_class_extensions_loaded ||= {} @_db_class_extensions_loaded[db_class] ||= begin db_class.class_eval { include Kiss::SequelDatabase } if db_class.name == 'Sequel::MySQL::Database' # add fetch_arrays, all_arrays methods Sequel::MySQL::Dataset.class_eval { include Kiss::SequelMySQLDataset } # turn off convert_tinyint_to_bool, unless app config says otherwise Sequel::MySQL. = false unless @_config[:convert_tinyint_to_bool] end require 'kiss/model' true end end |
#login ⇒ Object
533 534 535 |
# File 'lib/kiss.rb', line 533 def login {} end |
#models ⇒ Object Also known as: dbm
Kiss Model cache, used to invoke and store Kiss database models.
Example: models == database model for ‘users’ table
Tip: ‘dbm’ (stands for ‘database models’) is a shorthand alias for ‘models’.
394 395 396 397 398 |
# File 'lib/kiss.rb', line 394 def models # make sure we have a database connection # create new model cache unless exists already db.kiss_model_cache end |
#new_database_connection(database_config) ⇒ Object
346 347 348 349 350 |
# File 'lib/kiss.rb', line 346 def new_database_connection(database_config) db = Sequel.connect database_config load_db_class_extensions(db.class) db end |
#new_email(options = {}) ⇒ Object
Returns new Kiss::Mailer object using specified options.
504 505 506 507 508 509 |
# File 'lib/kiss.rb', line 504 def new_email( = {}) Kiss::Mailer.new({ :controller => self, :request => self }.merge()) end |
#rack(config = nil) ⇒ Object
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/kiss.rb', line 305 def rack(config = nil) merge_config(config) app = self = @_config[:rack_builder] || [] rack = Rack::Builder.new do .each do |builder_option| if builder_option.is_a?(Array) builder_args = builder_option builder_option = builder_args.shift else builder_args = [] end unless builder_option.is_a?(Class) builder_option = Rack.const_get(builder_option.to_s) end use(builder_option, *builder_args) end run app end.to_app end |
#return_database(db) ⇒ Object
401 402 403 |
# File 'lib/kiss.rb', line 401 def return_database(db) @_database_pool.push(db) end |
#run(options = nil) ⇒ Object
Runs Kiss application found at project_dir (default: ‘..’), with config read from config files plus additional options if passed in.
332 333 334 335 336 337 338 339 |
# File 'lib/kiss.rb', line 332 def run( = nil) merge_config() handler = @_config[:rack_handler] || Rack::Handler::WEBrick handler = Rack::Handler.const_get(handler.to_s) unless handler.is_a?(Class) handler.run(rack, @_config[:rack_handler_options] || {:Port => 4000}) end |
#send_email(options = {}) ⇒ Object
511 512 513 |
# File 'lib/kiss.rb', line 511 def send_email( = {}) new_email().send end |