require 'padrino-core/application/rendering'
require 'padrino-core/application/routing'
require 'padrino-core/application/flash'
require 'padrino-core/application/showexceptions'
module Padrino
class ApplicationSetupError < RuntimeError # @private
end
##
# Subclasses of this become independent Padrino applications (stemming from Sinatra::Application)
# These subclassed applications can be easily mounted into other Padrino applications as well.
#
class Application < Sinatra::Base
# Support for advanced routing, controllers, url_for
register Padrino::Routing
##
# Returns the logger for this application.
#
# @return [Padrino::Logger] Logger associated with this app.
#
def logger
Padrino.logger
end
class << self
def inherited(base) # @private
begun_at = Time.now
CALLERS_TO_IGNORE.concat(PADRINO_IGNORE_CALLERS)
base.default_configuration!
base.prerequisites.concat([
File.join(base.root, '/models.rb'),
File.join(base.root, '/models/**/*.rb'),
File.join(base.root, '/lib.rb'),
File.join(base.root, '/lib/**/*.rb')
]).uniq!
Padrino.require_dependencies(base.prerequisites)
logger.devel :setup, begun_at, base
super(base) # Loading the subclass inherited method
end
##
# Reloads the application files from all defined load paths
#
# This method is used from our Padrino Reloader during development mode
# in order to reload the source files.
#
# @return [TrueClass]
#
# @example
# MyApp.reload!
#
def reload!
logger.devel "Reloading #{settings}"
reset! # Reset sinatra app
reset_router! # Reset all routes
Padrino.require_dependencies(settings.app_file, :force => true) # Reload the app file
require_dependencies # Reload dependencies
default_filters! # Reload filters
default_routes! # Reload default routes
default_errors! # Reload our errors
I18n.reload! if defined?(I18n) # Reload also our translations
true
end
##
# Resets application routes to only routes not defined by the user
#
# @return [TrueClass]
#
# @example
# MyApp.reset_routes!
#
def reset_routes!
reset_router!
default_routes!
true
end
##
# Returns the routes of our app.
#
# @example
# MyApp.routes
#
def routes
router.routes
end
##
# Setup the application by registering initializers, load paths and logger
# Invoked automatically when an application is first instantiated
#
# @return [TrueClass]
#
def setup_application!
return if @_configured
settings.require_dependencies
settings.default_filters!
settings.default_routes!
settings.default_errors!
if defined?(I18n)
I18n.load_path << settings.locale_path
I18n.reload!
end
@_configured = true
@_configured
end
##
# Run the Padrino app as a self-hosted server using
# Thin, Mongrel or WEBrick (in that order)
#
# @see Padrino::Server#start
#
def run!(options={})
return unless Padrino.load!
Padrino.mount(settings.to_s).to('/')
Padrino.run!(options)
end
##
# @return [Array]
# directory that need to be added to +$LOAD_PATHS+ from this application
#
def load_paths
@_load_paths ||= %w[models lib mailers controllers helpers].map { |path| File.join(settings.root, path) }
end
##
# Returns default list of path globs to load as dependencies
# Appends custom dependency patterns to the be loaded for your Application
#
# @return [Array]
# list of path globs to load as dependencies
#
# @example
# MyApp.dependencies << "#{Padrino.root}/uploaders/**/*.rb"
# MyApp.dependencies << Padrino.root('other_app', 'controllers.rb')
#
def dependencies
[
'urls.rb', 'config/urls.rb', 'mailers/*.rb', 'mailers.rb',
'controllers/**/*.rb', 'controllers.rb', 'helpers/**/*.rb', 'helpers.rb'
].map { |file| Dir[File.join(settings.root, file)] }.flatten
end
##
# An array of file to load before your app.rb, basically are files wich our app depends on.
#
# By default we look for files:
#
# # List of default files that we are looking for:
# yourapp/models.rb
# yourapp/models/**/*.rb
# yourapp/lib.rb
# yourapp/lib/**/*.rb
#
# @example Adding a custom perequisite
# MyApp.prerequisites << Padrino.root('my_app', 'custom_model.rb')
#
def prerequisites
@_prerequisites ||= []
end
protected
##
# Defines default settings for Padrino application
#
def default_configuration!
# Overwriting Sinatra defaults
set :app_file, File.expand_path(caller_files.first || $0) # Assume app file is first caller
set :environment, Padrino.env
set :reload, Proc.new { development? }
set :logging, Proc.new { development? }
set :method_override, true
set :sessions, false
set :public_folder, Proc.new { Padrino.root('public', uri_root) }
set :views, Proc.new { File.join(root, 'views') }
set :images_path, Proc.new { File.join(public_folder, 'images') }
set :protection, true
# Haml specific
set :haml, { :ugly => (Padrino.env == :production) } if defined?(Haml)
# Padrino specific
set :uri_root, '/'
set :app_name, settings.to_s.underscore.to_sym
set :default_builder, 'StandardFormBuilder'
set :authentication, false
# Padrino locale
set :locale_path, Proc.new { Dir[File.join(settings.root, '/locale/**/*.{rb,yml}')] }
# Authenticity token
set :protect_from_csrf, false
set :allow_disabled_csrf, false
# Load the Global Configurations
class_eval(&Padrino.apps_configuration) if Padrino.apps_configuration
end
##
# We need to add almost __sinatra__ images.
#
def default_routes!
configure :development do
get '*__sinatra__/:image.png' do
content_type :png
filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
send_file filename
end
end
end
##
# This filter it's used for know the format of the request, and automatically set the content type.
#
def default_filters!
before do
unless @_content_type
@_content_type = :html
response['Content-Type'] = 'text/html;charset=utf-8'
end
end
end
##
# This log errors for production environments
#
def default_errors!
configure :production do
error ::Exception do
boom = env['sinatra.error']
logger.error ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n ")
response.status = 500
content_type 'text/html'
'<h1>Internal Server Error</h1>'
end unless errors.has_key?(::Exception)
end
end
##
# Requires all files within the application load paths
#
def require_dependencies
Padrino.set_load_paths(*load_paths)
Padrino.require_dependencies(dependencies, :force => true)
end
private
# Overrides the default middleware for Sinatra based on Padrino conventions
# Also initializes the application after setting up the middleware
def setup_default_middleware(builder)
setup_sessions builder
builder.use Padrino::ShowExceptions if show_exceptions?
builder.use Padrino::Logger::Rack, uri_root if Padrino.logger && logging?
builder.use Padrino::Reloader::Rack if reload?
builder.use Rack::MethodOverride if method_override?
builder.use Rack::Head
register Padrino::Flash
setup_protection builder
setup_csrf_protection builder
setup_application!
end
# sets up csrf protection for the app
def setup_csrf_protection(builder)
if protect_from_csrf? && !sessions?
raise(<<-ERROR)
`protect_from_csrf` is activated, but `sessions` are not. To enable csrf
protection, use:
enable :sessions
or deactivate protect_from_csrf:
disable :protect_from_csrf
ERROR
end
if protect_from_csrf?
if allow_disabled_csrf?
builder.use Rack::Protection::AuthenticityToken,
:reaction => :report,
:report_key => 'protection.csrf.failed',
:logger => logger
else
builder.use Rack::Protection::AuthenticityToken,
:logger => logger
end
end
end
end # self
end # Application
end # Padrino