actually add new padrino

This commit is contained in:
Thomas Reynolds 2013-11-22 08:47:07 -08:00
parent 48db12429a
commit 6b6e9b143d
201 changed files with 20182 additions and 0 deletions

View file

@ -0,0 +1,5 @@
lib/**/*.rb
bin/*
-
README.rdoc
LICENSE.txt

View file

@ -0,0 +1,22 @@
## MAC OS
.DS_Store
## TEXTMATE
*.tmproj
tmtags
## EMACS
*~
\#*
.\#*
## VIM
*.swp
## PROJECT::GENERAL
coverage
rdoc
pkg
## PROJECT::SPECIFIC
test/tmp/*

View file

@ -0,0 +1 @@
--title 'Padrino Core Documentation' --protected

View file

@ -0,0 +1,20 @@
Copyright (c) 2011 Padrino
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,294 @@
= Padrino (padrino-core)
Padrino is the godfather of Sinatra.
== Preface
Padrino is a ruby framework built upon the excellent {Sinatra Microframework}[http://www.sinatrarb.com].
Sinatra is a DSL for creating simple web applications in Ruby with speed and minimal effort.
This framework tries hard to make it as fun and easy as possible to code much more advanced web applications by
building upon the Sinatra philosophies and foundation.
== Introduction
Many people love Sinatra's simplicity and lightweight but often quickly come to miss a great deal
of functionality provided by other web frameworks such as Rails when building non-trivial applications.
Our goal with this framework is to match the essence of Sinatra and at the same time create a standard library
of tools, helpers and components that will make Sinatra suitable for more complex applications.
Here is a brief overview of functionality provided by the Padrino framework:
Agnostic:: Full support for many popular testing, templating, mocking, and data storage choices.
Generators:: Create Padrino applications, models, controllers i.e: padrino-gen project.
Mountable:: Unlike other ruby frameworks, principally designed for mounting multiple apps.
Routing:: Full url named routes, named params, before/after filter support.
Tag Helpers:: View helpers such as: tag, content_tag, input_tag.
Asset Helpers:: View helpers such as: link_to, image_tag, javascript_include_tag.
Form Helpers:: Builder support such as: form_tag, form_for, field_set_tag, text_field.
Text Helpers:: Useful formatting like: time_ago_in_words, js_escape_html, sanitize_html.
Mailer:: Fast and simple delivery support for sending emails (akin to ActionMailer).
Admin:: Builtin Admin interface (like Django)
Logging:: Provide a unified logger that can interact with your ORM or any library.
Reloading:: Automatically reloads server code during development.
Localization:: Full support of I18n language localization and can auto-set user's locale.
Keep in mind, the user will be able to pull in these components
{seperately into existing Sinatra applications}[http://www.padrinorb.com/guides/standalone-usage-in-sinatra]
or use them altogether for a comprehensive upgrade to Sinatra (a full-stack Padrino application).
== Installation
To install the padrino framework, simply grab the latest version from gemcutter:
$ sudo gem install padrino
This will install the necessary padrino gems to get you started.
Now you are ready to use this gem to enhance your sinatra projects or to create new Padrino applications.
For a more detailed look at Padrino installation,
check out the {Installation Guide}[http://www.padrinorb.com/guides/installation].
== Usage
Padrino is a framework which builds on the existing functionality and Sinatra and provides a variety of
additional tools and helpers to build upon that foundation. This README and Padrino documentation in general will focus
on the enhancements to the core Sinatra functionality. To use Padrino, one should be familiar with the basic
usage of Sinatra itself.
Please check out the
{Understanding Sinatra}[http://www.padrinorb.com/guides/underlying-sinatra-overview] guide
to learn more about these fundamentals.
For information on how to use a specific gem in isolation within an existing Sinatra project, checkout the guide for
{Using Padrino in Sinatra}[http://www.padrinorb.com/guides/standalone-usage-in-sinatra].
== Getting Started
Once a developer understands Sinatra, Padrino is quite easy to get comfortable with since Padrino is simply a superset
of existing Sinatra Functionality! Best way to get started with building Padrino applications is to read following resources:
* {Blog Tutorial}[http://www.padrinorb.com/guides/blog-tutorial] - Step-by-step guide to building a blog application with Padrino.
* {Quick Overview}[http://www.padrinorb.com/guides/basic-projects] - Outlines basic generation commands.
* {Padrino Examples}[http://www.padrinorb.com/guides/examples] - List of known Padrino applications which can serve as examples.
== Enhanced Base Application (padrino-core)
Sinatra has support for classes which can be extended to create an application: <tt>Sinatra::Base</tt> and <tt>Sinatra::Application</tt>
These classes can be extended in order to create a Sinatra web application. These classes provide support for all the basic
functionality afforded by Sinatra.
Padrino has support for an enhanced base application class <tt>Padrino::Application</tt>. <tt>Padrino::Application</tt>
expands the capabilities of Sinatra::Application and automatically provides the resulting application access to all of
the padrino framework's functionalities.
=== Simple Application Definition
Let us first take a look at the simplest possible Padrino application:
# app.rb
PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
require 'padrino'
Padrino.load!
class SimpleApp < Padrino::Application
get '/' do
'Hello world'
end
# and for read better we can divide with controllers
controller '/admin' do
get '/foo' do
'Url is /admin/foo'
end
end
end
=== Enhanced Route Definitions and Controllers
For a complete overview of the Padrino routing and controller system,
check out the {Routing and Controller guide}[http://www.padrinorb.com/guides/controllers].
Suppose we wanted to add additional routes to our Padrino application, and we want to organize the routes
within a more structured layout. Simply add a <tt>controllers</tt> or <tt>app/controllers</tt> folder and create a file as such:
# Simple Example
SimpleApp.controllers do
get "/test" do
"Text to return"
end
end
You can also do more complex route alias definitions:
# app/controllers/example.rb
SimpleApp.controllers :posts do
get :index do
...
end
get :show, :with => :id do
# url generated is '/posts/show/:id'
# access params[:id]
end
end
as well as mapping the route aliases to an explicit url:
# app/controllers/example.rb
SimpleApp.controllers do
get :index, :map => '/index' do
...
end
get :account, :map => '/the/accounts/:name/and/:id' do
# access params[:name] and params[:index]
end
end
and even configure the +provides+ for each route:
# app/controllers/example.rb
SimpleApp.controllers :admin do
get :show, :with => :id, :provides => :js do
"Url is /admin/show/#{params[:id]}.#{params[:format]}"
end
get :other, :with => [:id, :name], :provides => [:html, :json] do
case content_type
when :js then ... end
when :json then ... end
end
end
end
or auto lookup for current locale or content_type
# app/controllers/example.rb
SimpleApp.controllers :admin do
get :show, :with => :id, :provides => [:html, :js] do
render "admin/show"
end
end
When you visit :+show+ and your I18n.locale == :ru Padrino try to look for "admin/show.ru.js.*" if nothing match that path
they try "admin/show.ru.*" then "admin/show.js.*" if none match return "admin/show.erb" (or other engine i.e. haml)
For a complete overview of the routing and controller system, check out the
{Routing and Controller guide}[http://www.padrinorb.com/guides/controllers].
=== Rendering
Unlike Sinatra, Padrino supports automatic template lookups such as:
# searches for 'account/index.{erb,haml,...}
render 'account/index'
This render does not require any template engine to be specified and will choose the first one that is discovered.
The existing render function works as well if an engine type should be specified:
# example.haml
render :haml, 'account/index'
For a complete overview of the Padrino rendering system, check out the
{Routing and Controller guide}[http://www.padrinorb.com/guides/controllers].
=== Layout
With Padrino you can (like rails do) use for your custom layout, disable it
class SimpleApp < Padrino::Application
# Disable layouts
disable layout
# Use the layout located in views/layouts/custom.haml
layout :custom
For a complete overview of the layout functionality,
check out the {Routing and Controller guide}[http://www.padrinorb.com/guides/controllers].
=== Mounting Applications
Padrino applications are all automatically mountable into other Padrino projects. This means that a given Padrino
project directory can easily mount multiple applications. This allows for better organization of complex applications,
re-usable applications that can be applied (i.e admin, auth, blog) and even more flexibility.
You can think of mountable applications as a 'full-featured' merb slice or rails engine. Instead of a separate construct,
any application can simply be packaged and mounted into another project.
Padrino stores application mounting information by default within <tt>config/apps.rb</tt>. This file is intended
to keep all information regarding what applications are mounted to which uri's.
For a complete look at mounting applications within a Padrino project,
check out the guide on {Mounting Applications}[http://www.padrinorb.com/guides/mounting-applications].
=== Auto Load Paths
Padrino also intelligently supports requiring useful files within your application automatically and provides
functionality for easily splitting up your application into separate files. Padrino automatically requires <tt>config/database.rb</tt>
as a convention for establishing database connection. Also, any files within the <tt>lib</tt> folder will be required
automatically by Padrino.
For a complete overview of auto-loaded paths within Padrino,
check out the {Padrino Development Guide}[http://www.padrinorb.com/guides/development-and-terminal-commands].
=== Application Logging
Padrino also supports robust logging capabilities. By default, logging information will
go to the STDOUT in development (for use in a console) and in an environment-specific log file <tt>log/development.log</tt>
in test and production environments.
To use the logger within a Padrino application, simply refer to the <tt>logger</tt> method accessible
within your app and any controller or views:
# controllers/example.rb
SimpleApp.controllers do
get("/test") { logger.info "This is a test" }
end
For a complete overview of Padrino logger functionality, check out the
{Padrino Development Guide}[http://www.padrinorb.com/guides/development-and-terminal-commands].
=== Development Reloader
Padrino applications also have the enabled ability to automatically reload all changing application files without
the need to restart the server. Through the use of a customized Rack middleware, all files on the 'load path'
are monitored and reloaded whenever changes are applied.
This makes rapid development much easier and provides a better alternative to 'shotgun' or 'rerun'
which requires the application server to be restarted which makes requests take much longer to complete.
For a complete overview of code reloading in development,
check out the {Padrino Development Guide}[http://www.padrinorb.com/guides/development-and-terminal-commands].
=== Terminal Commands
Padrino also comes equipped with multiple useful terminal commands which can be activated to perform
common tasks such as starting / stopping the application, executing the unit tests or activating an irb session.
The following commands are available:
# starts the app server (non-daemonized)
$ padrino start
# starts the app server (daemonized) with given port, environment and adapter
$ padrino start -d -p 3000 -e development -a thin
# Stops a daemonized app server
$ padrino stop
# Bootup the Padrino console (irb)
$ padrino console
# Run/List tasks
$ padrino rake
You can also create custom rake tasks as well. Using these commands can simplify common tasks
making development that much smoother.
For a complete overview of Padrino terminal commands, check out the
{Padrino Commands Guide}[http://www.padrinorb.com/guides/development-and-terminal-commands].
== Copyright
Copyright (c) 2011-2013 Padrino. See LICENSE for details.

View file

@ -0,0 +1,5 @@
# coding:utf-8
RAKE_ROOT = __FILE__
require 'rubygems'
require File.expand_path(File.dirname(__FILE__) + '/../gem_rake_helper')

View file

@ -0,0 +1,9 @@
#!/usr/bin/env ruby
require 'rubygems' unless defined?(Gem)
require 'bundler/setup' if %w(Gemfile .components).all? { |f| File.exist?(f) }
padrino_core_path = File.expand_path('../../lib', __FILE__)
$:.unshift(padrino_core_path) if File.directory?(padrino_core_path) && !$:.include?(padrino_core_path)
require 'padrino-core/cli/base'
Padrino::Cli::Base.start(ARGV)

View file

@ -0,0 +1,200 @@
require 'sinatra/base'
require 'padrino-core/version'
require 'padrino-core/support_lite'
require 'padrino-core/application'
require 'padrino-core/caller'
require 'padrino-core/command'
require 'padrino-core/loader'
require 'padrino-core/logger'
require 'padrino-core/mounter'
require 'padrino-core/reloader'
require 'padrino-core/router'
require 'padrino-core/server'
require 'padrino-core/tasks'
require 'padrino-core/module'
PADRINO_ENV = ENV["PADRINO_ENV"] ||= ENV["RACK_ENV"] ||= "development" unless defined?(PADRINO_ENV)
PADRINO_ROOT = ENV["PADRINO_ROOT"] ||= File.dirname(Padrino.first_caller) unless defined?(PADRINO_ROOT)
module Padrino
class ApplicationLoadError < RuntimeError # @private
end
class << self
##
# Helper method for file references.
#
# @param [Array<String>] args
# The directories to join to {PADRINO_ROOT}.
#
# @return [String]
# The absolute path.
#
# @example
# # Referencing a file in config called settings.yml
# Padrino.root("config", "settings.yml")
# # returns PADRINO_ROOT + "/config/setting.yml"
#
def root(*args)
File.expand_path(File.join(PADRINO_ROOT, *args))
end
##
# Helper method that return {PADRINO_ENV}.
#
# @return [Symbol]
# The Padrino Environment.
#
def env
@_env ||= PADRINO_ENV.to_s.downcase.to_sym
end
##
# The resulting rack builder mapping each 'mounted' application.
#
# @return [Padrino::Router]
# The router for the application.
#
# @raise [ApplicationLoadError]
# No applications were mounted.
#
def application
raise ApplicationLoadError, "At least one app must be mounted!" unless Padrino.mounted_apps && Padrino.mounted_apps.any?
router = Padrino::Router.new
Padrino.mounted_apps.each { |app| app.map_onto(router) }
if middleware.present?
builder = Rack::Builder.new
middleware.each { |c,a,b| builder.use(c, *a, &b) }
builder.run(router)
builder.to_app
else
router
end
end
##
# Configure Global Project Settings for mounted apps. These can be overloaded
# in each individual app's own personal configuration. This can be used like:
#
# @yield []
# The given block will be called to configure each application.
#
# @example
# Padrino.configure_apps do
# enable :sessions
# disable :raise_errors
# end
#
def configure_apps(&block)
return unless block_given?
@@_global_configurations ||= []
@@_global_configurations << block
@_global_configuration = lambda do |app|
@@_global_configurations.each do |configuration|
app.class_eval(&configuration)
end
end
end
##
# Returns project-wide configuration settings defined in
# {configure_apps} block.
#
def apps_configuration
@_global_configuration
end
##
# Set +Encoding.default_internal+ and +Encoding.default_external+
# to +Encoding::UFT_8+.
#
# Please note that in +1.9.2+ with some template engines like +haml+
# you should turn off Encoding.default_internal to prevent problems.
#
# @see https://github.com/rtomayko/tilt/issues/75
#
# @return [NilClass]
#
def set_encoding
if RUBY_VERSION < '1.9'
$KCODE='u'
else
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
end
nil
end
##
# A Rack::Builder object that allows to add middlewares in front of all
# Padrino applications.
#
# @return [Array<Array<Class, Array, Proc>>]
# The middleware classes.
#
def middleware
@middleware ||= []
end
##
# Clears all previously configured middlewares.
#
# @return [Array]
# An empty array
#
def clear_middleware!
@middleware = []
end
##
# Convenience method for adding a Middleware to the whole padrino app.
#
# @param [Class] m
# The middleware class.
#
# @param [Array] args
# The arguments for the middleware.
#
# @yield []
# The given block will be passed to the initialized middleware.
#
def use(m, *args, &block)
middleware << [m, args, block]
end
##
# Registers a gem with padrino. This relieves the caller from setting up
# loadpaths by itself and enables Padrino to look up apps in gem folder.
#
# The name given has to be the proper gem name as given in the gemspec.
#
# @param [String] name
# The name of the gem being registered.
#
# @param [Module] main_module
# The main module of the gem.
#
# @returns The root path of the loaded gem
def gem(name, main_module)
_, spec = Gem.loaded_specs.find{|spec_pair| spec_pair[0] == name }
gems << spec
modules << main_module
spec.full_gem_path
end
##
# @returns [Gem::Specification]
def gems
@gems ||= []
end
##
# @returns [<Padrino::Module>]
def modules
@modules ||= []
end
end
end

View file

@ -0,0 +1,297 @@
require 'padrino-core/application/flash'
require 'padrino-core/application/rendering'
require 'padrino-core/application/routing'
require 'padrino-core/application/showexceptions'
module Padrino
class ApplicationSetupError < RuntimeError
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)
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_router!
Padrino.require_dependencies(settings.app_file, :force => true) # Reload the app file
require_dependencies
default_filters!
default_routes!
default_errors!
I18n.reload! if defined?(I18n)
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
# which 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 prerequisite
# 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
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
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
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
end
end

View file

@ -0,0 +1,229 @@
module Padrino
module Flash
class << self
def registered(app)
app.helpers Helpers
app.after do
session[:_flash] = @_flash.next if @_flash
end
end
end
class Storage
include Enumerable
# @private
def initialize(session=nil)
@_now = session || {}
@_next = {}
end
def now
@_now
end
def next
@_next
end
# @since 0.10.8
# @api public
def [](type)
@_now[type]
end
# @since 0.10.8
# @api public
def []=(type, message)
@_next[type] = message
end
# @since 0.10.8
# @api public
def delete(type)
@_now.delete(type)
self
end
# @since 0.10.8
# @api public
def keys
@_now.keys
end
# @since 0.10.8
# @api public
def key?(type)
@_now.key?(type)
end
# @since 0.10.8
# @api public
def each(&block)
@_now.each(&block)
end
# @since 0.10.8
# @api public
def replace(hash)
@_now.replace(hash)
self
end
# @since 0.10.8
# @api public
def update(hash)
@_now.update(hash)
self
end
alias_method :merge!, :update
# @since 0.10.8
# @api public
def sweep
@_now.replace(@_next)
@_next = {}
self
end
# @since 0.10.8
# @api public
def keep(key = nil)
if key
@_next[key] = @_now[key]
else
@_next.merge!(@_now)
end
self
end
# @since 0.10.8
# @api public
def discard(key = nil)
if key
@_next.delete(key)
else
@_next = {}
end
self
end
# @since 0.10.8
# @api public
def clear
@_now.clear
end
# @since 0.10.8
# @api public
def empty?
@_now.empty?
end
# @since 0.10.8
# @api public
def to_hash
@_now.dup
end
def length
@_now.length
end
alias_method :size, :length
# @since 0.10.8
# @api public
def to_s
@_now.to_s
end
# @since 0.10.8
# @api public
def error=(message)
self[:error] = message
end
# @since 0.10.8
# @api public
def error
self[:error]
end
# @since 0.10.8
# @api public
def notice=(message)
self[:notice] = message
end
# @since 0.10.8
# @api public
def notice
self[:notice]
end
# @since 0.10.8
# @api public
def success=(message)
self[:success] = message
end
# @since 0.10.8
# @api public
def success
self[:success]
end
end # Storage
module Helpers
###
# Overloads the existing redirect helper in-order to provide support for
# flash messages.
#
# @overload redirect(url)
# @param [String] url
#
# @overload redirect(url, status_code)
# @param [String] url
# @param [Fixnum] status_code
#
# @overload redirect(url, status_code, flash_messages)
# @param [String] url
# @param [Fixnum] status_code
# @param [Hash] flash_messages
#
# @overload redirect(url, flash_messages)
# @param [String] url
# @param [Hash] flash_messages
#
# @example
# redirect(dashboard, success: :user_created)
# redirect(new_location, 301, notice: 'This page has moved. Please update your bookmarks!!')
#
# @since 0.10.8
# @api public
def redirect(url, *args)
flashes = args.extract_options!
flashes.each do |type, message|
message = I18n.translate(message) if message.is_a?(Symbol) && defined?(I18n)
flash[type] = message
end
super(url, args)
end
alias_method :redirect_to, :redirect
###
# Returns the flash storage object.
#
# @return [Storage]
#
# @since 0.10.8
# @api public
def flash
@_flash ||= Storage.new(env['rack.session'] ? session[:_flash] : {})
end
end
end
end

View file

@ -0,0 +1,317 @@
require 'padrino-core/support_lite' unless defined?(SupportLite)
module Padrino
##
# Padrino enhances the Sinatra 'render' method to have support for
# automatic template engine detection, enhanced layout functionality,
# locale enabled rendering, among other features.
#
module Rendering
##
# A SafeTemplate assumes that its output is safe.
#
module SafeTemplate
def render(*)
super.html_safe
end
end
##
# Exception responsible for when an expected template did not exist.
#
class TemplateNotFound < RuntimeError
end
##
# This is an array of file patterns to ignore. If your editor add a
# suffix during editing to your files please add it like:
#
# @example
# Padrino::Rendering::IGNORE_FILE_PATTERN << /~$/
#
IGNORE_FILE_PATTERN = [
/~$/ # This is for Gedit
] unless defined?(IGNORE_FILE_PATTERN)
##
# Default options used in the resolve_template-method.
#
DEFAULT_RENDERING_OPTIONS = { :strict_format => false, :raise_exceptions => true } unless defined?(DEFAULT_RENDERING_OPTIONS)
class << self
##
# Default engine configurations for Padrino::Rendering.
#
# @return {Hash<Symbol,Hash>}
# The configurations, keyed by engine.
def engine_configurations
@engine_configurations ||= {}
end
def registered(app)
included(app)
engine_configurations.each do |engine, configs|
app.set engine, configs
end
end
def included(base)
base.send(:include, InstanceMethods)
base.extend(ClassMethods)
end
end
##
# Class methods responsible for rendering templates as part of a request.
#
module ClassMethods
##
# Use layout like rails does or if a block given then like sinatra.
# If used without a block, sets the current layout for the route.
#
# By default, searches in your:
#
# +app+/+views+/+layouts+/+application+.(+haml+|+erb+|+xxx+)
# +app+/+views+/+layout_name+.(+haml+|+erb+|+xxx+)
#
# If you define +layout+ :+custom+ then searches for your layouts in
# +app+/+views+/+layouts+/+custom+.(+haml+|+erb+|+xxx+)
# +app+/+views+/+custom+.(+haml+|+erb+|+xxx+)
#
# @param [Symbol] name (:layout)
# The layout to use.
#
# @yield []
#
def layout(name=:layout, &block)
return super(name, &block) if block_given?
@layout = name
end
##
# Returns the cached template file to render for a given url,
# content_type and locale.
#
# @param [Array<template_path, content_type, locale>] render_options
#
def fetch_template_file(render_options)
(@_cached_templates ||= {})[render_options]
end
##
# Caches the template file for the given rendering options.
#
# @param [String] template_file
# The path of the template file.
#
# @param [Array<template_path, content_type, locale>] render_options
#
def cache_template_file!(template_file, render_options)
(@_cached_templates ||= {})[render_options] = template_file || []
end
##
# Returns the cached layout path.
#
# @param [Symbol, nil] given_layout
# The requested layout.
#
def fetch_layout_path(given_layout=nil)
layout_name = given_layout || @layout || :application
@_cached_layout ||= {}
cached_layout_path = @_cached_layout[layout_name]
return cached_layout_path if cached_layout_path
has_layout_at_root = Dir["#{views}/#{layout_name}.*"].any?
layout_path = has_layout_at_root ? layout_name.to_sym : File.join('layouts', layout_name.to_s).to_sym
@_cached_layout[layout_name] = layout_path unless reload_templates?
layout_path
end
end
# Instance methods that allow enhanced rendering to function properly in Padrino.
module InstanceMethods
attr_reader :current_engine
##
# Get/Set the content_type
#
# @param [String, nil] type
# The Content-Type to use.
#
# @param [Symbol, nil] type.
# Look and parse the given symbol to the matched Content-Type.
#
# @param [Hash] params
# Additional params to append to the Content-Type.
#
# @example
# case content_type
# when :js then do_some
# when :css then do_another
# end
#
# content_type :js
# # => set the response with 'application/javascript' Content-Type
# content_type 'text/html'
#
# # => set directly the Content-Type to 'text/html'
#
def content_type(type=nil, params={})
unless type.nil?
super(type, params)
@_content_type = type
end
@_content_type
end
private
##
# Enhancing Sinatra render functionality for:
#
# * Using layout similar to rails
# * Use render 'path/to/my/template' (without symbols)
# * Use render 'path/to/my/template' (with engine lookup)
# * Use render 'path/to/template.haml' (with explicit engine lookup)
# * Use render 'path/to/template', :layout => false
# * Use render 'path/to/template', :layout => false, :engine => 'haml'
#
def render(engine, data=nil, options={}, locals={}, &block)
# If engine is nil, ignore engine parameter and shift up all arguments
# render nil, "index", { :layout => true }, { :localvar => "foo" }
engine, data, options = data, options, locals if engine.nil? && data
# Data is a hash of options when no engine isn't explicit
# render "index", { :layout => true }, { :localvar => "foo" }
# Data is options, and options is locals in this case
data, options, locals = nil, data, options if data.is_a?(Hash)
# If data is unassigned then this is a likely a template to be resolved
# This means that no engine was explicitly defined
data, engine = *resolve_template(engine, options.dup) if data.nil?
# Use @layout if it exists
layout_was = options[:layout]
options[:layout] = @layout if options[:layout].nil? || options[:layout] == true
# Resolve layouts similar to in Rails
if options[:layout].nil? && !settings.templates.has_key?(:layout)
layout_path, layout_engine = *resolved_layout
# We need to force layout false so sinatra don't try to render it
options[:layout] = layout_path || false
options[:layout] = false unless layout_engine == engine # TODO allow different layout engine
options[:layout_engine] = layout_engine || engine if options[:layout]
elsif options[:layout].present?
options[:layout] = settings.fetch_layout_path(options[:layout] || @layout)
end
# Default to original layout value if none found.
options[:layout] ||= layout_was
# Cleanup the template.
@current_engine, engine_was = engine, @current_engine
@_out_buf, _buf_was = ActiveSupport::SafeBuffer.new, @_out_buf
# Pass arguments to Sinatra render method.
super(engine, data, options.dup, locals, &block)
ensure
@current_engine = engine_was
@_out_buf = _buf_was
end
##
# Returns the located layout tuple to be used for the rendered template
# (if available).
#
# @example
# resolve_layout
# # => ["/layouts/custom", :erb]
# # => [nil, nil]
#
def resolved_layout
located_layout = resolve_template(settings.fetch_layout_path, :raise_exceptions => false, :strict_format => true)
located_layout ? located_layout : [nil, nil]
end
##
# Returns the template path and engine that match content_type (if present),
# I18n.locale.
#
# @param [String] template_path
# The path of the template.
#
# @param [Hash] options
# Additional options.
#
# @option options [Boolean] :strict_format (false)
# The resolved template must match the content_type of the request.
#
# @option options [Boolean] :raise_exceptions (false)
# Raises a {TemplateNotFound} exception if the template cannot be located.
#
# @return [Array<Symbol, Symbol>]
# The path and format of the template.
#
# @raise [TemplateNotFound]
# The template could not be found.
#
# @example
# get "/foo", :provides => [:html, :js] do; render 'path/to/foo'; end
# # If you request "/foo.js" with I18n.locale == :ru => [:"/path/to/foo.ru.js", :erb]
# # If you request "/foo" with I18n.locale == :de => [:"/path/to/foo.de.haml", :haml]
#
def resolve_template(template_path, options={})
began_at = Time.now
_content_type = content_type || :html
# Fetch cached template for rendering options
template_path = template_path.to_s[0] == ?/ ? template_path.to_s : "/#{template_path}"
rendering_options = [template_path, _content_type, locale]
cached_template = settings.fetch_template_file(rendering_options)
if cached_template
logger.debug :cached, began_at, cached_template[0] if settings.logging? && defined?(logger)
return cached_template
end
# Resolve view path and options.
options.reverse_merge!(DEFAULT_RENDERING_OPTIONS)
view_path = options.delete(:views) || settings.views || "./views"
target_extension = File.extname(template_path)[1..-1] || "none" # explicit template extension
template_path = template_path.chomp(".#{target_extension}")
# Generate potential template candidates
templates = Dir[File.join(view_path, template_path) + ".*"].map do |file|
template_engine = File.extname(file)[1..-1].to_sym # Retrieves engine extension
template_file = file.sub(view_path, '').chomp(".#{template_engine}").to_sym # retrieves template filename
[template_file, template_engine] unless IGNORE_FILE_PATTERN.any? { |pattern| template_engine.to_s =~ pattern }
end
# Check if we have a simple content type
simple_content_type = [:html, :plain].include?(_content_type)
# Resolve final template to render
located_template =
templates.find { |file, e| file.to_s == "#{template_path}.#{locale}.#{_content_type}" } ||
templates.find { |file, e| file.to_s == "#{template_path}.#{locale}" && simple_content_type } ||
templates.find { |file, e| File.extname(file.to_s) == ".#{target_extension}" or e.to_s == target_extension.to_s } ||
templates.find { |file, e| file.to_s == "#{template_path}.#{_content_type}" } ||
templates.find { |file, e| file.to_s == "#{template_path}" && simple_content_type } ||
(!options[:strict_format] && templates.first) # If not strict, fall back to the first located template.
raise TemplateNotFound, "Template '#{template_path}' not found in '#{view_path}'!" if !located_template && options[:raise_exceptions]
settings.cache_template_file!(located_template, rendering_options) unless settings.reload_templates?
logger.debug :template, began_at, located_template[0] if located_template && settings.logging? && defined?(logger)
located_template
end
##
# Return the I18n.locale if I18n is defined.
#
def locale
I18n.locale if defined?(I18n)
end
end
end
end
require 'padrino-core/application/rendering/extensions/haml'
require 'padrino-core/application/rendering/extensions/erubis'
require 'padrino-core/application/rendering/extensions/slim'

View file

@ -0,0 +1,64 @@
begin
require 'erubis'
module Padrino
module Erubis
##
# SafeBufferEnhancer is an Erubis Enhancer that compiles templates that
# are fit for using ActiveSupport::SafeBuffer as a Buffer.
#
# @api private
module SafeBufferEnhancer
def add_expr_literal(src, code)
src << " #{@bufvar}.concat((" << code << ').to_s);'
end
def add_expr_escaped(src, code)
src << " #{@bufvar}.safe_concat " << code << ';'
end
def add_text(src, text)
src << " #{@bufvar}.safe_concat '" << escape_text(text) << "';" unless text.empty?
end
end
##
# SafeBufferTemplate is the classic Erubis template, augmented with
# SafeBufferEnhancer.
#
# @api private
class SafeBufferTemplate < ::Erubis::Eruby
include SafeBufferEnhancer
end
##
# Modded ErubisTemplate that doesn't insist in an String as output
# buffer.
#
# @api private
class Template < Tilt::ErubisTemplate
def render(*args)
app = args.first
app_class = app.class
@padrino_app = app.kind_of?(Padrino::Application) ||
(app_class.respond_to?(:erb) && app_class.erb[:engine_class] == Padrino::Erubis::SafeBufferTemplate)
super
end
def precompiled_preamble(locals)
buf = @padrino_app ? "ActiveSupport::SafeBuffer.new" : "''"
old_postamble = super.split("\n")[0..-2]
[old_postamble, "#{@outvar} = _buf = (#{@outvar} || #{buf})"].join("\n")
end
end
end
end
Tilt.prefer(Padrino::Erubis::Template, :erb)
if defined? Padrino::Rendering
Padrino::Rendering.engine_configurations[:erb] =
{:engine_class => Padrino::Erubis::SafeBufferTemplate}
end
rescue LoadError
end

View file

@ -0,0 +1,28 @@
begin
require 'haml'
require 'haml/helpers/xss_mods'
require 'haml/helpers/action_view_extensions'
module Haml
module Helpers
include XssMods
include ActionViewExtensions
end
module Util
def self.rails_xss_safe?
true
end
end
end
if defined? Padrino::Rendering
Padrino::Rendering.engine_configurations[:haml] =
{:escape_html => true}
class Tilt::HamlTemplate
include Padrino::Rendering::SafeTemplate
end
end
rescue LoadError
end

View file

@ -0,0 +1,14 @@
begin
require 'slim'
if defined? Padrino::Rendering
Padrino::Rendering.engine_configurations[:slim] =
{:generator => Temple::Generators::RailsOutputBuffer,
:buffer => "@_out_buf", :use_html_safe => true}
class Slim::Template
include Padrino::Rendering::SafeTemplate
end
end
rescue LoadError
end

View file

@ -0,0 +1,21 @@
module Padrino
##
# This module extend Sinatra::ShowExceptions adding Padrino as "Framework".
#
# @private
class ShowExceptions < Sinatra::ShowExceptions
private
def frame_class(frame)
if frame.filename =~ /lib\/sinatra.*\.rb|lib\/padrino.*\.rb/
"framework"
elsif (defined?(Gem) && frame.filename.include?(Gem.dir)) ||
frame.filename =~ /\/bin\/(\w+)$/ ||
frame.filename =~ /Ruby\/Gems/
"system"
else
"app"
end
end
end
end

View file

@ -0,0 +1,53 @@
module Padrino
# List of callers in a Padrino application that should be ignored as part of a stack trace.
PADRINO_IGNORE_CALLERS = [
%r{lib/padrino-.*$},
%r{/padrino-.*/(lib|bin)},
%r{/bin/padrino$},
%r{/sinatra(/(base|main|showexceptions))?\.rb$},
%r{lib/tilt.*\.rb$},
%r{lib/rack.*\.rb$},
%r{lib/mongrel.*\.rb$},
%r{lib/shotgun.*\.rb$},
%r{bin/shotgun$},
%r{\(.*\)},
%r{shoulda/context\.rb$},
%r{mocha/integration},
%r{test/unit},
%r{rake_test_loader\.rb},
%r{custom_require\.rb$},
%r{active_support},
%r{/thor}
] unless defined?(PADRINO_IGNORE_CALLERS)
##
# Add rubinius (and hopefully other VM implementations) ignore patterns ...
#
PADRINO_IGNORE_CALLERS.concat(RUBY_IGNORE_CALLERS) if defined?(RUBY_IGNORE_CALLERS)
private
##
# The filename for the file that is the direct caller (first caller).
#
# @return [String]
# The file the caller method exists in.
#
def self.first_caller
caller_files.first
end
#
# Like +Kernel#caller+ but excluding certain magic entries and without
# line / method information; the resulting array contains filenames only.
#
# @return [Array<String>]
# The files of the calling methods.
#
def self.caller_files
caller(1).
map { |line| line.split(/:(?=\d|in )/)[0,2] }.
reject { |file,line| PADRINO_IGNORE_CALLERS.any? { |pattern| file =~ pattern } }.
map { |file,line| file }
end
end

View file

@ -0,0 +1,24 @@
module Padrino
module Cli
module Adapter
class << self
# Start for the given options a rackup handler
def start(options)
Padrino.run!(options.symbolize_keys)
end
# Method that stop (if exist) a running Padrino.application
def stop(options)
options.symbolize_keys!
if File.exist?(options[:pid])
pid = File.read(options[:pid]).to_i
print "=> Sending INT to process with pid #{pid} wait "
Process.kill(2, pid) rescue nil
else
puts "=> #{options[:pid]} not found!"
end
end
end
end
end
end

View file

@ -0,0 +1,159 @@
require 'thor'
module Padrino
module Cli
class Base < Thor
include Thor::Actions
class_option :chdir, :type => :string, :aliases => "-c", :desc => "Change to dir before starting."
class_option :environment, :type => :string, :aliases => "-e", :required => true, :default => :development, :desc => "Padrino Environment."
class_option :help, :type => :boolean, :desc => "Show help usage"
desc "start", "Starts the Padrino application (alternatively use 's')."
map "s" => :start
method_option :server, :type => :string, :aliases => "-a", :desc => "Rack Handler (default: autodetect)"
method_option :host, :type => :string, :aliases => "-h", :required => true, :default => '127.0.0.1', :desc => "Bind to HOST address."
method_option :port, :type => :numeric, :aliases => "-p", :required => true, :default => 3000, :desc => "Use PORT."
method_option :daemonize, :type => :boolean, :aliases => "-d", :desc => "Run daemonized in the background."
method_option :pid, :type => :string, :aliases => "-i", :desc => "File to store pid."
method_option :debug, :type => :boolean, :desc => "Set debugging flags."
def start
prepare :start
require File.expand_path("../adapter", __FILE__)
require File.expand_path('config/boot.rb')
Padrino::Cli::Adapter.start(options)
end
desc "stop", "Stops the Padrino application (alternatively use 'st')."
map "st" => :stop
method_option :pid, :type => :string, :aliases => "-p", :desc => "File to store pid", :default => 'tmp/pids/server.pid'
def stop
prepare :stop
require File.expand_path("../adapter", __FILE__)
Padrino::Cli::Adapter.stop(options)
end
desc "rake", "Execute rake tasks."
method_option :environment, :type => :string, :aliases => "-e", :required => true, :default => :development
method_option :list, :type => :string, :aliases => "-T", :desc => "Display the tasks (matching optional PATTERN) with descriptions, then exit."
method_option :trace, :type => :boolean, :aliases => "-t", :desc => "Turn on invoke/execute tracing, enable full backtrace."
def rake(*args)
prepare :rake
args << "-T" if options[:list]
args << options[:list] unless options[:list].nil? || options[:list].to_s == "list"
args << "--trace" if options[:trace]
args << "--verbose" if options[:verbose]
ARGV.clear
ARGV.concat(args)
puts "=> Executing Rake #{ARGV.join(' ')} ..."
load File.expand_path('../rake.rb', __FILE__)
Rake.application.init
Rake.application.instance_variable_set(:@rakefile, __FILE__)
load File.expand_path('Rakefile')
Rake.application.top_level
end
desc "console", "Boots up the Padrino application irb console (alternatively use 'c')."
map "c" => :console
def console(*args)
prepare :console
require File.expand_path("../../version", __FILE__)
ARGV.clear
require 'irb'
require "irb/completion"
require File.expand_path('config/boot.rb')
puts "=> Loading #{Padrino.env} console (Padrino v.#{Padrino.version})"
require File.expand_path('../console', __FILE__)
IRB.start
end
desc "generate", "Executes the Padrino generator with given options (alternatively use 'gen' or 'g')."
map ["gen", "g"] => :generate
def generate(*args)
begin
# We try to load the vendored padrino-gen if exist
padrino_gen_path = File.expand_path('../../../../../padrino-gen/lib', __FILE__)
$:.unshift(padrino_gen_path) if File.directory?(padrino_gen_path) && !$:.include?(padrino_gen_path)
require 'padrino-core/command'
require 'padrino-gen/command'
ARGV.shift
ARGV << 'help' if ARGV.empty?
Padrino.bin_gen(*ARGV)
rescue
puts "<= You need padrino-gen! Run: gem install padrino-gen"
end
end
desc "version", "Show current Padrino version."
map ["-v", "--version"] => :version
def version
require 'padrino-core/version'
puts "Padrino v. #{Padrino.version}"
end
desc "runner", "Run a piece of code in the Padrino application environment (alternatively use 'run' or 'r')."
map ["run", "r"] => :runner
def runner(*args)
prepare :runner
code_or_file = args.shift
abort "Please specify code or file" if code_or_file.nil?
require File.expand_path('config/boot.rb')
if File.exist?(code_or_file)
eval(File.read(code_or_file), nil, code_or_file)
else
eval(code_or_file)
end
end
private
def prepare(task)
if options.help?
help(task.to_s)
raise SystemExit
end
ENV["PADRINO_ENV"] ||= ENV["RACK_ENV"] ||= options.environment.to_s
chdir(options.chdir)
unless File.exist?('config/boot.rb')
puts "=> Could not find boot file in: #{options.chdir}/config/boot.rb !!!"
raise SystemExit
end
end
protected
def self.banner(task=nil, *args)
"padrino #{task.name}"
end
def chdir(dir)
return unless dir
begin
Dir.chdir(dir.to_s)
rescue Errno::ENOENT
puts "=> Specified Padrino root '#{dir}' does not appear to exist!"
rescue Errno::EACCES
puts "=> Specified Padrino root '#{dir}' cannot be accessed by the current user!"
end
end
def capture(stream)
begin
stream = stream.to_s
eval "$#{stream} = StringIO.new"
yield
result = eval("$#{stream}").string
ensure
eval("$#{stream} = #{stream.upcase}")
end
result
end
alias :silence :capture
end
end
end

View file

@ -0,0 +1,20 @@
# Reloads classes
def reload!
Padrino.reload!
end
# Show applications
def applications
puts "==== List of Mounted Applications ====\n\n"
Padrino.mounted_apps.each do |app|
puts " * %-10s mapped to %s" % [app.name, app.uri_root]
end
puts
Padrino.mounted_apps.map { |app| "#{app.name} => #{app.uri_root}" }
end
# Load apps
Padrino.mounted_apps.each do |app|
puts "=> Loading Application #{app.app_class}"
app.app_obj.setup_application!
end

View file

@ -0,0 +1,47 @@
require File.expand_path('../../tasks', __FILE__)
require 'rake'
require 'rake/dsl_definition'
require 'thor'
require 'securerandom' unless defined?(SecureRandom)
require 'padrino-gen'
module PadrinoTasks
def self.init(init=false)
$LOAD_PATH.unshift(File.expand_path("lib"))
Padrino::Tasks.files.flatten.uniq.each { |rakefile| Rake.application.add_import(rakefile) rescue puts "<= Failed load #{ext}" }
load(File.expand_path('../rake_tasks.rb', __FILE__))
Rake.application.load_imports
end
def self.use(task)
tasks << task
end
def self.tasks
@tasks ||= []
end
def self.load?(task, constant_present)
if constant_present && !PadrinoTasks.tasks.include?(task)
warn <<-WARNING.undent
Loading #{task} tasks automatically.
This functionality will be disabled in future versions. Please put
PadrinoTasks.use(#{task.inspect})
PadrinoTasks.init
and remove
require File.expand_path('../config/boot.rb', __FILE__)
in you Rakefile instead.
WARNING
end
constant_present || PadrinoTasks.tasks.include?(task)
end
end
def shell
@_shell ||= Thor::Base.shell.new
end

View file

@ -0,0 +1,52 @@
Dir["{lib/tasks/**,tasks/**,test,spec}/*.rake"].each do |file|
begin
load(file)
rescue LoadError => e
warn "#{file}: #{e.message}"
end
end
# Loads the Padrino applications mounted within the project.
# Setting up the required environment for Padrino.
task :environment do
require File.expand_path('config/boot.rb', Rake.application.original_dir)
Padrino.mounted_apps.each do |app|
app.app_obj.setup_application!
end
end
desc "Generate a secret key"
task :secret do
shell.say SecureRandom.hex(32)
end
def list_app_routes(app, args)
app_routes = app.named_routes
app_routes.reject! { |r| r.identifier.to_s !~ /#{args.query}/ } if args.query.present?
app_routes.map! { |r| [r.verb, r.name, r.path] }
return if app_routes.empty?
shell.say "\nApplication: #{app.app_class}", :yellow
app_routes.unshift(["REQUEST", "URL", "PATH"])
max_col_1 = app_routes.max { |a, b| a[0].size <=> b[0].size }[0].size
max_col_2 = app_routes.max { |a, b| a[1].size <=> b[1].size }[1].size
app_routes.each_with_index do |row, i|
message = [row[1].ljust(max_col_2+2), row[0].center(max_col_1+2), row[2]]
shell.say(" " + message.join(" "), i==0 ? :bold : nil)
end
end
desc "Displays a listing of the named routes within a project, optionally only those matched by [query]"
task :routes, [:query] => :environment do |t, args|
Padrino.mounted_apps.each do |app|
list_app_routes(app, args)
end
end
desc "Displays a listing of the named routes a given app [app]"
namespace :routes do
task :app, [:app] => :environment do |t, args|
app = Padrino.mounted_apps.find { |app| app.app_class == args.app }
list_app_routes(app, args) if app
end
end

View file

@ -0,0 +1,38 @@
require 'rbconfig'
module Padrino
##
# This method return the correct location of padrino bin or
# exec it using Kernel#system with the given args.
#
# @param [Array] args
# command or commands to execute
#
# @return [Boolean]
#
# @example
# Padrino.bin('start', '-e production')
#
def self.bin(*args)
@_padrino_bin ||= [self.ruby_command, File.expand_path("../../../bin/padrino", __FILE__)]
args.empty? ? @_padrino_bin : system(args.unshift(@_padrino_bin).join(" "))
end
##
# Return the path to the ruby interpreter taking into account multiple
# installations and windows extensions.
#
# @return [String]
# path to ruby bin executable
#
def self.ruby_command
@ruby_command ||= begin
ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
ruby << RbConfig::CONFIG['EXEEXT']
# escape string in case path to ruby executable contain spaces.
ruby.sub!(/.*\s.*/m, '"\&"')
ruby
end
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,224 @@
module Padrino
class << self
##
# Hooks to be called before a load/reload.
#
# @yield []
# The given block will be called before Padrino was loaded/reloaded.
#
# @return [Array<Proc>]
# The load/reload before hooks.
#
# @example
# before_load do
# pre_initialize_something
# end
#
def before_load(&block)
@_before_load ||= []
@_before_load << block if block_given?
@_before_load
end
##
# Hooks to be called after a load/reload.
#
# @yield []
# The given block will be called after Padrino was loaded/reloaded.
#
# @return [Array<Proc>]
# The load/reload hooks.
#
# @example
# after_load do
# DataMapper.finalize
# end
#
def after_load(&block)
@_after_load ||= []
@_after_load << block if block_given?
@_after_load
end
##
# The used +$LOAD_PATHS+ from Padrino.
#
# @return [Array<String>]
# The load paths used by Padrino.
#
def load_paths
@_load_paths_was = %w(lib models shared).map { |path| Padrino.root(path) }
@_load_paths ||= @_load_paths_was
end
##
# Requires necessary dependencies as well as application files from root
# lib and models.
#
# @return [Boolean]
# returns true if Padrino is not already bootstraped otherwise else.
#
def load!
return false if loaded?
t = Time.now
@_called_from = first_caller
Padrino.set_encoding
Padrino.set_load_paths(*load_paths)
Padrino::Logger.setup! # Initialize our logger
Padrino.require_dependencies("#{root}/config/database.rb", :nodeps => true) # Be sure to don't remove constants from dbs.
Padrino::Reloader.lock! # Now we can remove constant from here to down
Padrino.before_load.each(&:call) # Run before hooks
Padrino.dependency_paths.each { |path| Padrino.require_dependencies(path) }
Padrino.after_load.each(&:call) # Run after hooks
Padrino::Reloader.run!
Thread.current[:padrino_loaded] = true
Padrino.logger.devel "Loaded Padrino in #{Time.now - t} seconds"
end
##
# Clear the padrino env.
#
# @return [NilClass]
#
def clear!
Padrino.clear_middleware!
Padrino.mounted_apps.clear
@_load_paths = nil
@_dependency_paths = nil
@_global_configuration = nil
Padrino.before_load.clear
Padrino.after_load.clear
Padrino::Reloader.clear!
Thread.current[:padrino_loaded] = nil
end
##
# Method for reloading required applications and their files.
#
def reload!
return unless Padrino::Reloader.changed?
Padrino.before_load.each(&:call) # Run before hooks
Padrino::Reloader.reload! # detects the modified files
Padrino.after_load.each(&:call) # Run after hooks
end
##
# This adds the ability to instantiate {Padrino.load!} after
# {Padrino::Application} definition.
#
def called_from
@_called_from || first_caller
end
##
# Determines whether Padrino was loaded with {Padrino.load!}.
#
# @return [Boolean]
# Specifies whether Padrino was loaded.
#
def loaded?
Thread.current[:padrino_loaded]
end
##
# Attempts to require all dependency libs that we need.
# If you use this method we can perform correctly a Padrino.reload!
# Another good thing that this method are dependency check, for example:
#
# # models
# # \-- a.rb => require something of b.rb
# # \-- b.rb
#
# In the example above if we do:
#
# Dir["/models/*.rb"].each { |r| require r }
#
# We get an error, because we try to require first +a.rb+ that need
# _something_ of +b.rb+.
#
# With this method we don't have this problem.
#
# @param [Array<String>] paths
# The paths to require.
#
# @example For require all our app libs we need to do:
# require_dependencies("#{Padrino.root}/lib/**/*.rb")
#
def require_dependencies(*paths)
options = paths.extract_options!
# Extract all files to load
files = paths.flatten.map { |path| Dir[path] }.flatten.uniq.sort
while files.present?
errors, failed = [], []
size_at_start = files.size
# Now we try to require our dependencies, we dup files
# so we don't perform delete on the original array during
# iteration, this prevent problems with Rubinus
files.dup.each do |file|
begin
Padrino::Reloader.safe_load(file, options.dup)
files.delete(file)
rescue NameError, LoadError => e
Padrino.logger.devel "Problem while loading #{file}: #{e}"
errors << e
failed << file
rescue Exception => e
raise e
end
end
# Stop processing if nothing loads or if everything has loaded
raise errors.last if files.size == size_at_start && files.present?
break if files.empty?
end
end
##
# Returns default list of path globs to load as dependencies.
# Appends custom dependency patterns to the be loaded for Padrino.
#
# @return [Array<String>]
# The dependencey paths.
#
# @example
# Padrino.dependency_paths << "#{Padrino.root}/uploaders/*.rb"
#
def dependency_paths
@_dependency_paths ||= (dependency_paths_was + Array(module_paths))
end
##
# Concat to +$LOAD_PATH+ the given paths.
#
# @param [Array<String>] paths
# The paths to concat.
#
def set_load_paths(*paths)
$:.concat(paths); load_paths.concat(paths)
$:.uniq!; load_paths.uniq!
end
private
def module_paths
Padrino.modules.map(&:dependency_paths).flatten!
end
def dependency_paths_was
[
"#{root}/config/database.rb",
"#{root}/lib/**/*.rb",
"#{root}/shared/lib/**/*.rb",
"#{root}/models/**/*.rb",
"#{root}/shared/models/**/*.rb",
"#{root}/config/apps.rb"
]
end
end
end

View file

@ -0,0 +1,33 @@
cs:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d. %m. %Y"
short: "%d %b"
long: "%d. %B %Y"
only_day: "%e"
day_names: [Neděle, Pondělí, Úterý, Středa, Čtvrtek, Pátek, Sobota]
abbr_day_names: [Ne, Po, Út, St, Čt, Pá, So]
month_names: [~, Leden, Únor, Březen, Duben, Květen, Červen, Červenec, Srpen, Září, Říjen, Listopad, Prosinec]
abbr_month_names: [~, Led, Úno, Bře, Dub, Kvě, Čvn, Čvc, Srp, Zář, Říj, Lis, Pro]
order:
- day
- month
- year
time:
formats:
default: "%a %d. %B %Y %H:%M %z"
short: "%d. %m. %H:%M"
long: "%A %d. %B %Y %H:%M"
am: "dop"
pm: "odp"
support:
array:
words_connector: ", "
two_words_connector: " a "
last_word_connector: " a "

View file

@ -0,0 +1,33 @@
da:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d.%m.%Y"
short: "%e. %b %Y"
long: "%e. %B %Y"
only_day: "%e"
day_names: [søndag, mandag, tirsdag, onsdag, torsdag, fredag, lørdag]
abbr_day_names: [sø, ma, ti, 'on', to, fr, lø]
month_names: [~, januar, februar, marts, april, maj, juni, juli, august, september, oktober, november, december]
abbr_month_names: [~, jan, feb, mar, apr, maj, jun, jul, aug, sep, okt, nov, dec]
order:
- day
- month
- year
time:
formats:
default: "%e. %B %Y, %H:%M"
short: "%e. %b %Y, %H:%M"
long: "%A, %e. %B %Y, %H:%M"
am: ""
pm: ""
support:
array:
words_connector: ", "
two_words_connector: " og "
last_word_connector: " og "

View file

@ -0,0 +1,33 @@
de:
date:
formats:
# Benutze die strftime Parameter für die Formatierung
# Wenn keine Formate angegeben werden, wird "default" benutzt.
# Du kannst auch weitere Formate hinzufügen, wenn Du möchtest.
default: "&d.&m.%Y"
short: "%d. %b"
long: "%d. %B %Y"
only_day: "%e"
day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
abbr_day_names: [So, Mo, Di, Mi, Do, Fr, Sa]
month_names: [~, Januar, Februar, März, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember]
abbr_month_names: [~, Jan, Feb, Mär, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]
order:
- day
- month
- year
time:
formats:
default: "%a, %d. %b %Y %H:%M:%S %z"
short: "%d. %b %H:%M"
long: "%d. %B %Y %H:%M"
am: "am"
pm: "pm"
support:
array:
words_connector: ", "
two_words_connector: " und "
last_word_connector: " und "

View file

@ -0,0 +1,33 @@
en:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
only_day: "%e"
day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
order:
- year
- month
- day
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%B %d, %Y %H:%M"
am: "am"
pm: "pm"
support:
array:
words_connector: ", "
two_words_connector: " and "
last_word_connector: ", and "

View file

@ -0,0 +1,33 @@
es:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d-%m-%Y"
short: "%b %d"
long: "%B %d, %Y"
only_day: "%e"
day_names: [Domingo, Lunes, Martes, Miércoles, Jueves, Viernes, Sábado]
abbr_day_names: [Dom, Lun, Mar, Mie, Jue, Vie, Sab]
month_names: [~, Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre]
abbr_month_names: [~, Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic]
order:
- day
- month
- year
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%B %d, %Y %H:%M"
am: "am"
pm: "pm"
support:
array:
words_connector: ", "
two_words_connector: " y "
last_word_connector: ", y "

View file

@ -0,0 +1,33 @@
fr:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d-%m-%Y"
short: "%b %d"
long: "%B %d, %Y"
only_day: "%e"
day_names: [Dimanche, Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi]
abbr_day_names: [Dim, Lun, Mar, Mer, Jeu, Ven, Sam]
month_names: [~, Janvier, Février, Mars, Avril, Mai, Juin, Juillet, Août, Septembre, Octobre, Novembre, Décembre]
abbr_month_names: [~, Jan, Fev, Mar, Avr, Mai, Jun, Jul, Aou, Sep, Oct, Nov, Dec]
order:
- day
- month
- year
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%B %d, %Y %H:%M"
am: "am"
pm: "pm"
support:
array:
words_connector: ", "
two_words_connector: " et "
last_word_connector: ", et "

View file

@ -0,0 +1,33 @@
hu:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y-%m-%d"
short: "%b. %d."
long: "%Y. %B %d."
only_day: "%e"
day_names: [vasárnap, hétfő, kedd, szerda, csütörtök, péntek, szombat]
abbr_day_names: [vas, hét, kedd, sze, csüt, pén, szo]
month_names: [~, január, február, március, április, május, június, július, augusztus, szeptember, oktober, november, december]
abbr_month_names: [~, jan, febr, márc, ápr, máj, jún, júl, aug, szept, okt, nov, dec]
order:
- year
- month
- day
time:
formats:
default: "%Y. %B %d. %H:%M:%S %z, %A"
short: "%B %d. %H:%M"
long: "%Y. %B %d. %H:%M"
am: "de"
pm: "du"
support:
array:
words_connector: ", "
two_words_connector: " és "
last_word_connector: " és "

View file

@ -0,0 +1,39 @@
it:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d-%m-%Y"
short: "%d %b"
long: "%d %B %Y"
only_day: "%e"
day_names: [Domenica, Lunedì, Martedì, Mercoledì, Giovedì, Venerdì, Sabato]
abbr_day_names: [Dom, Lun, Mar, Mer, Gio, Ven, Sab]
month_names: [~, Gennaio, Febbraio, Marzo, Aprile, Maggio, Giugno, Luglio, Agosto, Settembre, Ottobre, Novembre, Dicembre]
abbr_month_names: [~, Gen, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic]
order:
- day
- month
- year
time:
formats:
default: "%a %d %b %Y, %H:%M:%S %z"
time: "%H:%M"
short: "%d %b %H:%M"
long: "%d %B %Y %H:%M"
only_second: "%S"
datetime:
formats:
default: "%d-%m-%YT%H:%M:%S%Z"
am: 'am'
pm: 'pm'
support:
array:
sentence_connector: "e"
skip_last_comma: false

View file

@ -0,0 +1,33 @@
ja:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y/%m/%d"
short: "%m/%d"
long: "%Y年%m月%d日"
only_day: "%e"
day_names: [日曜日, 月曜日, 火曜日, 水曜日, 木曜日, 金曜日, 土曜日]
abbr_day_names: [日, 月, 火, 水, 木, 金, 土]
month_names: [~, 一月, 二月, 三月, 四月, 五月, 六月, 七月, 八月, 九月, 十月, 十一月, 十二月]
abbr_month_names: [~, 一月, 二月, 三月, 四月, 五月, 六月, 七月, 八月, 九月, 十月, 十一月, 十二月]
order:
- year
- month
- day
time:
formats:
default: "%Y/%m/%d %H:%M:%S"
short: "%m/%d %H:%M"
long: "%Y年%m月%d日 %H時%M分%S秒"
am: "午前"
pm: "午後"
support:
array:
words_connector: ", "
two_words_connector: " と "
last_word_connector: ", と "

View file

@ -0,0 +1,33 @@
lv:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d.%m.%Y"
short: "%e. %B"
long: "%Y. gada %e. %B"
only_day: "%e"
day_names: [Svētdiena, Pirmdiena, Otrdiena, Trešdiena, Ceturtdiena, Piektdiena, Sestdiena]
abbr_day_names: [Sv, P, O, T, C, P, S]
month_names: [~, Janvāris, Februāris, Marts, Aprīlis, Maijs, Jūnijs, Jūlijs, Augusts, Septembris, Oktobris, Novembris, Decembris]
abbr_month_names: [~, Janv, Febr, Marts, Apr, Maijs, Jūn, Jūl, Aug, Sept, Okt, Nov, Dec]
order:
- day
- month
- year
time:
formats:
default: "%Y. gada %e. %B, %H:%M"
short: "%d.%m.%Y, %H:%M"
long: "%Y. gada %e. %B, %H:%M:%S"
am: "priekšpusdiena"
pm: "pēcpusdiena"
support:
array:
words_connector: ", "
two_words_connector: " un "
last_word_connector: ", un "

View file

@ -0,0 +1,33 @@
nl:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d-%m-%Y"
short: "%d %b"
long: "%d %B %Y"
only_day: "%e"
day_names: [zondag, maandag, dinsdag, woensdag, donderdag, vrijdag, zaterdag]
abbr_day_names: [zo, ma, di, wo, do, vr, za]
month_names: [~, januari, februari, maart, april, mei, juni, juli, augustus, september, oktober, november]
abbr_month_names: [~, jan, feb, maa, apr, mei, jun, jul, aug, sep, okt, nov, dec]
order:
- day
- month
- year
time:
formats:
default: "%a %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%d %B %Y %H:%M"
am: "am"
pm: "pm"
support:
array:
words_connector: ", "
two_words_connector: " en "
last_word_connector: " en "

View file

@ -0,0 +1,33 @@
"no":
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d.%m.%Y"
short: "%e. %b %Y"
long: "%e. %B %Y"
only_day: "%e"
day_names: [søndag, mandag, tirsdag, onsdag, torsdag, fredag, lørdag]
abbr_day_names: [sø, ma, ti, 'on', to, fr, lø]
month_names: [~, januar, februar, mars, april, mai, juni, juli, august, september, oktober, november, desember]
abbr_month_names: [~, jan, feb, mar, apr, maj, jun, jul, aug, sep, okt, nov, des]
order:
- day
- month
- year
time:
formats:
default: "%e. %B %Y, %H:%M"
short: "%e. %b %Y, %H:%M"
long: "%A, %e. %B %Y, %H:%M"
am: ""
pm: ""
support:
array:
words_connector: ", "
two_words_connector: " og "
last_word_connector: " og "

View file

@ -0,0 +1,33 @@
pl:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y-%m-%d"
short: "%d %b"
long: "%d %B %Y"
only_day: "%e"
day_names: [Niedziela, Poniedziałek, Wtorek, Środa, Czwartek, Piątek, Sobota]
abbr_day_names: [nie, pon, wto, śro, czw, pia, sob]
month_names: [~, Styczeń, Luty, Marzec, Kwiecień, Maj, Czerwiec, Lipiec, Sierpień, Wrzesień, Październik, Listopad, Grudzień]
abbr_month_names: [~, sty, lut, mar, kwi, maj, cze, lip, sie, wrz, paź, lis, gru]
order:
- year
- month
- day
time:
formats:
default: "%a, %d %b %Y, %H:%M:%S %z"
short: "%d %b, %H:%M"
long: "%d %B %Y, %H:%M"
am: "przed południem"
pm: "po południu"
support:
array:
words_connector: ", "
two_words_connector: " i "
last_word_connector: " i "

View file

@ -0,0 +1,39 @@
pt_br:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d-%m-%Y"
short: "%d %b"
long: "%d %B %Y"
only_day: "%e"
day_names: [Domingo, Segunda-feira, Terça-feira, Quarta-feira, Quinta-feira, Sexta-feira, Sábado]
abbr_day_names: [Dom, Seg, Ter, Qua, Qui, Sex, Sab]
month_names: [~, Janeiro, Fevereiro, Março, Abril, Maio, Junho, Julho, Agosto, Setembro, Outubro, Novembro, Dezembro]
abbr_month_names: [~, Jan, Fev, Mar, Abr, Maio, Jun, Jul, Ago, Set, Out, Nov, Dez]
order:
- day
- month
- year
time:
formats:
default: "%a %d %b %Y, %H:%M:%S %z"
time: "%H:%M"
short: "%d %b %H:%M"
long: "%d %B %Y %H:%M"
only_second: "%S"
datetime:
formats:
default: "%d-%m-%YT%H:%M:%S%Z"
am: 'am'
pm: 'pm'
support:
array:
sentence_connector: "e"
skip_last_comma: false

View file

@ -0,0 +1,33 @@
ro:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d.%m.%Y"
short: "%d %b"
long: "%d %B %Y"
only_day: "%e"
day_names: [Duminică, Luni, Marți, Miercuri, Joi, Vineri, Sâmbătă]
abbr_day_names: [Dum, Lun, Mar, Mie, Joi, Vin, Sâm]
month_names: [~, Ianuarie, Februarie, Martie, Aprilie, Mai, Iunie, Iulie, August, Septembrie, Octombrie, Noiembrie, Decembrie]
abbr_month_names: [~, Ian, Feb, Mar, Apr, Mai, Iun, Iul, Aug, Sep, Oct, Noi, Dec]
order:
- day
- month
- year
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%d, %B %Y %H:%M"
am: "am"
pm: "pm"
support:
array:
words_connector: ", "
two_words_connector: " și "
last_word_connector: " și "

View file

@ -0,0 +1,34 @@
ru:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d.%m.%Y"
short: "%d %b"
long: "%e %B, %Y"
only_day: "%e"
day_names: [Воскресенье, Понедельник, Вторник, Среда, Четверг, Пятница, Суббота]
abbr_day_names: [Вс, Пн, Вт, Ср, Чт, Пт, Сб]
month_names: [~, Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь]
abbr_month_names: [~, Янв, Фев, Мар, Апр, Май, Июн, Июл, Авг, Сен, Окт, Ноя, Дек]
order:
- year
- month
- day
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%e %B, %Y %H:%M"
am: "д.п."
pm: "п.п."
support:
array:
words_connector: ", "
two_words_connector: " и "
last_word_connector: " и "

View file

@ -0,0 +1,33 @@
sv:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
only_day: "%e"
day_names: [söndag, måndag, tisdag, onsdag, torsdag, fredag, lördag]
abbr_day_names: [sön, mån, tis, ons, tors, fre, lör]
month_names: [~, januari, februari, mars, april, maj, juni, juli, augusti, september, oktober, november, december]
abbr_month_names: [~, jan, feb, mar, apr, may, jun, jul, aug, sep, okt, nov, dec]
order:
- år
- månad
- dag
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%B %d, %Y %H:%M"
am: "fm"
pm: "em"
support:
array:
words_connector: ", "
two_words_connector: " och "
last_word_connector: ", och "

View file

@ -0,0 +1,33 @@
tr:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d/%m/%Y"
short: "%d %b"
long: "%d %B %Y"
only_day: "%e"
day_names: [Pazar, Pazartesi, Salı, Çarşamba, Perşembe, Cuma, Cumartesi]
abbr_day_names: [Paz, Pts, Sal, Çar, Per, Cum, Cts]
month_names: [~, Ocak, Şubat, Mart, Nisan, Mayıs, Haziran, Temmuz, Ağustos, Eylül, Ekim, Kasım, Aralık]
abbr_month_names: [~, Oca, Şub, Mar, Nis, May, Haz, Tem, Ağu, Eyl, Eki, Kas, Ara]
order:
- day
- month
- year
time:
formats:
default: "%a, %b %b %Y %H:%M:%S %z"
short: "%b %d %H:%M"
long: "%d %B, %Y %H:%M"
am: "öö"
pm: "ös"
support:
array:
words_connector: ", "
two_words_connector: " ve "
last_word_connector: " ve "

View file

@ -0,0 +1,33 @@
uk:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%d.%m.%Y"
short: "%d %b"
long: "%e %B, %Y"
only_day: "%e"
day_names: [Неділя, Понеділок, Вівторок, Середа, Четвер, Пятница, Субота]
abbr_day_names: [Нд, Пн, Вт, Ср, Чт, Пт, Сб]
month_names: [~, Січено, Лютий, Березень, Квітень, Травень, Червень, Липень, Серпень, Вересень, Жовтень, Листопад, Грудень]
abbr_month_names: [~, Січ, Лют, Бер, Кві, Тра, Чер, Лип, Сер, Вер, Жов, Лис, Гру]
order:
- year
- month
- day
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%e %B, %Y %H:%M"
am: "д.п."
pm: "п.п"
support:
array:
words_connector: ", "
two_words_connector: " і "
last_word_connector: ", і "

View file

@ -0,0 +1,33 @@
zh_cn:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y 年 %m 月 %d 日"
short: "%b 月 %d 日"
long: "公元 %Y 年 %B 月 %d 日"
only_day: "%e"
day_names: [星期日, 星期一, 星期二, 星期三, 星期四, 星期五, 星期六]
abbr_day_names: [日, 一, 二, 三, 四, 五, 六]
month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
abbr_month_names: [~, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
order:
- year
- month
- day
time:
formats:
default: "%Y 年 %b 月 %d 日 %H:%M:%S %z"
short: "%d 月 %b 日 %H:%M"
long: "%Y 年 %B 月 %d 日 %H 时 %M 分"
am: "上午"
pm: "下午"
support:
array:
words_connector: ""
two_words_connector: "和"
last_word_connector: ",还有"

View file

@ -0,0 +1,33 @@
zh_tw:
date:
formats:
# Use the strftime parameters for formats.
# When no format has been given, it uses default.
# You can provide other formats here if you like!
default: "%Y-%m-%d"
short: "%b%d日"
long: "%Y年%b%d日"
only_day: "%e"
day_names: [星期日, 星期一, 星期二, 星期三, 星期四, 星期五, 星期六]
abbr_day_names: [日, 一, 二, 三, 四, 五, 六]
month_names: [~, 一月, 二月, 三月, 四月, 五月, 六月, 七月, 八月, 九月, 十月, 十一月, 十二月]
abbr_month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
order:
- year
- month
- day
time:
formats:
default: "%Y年%b%d日 %A %H:%M:%S %Z"
short: "%b%d日 %H:%M"
long: "%Y年%b%d日 %H:%M"
am: "上午"
pm: "下午"
support:
array:
words_connector: ", "
two_words_connector: " 和 "
last_word_connector: ", 和 "

View file

@ -0,0 +1,438 @@
# Defines the log level for a Padrino project.
PADRINO_LOG_LEVEL = ENV['PADRINO_LOG_LEVEL'] unless defined?(PADRINO_LOG_LEVEL)
# Defines the logger used for a Padrino project.
PADRINO_LOGGER = ENV['PADRINO_LOGGER'] unless defined?(PADRINO_LOGGER)
module Padrino
##
# @return [Padrino::Logger]
#
# @example
# logger.debug "foo"
# logger.warn "bar"
#
def self.logger
Padrino::Logger.logger
end
##
# Set the padrino logger.
#
# @param [Object] value
# an object that respond to <<, write, puts, debug, warn etc..
#
# @return [Object]
# The given value.
#
# @example using ruby default logger
# require 'logger'
# Padrino.logger = Logger.new(STDOUT)
#
# @example using ActiveSupport
# require 'active_support/buffered_logger'
# Padrino.logger = Buffered.new(STDOUT)
#
def self.logger=(value)
Padrino::Logger.logger = value
end
##
# Padrinos internal logger, using all of Padrino log extensions.
#
class Logger
##
# Ruby (standard) logger levels:
#
# :fatal:: An not handleable error that results in a program crash
# :error:: A handleable error condition
# :warn:: A warning
# :info:: generic (useful) information about system operation
# :debug:: low-level information for developers
# :devel:: Development-related information that is unnecessary in debug mode
#
Levels = {
:fatal => 7,
:error => 6,
:warn => 4,
:info => 3,
:debug => 0,
:devel => -1,
} unless defined?(Levels)
module Extensions
##
# Generate the logging methods for {Padrino.logger} for each log level.
#
Padrino::Logger::Levels.each_pair do |name, number|
define_method(name) do |*args|
return if number < level
if args.size > 1
bench(args[0], args[1], args[2], name)
else
push(args * '', name)
end
end
define_method(:"#{name}?") do
number >= level
end
end
##
# Append a to development logger a given action with time.
#
# @param [string] action
# The action.
#
# @param [float] time
# Time duration for the given action.
#
# @param [message] string
# The message that you want to log.
#
# @example
# logger.bench 'GET', started_at, '/blog/categories'
# # => DEBUG - GET (0.0056s) - /blog/categories
#
def bench(action, began_at, message, level=:debug, color=:yellow)
@_pad ||= 8
@_pad = action.to_s.size if action.to_s.size > @_pad
duration = Time.now - began_at
color = :red if duration > 1
action = colorize(action.to_s.upcase.rjust(@_pad), color)
duration = colorize('%0.4fs' % duration, :bold, color)
push "#{action} (#{duration}) #{message}", level
end
##
# Appends a message to the log. The methods yield to an optional block and
# the output of this block will be appended to the message.
#
# @param [String] message
# The message that you want write to your stream.
#
# @param [String] level
# The level one of :debug, :warn etc. ...
#
#
def push(message = nil, level = nil)
add(Padrino::Logger::Levels[level], format(message, level))
end
##
# Formats the log message. This method is a noop and should be implemented by other
# logger components such as {Padrino::Logger}.
#
# @param [String] message
# The message to format.
#
# @param [String,Symbol] level
# The log level, one of :debug, :warn ...
def format(message, level)
message
end
##
# The debug level, with some style added. May be reimplemented.
#
# @example
# stylized_level(:debug) => DEBUG
#
# @param [String,Symbol] level
# The log level.
#
def stylized_level(level)
level.to_s.upcase.rjust(7)
end
##
# Colorizes a string for colored console output. This is a noop and can be reimplemented
# to colorize the string as needed.
#
# @see
# ColorizedLogger
#
# @param [string]
# The string to be colorized.
#
# @param [Array<Symbol>]
# The colors to use. Should be applied in the order given.
def colorize(string, *colors)
string
end
##
# Turns a logger with LoggingExtensions into a logger with colorized output.
#
# @example
# Padrino.logger = Logger.new($stdout)
# Padrino.logger.colorize!
# Padrino.logger.debug("Fancy Padrino debug string")
def colorize!
self.extend(Colorize)
end
end
module Colorize
# Colors for levels
ColoredLevels = {
:fatal => [:bold, :red],
:error => [:red],
:warn => [:yellow],
:info => [:green],
:debug => [:cyan],
:devel => [:magenta]
} unless defined?(ColoredLevels)
##
# Colorize our level.
#
# @param [String, Symbol] level
#
# @see Padrino::Logging::ColorizedLogger::ColoredLevels
#
def colorize(string, *colors)
colors.each do |c|
string = string.send(c)
end
string
end
def stylized_level(level)
style = ColoredLevels[level].map { |c| "\e[%dm" % String.colors[c] } * ''
[style, super, "\e[0m"] * ''
end
end
include Extensions
attr_accessor :auto_flush, :level, :log_static
attr_reader :buffer, :colorize_logging, :init_args, :log
##
# Configuration for a given environment, possible options are:
#
# :log_level:: Once of [:fatal, :error, :warn, :info, :debug]
# :stream:: Once of [:to_file, :null, :stdout, :stderr] our your custom stream
# :log_level::
# The log level from, e.g. :fatal or :info. Defaults to :warn in the
# production environment and :debug otherwise.
# :auto_flush::
# Whether the log should automatically flush after new messages are
# added. Defaults to true.
# :format_datetime:: Format of datetime. Defaults to: "%d/%b/%Y %H:%M:%S"
# :format_message:: Format of message. Defaults to: ""%s - - [%s] \"%s\"""
# :log_static:: Whether or not to show log messages for static files. Defaults to: false
# :colorize_logging:: Whether or not to colorize log messages. Defaults to: true
#
# @example
# Padrino::Logger::Config[:development] = { :log_level => :debug, :stream => :to_file }
# # or you can edit our defaults
# Padrino::Logger::Config[:development][:log_level] = :error
# # or you can use your stream
# Padrino::Logger::Config[:development][:stream] = StringIO.new
#
# Defaults are:
#
# :production => { :log_level => :warn, :stream => :to_file }
# :development => { :log_level => :debug, :stream => :stdout }
# :test => { :log_level => :fatal, :stream => :null }
#
# In some cases, configuring the loggers before loading the framework is necessary.
# You can do so by setting PADRINO_LOGGER:
#
# PADRINO_LOGGER = { :staging => { :log_level => :debug, :stream => :to_file }}
#
Config = {
:production => { :log_level => :warn, :stream => :to_file },
:development => { :log_level => :debug, :stream => :stdout, :format_datetime => '' },
:test => { :log_level => :debug, :stream => :null }
}
Config.merge!(PADRINO_LOGGER) if PADRINO_LOGGER
@@mutex = Mutex.new
def self.logger
@_logger || setup!
end
def self.logger=(logger)
logger.extend(Padrino::Logger::Extensions)
@_logger = logger
end
##
# Setup a new logger.
#
# @return [Padrino::Logger]
# A {Padrino::Logger} instance
#
def self.setup!
self.logger = begin
config_level = (PADRINO_LOG_LEVEL || Padrino.env || :test).to_sym # need this for PADRINO_LOG_LEVEL
config = Config[config_level]
unless config
warn("No logging configuration for :#{config_level} found, falling back to :production")
config = Config[:production]
end
stream = case config[:stream]
when :to_file
FileUtils.mkdir_p(Padrino.root('log')) unless File.exists?(Padrino.root('log'))
File.new(Padrino.root('log', "#{Padrino.env}.log"), 'a+')
when :null then StringIO.new
when :stdout then $stdout
when :stderr then $stderr
else config[:stream] # return itself, probabilly is a custom stream.
end
Padrino::Logger.new(config.merge(:stream => stream))
end
end
##
# To initialize the logger you create a new object, proxies to set_log.
#
# @param [Hash] options
#
# @option options [Symbol] :stream ($stdout)
# Either an IO object or a name of a logfile. Defaults to $stdout
#
# @option options [Symbol] :log_level (:production in the production environment and :debug otherwise)
# The log level from, e.g. :fatal or :info.
#
# @option options [Symbol] :auto_flush (true)
# Whether the log should automatically flush after new messages are
# added. Defaults to true.
#
# @option options [Symbol] :format_datetime (" [%d/%b/%Y %H:%M:%S] ")
# Format of datetime.
#
# @option options [Symbol] :format_message ("%s -%s%s")
# Format of message.
#
# @option options [Symbol] :log_static (false)
# Whether or not to show log messages for static files.
#
# @option options [Symbol] :colorize_logging (true)
# Whether or not to colorize log messages. Defaults to: true.
#
def initialize(options={})
@buffer = []
@auto_flush = options.has_key?(:auto_flush) ? options[:auto_flush] : true
@level = options[:log_level] ? Padrino::Logger::Levels[options[:log_level]] : Padrino::Logger::Levels[:debug]
@log = options[:stream] || $stdout
@log.sync = true
@format_datetime = options[:format_datetime] || "%d/%b/%Y %H:%M:%S"
@format_message = options[:format_message] || "%s - %s %s"
@log_static = options.has_key?(:log_static) ? options[:log_static] : false
@colorize_logging = options.has_key?(:colorize_logging) ? options[:colorize_logging] : true
colorize! if @colorize_logging
end
##
# Flush the entire buffer to the log object.
#
def flush
return unless @buffer.size > 0
@@mutex.synchronize do
@log.write(@buffer.join(''))
@buffer.clear
end
end
##
# Close and remove the current log object.
#
# @return [NilClass]
#
def close
flush
@log.close if @log.respond_to?(:close) && !@log.tty?
@log = nil
end
##
# Adds a message to the log - for compatibility with other loggers.
#
def add(level, message = nil)
write(message)
end
##
# Directly append message to the log.
#
# @param [String] message
# The message
#
def <<(message = nil)
message << "\n" unless message[-1] == ?\n
@@mutex.synchronize {
@buffer << message
}
flush if @auto_flush
message
end
alias :write :<<
def format(message, level)
@format_message % [stylized_level(level), colorize(Time.now.strftime(@format_datetime), :yellow), message.to_s.strip]
end
##
# Padrino::Logger::Rack forwards every request to an +app+ given, and
# logs a line in the Apache common log format to the +logger+, or
# rack.errors by default.
#
class Rack
def initialize(app, uri_root)
@app = app
@uri_root = uri_root.sub(/\/$/,"")
end
def call(env)
env['rack.logger'] = Padrino.logger
began_at = Time.now
status, header, body = @app.call(env)
log(env, status, header, began_at) if logger.debug?
[status, header, body]
end
private
def log(env, status, header, began_at)
return if env['sinatra.static_file'] && (!logger.respond_to?(:log_static) || !logger.log_static)
logger.bench(
env["REQUEST_METHOD"],
began_at,
[
@uri_root.to_s,
env["PATH_INFO"],
env["QUERY_STRING"].empty? ? "" : "?" + env["QUERY_STRING"],
' - ',
logger.colorize(status.to_s[0..3], :bold),
' ',
code_to_name(status)
] * '',
:debug,
:magenta
)
end
def code_to_name(status)
::Rack::Utils::HTTP_STATUS_CODES[status.to_i] || ''
end
end
end
end
module Kernel
##
# Define a logger available every where in our app
#
def logger
Padrino.logger
end
end

View file

@ -0,0 +1,58 @@
module Padrino
module Module
attr_accessor :root
##
# Register this module as being loaded from a gem. This automatically
# sets the root and therefore the dependency paths correctly.
#
# @param [String] name
# The name of the gem. Has to be the name as stated in the gemspec.
#
# @returns the gems root.
def gem!(name)
self.root = Padrino.gem(name, self)
end
##
# Helper method for file references within a Padrino module.
#
# @param [Array<String>] args
# The directories to join to {Module.root}.
#
# @return [String]
# The absolute path.
#
# @example
# module MyModule
# extend Padrino::Module
# gem! 'my_gem'
# end
# Module.root!
def root(*args)
File.expand_path(File.join(@root, *args))
end
##
# Returns the list of path globs to load as dependencies.
# Appends custom dependency patterns to the be loaded for Padrino.
#
# @return [Array<String>]
# The dependency paths.
#
# @example
# module MyModule
# extend Padrino::Module
# gem! 'my_gem'
# end
#
# Module.dependency_paths << "#{MyModule.root}/uploaders/*.rb"
#
def dependency_paths
[
"#{root}/lib/**/*.rb", "#{root}/shared/lib/**/*.rb",
"#{root}/models/**/*.rb", "#{root}/shared/models/**/*.rb"
]
end
end
end

View file

@ -0,0 +1,234 @@
module Padrino
##
# Represents a particular mounted Padrino application.
# Stores the name of the application (app folder name) and url mount path.
#
# @example
# Mounter.new("blog_app", :app_class => "Blog").to("/blog")
# Mounter.new("blog_app", :app_file => "/path/to/blog/app.rb").to("/blog")
#
class Mounter
class MounterException < RuntimeError
end
attr_accessor :name, :uri_root, :app_file, :app_class, :app_root, :app_obj, :app_host
##
# @param [String, Padrino::Application] name
# The app name or the {Padrino::Application} class.
#
# @param [Hash] options
# @option options [Symbol] :app_class (Detected from name)
# @option options [Symbol] :app_file (Automatically detected)
# @option options [Symbol] :app_obj (Detected)
# @option options [Symbol] :app_root (Directory of :app_file)
# @option options [Symbol] :gem The gem to load the app from (Detected from name)
#
def initialize(name, options={})
@name = name.to_s
@app_class = options[:app_class] || @name.camelize
@gem = options[:gem] || @app_class.split("::").first.underscore
@app_file = options[:app_file] || locate_app_file
@app_obj = options[:app_obj] || app_constant || locate_app_object
ensure_app_file! || ensure_app_object!
@app_root = options[:app_root] || File.dirname(@app_file)
@uri_root = "/"
Padrino::Reloader.exclude_constants << @app_class
end
##
# Registers the mounted application onto Padrino.
#
# @param [String] mount_url
# Path where we mount the app.
#
# @example
# Mounter.new("blog_app").to("/blog")
#
def to(mount_url)
@uri_root = mount_url
Padrino.insert_mounted_app(self)
self
end
##
# Registers the mounted application onto Padrino for the given host.
#
# @param [String] mount_host
# Host name.
#
# @example
# Mounter.new("blog_app").to("/blog").host("blog.padrino.org")
# Mounter.new("blog_app").host("blog.padrino.org")
# Mounter.new("catch_all").host(/.*\.padrino.org/)
#
def host(mount_host)
@app_host = mount_host
Padrino.insert_mounted_app(self)
self
end
##
# Maps Padrino application onto a Padrino::Router.
# For use in constructing a Rack application.
#
# @param [Padrino::Router]
#
# @return [Padrino::Router]
#
# @example
# @app.map_onto(router)
#
def map_onto(router)
app_data, app_obj = self, @app_obj
app_obj.set :uri_root, app_data.uri_root
app_obj.set :app_name, app_data.name
app_obj.set :app_file, app_data.app_file unless ::File.exist?(app_obj.app_file)
app_obj.set :root, app_data.app_root unless app_data.app_root.blank?
app_obj.set :public_folder, Padrino.root('public', app_data.uri_root) unless File.exists?(app_obj.public_folder)
app_obj.set :static, File.exist?(app_obj.public_folder) if app_obj.nil?
app_obj.setup_application! # Initializes the app here with above settings.
router.map(:to => app_obj, :path => app_data.uri_root, :host => app_data.app_host)
end
###
# Returns the route objects for the mounted application.
#
def routes
app_obj.routes
end
###
# Returns the basic route information for each named route.
#
# @return [Array]
# Array of routes.
#
def named_routes
app_obj.routes.map { |route|
name_array = "(#{route.name.to_s.split("_").map { |piece| %Q[:#{piece}] }.join(", ")})"
request_method = route.request_methods.first
next if route.name.blank? || request_method == 'HEAD'
original_path = route.original_path.is_a?(Regexp) ? route.original_path.inspect : route.original_path
full_path = File.join(uri_root, original_path)
OpenStruct.new(:verb => request_method, :identifier => route.name, :name => name_array, :path => full_path)
}.compact
end
##
# Makes two Mounters equal if they have the same name and uri_root.
#
# @param [Padrino::Mounter] other
#
def ==(other)
other.is_a?(Mounter) && self.app_class == other.app_class && self.uri_root == other.uri_root
end
##
# @return [Padrino::Application]
# the class object for the app if defined, nil otherwise.
#
def app_constant
klass = Object
for piece in app_class.split("::")
piece = piece.to_sym
if klass.const_defined?(piece)
klass = klass.const_get(piece)
else
return
end
end
klass
end
protected
##
# Locates and requires the file to load the app constant.
#
def locate_app_object
@_app_object ||= begin
ensure_app_file!
Padrino.require_dependencies(app_file)
app_constant
end
end
##
# Returns the determined location of the mounted application main file.
#
def locate_app_file
candidates = []
candidates << app_constant.app_file if app_constant.respond_to?(:app_file) && File.exist?(app_constant.app_file.to_s)
candidates << Padrino.first_caller if File.identical?(Padrino.first_caller.to_s, Padrino.called_from.to_s)
candidates << Padrino.mounted_root(name.downcase, "app.rb")
simple_name = name.split("::").last.downcase
mod_name = name.split("::")[0..-2].join("::")
Padrino.modules.each do |mod|
if mod.name == mod_name
candidates << mod.root(simple_name, "app.rb")
end
end
candidates << Padrino.root("app", "app.rb")
candidates.find { |candidate| File.exist?(candidate) }
end
###
# Raises an exception unless app_file is located properly.
#
def ensure_app_file!
message = "Unable to locate source file for app '#{app_class}', try with :app_file => '/path/app.rb'"
raise MounterException, message unless @app_file
end
###
# Raises an exception unless app_obj is defined properly.
#
def ensure_app_object!
message = "Unable to locate app for '#{app_class}', try with :app_class => 'MyAppClass'"
raise MounterException, message unless @app_obj
end
end
class << self
attr_writer :mounted_root # Set root directory where padrino searches mounted apps
##
# @param [Array] args
#
# @return [String]
# the root to the mounted apps base directory.
#
def mounted_root(*args)
Padrino.root(@mounted_root ||= "", *args)
end
##
# @return [Array]
# the mounted padrino applications (MountedApp objects)
#
def mounted_apps
@mounted_apps ||= []
end
##
# Inserts a Mounter object into the mounted applications (avoids duplicates).
#
# @param [Padrino::Mounter] mounter
#
def insert_mounted_app(mounter)
Padrino.mounted_apps.push(mounter) unless Padrino.mounted_apps.include?(mounter)
end
##
# Mounts a new sub-application onto Padrino project.
#
# @see Padrino::Mounter#new
#
# @example
# Padrino.mount("blog_app").to("/blog")
#
def mount(name, options={})
Mounter.new(name, options)
end
end
end

View file

@ -0,0 +1,341 @@
require 'pathname'
module Padrino
##
# High performance source code reloader middleware
#
module Reloader
##
# This reloader is suited for use in a many environments because each file
# will only be checked once and only one system call to stat(2) is made.
#
# Please note that this will not reload files in the background, and does so
# only when explicitly invoked.
#
# The modification times for every file in a project.
MTIMES = {}
# The list of files loaded as part of a project.
LOADED_FILES = {}
# The list of object constants and classes loaded as part of the project.
LOADED_CLASSES = {}
class << self
##
# Specified folders can be excluded from the code reload detection process.
# Default excluded directories at Padrino.root are: test, spec, features, tmp, config, db and public
#
def exclude
@_exclude ||= %w(test spec tmp features config public db).map { |path| Padrino.root(path) }
end
##
# Specified constants can be excluded from the code unloading process.
#
def exclude_constants
@_exclude_constants ||= Set.new
end
##
# Specified constants can be configured to be reloaded on every request.
# Default included constants are: [none]
#
def include_constants
@_include_constants ||= Set.new
end
##
# Reload all files with changes detected.
#
def reload!
# Detect changed files
rotation do |file, mtime|
# Retrive the last modified time
new_file = MTIMES[file].nil?
previous_mtime = MTIMES[file] ||= mtime
logger.devel "Detected a new file #{file}" if new_file
# We skip to next file if it is not new and not modified
next unless new_file || mtime > previous_mtime
# Now we can reload our file
apps = mounted_apps_of(file)
if apps.present?
apps.each { |app| app.app_obj.reload! }
else
safe_load(file, :force => new_file)
# Reload also apps
Padrino.mounted_apps.each do |app|
app.app_obj.reload! if app.app_obj.dependencies.include?(file)
end
end
end
end
##
# Remove files and classes loaded with stat
#
def clear!
clear_modification_times
clear_loaded_classes
clear_loaded_files_and_features
end
##
# Returns true if any file changes are detected and populates the MTIMES cache
#
def changed?
changed = false
rotation do |file, mtime|
new_file = MTIMES[file].nil?
previous_mtime = MTIMES[file]
changed = true if new_file || mtime > previous_mtime
end
changed
end
alias :run! :changed?
##
# We lock dependencies sets to prevent reloading of protected constants
#
def lock!
klasses = ObjectSpace.classes do |klass|
klass._orig_klass_name.split('::')[0]
end
klasses = klasses | Padrino.mounted_apps.map { |app| app.app_class }
Padrino::Reloader.exclude_constants.merge(klasses)
end
##
# A safe Kernel::require which issues the necessary hooks depending on results
#
def safe_load(file, options={})
began_at = Time.now
force = options[:force]
file = figure_path(file)
reload = should_reload?(file)
m_time = modification_time(file)
return if !force && m_time && !reload
remove_loaded_file_classes(file)
remove_loaded_file_features(file)
# Duplicate objects and loaded features before load file
klasses = ObjectSpace.classes
files = Set.new($LOADED_FEATURES.dup)
reload_deps_of_file(file)
# And finally load the specified file
begin
logger.devel :loading, began_at, file if !reload
logger.debug :reload, began_at, file if reload
$LOADED_FEATURES.delete(file) if files.include?(file)
Padrino::Utils.silence_output
loaded = false
require(file)
loaded = true
update_modification_time(file)
rescue SyntaxError => e
logger.error "Cannot require #{file} due to a syntax error: #{e.message}"
ensure
Padrino::Utils.unsilence_output
new_constants = ObjectSpace.new_classes(klasses)
if loaded
process_loaded_file(:file => file,
:constants => new_constants,
:files => files)
else
logger.devel "Failed to load #{file}; removing partially defined constants"
unload_constants(new_constants)
end
end
end
##
# Returns true if the file is defined in our padrino root.
#
def figure_path(file)
return file if Pathname.new(file).absolute?
$:.each do |path|
found = File.join(path, file)
return File.expand_path(found) if File.exist?(found)
end
file
end
##
# Removes the specified class and constant.
#
def remove_constant(const)
return if exclude_constants.any? { |c| const._orig_klass_name.index(c) == 0 } &&
!include_constants.any? { |c| const._orig_klass_name.index(c) == 0 }
begin
parts = const.to_s.sub(/^::(Object)?/, 'Object::').split('::')
object = parts.pop
base = parts.empty? ? Object : Inflector.constantize(parts * '::')
base.send :remove_const, object
logger.devel "Removed constant: #{const} from #{base}"
rescue NameError; end
end
private
###
# Clear instance variables that keep track of # loaded features/files/mtimes.
#
def clear_modification_times
MTIMES.clear
end
def clear_loaded_classes
LOADED_CLASSES.each do |file, klasses|
klasses.each { |klass| remove_constant(klass) }
LOADED_CLASSES.delete(file)
end
end
def clear_loaded_files_and_features
LOADED_FILES.each do |file, dependencies|
dependencies.each { |dependency| $LOADED_FEATURES.delete(dependency) }
$LOADED_FEATURES.delete(file)
end
end
###
# Macro for mtime query.
#
def modification_time(file)
MTIMES[file]
end
###
# Macro for mtime update.
#
def update_modification_time(file)
MTIMES[file] = File.mtime(file)
end
###
# Tracks loaded file features/classes/constants:
#
def process_loaded_file(*args)
options = args.extract_options!
new_constants = options[:constants]
files = options[:files]
file = options[:file]
# Store the file details
LOADED_CLASSES[file] = new_constants
LOADED_FILES[file] = Set.new($LOADED_FEATURES) - files - [file]
# Track only features in our Padrino.root
LOADED_FILES[file].delete_if { |feature| !in_root?(feature) }
end
###
# Unloads all constants in new_constants.
#
def unload_constants(new_constants)
new_constants.each { |klass| remove_constant(klass) }
end
###
# Safe load dependencies of a file.
#
def reload_deps_of_file(file)
if features = LOADED_FILES.delete(file)
features.each { |feature| safe_load(feature, :force => true) }
end
end
##
# Check if file was changed or if force a reload.
#
def should_reload?(file)
MTIMES[file] && File.mtime(file) > MTIMES[file]
end
##
# Removes all classes declared in the specified file.
#
def remove_loaded_file_classes(file)
if klasses = LOADED_CLASSES.delete(file)
klasses.each { |klass| remove_constant(klass) }
end
end
##
# Remove all loaded fatures with our file.
#
def remove_loaded_file_features(file)
if features = LOADED_FILES[file]
features.each { |feature| $LOADED_FEATURES.delete(feature) }
end
end
##
# Return the mounted_apps providing the app location.
# Can be an array because in one app.rb we can define multiple Padrino::Application.
#
def mounted_apps_of(file)
file = figure_path(file)
Padrino.mounted_apps.find_all { |app| File.identical?(file, app.app_file) }
end
##
# Returns true if file is in our Padrino.root.
#
def in_root?(file)
# This is better but slow:
# Pathname.new(Padrino.root).find { |f| File.identical?(Padrino.root(f), figure_path(file)) }
figure_path(file).index(Padrino.root) == 0
end
##
# Searches Ruby files in your +Padrino.load_paths+ , Padrino::Application.load_paths
# and monitors them for any changes.
#
def rotation
files_for_rotation.uniq.map do |file|
file = File.expand_path(file)
next if Padrino::Reloader.exclude.any? { |base| file.index(base) == 0 } || !File.exist?(file)
yield file, File.mtime(file)
end.compact
end
##
# Creates an array of paths for use in #rotation.
#
def files_for_rotation
files = Padrino.load_paths.map { |path| Dir["#{path}/**/*.rb"] }.flatten
files = files | Padrino.mounted_apps.map { |app| app.app_file }
files = files | Padrino.mounted_apps.map { |app| app.app_obj.dependencies }.flatten
end
end # self
##
# This class acts as a Rack middleware to be added to the application stack.
# This middleware performs a check and reload for source files at the start
# of each request, but also respects a specified cool down time
# during which no further action will be taken.
#
class Rack
def initialize(app, cooldown=1)
@app = app
@cooldown = cooldown
@last = (Time.now - cooldown)
end
# Invoked in order to perform the reload as part of the request stack.
def call(env)
if @cooldown && Time.now > @last + @cooldown
Thread.list.size > 1 ? Thread.exclusive { Padrino.reload! } : Padrino.reload!
@last = Time.now
end
@app.call(env)
end
end
end
end

View file

@ -0,0 +1,95 @@
module Padrino
##
# This class is an extended version of Rack::URLMap.
#
# Padrino::Router like Rack::URLMap dispatches in such a way that the
# longest paths are tried first, since they are most specific.
#
# Features:
#
# * Map a path to the specified App
# * Ignore server names (this solve issues with vhost and domain aliases)
# * Use hosts instead of server name for mappings (this help us with our vhost and domain aliases)
#
# @example
#
# routes = Padrino::Router.new do
# map(:path => "/", :to => PadrinoWeb, :host => "padrino.local")
# map(:path => "/", :to => Admin, :host => "admin.padrino.local")
# end
# run routes
#
# routes = Padrino::Router.new do
# map(:path => "/", :to => PadrinoWeb, :host => /*.padrino.local/)
# end
# run routes
#
# @api semipublic
class Router
def initialize(*mapping, &block)
@mapping = []
mapping.each { |m| map(m) }
instance_eval(&block) if block
end
##
# Map a route path and host to a specified application.
#
# @param [Hash] options
# The options to map.
# @option options [Sinatra::Application] :to
# The class of the application to mount.
# @option options [String] :path ("/")
# The path to map the specified application.
# @option options [String] :host
# The host to map the specified application.
#
# @example
# map(:path => "/", :to => PadrinoWeb, :host => "padrino.local")
#
# @return [Array] The sorted route mappings.
# @api semipublic
def map(options={})
path = options[:path] || "/"
host = options[:host]
app = options[:to]
raise ArgumentError, "paths need to start with /" if path[0] != ?/
raise ArgumentError, "app is required" if app.nil?
path = path.chomp('/')
match = Regexp.new("^#{Regexp.quote(path).gsub('/', '/+')}(.*)", nil, 'n')
host = Regexp.new("^#{Regexp.quote(host)}$", true, 'n') unless host.nil? || host.is_a?(Regexp)
@mapping << [host, path, match, app]
sort!
end
# The call handler setup to route a request given the mappings specified.
def call(env)
path_info = env["PATH_INFO"].to_s
script_name = env['SCRIPT_NAME']
http_host = env['HTTP_HOST']
@mapping.each do |host, path, match, app|
next unless host.nil? || http_host =~ host
next unless path_info =~ match && rest = $1
next unless rest.empty? || rest[0] == ?/
rest = "/" if rest.empty?
return app.call(
env.merge(
'SCRIPT_NAME' => (script_name + path),
'PATH_INFO' => rest))
end
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path_info}"]]
end
private
def sort!
@mapping = @mapping.sort_by { |h, p, m, a| -p.size }
end
end
end

View file

@ -0,0 +1,77 @@
module Padrino
##
# Runs the Padrino apps as a self-hosted server using:
# thin, mongrel, or WEBrick in that order.
#
# @example
# Padrino.run! # with these defaults => host: "127.0.0.1", port: "3000", adapter: the first found
# Padrino.run!("0.0.0.0", "4000", "mongrel") # use => host: "0.0.0.0", port: "4000", adapter: "mongrel"
#
def self.run!(options={})
Padrino.load!
Server.start(Padrino.application, options)
end
##
# This module builds a Padrino server to run the project based on available handlers.
#
class Server < Rack::Server
# Server Handlers
Handlers = [:thin, :puma, :mongrel, :trinidad, :webrick]
# Starts the application on the available server with specified options.
def self.start(app, opts={})
options = {}.merge(opts) # We use a standard hash instead of Thor::CoreExt::HashWithIndifferentAccess
options.symbolize_keys!
options[:Host] = options.delete(:host) || '127.0.0.1'
options[:Port] = options.delete(:port) || 3000
options[:AccessLog] = []
if options[:daemonize]
options[:pid] = File.expand_path(options[:pid].blank? ? 'tmp/pids/server.pid' : opts[:pid])
FileUtils.mkdir_p(File.dirname(options[:pid]))
end
options[:server] = detect_rack_handler if options[:server].blank?
new(options, app).start
end
def initialize(options, app)
@options, @app = options, app
end
# Starts the application on the available server with specified options.
def start
puts "=> Padrino/#{Padrino.version} has taken the stage #{Padrino.env} at http://#{options[:Host]}:#{options[:Port]}"
[:INT, :TERM].each { |sig| trap(sig) { exit } }
super
ensure
puts "<= Padrino leaves the gun, takes the cannoli" unless options[:daemonize]
end
# The application the server will run.
def app
@app
end
alias :wrapped_app :app
def options
@options
end
private
# Detects the supported handler to use.
#
# @example
# detect_rack_handler => <ThinHandler>
#
def self.detect_rack_handler
Handlers.each do |handler|
begin
return handler if Rack::Handler.get(handler.to_s.downcase)
rescue LoadError
rescue NameError
end
end
fail "Server handler (#{Handlers.join(', ')}) not found."
end
end
end

View file

@ -0,0 +1,260 @@
##
# This file loads certain extensions required by Padrino from ActiveSupport.
#
require 'active_support/core_ext/module/aliasing' # alias_method_chain
require 'active_support/core_ext/hash/keys' # symbolize_keys
require 'active_support/core_ext/hash/reverse_merge' # reverse_merge
require 'active_support/core_ext/hash/slice' # slice
require 'active_support/core_ext/object/blank' # present?
require 'active_support/core_ext/array/extract_options' # extract_options
require 'active_support/inflector/methods' # constantize
require 'active_support/inflector/inflections' # pluralize
require 'active_support/core_ext/string/output_safety' # SafeBuffer and html_safe
require 'active_support/inflections' # load default inflections
require 'yaml' unless defined?(YAML) # load yaml for i18n
require 'win32console' if RUBY_PLATFORM =~ /(win|m)32/ # ruby color support for win
##
# This is an adapted version of active_support/core_ext/string/inflections.rb
# to prevent loading several dependencies including I18n gem.
#
# Issue: https://github.com/rails/rails/issues/1526
#
class String
##
# Returns the plural form of the word in the string.
#
# "post".pluralize # => "posts"
# "octopus".pluralize # => "octopi"
# "sheep".pluralize # => "sheep"
# "words".pluralize # => "words"
# "the blue mailman".pluralize # => "the blue mailmen"
# "CamelOctopus".pluralize # => "CamelOctopi"
#
def pluralize
ActiveSupport::Inflector.pluralize(self)
end
##
# Returns the singular form of the word in the string.
#
# "posts".singularize # => "post"
# "octopi".singularize # => "octopus"
# "sheep".singularize # => "sheep"
# "words".singularize # => "word"
# "the blue mailmen".singularize # => "the blue mailman"
# "CamelOctopi".singularize # => "CamelOctopus"
#
def singularize
ActiveSupport::Inflector.singularize(self)
end
##
# +constantize+ tries to find a declared constant with the name specified
# in the string. It raises a NameError when the name is not in CamelCase
# or is not initialized.
#
# "Module".constantize # => Module
# "Class".constantize # => Class
#
def constantize
ActiveSupport::Inflector.constantize(self)
end
##
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
#
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
#
# "ActiveRecord".underscore # => "active_record"
# "ActiveRecord::Errors".underscore # => active_record/errors
#
def underscore
ActiveSupport::Inflector.underscore(self)
end
##
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
#
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
#
# "active_record".camelize # => "ActiveRecord"
# "active_record".camelize(:lower) # => "activeRecord"
# "active_record/errors".camelize # => "ActiveRecord::Errors"
# "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
#
def camelize(first_letter = :upper)
case first_letter
when :upper then ActiveSupport::Inflector.camelize(self, true)
when :lower then ActiveSupport::Inflector.camelize(self, false)
end
end
alias_method :camelcase, :camelize
##
# Create a class name from a plural table name like Rails does for table names to models.
# Note that this returns a string and not a class. (To convert to an actual class
# follow +classify+ with +constantize+.)
#
# "egg_and_hams".classify # => "EggAndHam"
# "posts".classify # => "Post"
#
# Singular names are not handled correctly.
#
# "business".classify # => "Busines"
#
def classify
ActiveSupport::Inflector.classify(self)
end
end
module ObjectSpace
class << self
##
# Returns all the classes in the object space.
# Optionally, a block can be passed, for example the following code
# would return the classes that start with the character "A":
#
# ObjectSpace.classes do |klass|
# if klass.to_s[0] == "A"
# klass
# end
# end
#
def classes(&block)
rs = Set.new
ObjectSpace.each_object(Class).each do |klass|
if block
if r = block.call(klass)
# add the returned value if the block returns something
rs << r
end
else
rs << klass
end
end
rs
end
##
# Returns a list of existing classes that are not included in "snapshot"
# This method is useful to get the list of new classes that were loaded
# after an event like requiring a file.
# Usage:
#
# snapshot = ObjectSpace.classes
# # require a file
# ObjectSpace.new_classes(snapshot)
#
def new_classes(snapshot)
self.classes do |klass|
if !snapshot.include?(klass)
klass
end
end
end
end
end
##
# FileSet helper method for iterating and interacting with files inside a directory
#
module FileSet
extend self
##
# Iterates over every file in the glob pattern and yields to a block
# Returns the list of files matching the glob pattern
# FileSet.glob('padrino-core/application/*.rb', __FILE__) { |file| load file }
#
def glob(glob_pattern, file_path=nil, &block)
glob_pattern = File.join(File.dirname(file_path), glob_pattern) if file_path
file_list = Dir.glob(glob_pattern).sort
file_list.each { |file| block.call(file) }
file_list
end
##
# Requires each file matched in the glob pattern into the application
# FileSet.glob_require('padrino-core/application/*.rb', __FILE__)
#
def glob_require(glob_pattern, file_path=nil)
glob(glob_pattern, file_path) { |f| require f }
end
end
##
# Removes indentation
# Add colors
#
# @example
# help <<-EOS.undent
# Here my help usage
# sample_code
#
# Fix
# EOS
# puts help.red.bold
#
class String
def self.colors
@_colors ||= {
:clear => 0,
:bold => 1,
:black => 30,
:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:magenta => 35,
:cyan => 36,
:white => 37
}
end
colors.each do |color, value|
define_method(color) do
["\e[", value.to_s, "m", self, "\e[", self.class.colors[:clear], "m"] * ''
end
end
def undent
gsub(/^.{#{slice(/^ +/).size}}/, '')
end
end
##
# Make sure we can always use the class name
# In reloader for accessing class_name Foo._orig_klass_name
#
class Module
alias :_orig_klass_name :to_s
end
##
# Loads our locale configuration files
#
I18n.load_path += Dir["#{File.dirname(__FILE__)}/locale/*.yml"] if defined?(I18n)
##
# Used to determine if this file has already been required
#
module SupportLite; end
module Padrino
class Utils
###
# Silences output verbosity level so load
# errors are not visible when safe_load(file)
#
def self.silence_output
@verbosity_level, $-v = $-v, nil
end
def self.unsilence_output
$-v = @verbosity_level
end
end
end

View file

@ -0,0 +1,21 @@
module Padrino
##
# This module it's used for bootstrap with padrino rake
# third party tasks, in your gem/plugin/extension you
# need only do this:
#
# @example
# Padrino::Tasks.files << yourtask.rb
# Padrino::Tasks.files.concat(Dir["/path/to/all/my/tasks/*.rb"])
# Padrino::Tasks.files.unshift("yourtask.rb")
#
module Tasks
##
# Returns a list of files to handle with padrino rake
#
def self.files
@files ||= []
end
end
end

View file

@ -0,0 +1,20 @@
#
# Manages current Padrino version for use in gem generation.
#
# We put this in a separate file so you can get padrino version
# without include full padrino core.
#
module Padrino
# The version constant for the current version of Padrino.
VERSION = '0.11.4' unless defined?(Padrino::VERSION)
#
# The current Padrino version.
#
# @return [String]
# The version number.
#
def self.version
VERSION
end
end # Padrino

View file

@ -0,0 +1,43 @@
#!/usr/bin/env gem build
# encoding: utf-8
require File.expand_path("../lib/padrino-core/version.rb", __FILE__)
Gem::Specification.new do |s|
s.name = "padrino-core"
s.rubyforge_project = "padrino-core"
s.authors = ["Padrino Team", "Nathan Esquenazi", "Davide D'Agostino", "Arthur Chiu"]
s.email = "padrinorb@gmail.com"
s.summary = "The required Padrino core gem"
s.homepage = "http://www.padrinorb.com"
s.description = "The Padrino core gem required for use of this framework"
s.required_rubygems_version = ">= 1.3.6"
s.version = Padrino.version
s.date = Time.now.strftime("%Y-%m-%d")
s.extra_rdoc_files = Dir["*.rdoc"]
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.rdoc_options = ["--charset=UTF-8"]
# TODO remove after a couple versions
# s.post_install_message = "\e[32m" + ("*" * 20)
# s.post_install_message << "\n UPGRADE NOTES\n\n\e[31m When upgrading, please 'enable :sessions' for each application"
# s.post_install_message << " as shown here:\e[0m http://bit.ly/kODKMx\n"
# s.post_install_message << "\e[31m When upgrading, please 'register Padrino::Rendering' for each application"
# s.post_install_message << " as shown here:\e[0m https://gist.github.com/1d36a35794dbbd664ea4"
# s.post_install_message << "\n\e[32m" + ("*" * 20) + "\n\e[0m"
s.add_dependency("tilt", "~> 1.4.1")
if ENV["SINATRA_EDGE"]
s.add_dependency("sinatra")
else
s.add_dependency("sinatra", "~> 1.4.2")
end
s.add_dependency("http_router", "~> 0.11.0")
s.add_dependency("thor", "~> 0.17.0")
s.add_dependency("activesupport", ">= 3.1", "< 4.0")
s.add_dependency("rack-protection", ">= 1.5.0")
end

View file

@ -0,0 +1,4 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in app_gem.gemspec
gemspec

View file

@ -0,0 +1,3 @@
class AppGem::App < Padrino::Application
set :version, AppGem::VERSION
end

View file

@ -0,0 +1,17 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/app_gem/version', __FILE__)
Gem::Specification.new do |gem|
gem.authors = ["Florian Gilcher"]
gem.email = ["florian.gilcher@asquera.de"]
gem.description = %q{TODO: Write a gem description}
gem.summary = %q{TODO: Write a gem summary}
gem.homepage = ""
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "app_gem"
gem.require_paths = ["app", "lib"]
gem.version = AppGem::VERSION
end

View file

@ -0,0 +1,7 @@
require 'padrino'
module AppGem
extend Padrino::Module
gem! 'app_gem'
end

View file

@ -0,0 +1,3 @@
module AppGem
VERSION = "0.0.1"
end

View file

@ -0,0 +1,6 @@
---
:test: bacon
:mock: mocha
:orm: datamapper
:renderer: erb
:script: jquery

View file

@ -0,0 +1,7 @@
.DS_Store
log/**/*
tmp/**/*
vendor/gems/gems
vendor/gems/specifications
vendor/gems/doc
vendor/gems/environment.rb

View file

@ -0,0 +1,32 @@
PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
module LibDemo
def self.give_me_a_random
@rand ||= rand(100)
end
end
class Complex1Demo < Padrino::Application
set :reload, true
get("/old"){ "Old Sinatra Way" }
end
class Complex2Demo < Padrino::Application
set :reload, true
get("/old"){ "Old Sinatra Way" }
controllers :var do
get(:destroy){ params.inspect }
end
get("/"){ "The magick number is: 12!" } # Change only the number!!!
end
Complex1Demo.controllers do
get("/"){ "Given random #{LibDemo.give_me_a_random}" }
end
Complex2Demo.controllers do
end
Padrino.load!

View file

@ -0,0 +1,33 @@
PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
# Remove this comment if you want do some like this: ruby PADRINO_ENV=test app.rb
#
# require 'rubygems'
# require 'padrino-core'
#
class SimpleDemo < Padrino::Application
set :reload, true
before { true }
after { true }
error(404) { "404" }
end
SimpleDemo.controllers do
get "/" do
'The magick number is: 2767356926488785838763860464013972991031534522105386787489885890443740254365!' # Change only the number!!!
end
get "/rand" do
rand(2 ** 256).to_s
end
end
## If you want use this as a standalone app uncomment:
#
# Padrino.mount("SimpleDemo").to("/")
# Padrino.run! unless Padrino.loaded? # If you enable reloader prevent to re-run the app
#
# Then run it from your console: ruby -I"lib" test/fixtures/apps/simple.rb
#
Padrino.load!

View file

@ -0,0 +1,9 @@
# This file will be safe loaded three times.
# The first one fail because B and C constant are not defined
# The second one file because B requires C constant so will not be loaded
# The third one B and C are defined
# But here we need some of b.rb
A_result = [B, C]
A = "A"

View file

@ -0,0 +1,4 @@
# But here we need some of c.rb and a.rb
B_result = C
B = "B"

View file

@ -0,0 +1,13 @@
class E
def self.fields
@fields ||= []
end
def self.inherited(subclass)
subclass.fields.replace fields.dup
end
G
fields << "name"
end

View file

@ -0,0 +1,4 @@
D = 0 unless defined?(D)
D += 1
raise "SomeThing"

View file

@ -0,0 +1,83 @@
ENV['PADRINO_ENV'] = 'test'
PADRINO_ROOT = File.dirname(__FILE__) unless defined?(PADRINO_ROOT)
require File.expand_path('../../../load_paths', __FILE__)
require File.expand_path('../mini_shoulda', __FILE__)
require 'padrino-core'
require 'json'
require 'rack/test'
require 'rack'
# Rubies < 1.9 don't handle hashes in the properly order so to prevent
# this issue for now we remove extra values from mimetypes.
Rack::Mime::MIME_TYPES.delete(".xsl") # In this way application/xml respond only to .xml
class Sinatra::Base
# Allow assertions in request context
include MiniTest::Assertions
end
class MiniTest::Spec
include Rack::Test::Methods
# Sets up a Sinatra::Base subclass defined with the block
# given. Used in setup or individual spec methods to establish
# the application.
def mock_app(base=Padrino::Application, &block)
@app = Sinatra.new(base, &block)
end
def app
Rack::Lint.new(@app)
end
# Asserts that a file matches the pattern
def assert_match_in_file(pattern, file)
assert File.exist?(file), "File '#{file}' does not exist!"
assert_match pattern, File.read(file)
end
# Delegate other missing methods to response.
def method_missing(name, *args, &block)
if response && response.respond_to?(name)
response.send(name, *args, &block)
else
super(name, *args, &block)
end
rescue Rack::Test::Error # no response yet
super(name, *args, &block)
end
alias :response :last_response
def create_template(name, content, options={})
FileUtils.mkdir_p(File.dirname(__FILE__) + "/views")
FileUtils.mkdir_p(File.dirname(__FILE__) + "/views/layouts")
path = "/views/#{name}"
path += ".#{options.delete(:locale)}" if options[:locale].present?
path += ".#{options[:format]}" if options[:format].present?
path += ".erb" unless options[:format].to_s =~ /haml|rss|atom/
path += ".builder" if options[:format].to_s =~ /rss|atom/
file = File.dirname(__FILE__) + path
File.open(file, 'w') { |io| io.write content }
file
end
alias :create_view :create_template
alias :create_layout :create_template
def remove_views
FileUtils.rm_rf(File.dirname(__FILE__) + "/views")
end
def with_template(name, content, options={})
# Build a temp layout
template = create_template(name, content, options)
yield
ensure
# Remove temp layout
File.unlink(template) rescue nil
remove_views
end
alias :with_view :with_template
alias :with_layout :with_template
end

View file

@ -0,0 +1,45 @@
gem 'minitest'
require 'minitest/autorun'
require 'minitest/spec'
require 'mocha/setup'
begin
require 'ruby-debug'
rescue LoadError; end
class MiniTest::Spec
class << self
alias :setup :before unless defined?(Rails)
alias :teardown :after unless defined?(Rails)
alias :should :it
alias :context :describe
def should_eventually(desc)
it("should eventually #{desc}") { skip("Should eventually #{desc}") }
end
end
alias :assert_no_match :refute_match
alias :assert_not_nil :refute_nil
alias :assert_not_equal :refute_equal
end
class ColoredIO
def initialize(io)
@io = io
end
def print(o)
case o
when "." then @io.send(:print, o.green)
when "E" then @io.send(:print, o.red)
when "F" then @io.send(:print, o.yellow)
when "S" then @io.send(:print, o.magenta)
else @io.send(:print, o)
end
end
def puts(*o)
super
end
end
MiniTest::Unit.output = ColoredIO.new(MiniTest::Unit.output)

View file

@ -0,0 +1,125 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require 'haml'
class PadrinoPristine < Padrino::Application; end
class PadrinoTestApp < Padrino::Application; end
class PadrinoTestApp2 < Padrino::Application; end
describe "Application" do
before { Padrino.clear! }
after { remove_views }
context 'for application functionality' do
should 'check default options' do
assert File.identical?(__FILE__, PadrinoPristine.app_file)
assert_equal :padrino_pristine, PadrinoPristine.app_name
assert_equal :test, PadrinoPristine.environment
assert_equal Padrino.root('views'), PadrinoPristine.views
assert PadrinoPristine.raise_errors
assert !PadrinoPristine.logging
assert !PadrinoPristine.sessions
assert !PadrinoPristine.dump_errors
assert !PadrinoPristine.show_exceptions
assert PadrinoPristine.raise_errors
assert !Padrino.configure_apps
end
should 'check haml options on production' do
assert defined?(Haml), 'Haml not defined'
assert_equal :test, PadrinoPristine.environment
assert !PadrinoPristine.haml[:ugly]
Padrino.stub :env, :production do
PadrinoPristine.send :default_configuration!
assert_equal :production, Padrino.env
assert_equal :production, PadrinoPristine.environment
assert PadrinoPristine.haml[:ugly]
PadrinoPristine.environment = :test
end
end
should 'check padrino specific options' do
assert !PadrinoPristine.instance_variable_get(:@_configured)
PadrinoPristine.send(:setup_application!)
assert_equal :padrino_pristine, PadrinoPristine.app_name
assert_equal 'StandardFormBuilder', PadrinoPristine.default_builder
assert PadrinoPristine.instance_variable_get(:@_configured)
assert !PadrinoPristine.reload?
end
should 'set global project settings' do
Padrino.configure_apps { enable :sessions; set :foo, "bar" }
PadrinoTestApp.send(:default_configuration!)
PadrinoTestApp2.send(:default_configuration!)
assert PadrinoTestApp.sessions, "should have sessions enabled"
assert_equal "bar", PadrinoTestApp.settings.foo, "should have foo assigned"
assert_equal PadrinoTestApp.session_secret, PadrinoTestApp2.session_secret
end
should 'be able to configure_apps multiple times' do
Padrino.configure_apps { set :foo1, "bar" }
Padrino.configure_apps { set :foo1, "bam" }
Padrino.configure_apps { set :foo2, "baz" }
PadrinoTestApp.send(:default_configuration!)
assert_equal "bam", PadrinoTestApp.settings.foo1, "should have foo1 assigned to bam"
assert_equal "baz", PadrinoTestApp.settings.foo2, "should have foo2 assigned to baz"
end
should "have shared sessions accessible in project" do
Padrino.configure_apps { enable :sessions; set :session_secret, 'secret' }
Padrino.mount("PadrinoTestApp").to("/write")
Padrino.mount("PadrinoTestApp2").to("/read")
PadrinoTestApp.send :default_configuration!
PadrinoTestApp.get('/') { session[:foo] = "shared" }
PadrinoTestApp2.send(:default_configuration!)
PadrinoTestApp2.get('/') { session[:foo] }
@app = Padrino.application
get '/write'
get '/read'
assert_equal 'shared', body
end
# compare to: test_routing: allow global provides
should "set content_type to nil if none can be determined" do
mock_app do
provides :xml
get("/foo"){ "Foo in #{content_type.inspect}" }
get("/bar"){ "Foo in #{content_type.inspect}" }
end
get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
assert_equal 'Foo in :xml', body
get '/foo'
assert_equal 'Foo in :xml', body
get '/bar', {}, { 'HTTP_ACCEPT' => 'application/xml' }
assert_equal "Foo in nil", body
end
context "errors" do
should "haven't mapped errors on development" do
mock_app { get('/'){ 'HI' } }
get "/"
assert @app.errors.empty?
end
should "have mapped errors on production" do
mock_app { set :environment, :production; get('/'){ 'HI' } }
get "/"
assert_equal 1, @app.errors.size
end
should "overide errors" do
mock_app do
set :environment, :production
get('/'){ raise }
error(::Exception){ 'custom error' }
end
get "/"
assert_equal 1, @app.errors.size
assert_equal 'custom error', body
end
end
end # application functionality
end

View file

@ -0,0 +1,77 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Core" do
def setup
Padrino.clear!
end
context 'for core functionality' do
should 'check some global methods' do
assert_respond_to Padrino, :root
assert_respond_to Padrino, :env
assert_respond_to Padrino, :application
assert_respond_to Padrino, :set_encoding
assert_respond_to Padrino, :load!
assert_respond_to Padrino, :reload!
assert_respond_to Padrino, :version
assert_respond_to Padrino, :configure_apps
end
should 'validate global helpers' do
assert_equal :test, Padrino.env
assert_match /\/test/, Padrino.root
assert_not_nil Padrino.version
end
should 'set correct utf-8 encoding' do
Padrino.set_encoding
if RUBY_VERSION <'1.9'
assert_equal 'UTF8', $KCODE
else
assert_equal Encoding.default_external, Encoding::UTF_8
assert_equal Encoding.default_internal, Encoding::UTF_8
end
end
should 'have load paths' do
assert_equal [Padrino.root('lib'), Padrino.root('models'), Padrino.root('shared')], Padrino.load_paths
end
should 'raise application error if I instantiate a new padrino application without mounted apps' do
assert_raises(Padrino::ApplicationLoadError) { Padrino.application.new }
end
should "check before/after padrino load hooks" do
Padrino.before_load { @_foo = 1 }
Padrino.after_load { @_foo += 1 }
Padrino.load!
assert_equal 1, Padrino.before_load.size
assert_equal 1, Padrino.after_load.size
assert_equal 2, @_foo
end
should "add middlewares in front if specified" do
test = Class.new {
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers["Middleware-Called"] = "yes"
return status, headers, body
end
}
class Foo < Padrino::Application; end
Padrino.use(test)
Padrino.mount(Foo).to("/")
res = Rack::MockRequest.new(Padrino.application).get("/")
assert_equal "yes", res["Middleware-Called"]
end
end
end

View file

@ -0,0 +1,80 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Application" do
before { Padrino.clear! }
after { remove_views }
context 'CSRF protection' do
context "with CSRF protection on" do
before do
mock_app do
enable :sessions
enable :protect_from_csrf
post('/'){ 'HI' }
end
end
should "not allow requests without tokens" do
post "/"
assert_equal 403, status
end
should "allow requests with correct tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "a"}
assert_equal 200, status
end
should "not allow requests with incorrect tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
assert_equal 403, status
end
end
context "without CSRF protection on" do
before do
mock_app do
enable :sessions
disable :protect_from_csrf
post('/'){ 'HI' }
end
end
should "allows requests without tokens" do
post "/"
assert_equal 200, status
end
should "allow requests with correct tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "a"}
assert_equal 200, status
end
should "allow requests with incorrect tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
assert_equal 200, status
end
end
context "with optional CSRF protection" do
before do
mock_app do
enable :sessions
enable :protect_from_csrf
set :allow_disabled_csrf, true
post('/on') { 'HI' }
post('/off', :csrf_protection => false) { 'HI' }
end
end
should "allow access to routes with csrf_protection off" do
post "/off"
assert_equal 200, status
end
should "not allow access to routes with csrf_protection on" do
post "/on"
assert_equal 403, status
end
end
end
end

View file

@ -0,0 +1,44 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Dependencies" do
context 'when we require a dependency that have another dependency' do
should 'raise an error without reloading it twice' do
capture_io do
assert_raises(RuntimeError) do
Padrino.require_dependencies(
Padrino.root("fixtures/dependencies/a.rb"),
Padrino.root("fixtures/dependencies/b.rb"),
Padrino.root("fixtures/dependencies/c.rb"),
Padrino.root("fixtures/dependencies/d.rb")
)
end
end
assert_equal 1, D
end
should 'resolve dependency problems' do
capture_io do
Padrino.require_dependencies(
Padrino.root("fixtures/dependencies/a.rb"),
Padrino.root("fixtures/dependencies/b.rb"),
Padrino.root("fixtures/dependencies/c.rb")
)
end
assert_equal ["B", "C"], A_result
assert_equal "C", B_result
end
should 'remove partially loaded constants' do
capture_io do
Padrino.require_dependencies(
Padrino.root("fixtures/dependencies/circular/e.rb"),
Padrino.root("fixtures/dependencies/circular/f.rb"),
Padrino.root("fixtures/dependencies/circular/g.rb")
)
end
assert_equal ["name"], F.fields
end
end
end

View file

@ -0,0 +1,348 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Filters" do
should "filters by accept header" do
mock_app do
get '/foo', :provides => [:xml, :js] do
request.env['HTTP_ACCEPT']
end
end
get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' }
assert ok?
assert_equal 'application/xml', body
assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
get '/foo.xml'
assert ok?
assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type']
get '/foo', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
assert ok?
assert_equal 'application/javascript', body
assert_equal 'application/javascript;charset=utf-8', response.headers['Content-Type']
get '/foo.js'
assert ok?
assert_equal 'application/javascript;charset=utf-8', response.headers['Content-Type']
get '/foo', {}, { "HTTP_ACCEPT" => 'text/html' }
assert_equal 406, status
end
should "allow passing & halting in before filters" do
mock_app do
controller do
before { env['QUERY_STRING'] == 'secret' or pass }
get :index do
"secret index"
end
end
controller do
before { env['QUERY_STRING'] == 'halt' and halt 401, 'go away!' }
get :index do
"index"
end
end
end
get "/?secret"
assert_equal "secret index", body
get "/?halt"
assert_equal "go away!", body
assert_equal 401, status
get "/"
assert_equal "index", body
end
should 'scope filters in the given controller' do
mock_app do
before { @global = 'global' }
after { @global = nil }
controller :foo do
before { @foo = :foo }
after { @foo = nil }
get("/") { [@foo, @bar, @global].compact.join(" ") }
end
get("/") { [@foo, @bar, @global].compact.join(" ") }
controller :bar do
before { @bar = :bar }
after { @bar = nil }
get("/") { [@foo, @bar, @global].compact.join(" ") }
end
end
get "/bar"
assert_equal "bar global", body
get "/foo"
assert_equal "foo global", body
get "/"
assert_equal "global", body
end
should 'be able to access params in a before filter' do
username_from_before_filter = nil
mock_app do
before do
username_from_before_filter = params[:username]
end
get :users, :with => :username do
end
end
get '/users/josh'
assert_equal 'josh', username_from_before_filter
end
should "be able to access params normally when a before filter is specified" do
mock_app do
before { }
get :index do
params.inspect
end
end
get '/?test=what'
assert_equal '{"test"=>"what"}', body
end
should "be able to filter based on a path" do
mock_app do
before('/') { @test = "#{@test}before"}
get :index do
@test
end
get :main do
@test
end
end
get '/'
assert_equal 'before', body
get '/main'
assert_equal '', body
end
should "be able to filter based on a symbol" do
mock_app do
before(:index) { @test = 'before'}
get :index do
@test
end
get :main do
@test
end
end
get '/'
assert_equal 'before', body
get '/main'
assert_equal '', body
end
should "be able to filter based on a symbol for a controller" do
mock_app do
controller :foo do
before(:test) { @test = 'foo'}
get :test do
@test.to_s + " response"
end
end
controller :bar do
before(:test) { @test = 'bar'}
get :test do
@test.to_s + " response"
end
end
end
get '/foo/test'
assert_equal 'foo response', body
get '/bar/test'
assert_equal 'bar response', body
end
should "be able to filter based on a symbol or path" do
mock_app do
before(:index, '/main') { @test = 'before'}
get :index do
@test
end
get :main do
@test
end
end
get '/'
assert_equal 'before', body
get '/main'
assert_equal 'before', body
end
should "be able to filter based on a symbol or regexp" do
mock_app do
before(:index, /main/) { @test = 'before'}
get :index do
@test
end
get :main do
@test
end
get :profile do
@test
end
end
get '/'
assert_equal 'before', body
get '/main'
assert_equal 'before', body
get '/profile'
assert_equal '', body
end
should "be able to filter excluding based on a symbol" do
mock_app do
before(:except => :index) { @test = 'before'}
get :index do
@test
end
get :main do
@test
end
end
get '/'
assert_equal '', body
get '/main'
assert_equal 'before', body
end
should "be able to filter based on a request param" do
mock_app do
before(:agent => /IE/) { @test = 'before'}
get :index do
@test
end
end
get '/'
assert_equal '', body
get "/", {}, {'HTTP_USER_AGENT' => 'This is IE'}
assert_equal 'before', body
end
should "be able to filter based on a symbol or path in multiple controller" do
mock_app do
controllers :foo do
before(:index, '/foo/main') { @test = 'before' }
get :index do
@test
end
get :main do
@test
end
end
controllers :bar do
before(:index, '/bar/main') { @test = 'also before' }
get :index do
@test
end
get :main do
@test
end
end
end
get '/foo'
assert_equal 'before', body
get '/bar'
assert_equal 'also before', body
get '/foo/main'
assert_equal 'before', body
get '/bar/main'
assert_equal 'also before', body
end
should "call before filters even if there was no match" do
test = nil
mock_app do
before(:index, '/foo') { test = 'before' }
get :index do
''
end
end
get '/foo'
assert_equal 'before', test
end
should "call before filters only once" do
once = ''
mock_app do
error 500 do
'error 500'
end
before do
once += 'before'
end
get :index do
raise Exception, 'Oops'
end
end
get '/'
assert_equal 'before', once
end
should 'catch exceptions in before filters' do
doodle = nil
mock_app do
after do
doodle = 'Been after'
end
before do
raise StandardError, "before"
end
get :index do
doodle = 'Been now'
end
error 500 do
"We broke #{env['sinatra.error'].message}"
end
end
get '/'
assert_equal 'We broke before', body
assert_equal nil, doodle
end
should 'catch exceptions in after filters if no exceptions caught before' do
doodle = ''
mock_app do
after do
doodle += ' and after'
raise StandardError, "after"
end
get :foo do
doodle = 'Been now'
raise StandardError, "now"
end
get :index do
doodle = 'Been now'
end
error 500 do
"We broke #{env['sinatra.error'].message}"
end
end
get '/foo'
assert_equal 'We broke now', body
assert_equal 'Been now', doodle
doodle = ''
get '/'
assert_equal 'We broke after', body
assert_equal 'Been now and after', doodle
end
end

View file

@ -0,0 +1,168 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe Padrino::Flash do
context 'storage' do
before do
@storage = Padrino::Flash::Storage.new(
:success => 'Success msg',
:error => 'Error msg',
:notice => 'Notice msg',
:custom => 'Custom msg'
)
@storage[:one] = 'One msg'
@storage[:two] = 'Two msg'
end
should 'acts like hash' do
assert_respond_to @storage, :[]
end
should 'know its size' do
assert_equal 4, @storage.length
assert_equal @storage.length, @storage.size
end
should 'sweep its content' do
assert_equal 2, @storage.sweep.size
assert_empty @storage.sweep
end
should 'discard everything' do
assert_empty @storage.discard.sweep
end
should 'discard specified key' do
assert_equal 1, @storage.discard(:one).sweep.size
end
should 'keep everything' do
assert_equal 2, @storage.sweep.keep.sweep.size
end
should 'keep only specified key' do
assert_equal 1, @storage.sweep.keep(:one).sweep.size
end
should 'not know the values you set right away' do
@storage[:foo] = 'bar'
assert_nil @storage[:foo]
end
should 'knows the values you set next time' do
@storage[:foo] = 'bar'
@storage.sweep
assert_equal 'bar', @storage[:foo]
end
should 'set values for now' do
@storage.now[:foo] = 'bar'
assert_equal 'bar', @storage[:foo]
end
should 'forgets values you set only for now next time' do
@storage.now[:foo] = 'bar'
@storage.sweep
assert_nil @storage[:foo]
end
end
routes = Proc.new do
get :index do
params[:key] ? flash[params[:key].to_sym].to_s : flash.now.inspect
end
post :index do
params.each { |k,v| flash[k.to_sym] = v.to_s }
flash.next.inspect
end
get :session do
settings.sessions?.inspect
end
get :redirect do
redirect url(:index, :key => :foo), 301, :foo => 'redirected!'
end
get :success do
flash.success = 'Yup'
end
get :error do
flash.error = 'Arg'
end
get :notice do
flash.notice = 'Mmm'
end
end
context 'padrino application without sessions' do
before { mock_app(&routes) }
should 'show nothing' do
get '/'
assert_equal '{}', body
end
should 'set a flash' do
post '/', :foo => :bar
assert_equal '{:foo=>"bar"}', body
end
end
context 'padrino application with sessions' do
before do
mock_app { enable :sessions; class_eval(&routes) }
end
should 'be sure have sessions enabled' do
assert @app.sessions
get '/session'
assert_equal 'true', body
end
should 'show nothing' do
get '/'
assert_equal '{}', body
end
should 'set a flash' do
post '/', :foo => :bar
assert_equal '{:foo=>"bar"}', body
end
should 'get a flash' do
post '/', :foo => :bar
get '/', :key => :foo
assert_equal 'bar', body
post '/'
assert_equal '{}', body
end
should 'follow redirects with flash' do
get '/redirect'
follow_redirect!
assert_equal 'redirected!', body
assert 301, status
end
should 'set success' do
get '/success'
get '/', :key => :success
assert_equal 'Yup', body
end
should 'set error' do
get '/error'
get '/', :key => :error
assert_equal 'Arg', body
end
should 'set notice' do
get '/notice'
get '/', :key => :notice
assert_equal 'Mmm', body
end
end
end

View file

@ -0,0 +1,21 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Locales" do
Dir[File.expand_path("../../lib/padrino-core/locale/*.yml", __FILE__)].each do |file|
base_original = YAML.load_file(file)
name = File.basename(file, '.yml')
should "should have correct locale for #{name}" do
base = base_original[name]['date']['formats']
assert base['default'].present?
assert base['short'].present?
assert base['long'].present?
assert base['only_day'].present?
base = base_original[name]['date']
assert base['day_names'].present?
assert base['abbr_day_names'].present?
assert base['month_names'].present?
assert base['abbr_month_names'].present?
assert base['order'].present?
end
end
end

View file

@ -0,0 +1,210 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require 'lumberjack'
require 'logger'
describe "PadrinoLogger" do
def setup
Padrino::Logger::Config[:test][:stream] = :null # The default
Padrino::Logger.setup!
end
def setup_logger(options={})
@log = StringIO.new
@logger = Padrino::Logger.new(options.merge(:stream => @log))
end
context 'for logger functionality' do
context 'check stream config' do
should 'use stdout if stream is nil' do
Padrino::Logger::Config[:test][:stream] = nil
Padrino::Logger.setup!
assert_equal $stdout, Padrino.logger.log
end
should 'use StringIO as default for test' do
assert_instance_of StringIO, Padrino.logger.log
end
should 'use a custom stream' do
my_stream = StringIO.new
Padrino::Logger::Config[:test][:stream] = my_stream
Padrino::Logger.setup!
assert_equal my_stream, Padrino.logger.log
end
end
should 'log something' do
setup_logger(:log_level => :error)
@logger.error "You log this error?"
assert_match(/You log this error?/, @log.string)
@logger.debug "You don't log this error!"
assert_no_match(/You don't log this error!/, @log.string)
@logger << "Yep this can be logged"
assert_match(/Yep this can be logged/, @log.string)
end
should 'respond to #write for Rack::CommonLogger' do
setup_logger(:log_level => :error)
@logger.error "Error message"
assert_match /Error message/, @log.string
@logger << "logged anyways"
assert_match /logged anyways/, @log.string
@logger.write "log via alias"
assert_match /log via alias/, @log.string
end
should 'log an application' do
mock_app do
enable :logging
get("/"){ "Foo" }
end
get "/"
assert_equal "Foo", body
assert_match /GET/, Padrino.logger.log.string
end
should 'log an application\'s status code' do
mock_app do
enable :logging
get("/"){ "Foo" }
end
get "/"
assert_match /\e\[1m200\e\[0m OK/, Padrino.logger.log.string
end
context "static asset logging" do
should 'not log static assets by default' do
mock_app do
enable :logging
get("/images/something.png"){ env["sinatra.static_file"] = '/public/images/something.png'; "Foo" }
end
get "/images/something.png"
assert_equal "Foo", body
assert_match "", Padrino.logger.log.string
end
should 'allow turning on static assets logging' do
Padrino.logger.instance_eval{ @log_static = true }
mock_app do
enable :logging
get("/images/something.png"){ env["sinatra.static_file"] = '/public/images/something.png'; "Foo" }
end
get "/images/something.png"
assert_equal "Foo", body
assert_match /GET/, Padrino.logger.log.string
Padrino.logger.instance_eval{ @log_static = false }
end
end
context "health-check requests logging" do
def access_to_mock_app
mock_app do
enable :logging
get("/"){ "Foo" }
end
get "/"
end
should 'output under debug level' do
Padrino.logger.instance_eval{ @level = Padrino::Logger::Levels[:debug] }
access_to_mock_app
assert_match /\e\[36m DEBUG\e\[0m/, Padrino.logger.log.string
Padrino.logger.instance_eval{ @level = Padrino::Logger::Levels[:devel] }
access_to_mock_app
assert_match /\e\[36m DEBUG\e\[0m/, Padrino.logger.log.string
end
should 'not output over debug level' do
Padrino.logger.instance_eval{ @level = Padrino::Logger::Levels[:info] }
access_to_mock_app
assert_equal '', Padrino.logger.log.string
Padrino.logger.instance_eval{ @level = Padrino::Logger::Levels[:error] }
access_to_mock_app
assert_equal '', Padrino.logger.log.string
end
end
end
end
describe "alternate logger: Lumberjack" do
def setup_logger
@log = StringIO.new
Padrino.logger = Lumberjack::Logger.new(@log, :level => :debug)
end
should "annotate the logger to support additional Padrino fancyness" do
setup_logger
Padrino.logger.debug("Debug message")
assert_match(/Debug message/, @log.string)
end
should "colorize log output after colorize! is called" do
setup_logger
Padrino.logger.colorize!
mock_app do
enable :logging
get("/"){ "Foo" }
end
get "/"
assert_match /\e\[1m200\e\[0m OK/, @log.string
end
end
describe "alternate logger: stdlib logger" do
def setup_logger
@log = StringIO.new
Padrino.logger = Logger.new(@log)
end
should "annotate the logger to support additional Padrino fancyness" do
setup_logger
Padrino.logger.debug("Debug message")
assert_match(/Debug message/, @log.string)
end
should "colorize log output after colorize! is called" do
setup_logger
Padrino.logger.colorize!
mock_app do
enable :logging
get("/"){ "Foo" }
end
get "/"
assert_match /\e\[1m200\e\[0m OK/, @log.string
end
end
describe "options :colorize_logging" do
def access_to_mock_app
mock_app do
enable :logging
get("/"){ "Foo" }
end
get "/"
end
context 'default' do
should 'use colorize logging' do
Padrino::Logger.setup!
access_to_mock_app
assert_match /\e\[1m200\e\[0m OK/, Padrino.logger.log.string
end
end
context 'set value is false' do
should 'not use colorize logging' do
Padrino::Logger::Config[:test][:colorize_logging] = false
Padrino::Logger.setup!
access_to_mock_app
assert_match /200 OK/, Padrino.logger.log.string
end
end
end

View file

@ -0,0 +1,199 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Mounter" do
class ::TestApp < Padrino::Application; end
def setup
$VERBOSE, @_verbose_was = nil, $VERBOSE
Padrino.clear!
end
def teardown
$VERBOSE = @_verbose_was
end
context 'for mounter functionality' do
should 'check methods' do
mounter = Padrino::Mounter.new("test_app", :app_file => "/path/to/test.rb")
mounter.to("/test_app")
assert_kind_of Padrino::Mounter, mounter
assert_respond_to Padrino::Mounter, :new
assert_respond_to mounter, :to
assert_respond_to mounter, :map_onto
assert_equal "test_app", mounter.name
assert_equal "TestApp", mounter.app_class
assert_equal "/path/to/test.rb", mounter.app_file
assert_equal "/test_app", mounter.uri_root
assert_equal File.dirname(mounter.app_file), mounter.app_root
end
should 'check locate_app_file with __FILE__' do
mounter = Padrino::Mounter.new("test_app", :app_file => __FILE__)
mounter.to("/test_app")
assert_equal "test_app", mounter.name
assert_equal "TestApp", mounter.app_class
assert_equal __FILE__, mounter.app_file
assert_equal "/test_app", mounter.uri_root
assert_equal File.dirname(mounter.app_file), mounter.app_root
end
should 'mount an app' do
class ::AnApp < Padrino::Application; end
Padrino.mount("an_app").to("/")
assert_equal AnApp, Padrino.mounted_apps.first.app_obj
assert_equal ["an_app"], Padrino.mounted_apps.map(&:name)
end
should 'correctly mount an app in a namespace' do
module ::SomeNamespace
class AnApp < Padrino::Application; end
end
Padrino.mount("some_namespace/an_app").to("/")
assert_equal SomeNamespace::AnApp, Padrino.mounted_apps.first.app_obj
assert_equal ["some_namespace/an_app"], Padrino.mounted_apps.map(&:name)
end
should 'mount a primary app to root uri' do
mounter = Padrino.mount("test_app", :app_file => __FILE__).to("/")
assert_equal "test_app", mounter.name
assert_equal "TestApp", mounter.app_class
assert_equal TestApp, mounter.app_obj
assert_equal __FILE__, mounter.app_file
assert_equal "/", mounter.uri_root
assert_equal File.dirname(mounter.app_file), mounter.app_root
end
should 'mount a primary app to sub_uri' do
mounter = Padrino.mount("test_app", :app_file => __FILE__).to('/me')
assert_equal "test_app", mounter.name
assert_equal "TestApp", mounter.app_class
assert_equal TestApp, mounter.app_obj
assert_equal __FILE__, mounter.app_file
assert_equal "/me", mounter.uri_root
assert_equal File.dirname(mounter.app_file), mounter.app_root
end
should "raise error when app has no located file" do
# TODO enabling this screws minitest
# assert_raises(Padrino::Mounter::MounterException) { Padrino.mount("tester_app").to('/test') }
assert_equal 0, Padrino.mounted_apps.size
end
should "raise error when app has no located object" do
assert_raises(Padrino::Mounter::MounterException) { Padrino.mount("tester_app", :app_file => "/path/to/file.rb").to('/test') }
assert_equal 0, Padrino.mounted_apps.size
end
should 'mount multiple apps' do
class ::OneApp < Padrino::Application; end
class ::TwoApp < Padrino::Application; end
Padrino.mount("one_app").to("/one_app")
Padrino.mount("two_app").to("/two_app")
# And testing no duplicates
Padrino.mount("one_app").to("/one_app")
Padrino.mount("two_app").to("/two_app")
assert_equal OneApp, Padrino.mounted_apps[0].app_obj
assert_equal TwoApp, Padrino.mounted_apps[1].app_obj
assert_equal 2, Padrino.mounted_apps.size, "should not mount duplicate apps"
assert_equal ["one_app", "two_app"], Padrino.mounted_apps.map(&:name)
end
should 'change mounted_root' do
Padrino.mounted_root = "fixtures"
assert_equal Padrino.root("fixtures", "test", "app.rb"), Padrino.mounted_root("test", "app.rb")
Padrino.mounted_root = "apps"
assert_equal Padrino.root("apps", "test", "app.rb"), Padrino.mounted_root("test", "app.rb")
Padrino.mounted_root = nil
assert_equal Padrino.root("test", "app.rb"), Padrino.mounted_root("test", "app.rb")
end
should "be able to access routes data for mounted apps" do
class ::OneApp < Padrino::Application
get("/test") { "test" }
get(:index, :provides => [:js, :json]) { "index" }
get(%r{/foo|/baz}) { "regexp" }
controllers :posts do
get(:index) { "index" }
get(:new, :provides => :js) { "new" }
get(:show, :provides => [:js, :html], :with => :id) { "show" }
post(:create, :provides => :js, :with => :id) { "create" }
get(:regexp, :map => %r{/foo|/baz}) { "regexp" }
end
end
class ::TwoApp < Padrino::Application
controllers :users do
get(:index) { "users" }
get(:new) { "users new" }
post(:create) { "users create" }
put(:update) { "users update" }
delete(:destroy) { "users delete" }
end
end
Padrino.mount("one_app").to("/")
Padrino.mount("two_app").to("/two_app")
assert_equal 15, Padrino.mounted_apps[0].routes.size
assert_equal 7, Padrino.mounted_apps[1].routes.size
assert_equal 6, Padrino.mounted_apps[0].named_routes.size
assert_equal 5, Padrino.mounted_apps[1].named_routes.size
first_route = Padrino.mounted_apps[0].named_routes[3]
assert_equal "posts_show", first_route.identifier.to_s
assert_equal "(:posts, :show)", first_route.name
assert_equal "GET", first_route.verb
assert_equal "/posts/show/:id(.:format)", first_route.path
another_route = Padrino.mounted_apps[1].named_routes[2]
assert_equal "users_create", another_route.identifier.to_s
assert_equal "(:users, :create)", another_route.name
assert_equal "POST", another_route.verb
assert_equal "/two_app/users/create", another_route.path
regexp_route = Padrino.mounted_apps[0].named_routes[5]
assert_equal "posts_regexp", regexp_route.identifier.to_s
assert_equal "(:posts, :regexp)", regexp_route.name
assert_equal "/\\/foo|\\/baz/", regexp_route.path
end
should 'correctly instantiate a new padrino application' do
mock_app do
get("/demo_1"){ "Im Demo 1" }
get("/demo_2"){ "Im Demo 2" }
end
get '/demo_1'
assert_equal "Im Demo 1", response.body
get '/demo_2'
assert_equal "Im Demo 2", response.body
end
should "not clobber the public setting when mounting an app" do
class ::PublicApp < Padrino::Application
set :root, "/root"
set :public_folder, File.expand_path(File.dirname(__FILE__))
end
Padrino.mount("public_app").to("/public")
res = Rack::MockRequest.new(Padrino.application).get("/public/test_mounter.rb")
assert res.ok?
assert_equal File.read(__FILE__), res.body
end
should "load apps from gems" do
spec_file = Padrino.root("fixtures", "app_gem", "app_gem.gemspec")
spec = Gem::Specification.load(spec_file)
spec.activate
def spec.full_gem_path
Padrino.root("fixtures", "app_gem")
end
require Padrino.root("fixtures", "app_gem", "lib", "app_gem")
Padrino.mount("AppGem::App").to("/from_gem")
mounter = Padrino.mounted_apps[0]
assert_equal AppGem::App, mounter.app_obj
assert_equal Padrino.root('public'), mounter.app_obj.public_folder
end
end
end

View file

@ -0,0 +1,75 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require File.expand_path(File.dirname(__FILE__) + '/fixtures/apps/complex')
describe "ComplexReloader" do
context 'for complex reload functionality' do
setup do
Padrino.clear!
Padrino.mount("complex_1_demo").to("/complex_1_demo")
Padrino.mount("complex_2_demo").to("/complex_2_demo")
end
should 'correctly instantiate Complex(1-2)Demo fixture' do
assert_equal ["/complex_1_demo", "/complex_2_demo"], Padrino.mounted_apps.map(&:uri_root)
assert_equal ["complex_1_demo", "complex_2_demo"], Padrino.mounted_apps.map(&:name)
assert Complex1Demo.reload?
assert Complex2Demo.reload?
assert_match %r{fixtures/apps/complex.rb}, Complex1Demo.app_file
assert_match %r{fixtures/apps/complex.rb}, Complex2Demo.app_file
end
should 'correctly reload Complex(1-2)Demo fixture' do
assert_match %r{fixtures/apps/complex.rb}, Complex1Demo.app_file
@app = Padrino.application
get "/"
assert_equal 404, status
get "/complex_1_demo"
assert_equal "Given random #{LibDemo.give_me_a_random}", body
get "/complex_2_demo"
assert_equal 200, status
get "/complex_1_demo/old"
assert_equal 200, status
get "/complex_2_demo/old"
assert_equal 200, status
get "/complex_2_demo/var/destroy"
assert_equal '{}', body
new_phrase = "The magick number is: #{rand(2**255)}!"
buffer = File.read(Complex1Demo.app_file)
new_buffer = buffer.gsub(/The magick number is: \d+!/, new_phrase)
new_buffer.gsub!(/get\(:destroy\)/, 'get(:destroy, :with => :id)')
begin
File.open(Complex1Demo.app_file, "w") { |f| f.write(new_buffer) }
sleep 1.2 # We need at least a cooldown of 1 sec.
get "/complex_2_demo"
assert_equal new_phrase, body
# Re-Check that we didn't forget any route
get "/complex_1_demo"
assert_equal "Given random #{LibDemo.give_me_a_random}", body
get "/complex_2_demo"
assert_equal 200, status
get "/complex_1_demo/old"
assert_equal 200, status
get "/complex_2_demo/old"
assert_equal 200, status
get "/complex_2_demo/var/destroy/variable"
assert_equal '{:id=>"variable"}', body
ensure
# Now we need to prevent to commit a new changed file so we revert it
File.open(Complex1Demo.app_file, "w") { |f| f.write(buffer) }
end
end
end
end

View file

@ -0,0 +1,98 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require File.expand_path(File.dirname(__FILE__) + '/fixtures/apps/simple')
describe "SimpleReloader" do
context 'for simple reset functionality' do
should 'reset routes' do
mock_app do
(1..10).each do |i|
get("/#{i}") { "Foo #{i}" }
end
end
(1..10).each do |i|
get "/#{i}"
assert_equal "Foo #{i}", body
end
@app.reset_routes!
(1..10).each do |i|
get "/#{i}"
assert_equal 404, status
end
end
should 'keep sinatra routes on development' do
mock_app do
set :environment, :development
get("/"){ "ok" }
end
assert_equal :development, @app.environment
get "/"
assert_equal 200, status
get "/__sinatra__/404.png"
assert_equal 200, status
assert_match /image\/png/, response["Content-Type"]
@app.reset_routes!
get "/"
assert_equal 404, status
get "/__sinatra__/404.png"
assert_equal 200, status
assert_match /image\/png/, response["Content-Type"]
end
end
context 'for simple reload functionality' do
should 'correctly instantiate SimpleDemo fixture' do
Padrino.clear!
Padrino.mount("simple_demo").to("/")
assert_equal ["simple_demo"], Padrino.mounted_apps.map(&:name)
assert SimpleDemo.reload?
assert_match %r{fixtures/apps/simple.rb}, SimpleDemo.app_file
end
should_eventually 'correctly reload SimpleDemo fixture' do
# TODO fix this test
@app = SimpleDemo
get "/"
assert ok?
new_phrase = "The magick number is: #{rand(2**255)}!"
buffer = File.read(SimpleDemo.app_file)
new_buffer = buffer.gsub(/The magick number is: \d+!/, new_phrase)
File.open(SimpleDemo.app_file, "w") { |f| f.write(new_buffer) }
sleep 2 # We need at least a cooldown of 1 sec.
get "/"
assert_equal new_phrase, body
# Now we need to prevent to commit a new changed file so we revert it
File.open(SimpleDemo.app_file, "w") { |f| f.write(buffer) }
Padrino.reload!
end
should 'correctly reset SimpleDemo fixture' do
@app = SimpleDemo
@app.reload!
get "/rand"
assert ok?
last_body = body
assert_equal 2, @app.filters[:before].size # one is ours the other is default_filter for content type
assert_equal 1, @app.errors.size
assert_equal 2, @app.filters[:after].size # app + content-type + padrino-flash
assert_equal 0, @app.middleware.size
assert_equal 4, @app.routes.size # GET+HEAD of "/" + GET+HEAD of "/rand" = 4
assert_equal 3, @app.extensions.size # [Padrino::Routing, Padrino::Rendering, Padrino::Flash]
assert_equal 0, @app.templates.size
@app.reload!
get "/rand"
assert_not_equal last_body, body
assert_equal 2, @app.filters[:before].size # one is ours the other is default_filter for content type
assert_equal 1, @app.errors.size
assert_equal 2, @app.filters[:after].size
assert_equal 0, @app.middleware.size
assert_equal 4, @app.routes.size # GET+HEAD of "/" = 2
assert_equal 3, @app.extensions.size # [Padrino::Routing, Padrino::Rendering, Padrino::Flash]
assert_equal 0, @app.templates.size
end
end
end

View file

@ -0,0 +1,545 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require 'i18n'
require 'slim'
describe "Rendering" do
def setup
Padrino::Application.send(:register, Padrino::Rendering)
Padrino::Rendering::DEFAULT_RENDERING_OPTIONS[:strict_format] = false
end
def teardown
remove_views
end
context 'for application layout functionality' do
should 'get no layout' do
mock_app do
get("/"){ "no layout" }
end
get "/"
assert_equal "no layout", body
end
should 'be compatible with sinatra layout' do
mock_app do
layout do
"this is a <%= yield %>"
end
get("/"){ render :erb, "sinatra layout", :layout => true }
end
get "/"
assert_equal "this is a sinatra layout", body
end
should 'use rails way layout' do
with_layout :application, "this is a <%= yield %>" do
mock_app do
get("/"){ render :erb, "rails way layout" }
end
get "/"
assert_equal "this is a rails way layout", body
end
end
should 'use rails way for a custom layout' do
with_layout "layouts/custom", "this is a <%= yield %>" do
mock_app do
layout :custom
get("/"){ render :erb, "rails way custom layout" }
end
get "/"
assert_equal "this is a rails way custom layout", body
end
end
should 'not use layout' do
with_layout :application, "this is an <%= yield %>" do
with_view :index, "index" do
mock_app do
get("/with/layout"){ render :index }
get("/without/layout"){ render :index, :layout => false }
end
get "/with/layout"
assert_equal "this is an index", body
get "/without/layout"
assert_equal "index", body
end
end
end
should 'not use layout with js format' do
create_layout :application, "this is an <%= yield %>"
create_view :foo, "erb file"
create_view :foo, "js file", :format => :js
mock_app do
get('/layout_test', :provides => [:html, :js]){ render :foo }
end
get "/layout_test"
assert_equal "this is an erb file", body
get "/layout_test.js"
assert_equal "js file", body
end
should 'use correct layout for each format' do
create_layout :application, "this is an <%= yield %>"
create_layout :application, "document start <%= yield %> end", :format => :xml
create_view :foo, "erb file"
create_view :foo, "xml file", :format => :xml
mock_app do
get('/layout_test', :provides => [:html, :xml]){ render :foo }
end
get "/layout_test"
assert_equal "this is an erb file", body
get "/layout_test.xml"
assert_equal "document start xml file end", body
end
should 'by default use html file when no other is given' do
create_layout :baz, "html file", :format => :html
mock_app do
get('/content_type_test', :provides => [:html, :xml]) { render :baz }
end
get "/content_type_test"
assert_equal "html file", body
get "/content_type_test.html"
assert_equal "html file", body
get "/content_type_test.xml"
assert_equal "html file", body
end
should 'not use html file when DEFAULT_RENDERING_OPTIONS[:strict_format] == true' do
create_layout :foo, "html file", :format => :html
mock_app do
get('/default_rendering_test', :provides => [:html, :xml]) { render :foo }
end
@save = Padrino::Rendering::DEFAULT_RENDERING_OPTIONS
Padrino::Rendering::DEFAULT_RENDERING_OPTIONS[:strict_format] = true
get "/default_rendering_test"
assert_equal "html file", body
assert_raises Padrino::Rendering::TemplateNotFound do
get "/default_rendering_test.xml"
end
Padrino::Rendering::DEFAULT_RENDERING_OPTIONS.merge!(@save)
end
should 'use correct layout with each controller' do
create_layout :foo, "foo layout at <%= yield %>"
create_layout :bar, "bar layout at <%= yield %>"
create_layout :baz, "baz layout at <%= yield %>"
create_layout :application, "default layout at <%= yield %>"
mock_app do
get("/"){ render :erb, "application" }
controller :foo do
layout :foo
get("/"){ render :erb, "foo" }
end
controller :bar do
layout :bar
get("/"){ render :erb, "bar" }
end
controller :baz do
layout :baz
get("/"){ render :erb, "baz", :layout => true }
end
controller :none do
get("/") { render :erb, "none" }
get("/with_foo_layout") { render :erb, "none with layout", :layout => :foo }
end
end
get "/foo"
assert_equal "foo layout at foo", body
get "/bar"
assert_equal "bar layout at bar", body
get "/baz"
assert_equal "baz layout at baz", body
get "/none"
assert_equal "default layout at none", body
get "/none/with_foo_layout"
assert_equal "foo layout at none with layout", body
get "/"
assert_equal "default layout at application", body
end
end
should 'solve layout in layouts paths' do
create_layout :foo, "foo layout <%= yield %>"
create_layout :"layouts/bar", "bar layout <%= yield %>"
mock_app do
get("/") { render :erb, "none" }
get("/foo") { render :erb, "foo", :layout => :foo }
get("/bar") { render :erb, "bar", :layout => :bar }
end
get "/"
assert_equal "none", body
get "/foo"
assert_equal "foo layout foo", body
get "/bar"
assert_equal "bar layout bar", body
end
should 'render correctly if layout was not found or not exist' do
create_layout :application, "application layout for <%= yield %>"
create_view :foo, "index", :format => :html
create_view :foo, "xml.rss", :format => :rss
mock_app do
get("/foo", :provides => [:html, :rss]) { render('foo') }
get("/baz", :provides => :js) { render(:erb, 'baz') }
get("/bar") { render :haml, "haml" }
end
get "/foo"
assert_equal "application layout for index", body
get "/foo.rss"
assert_equal "<rss/>", body.chomp
get "/baz.js"
assert_equal "baz", body
get "/bar"
assert_equal "haml", body.chomp
end
context 'for application render functionality' do
should "work properly with logging and missing layout" do
create_view :index, "<%= foo %>"
mock_app do
enable :logging
get("/") { render "index", { :layout => nil }, { :foo => "bar" } }
end
get "/"
assert_equal "bar", body
end
should "work properly with logging and layout" do
create_layout :application, "layout <%= yield %>"
create_view :index, "<%= foo %>"
mock_app do
enable :logging
get("/") { render "index", { :layout => true }, { :foo => "bar" } }
end
get "/"
assert_equal "layout bar", body
end
should 'be compatible with sinatra render' do
mock_app do
get("/"){ render :erb, "<%= 1+2 %>" }
end
get "/"
assert_equal "3", body
end
should "support passing locals into render" do
create_layout :application, "layout <%= yield %>"
create_view :index, "<%= foo %>"
mock_app do
get("/") { render "index", { :layout => true }, { :foo => "bar" } }
end
get "/"
assert_equal "layout bar", body
end
should "support passing locals into sinatra render" do
create_layout :application, "layout <%= yield %>"
create_view :index, "<%= foo %>"
mock_app do
get("/") { render :erb, :index, { :layout => true }, { :foo => "bar" } }
end
get "/"
assert_equal "layout bar", body
end
should "support passing locals into special nil engine render" do
create_layout :application, "layout <%= yield %>"
create_view :index, "<%= foo %>"
mock_app do
get("/") { render nil, :index, { :layout => true }, { :foo => "bar" } }
end
get "/"
assert_equal "layout bar", body
end
should 'be compatible with sinatra views' do
with_view :index, "<%= 1+2 %>" do
mock_app do
get("/foo") { render :erb, :index }
get("/bar") { erb :index }
get("/dir") { "3" }
get("/inj") { erb "<%= 2+1 %>" }
get("/rnj") { render :erb, "<%= 2+1 %>" }
end
get "/foo"
assert_equal "3", body
get "/bar"
assert_equal "3", body
get "/dir"
assert_equal "3", body
get "/inj"
assert_equal "3", body
get "/rnj"
assert_equal "3", body
end
end
should 'resolve template engine' do
with_view :index, "<%= 1+2 %>" do
mock_app do
get("/foo") { render :index }
get("/bar") { render "/index" }
end
get "/foo"
assert_equal "3", body
get "/bar"
assert_equal "3", body
end
end
should 'resolve template content type' do
create_view :foo, "Im Js", :format => :js
create_view :foo, "Im Erb"
mock_app do
get("/foo", :provides => :js) { render :foo }
get("/bar.js") { render :foo }
end
get "/foo.js"
assert_equal "Im Js", body
# TODO: implement this!
# get "/bar.js"
# assert_equal "Im Js", body
end
should 'resolve with explicit template format' do
create_view :foo, "Im Js", :format => :js
create_view :foo, "Im Haml", :format => :haml
create_view :foo, "Im Xml", :format => :xml
mock_app do
get("/foo_normal", :provides => :js) { render 'foo' }
get("/foo_haml", :provides => :js) { render 'foo.haml' }
get("/foo_xml", :provides => :js) { render 'foo.xml' }
end
get "/foo_normal.js"
assert_equal "Im Js", body
get "/foo_haml.js"
assert_equal "Im Haml\n", body
get "/foo_xml.js"
assert_equal "Im Xml", body
end
should 'resolve without explict template format' do
create_view :foo, "Im Html"
create_view :foo, "xml.rss", :format => :rss
mock_app do
get(:index, :map => "/", :provides => [:html, :rss]){ render 'foo' }
end
get "/", {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' }
assert_equal "Im Html", body
get ".rss"
assert_equal "<rss/>\n", body
end
should "ignore files ending in tilde and not render them" do
create_view :foo, "Im Wrong", :format => 'haml~'
create_view :foo, "Im Haml", :format => :haml
create_view :bar, "Im Haml backup", :format => 'haml~'
mock_app do
get('/foo') { render 'foo' }
get('/bar') { render 'bar' }
end
get '/foo'
assert_equal "Im Haml\n", body
assert_raises(Padrino::Rendering::TemplateNotFound) { get '/bar' }
end
should 'resolve template locale' do
create_view :foo, "Im English", :locale => :en
create_view :foo, "Im Italian", :locale => :it
mock_app do
get("/foo") { render :foo }
end
I18n.locale = :en
get "/foo"
assert_equal "Im English", body
I18n.locale = :it
get "/foo"
assert_equal "Im Italian", body
end
should 'resolve template content_type and locale' do
create_view :foo, "Im Js", :format => :js
create_view :foo, "Im Erb"
create_view :foo, "Im English Erb", :locale => :en
create_view :foo, "Im Italian Erb", :locale => :it
create_view :foo, "Im English Js", :format => :js, :locale => :en
create_view :foo, "Im Italian Js", :format => :js, :locale => :it
mock_app do
get("/foo", :provides => [:html, :js]) { render :foo }
end
I18n.locale = :none
get "/foo.js"
assert_equal "Im Js", body
get "/foo"
assert_equal "Im Erb", body
I18n.locale = :en
get "/foo"
assert_equal "Im English Erb", body
I18n.locale = :it
get "/foo"
assert_equal "Im Italian Erb", body
I18n.locale = :en
get "/foo.js"
assert_equal "Im English Js", body
I18n.locale = :it
get "/foo.js"
assert_equal "Im Italian Js", body
I18n.locale = :en
get "/foo.pk"
assert_equal 404, status
end
should 'resolve template content_type and locale with layout' do
create_layout :foo, "Hello <%= yield %> in a Js layout", :format => :js
create_layout :foo, "Hello <%= yield %> in a Js-En layout", :format => :js, :locale => :en
create_layout :foo, "Hello <%= yield %> in a Js-It layout", :format => :js, :locale => :it
create_layout :foo, "Hello <%= yield %> in a Erb-En layout", :locale => :en
create_layout :foo, "Hello <%= yield %> in a Erb-It layout", :locale => :it
create_layout :foo, "Hello <%= yield %> in a Erb layout"
create_view :bar, "Im Js", :format => :js
create_view :bar, "Im Erb"
create_view :bar, "Im English Erb", :locale => :en
create_view :bar, "Im Italian Erb", :locale => :it
create_view :bar, "Im English Js", :format => :js, :locale => :en
create_view :bar, "Im Italian Js", :format => :js, :locale => :it
create_view :bar, "Im a json", :format => :json
mock_app do
layout :foo
get("/bar", :provides => [:html, :js, :json]) { render :bar }
end
I18n.locale = :none
get "/bar.js"
assert_equal "Hello Im Js in a Js layout", body
get "/bar"
assert_equal "Hello Im Erb in a Erb layout", body
I18n.locale = :en
get "/bar"
assert_equal "Hello Im English Erb in a Erb-En layout", body
I18n.locale = :it
get "/bar"
assert_equal "Hello Im Italian Erb in a Erb-It layout", body
I18n.locale = :en
get "/bar.js"
assert_equal "Hello Im English Js in a Js-En layout", body
I18n.locale = :it
get "/bar.js"
assert_equal "Hello Im Italian Js in a Js-It layout", body
I18n.locale = :en
get "/bar.json"
assert_equal "Im a json", body
get "/bar.pk"
assert_equal 404, status
end
should 'renders erb with blocks' do
mock_app do
def container
@_out_buf << "THIS."
yield
@_out_buf << "SPARTA!"
end
def is; "IS."; end
get '/' do
render :erb, '<% container do %> <%= is %> <% end %>'
end
end
get '/'
assert ok?
assert_equal 'THIS. IS. SPARTA!', body
end
should 'render erb to a SafeBuffer' do
mock_app do
layout do
"this is a <%= yield %>"
end
get '/' do
render :erb, '<p><%= %q{<script lang="ronin">alert("https://github.com/ronin-ruby/ronin")</script>} %></p>', :layout => false
end
get '/with_layout' do
render :erb, '<span>span</span>', :layout => true
end
end
get '/'
assert ok?
assert_equal '<p>&lt;script lang=&quot;ronin&quot;&gt;alert(&quot;https://github.com/ronin-ruby/ronin&quot;)&lt;/script&gt;</p>', body
get '/with_layout'
assert ok?
assert_equal 'this is a <span>span</span>', body
end
should 'render haml to a SafeBuffer' do
mock_app do
layout do
"%p= yield"
end
get '/' do
render :haml, '%p= %s{<script lang="ronin">alert("https://github.com/ronin-ruby/ronin")</script>}', :layout => false
end
get '/with_layout' do
render :haml, "%div\n foo", :layout => true
end
end
get '/'
assert ok?
assert_equal '<p>&lt;script lang=&quot;ronin&quot;&gt;alert(&quot;https://github.com/ronin-ruby/ronin&quot;)&lt;/script&gt;</p>', body.strip
get 'with_layout'
assert ok?
assert_equal '<p><div>foo</div></p>', body.gsub(/\s+/, "")
end
should 'render slim to a SafeBuffer' do
mock_app do
layout do
"p= yield"
end
get '/' do
render :slim, 'p = %q{<script lang="ronin">alert("https://github.com/ronin-ruby/ronin")</script>}', :layout => false
end
get "/with_layout" do
render :slim, 'div foo', :layout => true
end
end
get '/'
assert ok?
assert_equal '<p>&lt;script lang=&quot;ronin&quot;&gt;alert(&quot;https://github.com/ronin-ruby/ronin&quot;)&lt;/script&gt;</p>', body.strip
get '/with_layout'
assert ok?
assert_equal '<p><div>foo</div></p>', body.strip
end
should "render correct erb when use sinatra as middleware" do
class Bar < Sinatra::Base
get "/" do
render :erb, "<&'>"
end
end
mock_app do
use Bar
end
get "/"
assert_equal "<&'>", body
end
end
end

View file

@ -0,0 +1,14 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Rendering Extensions" do
context 'for haml' do
should 'render haml_tag correctly' do
mock_app do
get('/') { render :haml, '-haml_tag :div'}
end
get '/'
assert_match '<div></div>', last_response.body
end
end
end

View file

@ -0,0 +1,33 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Routing" do
should 'perform restul routing' do
mock_app do
controller :parent => :parents do
get :index do
"#{url_for(:index, params[:parent_id])} get"
end
put :index, :with => :asset_id do
"#{url_for(:index, params[:parent_id], :asset_id => params[:asset_id])} put"
end
post :index, :with => :asset_id do
"#{url_for(:index, :parent_id => params[:parent_id], :asset_id => params[:asset_id])} post"
end
delete :index, :with => :asset_id do
"#{url_for(:index, params[:parent_id], :asset_id => params[:asset_id])} delete"
end
end
end
get "/parents/1"
assert_equal "/parents/1 get", body
put "/parents/1/hi"
assert_equal "/parents/1/hi put", body
post "/parents/1/hi"
assert_equal "/parents/1/hi post", body
delete "/parents/1/hi"
assert_equal "/parents/1/hi delete", body
end
end

View file

@ -0,0 +1,146 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require File.expand_path(File.dirname(__FILE__) + '/fixtures/apps/simple')
describe "Router" do
def setup
Padrino.clear!
end
should "dispatch paths correctly" do
app = lambda { |env|
[200, {
'X-ScriptName' => env['SCRIPT_NAME'],
'X-PathInfo' => env['PATH_INFO'],
'Content-Type' => 'text/plain'
}, [""]]
}
map = Padrino::Router.new(
{ :path => '/bar', :to => app },
{ :path => '/foo', :to => app },
{ :path => '/foo/bar', :to => app }
)
res = Rack::MockRequest.new(map).get("/")
assert res.not_found?
res = Rack::MockRequest.new(map).get("/qux")
assert res.not_found?
res = Rack::MockRequest.new(map).get("/foo")
assert res.ok?
assert_equal "/foo", res["X-ScriptName"]
assert_equal "/", res["X-PathInfo"]
res = Rack::MockRequest.new(map).get("/foo/")
assert res.ok?
assert_equal "/foo", res["X-ScriptName"]
assert_equal "/", res["X-PathInfo"]
res = Rack::MockRequest.new(map).get("/foo/bar")
assert res.ok?
assert_equal "/foo/bar", res["X-ScriptName"]
assert_equal "/", res["X-PathInfo"]
res = Rack::MockRequest.new(map).get("/foo/bar/")
assert res.ok?
assert_equal "/foo/bar", res["X-ScriptName"]
assert_equal "/", res["X-PathInfo"]
res = Rack::MockRequest.new(map).get("/foo///bar//quux")
assert_equal 200, res.status
assert res.ok?
assert_equal "/foo/bar", res["X-ScriptName"]
assert_equal "//quux", res["X-PathInfo"]
res = Rack::MockRequest.new(map).get("/foo/quux", "SCRIPT_NAME" => "/bleh")
assert res.ok?
assert_equal "/bleh/foo", res["X-ScriptName"]
assert_equal "/quux", res["X-PathInfo"]
res = Rack::MockRequest.new(map).get("/bar", 'HTTP_HOST' => 'foo.org')
assert res.ok?
assert_equal "/bar", res["X-ScriptName"]
assert_equal "/", res["X-PathInfo"]
res = Rack::MockRequest.new(map).get("/bar/", 'HTTP_HOST' => 'foo.org')
assert res.ok?
assert_equal "/bar", res["X-ScriptName"]
assert_equal "/", res["X-PathInfo"]
end
should "dispatches hosts correctly" do
map = Padrino::Router.new(
{ :host => "foo.org", :to => lambda { |env|
[200,
{ "Content-Type" => "text/plain",
"X-Position" => "foo.org",
"X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
}, [""]]}},
{ :host => "subdomain.foo.org", :to => lambda { |env|
[200,
{ "Content-Type" => "text/plain",
"X-Position" => "subdomain.foo.org",
"X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
}, [""]]}},
{ :host => /.*\.bar.org/, :to => lambda { |env|
[200,
{ "Content-Type" => "text/plain",
"X-Position" => "bar.org",
"X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"],
}, [""]]}}
)
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "bar.org")
assert res.not_found?
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "at.bar.org")
assert res.ok?
assert_equal "bar.org", res["X-Position"]
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "foo.org")
assert res.ok?
assert_equal "foo.org", res["X-Position"]
res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "subdomain.foo.org", "SERVER_NAME" => "foo.org")
assert res.ok?
assert_equal "subdomain.foo.org", res["X-Position"]
end
should "works with padrino core applications" do
Padrino.mount("simple_demo").host("padrino.org")
assert_equal ["simple_demo"], Padrino.mounted_apps.map(&:name)
assert_equal ["padrino.org"], Padrino.mounted_apps.map(&:app_host)
res = Rack::MockRequest.new(Padrino.application).get("/")
assert res.not_found?
res = Rack::MockRequest.new(Padrino.application).get("/", "HTTP_HOST" => "bar.org")
assert res.not_found?
res = Rack::MockRequest.new(Padrino.application).get("/", "HTTP_HOST" => "padrino.org")
assert res.ok?
end
should "works with padrino applications" do
Padrino.mount("simple_demo").to("/foo").host(/.*\.padrino.org/)
res = Rack::MockRequest.new(Padrino.application).get("/")
assert res.not_found?
res = Rack::MockRequest.new(Padrino.application).get("/", "HTTP_HOST" => "bar.org")
assert res.not_found?
res = Rack::MockRequest.new(Padrino.application).get("/", "HTTP_HOST" => "padrino.org")
assert res.not_found?
res = Rack::MockRequest.new(Padrino.application).get("/none", "HTTP_HOST" => "foo.padrino.org")
assert res.not_found?
res = Rack::MockRequest.new(Padrino.application).get("/foo", "HTTP_HOST" => "bar.padrino.org")
assert res.ok?
res = Rack::MockRequest.new(Padrino.application).get("/foo/", "HTTP_HOST" => "bar.padrino.org")
assert res.ok?
end
end

View file

@ -0,0 +1,56 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "ObjectSpace" do
def setup
end
def teardown
end
context "#classes" do
should "take an snapshot of the current loaded classes" do
snapshot = ObjectSpace.classes
assert_equal snapshot.include?(Padrino::Logger), true
end
should "return a Set object" do
snapshot = ObjectSpace.classes
assert_equal snapshot.kind_of?(Set), true
end
should "be able to process a the class name given a block" do
klasses = ObjectSpace.classes do |klass|
if klass.name =~ /^Padrino::/
klass
end
end
assert_equal (klasses.size > 1), true
klasses.each do |klass|
assert_match /^Padrino::/, klass.to_s
end
end
end
context "#new_classes" do
setup do
@snapshot = ObjectSpace.classes
end
should "return list of new classes" do
class OSTest; end
module OSTestModule; class B; end; end
new_classes = ObjectSpace.new_classes(@snapshot)
assert_equal new_classes.size, 2
assert_equal new_classes.include?(OSTest), true
assert_equal new_classes.include?(OSTestModule::B), true
end
should "return a Set object" do
new_classes = ObjectSpace.new_classes(@snapshot)
assert_equal new_classes.kind_of?(Set), true
end
end
end

View file

@ -0,0 +1,5 @@
lib/**/*.rb
bin/*
-
README.rdoc
LICENSE.txt

View file

@ -0,0 +1,21 @@
## MAC OS
.DS_Store
## TEXTMATE
*.tmproj
tmtags
## EMACS
*~
\#*
.\#*
## VIM
*.swp
## PROJECT::GENERAL
coverage
rdoc
pkg
## PROJECT::SPECIFIC

View file

@ -0,0 +1 @@
--title 'Padrino Helpers Documentation' --protected

View file

@ -0,0 +1,20 @@
Copyright (c) 2011 Padrino
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,239 @@
= Application Extensions and Helpers (padrino-helpers)
=== Overview
This component provides a great deal of view helpers related to html markup generation.
There are helpers for generating tags, forms, links, images, and more. Most of the basic
methods should be very familiar to anyone who has used rails view helpers.
=== Output Helpers
Output helpers are a collection of important methods for managing, capturing and displaying output
in various ways and is used frequently to support higher-level helper functions. There are
three output helpers worth mentioning: <tt>content_for</tt>, <tt>capture_html</tt>, and <tt>concat_content</tt>
The content_for functionality supports capturing content and then rendering this into a different place
such as within a layout. One such popular example is including assets onto the layout from a template:
# app/views/site/index.erb
...
<% content_for :assets do %>
<%= stylesheet_link_tag 'index', 'custom' %>
<% end %>
...
Added to a template, this will capture the includes from the block and allow them to be yielded into the layout:
# app/views/layout.erb
...
<head>
<title>Example</title>
<%= stylesheet_link_tag 'style' %>
<%= yield_content :assets %>
</head>
...
This will automatically insert the contents of the block (in this case a stylesheet include) into the
location the content is yielded within the layout. You can also check if content exists for a block using
<tt>content_for?(true)</tt> which returns true if content exists.
The capture_html and the concat_content methods allow content to be manipulated and stored for use in building
additional helpers accepting blocks or displaying information in a template. One example is the use of
these in constructing a simplified 'form_tag' helper which accepts a block.
# form_tag '/register' do ... end
def form_tag(url, options={}, &block)
# ... truncated ...
inner_form_html = capture_html(&block)
concat_content '<form>' + inner_form_html + '</form>'
end
This will capture the template body passed into the form_tag block and then append the content
to the template through the use of <tt>concat_content</tt>. Note have been built to work for both haml and erb
templates using the same syntax.
For more information on using output helpers, check out the guide for
{Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers].
=== Tag Helpers
Tag helpers are the basic building blocks used to construct html 'tags' within a view template. There
are three major functions for this category: <tt>tag</tt>, <tt>content_tag</tt> and <tt>input_tag</tt>.
The tag and content_tag are for building arbitrary html tags with a name and specified options. If
the tag contains 'content' within then <tt>content_tag</tt> is used. For example:
tag(:br, :style => 'clear:both') => <br style="clear:both" />
content_tag(:p, "demo", :class => 'light') => <p class="light">demo</p>
The input_tag is used to build tags that are related to accepting input from the user:
input_tag :text, :class => "demo" => <input type='text' class='demo' />
input_tag :password, :value => "secret", :class => "demo"
Note that all of these accept html options and result in returning a string containing html tags.
For more information on using tag helpers, check out the guide for
{Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers].
=== Asset Helpers
Asset helpers are intended to help insert useful html onto a view template such as 'flash' notices,
hyperlinks, mail_to links, images, stylesheets and javascript. An example of their uses would be on a
simple view template:
# app/views/example.haml
...
%head
= stylesheet_link_tag 'layout'
= javascript_include_tag 'application'
%body
...
= flash_tag :notice
%p= link_to 'Blog', '/blog', :class => 'example'
%p Mail me at #{mail_to 'fake@faker.com', "Fake Email Link", :cc => "test@demo.com"}
%p= image_tag 'padrino.png', :width => '35', :class => 'logo'
For more information on using asset helpers, check out the guide for
{Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers].
=== Form Helpers
Form helpers are the 'standard' form tag helpers you would come to expect when building forms. A simple
example of constructing a non-object form would be:
- form_tag '/destroy', :class => 'destroy-form', :method => 'delete' do
= flash_tag(:notice)
- field_set_tag do
%p
= label_tag :username, :class => 'first'
= text_field_tag :username, :value => params[:username]
%p
= label_tag :password, :class => 'first'
= password_field_tag :password, :value => params[:password]
%p
= label_tag :strategy
= select_tag :strategy, :options => ['delete', 'destroy'], :selected => 'delete'
%p
= check_box_tag :confirm_delete
- field_set_tag(:class => 'buttons') do
= submit_tag "Remove"
For more information on using form helpers, check out the guide for
{Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers].
=== FormBuilders
Form builders are full-featured objects allowing the construction of complex object-based forms
using a simple, intuitive syntax.
A form_for using these basic fields might look like:
- form_for @user, '/register', :id => 'register' do |f|
= f.error_messages
%p
= f.label :username, :caption => "Nickname"
= f.text_field :username
%p
= f.label :email
= f.text_field :email
%p
= f.label :password
= f.password_field :password
%p
= f.label :is_admin, :caption => "Admin User?"
= f.check_box :is_admin
%p
= f.label :color, :caption => "Favorite Color?"
= f.select :color, :options => ['red', 'black']
%p
- fields_for @user.location do |location|
= location.text_field :street
= location.text_field :city
%p
= f.submit "Create", :class => 'button'
Forms can also accept nested attributes using `fields_for` within the form builder in recent releases. Check out the guide for {Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers] to learn more about nested forms.
There is also an additional StandardFormBuilder which builds on the abstract fields that can be used within a form_for.
A form_for using these standard fields might be:
- form_for @user, '/register', :id => 'register' do |f|
= f.error_messages
= f.text_field_block :name, :caption => "Full name"
= f.text_field_block :email
= f.check_box_block :remember_me
= f.select_block :fav_color, :options => ['red', 'blue']
= f.password_field_block :password
= f.submit_block "Create", :class => 'button'
and would generate this html (with each input contained in a paragraph and containing a label):
<form id="register" action="/register" method="post">
<p><label for="user_name">Full name: </label><input type="text" id="user_name" name="user[name]"></p>
...omitted...
<p><input type="submit" value="Create" class="button"></p>
</form>
You can also easily build your own FormBuilder which allows for customized fields and behavior.
For more information on using the Padrino form builders, check out the guide for
{Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers].
=== Format Helpers
Format helpers are several useful utilities for manipulating the format of text to achieve a goal.
The four format helpers are <tt>escape_html</tt>, <tt>time_ago_in_words</tt>, and <tt>js_escape_html</tt>.
The escape_html and js_escape_html function are for taking an html string and escaping certain characters.
<tt>escape_html</tt> will escape ampersands, brackets and quotes to their HTML/XML entities. This is useful
to sanitize user content before displaying this on a template. <tt>js_escape_html</tt> is used for
passing javascript information from a js template to a javascript function.
escape_html('<hello>&<goodbye>') # => &lt;hello&gt;&amp;&lt;goodbye&gt;
There is also an alias for escape_html called <tt>h</tt> for even easier usage within templates.
Format helpers also includes a number of useful text manipulation functions such as <tt>simple_format</tt>,
<tt>pluralize</tt>, <tt>word_wrap</tt>, <tt>truncate</tt> and <tt>truncate_words</tt>.
simple_format("hello\nworld") # => "<p>hello<br/>world</p>"
pluralize(2, 'person') => '2 people'
word_wrap('Once upon a time', :line_width => 8) => "Once upon\na time"
truncate("Once upon a time in a world far far away", :length => 8) => "Once upon..."
truncate_words("Once upon a time in a world far far away", :length => 4) => "Once upon a time..."
These helpers can be invoked from any route or view within your application.
For more information on using the format helpers, check out the guide for
{Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers].
=== Render Helpers
This component provides a number of rendering helpers making the process of displaying templates a bit easier.
This plugin also has support for useful additions such as partials (with support for :collection) for the templating system.
Using render plugin helpers is extremely simple. If you want to render an erb template in your view path:
render :erb, 'path/to/erb/template'
or using haml templates works just as well:
render :haml, 'path/to/haml/template'
There is also a method which renders the first view matching the path and removes the need to define an engine:
render 'path/to/any/template'
Finally, we have the all-important partials support for rendering mini-templates onto a page:
partial 'photo/_item', :object => @photo, :locals => { :foo => 'bar' }
partial 'photo/_item', :collection => @photos
For more information on using the render and partial helpers, check out the guide for
{Padrino Helpers}[http://www.padrinorb.com/guides/application-helpers].
== Copyright
Copyright (c) 2011-2013 Padrino. See LICENSE for details.

View file

@ -0,0 +1,5 @@
# coding:utf-8
RAKE_ROOT = __FILE__
require 'rubygems'
require File.expand_path(File.dirname(__FILE__) + '/../gem_rake_helper')

View file

@ -0,0 +1,57 @@
require 'padrino-core/support_lite' unless defined?(SupportLite)
require 'i18n'
require 'enumerator'
require 'active_support/time_with_zone' # next extension depends on this
require 'active_support/core_ext/string/conversions' # to_date
require 'active_support/option_merger' # with_options
require 'active_support/core_ext/object/with_options' # with_options
require 'active_support/inflector' # humanize
FileSet.glob_require('padrino-helpers/**/*.rb', __FILE__)
I18n.load_path += Dir["#{File.dirname(__FILE__)}/padrino-helpers/locale/*.yml"]
module Padrino
##
# This component provides a variety of view helpers related to html markup generation.
# There are helpers for generating tags, forms, links, images, and more.
# Most of the basic methods should be very familiar to anyone who has used rails view helpers.
#
module Helpers
class << self
##
# Registers these helpers into your application:
#
# Padrino::Helpers::OutputHelpers
# Padrino::Helpers::TagHelpers
# Padrino::Helpers::AssetTagHelpers
# Padrino::Helpers::FormHelpers
# Padrino::Helpers::FormatHelpers
# Padrino::Helpers::RenderHelpers
# Padrino::Helpers::NumberHelpers
# Padrino::Helpers::Breadcrumbs
#
# @param [Sinatra::Application] app
# The specified Padrino application.
#
# @example Register the helper module
# require 'padrino-helpers'
# class Padrino::Application
# register Padrino::Helpers
# end
#
def registered(app)
app.set :default_builder, 'StandardFormBuilder'
app.helpers Padrino::Helpers::OutputHelpers
app.helpers Padrino::Helpers::TagHelpers
app.helpers Padrino::Helpers::AssetTagHelpers
app.helpers Padrino::Helpers::FormHelpers
app.helpers Padrino::Helpers::FormatHelpers
app.helpers Padrino::Helpers::RenderHelpers
app.helpers Padrino::Helpers::NumberHelpers
app.helpers Padrino::Helpers::TranslationHelpers
app.helpers Padrino::Helpers::Breadcrumbs
end
alias :included :registered
end
end
end

View file

@ -0,0 +1,401 @@
module Padrino
module Helpers
###
# Helpers related to producing assets (images, stylesheets, js, etc) within templates.
#
module AssetTagHelpers
FRAGMENT_HASH = "#".html_safe.freeze
APPEND_ASSET_EXTENSIONS = ["js", "css"]
ABSOLUTE_URL_PATTERN = %r{^(https?://)}
##
# Creates a div to display the flash of given type if it exists.
#
# @param [Symbol] kind
# The type of flash to display in the tag.
# @param [Hash] options
# The html options for this section.
# use :bootstrap => true to support Twitter's bootstrap dismiss alert button.
#
# @return [String] Flash tag html with specified +options+.
#
# @example
# flash_tag(:notice, :id => 'flash-notice')
# # Generates: <div class="notice">flash-notice</div>
# flash_tag(:error, :success)
# # Generates: <div class="error">flash-error</div>
# # <div class="success">flash-success</div>
#
def flash_tag(*args)
options = args.extract_options!
bootstrap = options.delete(:bootstrap) if options[:bootstrap]
args.inject(''.html_safe) do |html,kind|
flash_text = flash[kind]
next html if flash_text.blank?
flash_text << safe_content_tag(:button, "&times;", {:type => :button, :class => :close, :'data-dismiss' => :alert}) if bootstrap
html << safe_content_tag(:div, flash_text, options.reverse_merge(:class => kind))
end
end
##
# Creates a link element with given name, url and options.
#
# @overload link_to(caption, url, options={})
# @param [String] caption The text caption.
# @param [String] url The url href.
# @param [Hash] options The html options.
# @overload link_to(url, options={}, &block)
# @param [String] url The url href.
# @param [Hash] options The html options.
# @param [Proc] block The link content.
#
# @option options [String] :anchor
# The anchor for the link (i.e #something).
# @option options [String] :fragment
# Synonym for anchor.
# @option options [Boolean] :if
# If true, the link will appear, otherwise not.
# @option options [Boolean] :unless
# If false, the link will appear, otherwise not.
# @option options [Boolean] :remote
# If true, this link should be handled by an ajax ujs handler.
# @option options [String] :confirm
# Instructs ujs handler to alert confirm message.
# @option options [Symbol] :method
# Instructs ujs handler to use different http method (i.e :post, :delete).
#
# @return [String] Link tag html with specified +options+.
#
# @example
# link_to('click me', '/dashboard', :class => 'linky')
# link_to('click me', '/dashboard', :remote => true)
# link_to('click me', '/dashboard', :method => :delete)
# link_to('click me', :class => 'blocky') do; end
#
# Note that you can pass :+if+ or :+unless+ conditions, but if you provide :current as
# condition padrino return true/false if the request.path_info match the given url.
#
def link_to(*args, &block)
options = args.extract_options!
fragment = options.delete(:anchor).to_s if options[:anchor]
fragment = options.delete(:fragment).to_s if options[:fragment]
url = ActiveSupport::SafeBuffer.new
if block_given?
if args[0]
url.concat(args[0])
url.concat(FRAGMENT_HASH).concat(fragment) if fragment
else
url.concat(FRAGMENT_HASH)
url.concat(fragment) if fragment
end
options.reverse_merge!(:href => url)
link_content = capture_html(&block)
return '' unless parse_conditions(url, options)
result_link = content_tag(:a, link_content, options)
block_is_template?(block) ? concat_content(result_link) : result_link
else
if args[1]
url.concat(args[1])
url.safe_concat(FRAGMENT_HASH).concat(fragment) if fragment
else
url = FRAGMENT_HASH
url.concat(fragment) if fragment
end
name = args[0]
return name unless parse_conditions(url, options)
options.reverse_merge!(:href => url)
content_tag(:a, name, options)
end
end
##
# Creates a link tag that browsers and news readers can use to auto-detect an RSS or ATOM feed.
#
# @param [Symbol] mime
# The mime type of the feed (i.e :atom or :rss).
# @param [String] url
# The url for the feed tag to reference.
# @param[Hash] options
# The options for the feed tag.
# @option options [String] :rel ("alternate")
# Specify the relation of this link.
# @option options [String] :type
# Override the auto-generated mime type.
# @option options [String] :title
# Specify the title of the link, defaults to the type.
#
# @return [String] Feed link html tag with specified +options+.
#
# @example
# feed_tag :atom, url(:blog, :posts, :format => :atom), :title => "ATOM"
# # Generates: <link type="application/atom+xml" rel="alternate" href="/blog/posts.atom" title="ATOM" />
# feed_tag :rss, url(:blog, :posts, :format => :rss)
# # Generates: <link type="application/rss+xml" rel="alternate" href="/blog/posts.rss" title="rss" />
#
def feed_tag(mime, url, options={})
full_mime = (mime == :atom) ? 'application/atom+xml' : 'application/rss+xml'
tag(:link, options.reverse_merge(:rel => 'alternate', :type => full_mime, :title => mime, :href => url))
end
##
# Creates a mail link element with given name and caption.
#
# @param [String] email
# The email address for the link.
# @param [String] caption
# The caption for the link.
# @param [Hash] mail_options
# The options for the mail link. Accepts html options.
# @option mail_options [String] cc The cc recipients.
# @option mail_options [String] bcc The bcc recipients.
# @option mail_options [String] subject The subject line.
# @option mail_options [String] body The email body.
#
# @return [String] Mail link html tag with specified +options+.
#
# @example
# # Generates: <a href="mailto:me@demo.com">me@demo.com</a>
# mail_to "me@demo.com"
# # Generates: <a href="mailto:me@demo.com">My Email</a>
# mail_to "me@demo.com", "My Email"
#
def mail_to(email, caption=nil, mail_options={})
html_options = mail_options.slice!(:cc, :bcc, :subject, :body)
mail_query = Rack::Utils.build_query(mail_options).gsub(/\+/, '%20').gsub('%40', '@').gsub('&', '&amp;')
mail_href = "mailto:#{email}"; mail_href << "?#{mail_query}" if mail_query.present?
link_to((caption || email), mail_href, html_options)
end
##
# Creates a meta element with the content and given options.
#
# @param [String] content
# The content for the meta tag.
# @param [Hash] options
# The html options for the meta tag.
#
# @return [String] Meta html tag with specified +options+.
#
# @example
# # Generates: <meta name="keywords" content="weblog,news" />
# meta_tag "weblog,news", :name => "keywords"
#
# # Generates: <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
# meta_tag "text/html; charset=UTF-8", 'http-equiv' => "Content-Type"
#
def meta_tag(content, options={})
options.reverse_merge!("content" => content)
tag(:meta, options)
end
##
# Generates a favicon link. Looks inside images folder
#
# @param [String] source
# The source image path for the favicon link tag.
# @param [Hash] options
# The html options for the favicon link tag.
#
# @return [String] The favicon link html tag with specified +options+.
#
# @example
# favicon_tag 'favicon.png'
# favicon_tag 'icons/favicon.png'
# # or override some options
# favicon_tag 'favicon.png', :type => 'image/ico'
#
def favicon_tag(source, options={})
type = File.extname(source).gsub('.','')
options = options.dup.reverse_merge!(:href => image_path(source), :rel => 'icon', :type => "image/#{type}")
tag(:link, options)
end
##
# Creates an image element with given url and options.
#
# @param [String] url
# The source path for the image tag.
# @param [Hash] options
# The html options for the image tag.
#
# @return [String] Image html tag with +url+ and specified +options+.
#
# @example
# image_tag('icons/avatar.png')
#
def image_tag(url, options={})
options.reverse_merge!(:src => image_path(url))
tag(:img, options)
end
##
# Returns an html script tag for each of the sources provided.
# You can pass in the filename without extension or a symbol and we search it in your +appname.public_folder+
# like app/public/stylesheets for inclusion. You can provide also a full path.
#
# @overload stylesheet_link_tag(*sources, options={})
# @param [Array<String>] sources Splat of css source paths
# @param [Hash] options The html options for the link tag
#
# @return [String] Stylesheet link html tag for +sources+ with specified +options+.
#
# @example
# stylesheet_link_tag 'style', 'application', 'layout'
#
# @api public.
def stylesheet_link_tag(*sources)
options = sources.extract_options!.symbolize_keys
options.reverse_merge!(:media => 'screen', :rel => 'stylesheet', :type => 'text/css')
sources.flatten.map { |source|
tag(:link, options.reverse_merge(:href => asset_path(:css, source)))
}.join("\n").html_safe
end
##
# Returns an html script tag for each of the sources provided.
# You can pass in the filename without extension or a symbol and we search it in your +appname.public_folder+
# like app/public/javascript for inclusion. You can provide also a full path.
#
# @overload javascript_include_tag(*sources, options={})
# @param [Array<String>] sources Splat of js source paths
# @param [Hash] options The html options for the script tag
#
# @return [String] Script tag for +sources+ with specified +options+.
#
# @example
# javascript_include_tag 'application', :extjs
#
def javascript_include_tag(*sources)
options = sources.extract_options!.symbolize_keys
options.reverse_merge!(:type => 'text/javascript')
sources.flatten.map { |source|
content_tag(:script, nil, options.reverse_merge(:src => asset_path(:js, source)))
}.join("\n").html_safe
end
##
# Returns the path to the image, either relative or absolute. We search it in your +appname.public_folder+
# like app/public/images for inclusion. You can provide also a full path.
#
# @param [String] src
# The path to the image file (relative or absolute).
#
# @return [String] Path to an image given the +kind+ and +source+.
#
# @example
# # Generates: /images/foo.jpg?1269008689
# image_path("foo.jpg")
#
# @api public
def image_path(src)
asset_path(:images, src)
end
##
# Returns the path to the specified asset (css or javascript).
#
# @param [String] kind
# The kind of asset (i.e :images, :js, :css).
# @param [String] source
# The path to the asset (relative or absolute).
#
# @return [String] Path for the asset given the +kind+ and +source+.
#
# @example
# # Generates: /javascripts/application.js?1269008689
# asset_path :js, :application
#
# # Generates: /stylesheets/application.css?1269008689
# asset_path :css, :application
#
# # Generates: /images/example.jpg?1269008689
# asset_path :images, 'example.jpg'
#
def asset_path(kind, source)
source = asset_normalize_extension(kind, URI.escape(source.to_s))
return source if source =~ ABSOLUTE_URL_PATTERN || source =~ /^\// # absolute source
source = File.join(asset_folder_name(kind), source)
timestamp = asset_timestamp(source)
result_path = uri_root_path(source)
"#{result_path}#{timestamp}"
end
private
##
# Returns the URI root of the application with optional paths appended.
#
# @example
# uri_root_path("/some/path") => "/root/some/path"
# uri_root_path("javascripts", "test.js") => "/uri/root/javascripts/test.js"
#
def uri_root_path(*paths)
root_uri = self.class.uri_root if self.class.respond_to?(:uri_root)
File.join(ENV['RACK_BASE_URI'].to_s, root_uri || '/', *paths)
end
##
# Returns the timestamp mtime for an asset.
#
# @example
# asset_timestamp("some/path/to/file.png") => "?154543678"
#
def asset_timestamp(file_path)
return nil if file_path =~ /\?/ || (self.class.respond_to?(:asset_stamp) && !self.class.asset_stamp)
public_path = self.class.public_folder if self.class.respond_to?(:public_folder)
public_path ||= Padrino.root("public") if Padrino.respond_to?(:root)
public_file_path = File.join(public_path, file_path) if public_path
stamp = File.mtime(public_file_path).to_i if public_file_path && File.exist?(public_file_path)
stamp ||= Time.now.to_i
"?#{stamp}"
end
###
# Returns the asset folder given a kind.
#
# @example
# asset_folder_name(:css) => 'stylesheets'
# asset_folder_name(:js) => 'javascripts'
# asset_folder_name(:images) => 'images'
#
def asset_folder_name(kind)
case kind
when :css then 'stylesheets'
when :js then 'javascripts'
else kind.to_s
end
end
##
# Normalizes the extension for a given asset.
#
# @example
#
# asset_normalize_extension(:images, "/foo/bar/baz.png") => "/foo/bar/baz.png"
# asset_normalize_extension(:js, "/foo/bar/baz") => "/foo/bar/baz.js"
#
def asset_normalize_extension(kind, source)
ignore_extension = !APPEND_ASSET_EXTENSIONS.include?(kind.to_s)
source << ".#{kind}" unless ignore_extension || source =~ /\.#{kind}/ || source =~ ABSOLUTE_URL_PATTERN
source
end
##
# Parses link_to options for given correct conditions.
#
# @example
# parse_conditions("/some/url", :if => false) => true
#
def parse_conditions(url, options)
if options.has_key?(:if)
condition = options.delete(:if)
condition == :current ? url == request.path_info : condition
elsif condition = options.delete(:unless)
condition == :current ? url != request.path_info : !condition
else
true
end
end
end
end
end

Some files were not shown because too many files have changed in this diff Show more