vendor padrino

This commit is contained in:
Thomas Reynolds 2011-08-20 17:09:30 -07:00
parent 8de3d2b35d
commit feca0a0a2b
171 changed files with 14786 additions and 4 deletions

View file

@ -1,3 +1,11 @@
2.0.5
=====
- Vendored Padrino 0.10.0
2.0.4
=====
- Pulled out undocumented remote data feature into its own extension
2.0.3
=====
- Pulled out undocumented Blog feature into its own extension

View file

@ -55,6 +55,12 @@
libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
padrino_core_path = File.join(libdir, "middleman", "vendor", "padrino-core-0.10.0", "lib")
$LOAD_PATH.unshift(padrino_core_path) unless $LOAD_PATH.include?(padrino_core_path)
padrino_helpers_path = File.join(libdir, "middleman", "vendor", "padrino-helpers-0.10.0", "lib")
$LOAD_PATH.unshift(padrino_helpers_path) unless $LOAD_PATH.include?(padrino_helpers_path)
# We're riding on Sinatra, so let's include it.
require "sinatra/base"
@ -164,7 +170,7 @@ module Middleman
extensions.each do |spec|
require spec.name
$stderr.puts "require: #{spec.name}"
# $stderr.puts "require: #{spec.name}"
end
end

View file

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

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,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, respond_to support, 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: relative_time_ago, 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 users 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 respond_to 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], respond_to => [: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 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,119 @@
require 'sinatra/base'
require 'padrino-core/support_lite' unless defined?(SupportLite)
FileSet.glob_require('padrino-core/application/*.rb', __FILE__)
FileSet.glob_require('padrino-core/*.rb', __FILE__)
# Defines our Constants
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 #:nodoc:
end
class << self
##
# Helper method for file references.
#
# ==== Examples
#
# # 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
#
def env
@_env ||= PADRINO_ENV.to_s.downcase.to_sym
end
##
# Returns the resulting rack builder mapping each 'mounted' application
#
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) }
unless middleware.empty?
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:
#
# Padrino.configure_apps do
# enable :sessions
# disable :raise_errors
# end
#
def configure_apps(&block)
@_global_configuration = block if block_given?
end
###
# Returns project-wide configuration settings
# defined in 'configure_apps' block
#
def apps_configuration
@_global_configuration
end
##
# Default encoding to UTF8.
#
def set_encoding
if RUBY_VERSION < '1.9'
$KCODE='u'
else
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = nil # Encoding::UTF_8
end
nil
end
##
# Return bundle status :+:locked+ if .bundle/environment.rb exist :+:unlocked if Gemfile exist
# otherwise return nil
#
def bundle
return :locked if File.exist?(root('Gemfile.lock'))
return :unlocked if File.exist?(root("Gemfile"))
end
##
# A Rack::Builder object that allows to add middlewares in front of all
# Padrino applications
#
def middleware
@middleware ||= []
end
##
# Clears all previously configured middlewares
#
def clear_middleware!
@middleware = []
end
##
# Convenience method for adding a Middleware to the whole padrino app.
#
def use(m, *args, &block)
middleware << [m, args, block]
end
end # self
end # Padrino

View file

@ -0,0 +1,259 @@
module Padrino
class ApplicationSetupError < RuntimeError #:nodoc:
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
register Padrino::Routing # Support for advanced routing, controllers, url_for
class << self
def inherited(base) #:nodoc:
logger.devel "Setup #{base}"
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)
super(base) # Loading the subclass inherited method
end
##
# Hooks into when a new instance of the application is created
# This is used because putting the configuration into inherited doesn't
# take into account overwritten app settings inside subclassed definitions
# Only performs the setup first time application is initialized.
#
def new(*args, &bk)
setup_application!
logging, logging_was = false, logging
show_exceptions, show_exceptions_was = false, show_exceptions
super(*args, &bk)
ensure
logging, show_exceptions = logging_was, show_exceptions_was
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.
#
# ==== Examples
#
# MyApp.reload!
#
def reload!
logger.devel "Reloading #{self}"
@_dependencies = nil # Reset dependencies
reset! # Reset sinatra app
reset_routes! # Remove all existing user-defined application routes
Padrino.require_dependencies(self.app_file, :force => true) # Reload the app file
require_dependencies # Reload dependencies
register_initializers # Reload our middlewares
default_filters! # Reload filters
default_errors! # Reload our errors
I18n.reload! if defined?(I18n) # Reload also our translations
end
##
# Resets application routes to only routes not defined by the user
#
# ==== Examples
#
# MyApp.reset_routes!
#
def reset_routes!
reset_router!
default_routes!
end
##
# Returns the routes of our app.
#
# ==== Examples
#
# 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
#
def setup_application!
return if @_configured
self.register_initializers
self.require_dependencies
self.disable :logging # We need do that as default because Sinatra use commonlogger.
self.default_filters!
self.default_routes!
self.default_errors!
if defined?(I18n)
I18n.load_path << self.locale_path
I18n.reload!
end
@_configured = true
end
##
# Run the Padrino app as a self-hosted server using
# Thin, Mongrel or WEBrick (in that order)
#
def run!(options={})
return unless Padrino.load!
Padrino.mount(self.to_s).to("/")
Padrino.run!(options)
end
##
# Returns the used $LOAD_PATHS from this application
#
def load_paths
@_load_paths ||= %w(models lib mailers controllers helpers).map { |path| File.join(self.root, path) }
end
##
# Returns default list of path globs to load as dependencies
# Appends custom dependency patterns to the be loaded for your Application
#
# ==== Examples
# MyApp.dependencies << "#{Padrino.root}/uploaders/**/*.rb"
# MyApp.dependencies << Padrino.root('other_app', 'controllers.rb')
#
def dependencies
@_dependencies ||= [
"urls.rb", "config/urls.rb", "mailers/*.rb", "mailers.rb",
"controllers/**/*.rb", "controllers.rb", "helpers/**/*.rb", "helpers.rb"
].map { |file| Dir[File.join(self.root, file)] }.flatten
end
##
# An array of file to load before your app.rb, basically are files wich our app depends on.
#
# By default we look for files:
#
# yourapp/models.rb
# yourapp/models/**/*.rb
# yourapp/lib.rb
# yourapp/lib/**/*.rb
#
# ==== Examples
# 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, Proc.new { Padrino.root('public', uri_root) }
set :views, Proc.new { File.join(root, "views") }
set :images_path, Proc.new { File.join(public, "images") }
# Padrino specific
set :uri_root, "/"
set :app_name, self.to_s.underscore.to_sym
set :default_builder, 'StandardFormBuilder'
set :flash, defined?(Rack::Flash)
set :authentication, false
# Padrino locale
set :locale_path, Proc.new { Dir[File.join(self.root, "/locale/**/*.{rb,yml}")] }
# Load the Global Configurations
class_eval(&Padrino.apps_configuration) if Padrino.apps_configuration
end
##
# We need to add almost __sinatra__ images.
#
def default_routes!
configure :development do
get '/__sinatra__/:image.png' do
content_type :png
filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
send_file filename
end
end
end
##
# This filter it's used for know the format of the request, and automatically set the content type.
#
def default_filters!
before do
unless @_content_type
@_content_type = :html
response['Content-Type'] = 'text/html;charset=utf-8'
end
end
end
##
# This log errors for production environments
#
def default_errors!
configure :production do
error ::Exception do
boom = env['sinatra.error']
logger.error ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n ")
response.status = 500
content_type 'text/html'
'<h1>Internal Server Error</h1>'
end
end
end
##
# Requires the Padrino middleware
#
def register_initializers
use Padrino::ShowExceptions if show_exceptions?
use Padrino::Logger::Rack, uri_root if Padrino.logger && logging?
use Padrino::Reloader::Rack if reload?
use Rack::Flash, :sweep => true if flash?
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
end # self
# TODO Remove deprecated render inclusion in a few versions
# Detects if a user is incorrectly using 'render' and warns them about the fix
# In 0.10.0, Padrino::Rendering now has to be explicitly included in the application
def render(*args)
if !defined?(DEFAULT_RENDERING_OPTIONS) && !@_render_included &&
(args.size == 1 || (args.size == 2 && args[0].is_a?(String) && args[1].is_a?(Hash)))
logger.warn "[Deprecation] Please 'register Padrino::Rendering' for each application as shown here:
https://gist.github.com/1d36a35794dbbd664ea4 for 'render' to function as expected"
self.class.instance_eval { register Padrino::Rendering }
@_render_included = true
render(*args)
else # pass through, rendering is valid
super(*args)
end
end # render method
end # Application
end # Padrino

View file

@ -0,0 +1,228 @@
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
class TemplateNotFound < RuntimeError #:nodoc:
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:
#
# Padrino::Rendering::IGNORE_FILE_PATTERN << /~$/
#
IGNORE_FILE_PATTERN = [
/~$/ # This is for Gedit
] unless defined?(IGNORE_FILE_PATTERN)
##
# Default rendering options used in the #render-method
#
DEFAULT_RENDERING_OPTIONS = { :strict_format => false, :raise_exceptions => true } unless defined?(DEFAULT_RENDERING_OPTIONS)
##
# Main class that register this extension
#
class << self
def registered(app)
app.send(:include, InstanceMethods)
app.extend(ClassMethods)
end
alias :included :registered
end
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+)
#
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.
#
# render_options = [template_path, content_type, locale]
#
def fetch_template_file(render_options)
(@_cached_templates ||= {})[render_options]
end
###
# Caches the template file for the given rendering options
#
# render_options = [template_path, content_type, locale]
#
def cache_template_file!(template_file, render_options)
(@_cached_templates ||= {})[render_options] = template_file || []
end
##
# Returns the cached layout path.
#
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
module InstanceMethods
attr_reader :current_engine
def content_type(type=nil, params={}) #:nodoc:
type.nil? ? @_content_type : super(type, params)
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'
# * Use render { :a => 1, :b => 2, :c => 3 } # => return a json string
#
def render(engine, data=nil, options={}, locals={}, &block)
# If engine is a hash then render data converted to json
return engine.to_json if engine.is_a?(Hash)
# 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) if data.nil?
# Setup root
root = settings.respond_to?(:root) ? settings.root : ""
# Use @layout if it exists
options[:layout] = @layout if options[:layout].nil?
# Resolve layouts similar to in Rails
if (options[:layout].nil? || options[:layout] == true) && !settings.templates.has_key?(:layout)
layout_path, layout_engine = *resolved_layout
options[:layout] = layout_path || false # We need to force layout false so sinatra don't try to render it
options[:layout] = false unless layout_engine == engine # TODO allow different layout engine
options[:layout_engine] = layout_engine || engine if options[:layout]
logger.debug "Resolving layout #{root}/views#{options[:layout]}" if defined?(logger) && options[:layout].present?
elsif options[:layout].present?
options[:layout] = settings.fetch_layout_path(options[:layout] || @layout)
logger.debug "Resolving layout #{root}/views#{options[:layout]}" if defined?(logger)
end
# Cleanup the template
@current_engine, engine_was = engine, @current_engine
@_out_buf, _buf_was = "", @_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.
#
# === Options
#
# :strict_format:: The resolved template must match the content_type of the request (defaults to false)
# :raise_exceptions:: Raises a +TemplateNotFound+ exception if the template cannot be located.
#
# ==== 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={})
# Fetch cached template for rendering options
template_path = "/#{template_path}" unless template_path.to_s[0] == ?/
rendering_options = [template_path, content_type, locale]
cached_template = settings.fetch_template_file(rendering_options)
return cached_template if cached_template
# 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?
located_template
end
##
# Return the I18n.locale if I18n is defined
#
def locale
I18n.locale if defined?(I18n)
end
end # InstanceMethods
end # Rendering
end # Padrino

View file

@ -0,0 +1,821 @@
require 'http_router' unless defined?(HttpRouter)
require 'padrino-core/support_lite' unless defined?(SupportLite)
class Sinatra::Request #:nodoc:
attr_accessor :route_obj, :runner
def runner=(runner)
@runner = runner
env['padrino.instance'] = runner
end
def controller
route_obj && route_obj.controller
end
end
class HttpRouter #:nodoc:
def rewrite_partial_path_info(env, request); end
def rewrite_path_info(env, request); end
def process_destination_path(path, env)
env['padrino.instance'].instance_eval do
request.route_obj = path.route
@_response_buffer = nil
@params ||= {}
@params.update(env['router.params'])
@block_params = if path.route.is_a?(HttpRouter::RegexRoute)
params_list = env['router.request'].extra_env['router.regex_match'].to_a
params_list.shift
@params[:captures] = params_list
params_list
else
env['router.request'].params
end
# Provide access to the current controller to the request
# Now we can eval route, but because we have "throw halt" we need to be
# (en)sure to reset old layout and run controller after filters.
old_params = @params
parent_layout = @layout
successful = false
begin
filter! :before
(path.route.before_filters - self.class.filters[:before]).each { |filter| instance_eval(&filter)} if path.route.before_filters
# If present set current controller layout
@layout = path.route.use_layout if path.route.use_layout
@route = path.route
@route.custom_conditions.each { |blk| pass if instance_eval(&blk) == false } if @route.custom_conditions
@block_params = @block_params.slice(0, path.route.dest.arity) if path.route.dest.arity > 0
halt_response = catch(:halt) { route_eval(&path.route.dest) }
@_response_buffer = halt_response.is_a?(Array) ? halt_response.last : halt_response
successful = true
halt @_response_buffer
ensure
(@_pending_after_filters ||= []).concat(path.route.after_filters) if path.route.after_filters && successful
@layout = parent_layout
@params = old_params
end
end
end
class Route #:nodoc:
attr_reader :before_filters, :after_filters
attr_accessor :custom_conditions, :use_layout, :controller, :cache
def add_before_filter(filter)
@before_filters ||= []
@before_filters << filter
end
def add_after_filter(filter)
@after_filters ||= []
@after_filters << filter
end
def before_filters=(filters)
filters.each { |filter| add_before_filter(filter) } if filters
end
def after_filters=(filters)
filters.each { |filter| add_after_filter(filter) } if filters
end
def custom_conditions=(custom_conditions)
@custom_conditions = custom_conditions
end
end
end
module Padrino
class Filter
attr_reader :block
def initialize(mode, scoped_controller, options, args, &block)
@mode, @scoped_controller, @options, @args, @block = mode, scoped_controller, options, args, block
end
def apply?(request)
return true if @args.empty? && @options.empty?
detect = @args.any? do |arg|
case arg
when Symbol then request.route_obj.named == arg or request.route_obj.named == [@scoped_controller, arg].flatten.join("_").to_sym
else arg === request.path_info
end
end || @options.any? { |name, val|
case name
when :agent then val === request.user_agent
else val === request.send(name)
end
}
detect ^ !@mode
end
def to_proc
filter = self
proc {
instance_eval(&filter.block) if filter.apply?(request)
}
end
end
##
# Padrino provides advanced routing definition support to make routes and url generation much easier.
# This routing system supports named route aliases and easy access to url paths.
# The benefits of this is that instead of having to hard-code route urls into every area of your application,
# now we can just define the urls in a single spot and then attach an alias which can be used to refer
# to the url throughout the application.
#
module Routing
CONTENT_TYPE_ALIASES = { :htm => :html } unless defined?(CONTENT_TYPE_ALIASES)
ROUTE_PRIORITY = {:high => 0, :normal => 1, :low => 2}
class UnrecognizedException < RuntimeError #:nodoc:
end
##
# Keeps information about parent scope.
#
class Parent < String
attr_reader :map
attr_reader :optional
attr_reader :options
alias_method :optional?, :optional
def initialize(value, options={})
super(value.to_s)
@map = options.delete(:map)
@optional = options.delete(:optional)
@options = options
end
end
##
# Main class that register this extension
#
class << self
def registered(app)
app.send(:include, InstanceMethods)
app.extend(ClassMethods)
end
alias :included :registered
end
module ClassMethods
##
# Method for organize in a better way our routes like:
#
# controller :admin do
# get :index do; ...; end
# get :show, :with => :id do; ...; end
# end
#
# Now you can call your actions with:
#
# url(:admin_index) # => "/admin"
# url(:admin_show, :id => 1) # "/admin/show/1"
#
# You can instead using named routes follow the sinatra way like:
#
# controller "/admin" do
# get "/index" do; ...; end
# get "/show/:id" do; ...; end
# end
#
# and you can call directly these urls:
#
# # => "/admin"
# # => "/admin/show/1"
#
# You can supply provides to all controller routes:
#
# controller :provides => [:html, :xml, :json] do
# get :index do; "respond to html, xml and json"; end
# post :index do; "respond to html, xml and json"; end
# get :foo do; "respond to html, xml and json"; end
# end
#
# You can specify parent resources in padrino with the :parent option on the controller:
#
# controllers :product, :parent => :user do
# get :index do
# # url is generated as "/user/#{params[:user_id]}/product"
# # url_for(:product, :index, :user_id => 5) => "/user/5/product"
# end
# get :show, :with => :id do
# # url is generated as "/user/#{params[:user_id]}/product/show/#{params[:id]}"
# # url_for(:product, :show, :user_id => 5, :id => 10) => "/user/5/product/show/10"
# end
# end
#
# You can specify conditions to run for all routes:
#
# controller :conditions => {:protect => true} do
# def self.protect(protected)
# condition do
# halt 403, "No secrets for you!" unless params[:key] == "s3cr3t"
# end if protected
# end
#
# # This route will only return "secret stuff" if the user goes to
# # `/private?key=s3cr3t`.
# get("/private") { "secret stuff" }
#
# # And this one, too!
# get("/also-private") { "secret stuff" }
#
# # But you can override the conditions for each route as needed.
# # This route will be publicly accessible without providing the
# # secret key.
# get :index, :protect => false do
# "Welcome!"
# end
# end
#
# You can supply default values:
#
# controller :lang => :de do
# get :index, :map => "/:lang" do; "params[:lang] == :de"; end
# end
#
# In a controller before and after filters are scoped and didn't affect other controllers or main app.
# In a controller layout are scoped and didn't affect others controllers and main app.
#
# controller :posts do
# layout :post
# before { foo }
# after { bar }
# end
#
def controller(*args, &block)
if block_given?
options = args.extract_options!
# Controller defaults
@_controller, original_controller = args, @_controller
@_parents, original_parent = options.delete(:parent), @_parents
@_provides, original_provides = options.delete(:provides), @_provides
@_use_format, original_use_format = options.delete(:use_format), @_use_format
@_cache, original_cache = options.delete(:cache), @_cache
@_map, original_map = options.delete(:map), @_map
@_conditions, original_conditions = options.delete(:conditions), @_conditions
@_defaults, original_defaults = options, @_defaults
# Application defaults
@filters, original_filters = { :before => @filters[:before].dup, :after => @filters[:after].dup }, @filters
@layout, original_layout = nil, @layout
instance_eval(&block)
# Application defaults
@filters = original_filters
@layout = original_layout
# Controller defaults
@_controller, @_parents, @_cache = original_controller, original_parent, original_cache
@_defaults, @_provides, @_map = original_defaults, original_provides, original_map
@_conditions, @_use_format = original_conditions, original_use_format
else
include(*args) if extensions.any?
end
end
alias :controllers :controller
def before(*args, &block)
add_filter :before, &(args.empty? ? block : construct_filter(*args, &block))
end
def after(*args, &block)
add_filter :after, &(args.empty? ? block : construct_filter(*args, &block))
end
def construct_filter(*args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
except = options.key?(:except) && Array(options.delete(:except))
raise("You cannot use except with other options specified") if except && (!args.empty? || !options.empty?)
options = except.last.is_a?(Hash) ? except.pop : {} if except
Filter.new(!except, @_controller, options, Array(except || args), &block)
end
##
# Provides many parents with shallowing.
#
# ==== Examples
#
# controllers :product do
# parent :shop, :optional => true, :map => "/my/stand"
# parent :category, :optional => true
# get :show, :with => :id do
# # generated urls:
# # "/product/show/#{params[:id]}"
# # "/my/stand/#{params[:shop_id]}/product/show/#{params[:id]}"
# # "/my/stand/#{params[:shop_id]}/category/#{params[:category_id]}/product/show/#{params[:id]}"
# # url_for(:product, :show, :id => 10) => "/product/show/10"
# # url_for(:product, :show, :shop_id => 5, :id => 10) => "/my/stand/5/product/show/10"
# # url_for(:product, :show, :shop_id => 5, :category_id => 1, :id => 10) => "/my/stand/5/category/1/product/show/10"
# end
# end
#
def parent(name, options={})
defaults = { :optional => false, :map => name.to_s }
options = defaults.merge(options)
@_parents = Array(@_parents) unless @_parents.is_a?(Array)
@_parents << Parent.new(name, options)
end
##
# Using HTTPRouter, for features and configurations see: http://github.com/joshbuddy/http_router
#
# ==== Examples
#
# router.add('/greedy/:greed')
# router.recognize('/simple')
#
def router
@router ||= HttpRouter.new
block_given? ? yield(@router) : @router
end
alias :urls :router
def compiled_router
if deferred_routes.empty?
router
else
deferred_routes.each { |_, routes| routes.each { |(route, dest)| route.to(dest) } }
@deferred_routes = nil
router
end
end
def deferred_routes
@deferred_routes ||= Hash[ROUTE_PRIORITY.values.sort.map{|p| [p, []]}]
end
def reset_router!
@deferred_routes = nil
router.reset!
end
def recognize_path(path)
if response = @router.recognize(Rack::MockRequest.env_for(path))
[response.path.route.named, response.params]
end
end
##
# Instance method for url generation like:
#
# ==== Examples
#
# url(:show, :id => 1)
# url(:show, :name => 'test', :id => 24)
# url(:show, 1)
#
def url(*args)
params = args.extract_options! # parameters is hash at end
names, params_array = args.partition{|a| a.is_a?(Symbol)}
name = names.join("_").to_sym # route name is concatenated with underscores
if params.is_a?(Hash)
params[:format] = params[:format].to_s unless params[:format].nil?
params = value_to_param(params)
end
url = if params_array.empty?
compiled_router.url(name, params)
else
compiled_router.url(name, *(params_array << params))
end
url[0,0] = conform_uri(uri_root) if defined?(uri_root)
url[0,0] = conform_uri(ENV['RACK_BASE_URI']) if ENV['RACK_BASE_URI']
url = "/" if url.blank?
url
rescue HttpRouter::InvalidRouteException
route_error = "route mapping for url(#{name.inspect}) could not be found!"
raise Padrino::Routing::UnrecognizedException.new(route_error)
end
alias :url_for :url
def get(path, *args, &block)
conditions = @conditions.dup
route('GET', path, *args, &block)
@conditions = conditions
route('HEAD', path, *args, &block)
end
def current_controller
@_controller && @_controller.last
end
private
# Parse params from the url method
def value_to_param(value)
case value
when Array
value.map { |v| value_to_param(v) }.compact
when Hash
value.inject({}) do |memo, (k,v)|
v = value_to_param(v)
memo[k] = v unless v.nil?
memo
end
when nil then nil
else value.respond_to?(:to_param) ? value.to_param : value
end
end
# Add prefix slash if its not present and remove trailing slashes.
def conform_uri(uri_string)
uri_string.gsub(/^(?!\/)(.*)/, '/\1').gsub(/[\/]+$/, '')
end
##
# Rewrite default because now routes can be:
#
# ==== Examples
#
# get :index # => "/"
# get :index, "/" # => "/"
# get :index, :map => "/" # => "/"
# get :show, "/show-me" # => "/show-me"
# get :show, :map => "/show-me" # => "/show-me"
# get "/foo/bar" # => "/show"
# get :index, :parent => :user # => "/user/:user_id/index"
# get :show, :with => :id, :parent => :user # => "/user/:user_id/show/:id"
# get :show, :with => :id # => "/show/:id"
# get [:show, :id] # => "/show/:id"
# get :show, :with => [:id, :name] # => "/show/:id/:name"
# get [:show, :id, :name] # => "/show/:id/:name"
# get :list, :provides => :js # => "/list.{:format,js)"
# get :list, :provides => :any # => "/list(.:format)"
# get :list, :provides => [:js, :json] # => "/list.{!format,js|json}"
# get :list, :provides => [:html, :js, :json] # => "/list(.{!format,js|json})"
# get :list, :priority => :low # Defers route to be last
#
def route(verb, path, *args, &block)
options = case args.size
when 2
args.last.merge(:map => args.first)
when 1
map = args.shift if args.first.is_a?(String)
if args.first.is_a?(Hash)
map ? args.first.merge(:map => map) : args.first
else
{:map => map || args.first}
end
when 0
{}
else raise
end
# Do padrino parsing. We dup options so we can build HEAD request correctly
route_options = options.dup
route_options[:provides] = @_provides if @_provides
path, *route_options[:with] = path if path.is_a?(Array)
path, name, options = *parse_route(path, route_options, verb)
options.reverse_merge!(@_conditions) if @_conditions
# Sinatra defaults
method_name = "#{verb} #{path}"
define_method(method_name, &block)
unbound_method = instance_method("#{verb} #{path}")
remove_method(method_name)
block_arity = block.arity
block = block_arity != 0 ?
proc { @block_params = @block_params[0, block_arity]; unbound_method.bind(self).call(*@block_params) } :
proc { unbound_method.bind(self).call }
invoke_hook(:route_added, verb, path, block)
# HTTPRouter route construction
route = router.add(path)
route.name(name) if name
priority_name = options.delete(:priority) || :normal
priority = ROUTE_PRIORITY[priority_name] or raise("Priority #{priority_name} not recognized, try #{ROUTE_PRIORITY.keys.join(', ')}")
route.cache = options.key?(:cache) ? options.delete(:cache) : @_cache
route.send(verb.downcase.to_sym)
route.host(options.delete(:host)) if options.key?(:host)
route.user_agent(options.delete(:agent)) if options.key?(:agent)
if options.key?(:default_values)
defaults = options.delete(:default_values)
route.default(defaults) if defaults
end
options.delete_if do |option, args|
if route.send(:significant_variable_names).include?(option)
route.matching(option => Array(args).first)
true
end
end
# Add Sinatra conditions
options.each { |o, a| route.respond_to?(o) ? route.send(o, *a) : send(o, *a) }
conditions, @conditions = @conditions, []
route.custom_conditions = conditions
invoke_hook(:padrino_route_added, route, verb, path, args, options, block)
# Add Application defaults
route.before_filters = @filters[:before]
route.after_filters = @filters[:after]
if @_controller
route.use_layout = @layout
route.controller = Array(@_controller).first.to_s
end
deferred_routes[priority] << [route, block]
route
end
##
# Returns the final parsed route details (modified to reflect all Padrino options)
# given the raw route. Raw route passed in could be a named alias or a string and
# is parsed to reflect provides formats, controllers, parents, 'with' parameters,
# and other options.
#
def parse_route(path, options, verb)
# We need save our originals path/options so we can perform correctly cache.
original = [path, options.dup]
# We need check if path is a symbol, if that it's a named route
map = options.delete(:map)
if path.kind_of?(Symbol) # path i.e :index or :show
name = path # The route name
path = map ? map.dup : path.to_s # The route path
end
if path.kind_of?(String) # path i.e "/index" or "/show"
# Now we need to parse our 'with' params
if with_params = options.delete(:with)
path = process_path_for_with_params(path, with_params)
end
# Now we need to parse our provides
options.delete(:provides) if options[:provides].nil?
if @_use_format or format_params = options[:provides]
process_path_for_provides(path, format_params)
options[:matching] ||= {}
options[:matching][:format] = /[^\.]+/
end
# Build our controller
controller = Array(@_controller).map { |c| c.to_s }
absolute_map = map && map[0] == ?/
unless controller.empty?
# Now we need to add our controller path only if not mapped directly
if map.blank? and !absolute_map
controller_path = controller.join("/")
path.gsub!(%r{^\(/\)|/\?}, "")
path = File.join(controller_path, path)
end
# Here we build the correct name route
if name
controller_name = controller.join("_")
name = "#{controller_name}_#{name}".to_sym unless controller_name.blank?
end
end
# Now we need to parse our 'parent' params and parent scope
if !absolute_map and parent_params = options.delete(:parent) || @_parents
parent_params = Array(@_parents) + Array(parent_params)
path = process_path_for_parent_params(path, parent_params)
end
# Add any controller level map to the front of the path
path = "#{@_map}/#{path}".squeeze('/') unless absolute_map or @_map.blank?
# Small reformats
path.gsub!(%r{/\?$}, '(/)') # Remove index path
path.gsub!(%r{/?index/?}, '/') # Remove index path
path.gsub!(%r{//$}, '/') # Remove index path
path[0,0] = "/" unless path =~ %r{^\(?/} # Paths must start with a /
path.sub!(%r{/(\))?$}, '\\1') if path != "/" # Remove latest trailing delimiter
path.gsub!(/\/(\(\.|$)/, '\\1') # Remove trailing slashes
end
# Merge in option defaults
options.reverse_merge!(:default_values => @_defaults)
[path, name, options]
end
##
# Processes the existing path and appends the 'with' parameters onto the route
# Used for calculating path in route method
#
def process_path_for_with_params(path, with_params)
File.join(path, Array(with_params).map(&:inspect).join("/"))
end
##
# Processes the existing path and prepends the 'parent' parameters onto the route
# Used for calculating path in route method
#
def process_path_for_parent_params(path, parent_params)
parent_prefix = parent_params.flatten.compact.uniq.map do |param|
map = (param.respond_to?(:map) && param.map ? param.map : param.to_s)
part = "#{map}/:#{param}_id/"
part = "(#{part})" if param.respond_to?(:optional) && param.optional?
part
end
[parent_prefix, path].flatten.join("")
end
##
# Processes the existing path and appends the 'format' suffix onto the route
# Used for calculating path in route method
#
def process_path_for_provides(path, format_params)
path << "(.:format)" unless path[-10, 10] == '(.:format)'
end
##
# Allows routing by MIME-types specified in the URL or ACCEPT header.
#
# By default, if a non-provided mime-type is specified in a URL, the
# route will not match an thus return a 404.
#
# Setting the :treat_format_as_accept option to true allows treating
# missing mime types specified in the URL as if they were specified
# in the ACCEPT header and thus return 406.
#
# If no type is specified, the first in the provides-list will be
# returned.
#
# ==== Examples
# get "/a", :provides => [:html, :js]
# # => GET /a => :html
# # => GET /a.js => :js
# # => GET /a.xml => 404
#
# get "/b", :provides => [:html]
# # => GET /b; ACCEPT: html => html
# # => GET /b; ACCEPT: js => 406
#
# enable :treat_format_as_accept
# get "/c", :provides => [:html, :js]
# # => GET /c.xml => 406
#
def provides(*types)
@_use_format = true
condition do
mime_types = types.map { |t| mime_type(t) }
request.path_info =~ /\.([^\.\/]+)$/
url_format = $1.to_sym if $1
accepts = request.accept.map { |a| a.split(";")[0].strip }
# per rfc2616-sec14:
# Assume */* if no ACCEPT header is given.
catch_all = (accepts.delete "*/*" || accepts.empty?)
matching_types = accepts.empty? ? mime_types.slice(0,1) : (accepts & mime_types)
if params[:format]
accept_format = params[:format]
elsif !url_format && matching_types.first
type = ::Rack::Mime::MIME_TYPES.find { |k, v| v == matching_types.first }[0].sub(/\./,'').to_sym
accept_format = CONTENT_TYPE_ALIASES[type] || type
elsif catch_all
type = types.first
accept_format = CONTENT_TYPE_ALIASES[type] || type
end
matched_format = types.include?(:any) ||
types.include?(accept_format) ||
types.include?(url_format) ||
((!url_format) && request.accept.empty? && types.include?(:html))
# per rfc2616-sec14:
# answer with 406 if accept is given but types to not match any
# provided type
halt 406 if
(!url_format && !accepts.empty? && !matched_format) ||
(settings.respond_to?(:treat_format_as_accept) && settings.treat_format_as_accept && url_format && !matched_format)
if matched_format
@_content_type = url_format || accept_format || :html
content_type(@_content_type, :charset => 'utf-8')
end
matched_format
end
end
end
module InstanceMethods
##
# Instance method for url generation like:
#
# ==== Examples
#
# url(:show, :id => 1)
# url(:show, :name => :test)
# url(:show, 1)
# url("/foo")
#
def url(*args)
# Delegate to Sinatra 1.2 for simple url("/foo")
# http://www.sinatrarb.com/intro#Generating%20URLs
return super if args.first.is_a?(String) && !args[1].is_a?(Hash)
# Delegate to Padrino named route url generation
self.class.url(*args)
end
alias :url_for :url
def recognize_path(path)
self.class.recognize_path(path)
end
def current_path(*path_params)
if path_params.last.is_a?(Hash)
path_params[-1] = params.merge(path_params[-1])
else
path_params << params
end
@route.url(*path_params)
end
##
# This is mostly just a helper so request.path_info isn't changed when
# serving files from the public directory
#
def static_file?(path_info)
return if (public_dir = settings.public).nil?
public_dir = File.expand_path(public_dir)
path = File.expand_path(public_dir + unescape(path_info))
return if path[0, public_dir.length] != public_dir
return unless File.file?(path)
return path
end
##
# Method for deliver static files.
#
def static!
if path = static_file?(request.path_info)
env['sinatra.static_file'] = path
send_file(path, :disposition => nil)
end
end
##
# Return the request format, this is useful when we need to respond to a given content_type like:
#
# ==== Examples
#
# get :index, :provides => :any do
# case content_type
# when :js then ...
# when :json then ...
# when :html then ...
# end
# end
#
def content_type(type=nil, params={})
type.nil? ? @_content_type : super(type, params)
end
private
def dispatch!
static! if settings.static? && (request.get? || request.head?)
route!
rescue Sinatra::NotFound => boom
handle_not_found!(boom)
rescue ::Exception => boom
handle_exception!(boom)
ensure
@_pending_after_filters.each { |filter| instance_eval(&filter)} if @_pending_after_filters
end
def route!(base=self.class, pass_block=nil)
@request.env['padrino.instance'] = self
if base.compiled_router and match = base.router.call(@request.env)
if match.respond_to?(:each)
route_eval do
match[1].each {|k,v| response[k] = v}
status match[0]
route_missing if match[0] == 404
end
end
else
filter! :before
end
# Run routes defined in superclass.
if base.superclass.respond_to?(:router)
route!(base.superclass, pass_block)
return
end
route_eval(&pass_block) if pass_block
route_missing
ensure
end
end # InstanceMethods
end # Routing
end # Padrino

View file

@ -0,0 +1,18 @@
module Padrino
##
# This module extend Sinatra::ShowExceptions adding Padrino as "Framework"
#
class ShowExceptions < Sinatra::ShowExceptions
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 # ShowExceptions
end # Padrino

View file

@ -0,0 +1,45 @@
module Padrino
PADRINO_IGNORE_CALLERS = [
%r{lib/padrino-.*$}, # all padrino code
%r{/padrino-.*/(lib|bin)}, # all padrino code
%r{/bin/padrino$}, # all padrino code
%r{/sinatra(/(base|main|showexceptions))?\.rb$}, # all sinatra code
%r{lib/tilt.*\.rb$}, # all tilt code
%r{lib/rack.*\.rb$}, # all rack code
%r{lib/mongrel.*\.rb$}, # all mongrel code
%r{lib/shotgun.*\.rb$}, # all shotgun lib
%r{bin/shotgun$}, # shotgun binary
%r{\(.*\)}, # generated code
%r{shoulda/context\.rb$}, # shoulda hacks
%r{mocha/integration}, # mocha hacks
%r{test/unit}, # test unit hacks
%r{rake_test_loader\.rb}, # rake hacks
%r{custom_require\.rb$}, # rubygems require hacks
%r{active_support}, # active_support require hacks
%r{/thor} # thor require hacks
] unless defined?(PADRINO_IGNORE_CALLERS)
##
# Add rubinius (and hopefully other VM impls) ignore patterns ...
#
PADRINO_IGNORE_CALLERS.concat(RUBY_IGNORE_CALLERS) if defined?(RUBY_IGNORE_CALLERS)
private
##
# Returns the filename for the file that is the direct caller (first caller)
#
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.
#
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 # Padrino

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 # self
end # Adapter
end # Cli
end # Padrino

View file

@ -0,0 +1,152 @@
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"
method_option :server, :type => :string, :aliases => "-a", :desc => "Rack Handler (default: autodetect)"
method_option :host, :type => :string, :aliases => "-h", :required => true, :default => "0.0.0.0", :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 "s", "Starts the Padrino application"
def s
invoke :start
end
desc "stop", "Stops the Padrino application"
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."
method_option :verbose, :type => :boolean, :aliases => "-v", :desc => "Log message to standard output."
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(' ')} ..."
ENV['PADRINO_LOG_LEVEL'] ||= "test"
load File.expand_path('../rake.rb', __FILE__)
silence(:stdout) { require File.expand_path('config/boot.rb') }
PadrinoTasks.init(true)
end
desc "console", "Boots up the Padrino application irb console"
def console
prepare :console
require File.expand_path("../../version", __FILE__)
ARGV.clear
puts "=> Loading #{options.environment} console (Padrino v.#{Padrino.version})"
require 'irb'
require "irb/completion"
require File.expand_path('config/boot.rb')
require File.expand_path('../console', __FILE__)
IRB.start
end
desc "generate", "Executes the Padrino generator with given options."
def generate(*args)
# Build Padrino g as an alias of padrino-gen
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
Padrino.bin_gen(ARGV)
rescue
puts "<= You need padrino-gen! Run: gem install padrino-gen"
end
end
desc "g", "Executes the Padrino generator with given options."
def g(*args)
invoke(:generate, args)
end
desc "gen", "Executes the Padrino generator with given options."
def gen(*args)
invoke(:generate, args)
end
desc "version", "Show current Padrino Version"
map "-v" => :version, "--version" => :version
def version
require 'padrino-core/version'
puts "Padrino v. #{Padrino.version}"
end
private
def prepare(task)
if options.help?
help(task.to_s)
raise SystemExit
end
ENV["PADRINO_ENV"] ||= options.environment.to_s
ENV["RACK_ENV"] = ENV["PADRINO_ENV"] # Also set this for middleware
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 # Base
end # Cli
end # Padrino

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,24 @@
require File.expand_path('../../tasks', __FILE__)
require 'rake'
require 'thor'
require 'securerandom' unless defined?(SecureRandom)
module PadrinoTasks
def self.init(init=false)
Padrino::Tasks.files.flatten.uniq.each { |rakefile| Rake.application.add_import(rakefile) rescue puts "<= Failed load #{ext}" }
if init
Rake.application.init
Rake.application.instance_variable_set(:@rakefile, __FILE__)
load(File.expand_path('../rake_tasks.rb', __FILE__))
Rake.application.load_imports
Rake.application.top_level
else
load(File.expand_path('../rake_tasks.rb', __FILE__))
Rake.application.load_imports
end
end
end
def shell
@_shell ||= Thor::Base.shell.new
end

View file

@ -0,0 +1,59 @@
# Load rake tasks from common rake task definition locations
Dir["lib/tasks/**/*.rake"].
concat(Dir["tasks/**/*.rake"]).
concat(Dir["{test,spec}/*.rake"]).each { |rake| load(rake) }
# Loads the Padrino applications mounted within the project
# setting up the required environment for Padrino
task :environment do
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
# lists all routes of a given app
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
desc "Generate the Rakefile"
task :gen do
File.open(Padrino.root("Rakefile"), "w") do |file|
file.puts <<-RUBY.gsub(/^ {6}/, '')
require File.expand_path('../config/boot.rb', __FILE__)
require 'padrino-core/cli/rake'
PadrinoTasks.init
RUBY
end
end

View file

@ -0,0 +1,27 @@
require 'rbconfig'
module Padrino
##
# This method return the correct location of padrino bin or
# exec it using Kernel#system with the given args
#
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.
#
def self.ruby_command
@ruby_command ||= begin
ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
ruby << Config::CONFIG['EXEEXT']
# escape string in case path to ruby executable contain spaces.
ruby.sub!(/.*\s.*/m, '"\&"')
ruby
end
end
end # Padrino

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,182 @@
module Padrino
class << self
##
# Hooks to be called before a load/reload
#
# ==== Examples
#
# 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
#
# ==== Examples
#
# after_load do
# DataMapper.finalize
# end
#
#
def after_load(&block)
@_after_load ||= []
@_after_load << block if block_given?
@_after_load
end
##
# Returns the used $LOAD_PATHS from 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
#
def load!
return false if loaded?
@_called_from = first_caller
Padrino.set_encoding
Padrino.set_load_paths(*load_paths) # We set the padrino 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
end
##
# Clear the padrino env
#
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!
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 ablity to instantiate Padrino.load! after Padrino::Application definition.
#
def called_from
@_called_from || first_caller
end
##
# Return true if Padrino was loaded with Padrino.load!
#
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 +require_dependencies+ we don't have this problem.
#
# ==== Examples
#
# # 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?
# List of errors and failed files
errors, failed = [], []
# We need a size to make sure things are loading
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 LoadError => e
errors << e
failed << file
rescue NameError => 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
#
# ==== Examples
# Padrino.dependency_paths << "#{Padrino.root}/uploaders/*.rb"
#
def dependency_paths
@_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"
]
@_dependency_paths ||= @_dependency_paths_was
end
##
# Concat to $LOAD_PATH the given paths
#
def set_load_paths(*paths)
$:.concat(paths); load_paths.concat(paths)
$:.uniq!; load_paths.uniq!
end
end # self
end # Padrino

View file

@ -0,0 +1,30 @@
cz:
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"
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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " a "
last_word_connector: " a "

View file

@ -0,0 +1,30 @@
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"
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: ""
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " og "
last_word_connector: " og "

View file

@ -0,0 +1,30 @@
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: "%b %d"
long: "%B %d, %Y"
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: "%B %d, %Y %H:%M"
am: "am"
pm: "pm"
# Benutzt in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " und "
last_word_connector: ", und "

View file

@ -0,0 +1,30 @@
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"
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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " and "
last_word_connector: ", and "

View file

@ -0,0 +1,30 @@
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"
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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " y "
last_word_connector: ", y "

View file

@ -0,0 +1,30 @@
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"
day_names: [Dimanche, Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi]
abbr_day_names: [Lun, Mar, Mer, Jeu, Ven, Sam, Dim]
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, Jui, Jui, 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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " et "
last_word_connector: ", et "

View file

@ -0,0 +1,30 @@
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."
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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " és "
last_word_connector: " és "

View file

@ -0,0 +1,37 @@
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'
# Used in array.to_sentence.
support:
array:
sentence_connector: "e"
skip_last_comma: false

View file

@ -0,0 +1,30 @@
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日"
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: "午後"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " と "
last_word_connector: ", と "

View file

@ -0,0 +1,30 @@
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"
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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " en "
last_word_connector: " en "

View file

@ -0,0 +1,31 @@
"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"
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: ""
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " og "
last_word_connector: " og "

View file

@ -0,0 +1,30 @@
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"
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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " i "
last_word_connector: " i "

View file

@ -0,0 +1,37 @@
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: [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'
# Used in array.to_sentence.
support:
array:
sentence_connector: "e"
skip_last_comma: false

View file

@ -0,0 +1,30 @@
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"
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: "п.п"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " и "
last_word_connector: ", и "

View file

@ -0,0 +1,30 @@
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"
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"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " ve "
last_word_connector: " ve "

View file

@ -0,0 +1,30 @@
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"
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: "п.п"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " і "
last_word_connector: ", і "

View file

@ -0,0 +1,30 @@
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日"
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: "下午"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " 和 "
last_word_connector: ", 和 "

View file

@ -0,0 +1,30 @@
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日"
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: "下午"
# Used in array.to_sentence.
support:
array:
words_connector: ", "
two_words_connector: " 和 "
last_word_connector: ", 和 "

View file

@ -0,0 +1,344 @@
# Defines our PADRINO_LOG_LEVEL
PADRINO_LOG_LEVEL = ENV['PADRINO_LOG_LEVEL'] unless defined?(PADRINO_LOG_LEVEL)
module Padrino
##
# Returns the padrino logger
#
# ==== Examples
#
# logger.debug "foo"
# logger.warn "bar"
#
def self.logger
Padrino::Logger.setup! if Thread.current[:padrino_logger].nil?
Thread.current[:padrino_logger]
end
##
# Set the padrino logger
#
def self.logger=(value)
Thread.current[:padrino_logger] = value
end
##
# Extensions to the built in Ruby logger.
#
# ==== Examples
#
# logger.debug "foo"
# logger.warn "bar"
#
class Logger
attr_accessor :level
attr_accessor :auto_flush
attr_reader :buffer
attr_reader :log
attr_reader :init_args
attr_accessor :log_static
##
# Ruby (standard) logger levels:
#
# :fatal:: An unhandleable 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
#
Levels = {
:fatal => 7,
:error => 6,
:warn => 4,
:info => 3,
:debug => 0,
:devel => -1,
} unless const_defined?(:Levels)
@@mutex = {}
##
# 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 :debug 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
#
# ==== Examples
#
# 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 }
#
Config = {
:production => { :log_level => :warn, :stream => :to_file },
:development => { :log_level => :debug, :stream => :stdout },
:test => { :log_level => :debug, :stream => :null }
}
# Embed in a String to clear all previous ANSI sequences.
CLEAR = "\e[0m"
# The start of an ANSI bold sequence.
BOLD = "\e[1m"
# Set the terminal's foreground ANSI color to black.
BLACK = "\e[30m"
# Set the terminal's foreground ANSI color to red.
RED = "\e[31m"
# Set the terminal's foreground ANSI color to green.
GREEN = "\e[32m"
# Set the terminal's foreground ANSI color to yellow.
YELLOW = "\e[33m"
# Set the terminal's foreground ANSI color to blue.
BLUE = "\e[34m"
# Set the terminal's foreground ANSI color to magenta.
MAGENTA = "\e[35m"
# Set the terminal's foreground ANSI color to cyan.
CYAN = "\e[36m"
# Set the terminal's foreground ANSI color to white.
WHITE = "\e[37m"
# Colors for levels
ColoredLevels = {
:fatal => [BOLD, RED],
:error => [RED],
:warn => [YELLOW],
:info => [GREEN],
:debug => [CYAN],
:devel => [MAGENTA]
} unless defined?(ColoredLevels)
##
# Setup a new logger
#
def self.setup!
config_level = (PADRINO_LOG_LEVEL || Padrino.env || :test).to_sym # need this for PADRINO_LOG_LEVEL
config = Config[config_level]
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
Thread.current[:padrino_logger] = Padrino::Logger.new(config.merge(:stream => stream))
end
##
# To initialize the logger you create a new object, proxies to set_log.
#
# ==== Options
#
# :stream:: Either an IO object or a name of a logfile. Defaults to $stdout
# :log_level::
# The log level from, e.g. :fatal or :info. Defaults to :debug 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
#
def initialize(options={})
@buffer = []
@auto_flush = options.has_key?(:auto_flush) ? options[:auto_flush] : true
@level = options[:log_level] ? Levels[options[:log_level]] : Levels[:debug]
@log = options[:stream] || $stdout
@log.sync = true
@mutex = @@mutex[@log] ||= Mutex.new
@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
end
##
# Colorize our level
#
def colored_level(level)
style = ColoredLevels[level.to_s.downcase.to_sym].join("")
"#{style}#{level.to_s.upcase.rjust(7)}#{CLEAR}"
end
##
# Set a color for our string. Color can be a symbol/string
#
def set_color(string, color, bold=false)
color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
bold = bold ? BOLD : ""
"#{bold}#{color}#{string}#{CLEAR}"
end
##
# Flush the entire buffer to the log object.
#
def flush
return unless @buffer.size > 0
@mutex.synchronize do
@log.write(@buffer.slice!(0..-1).join(''))
end
end
##
# Close and remove the current log object.
#
def close
flush
@log.close if @log.respond_to?(:close) && !@log.tty?
@log = nil
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.
#
def push(message = nil, level = nil)
self << @format_message % [colored_level(level), set_color(Time.now.strftime(@format_datetime), :yellow), message.to_s.strip]
end
##
# Directly append message to the log.
#
def <<(message = nil)
message << "\n" unless message[-1] == ?\n
@buffer << message
flush if @auto_flush
message
end
alias :write :<<
##
# Generate the logging methods for Padrino.logger for each log level.
#
Levels.each_pair do |name, number|
class_eval <<-LEVELMETHODS, __FILE__, __LINE__
# Appends a message to the log if the log level is at least as high as
# the log level of the logger.
#
# ==== Parameters
# message:: The message to be logged. Defaults to nil.
#
# ==== Returns
# self:: The logger object for chaining.
def #{name}(message = nil)
if #{number} >= level
message = block_given? ? yield : message
self.push(message, :#{name}) if #{number} >= level
end
self
end
# Appends a message to the log if the log level is at least as high as
# the log level of the logger. The bang! version of the method also auto
# flushes the log buffer to disk.
#
# ==== Parameters
# message:: The message to be logged. Defaults to nil.
#
# ==== Returns
# self:: The logger object for chaining.
def #{name}!(message = nil)
if #{number} >= level
message = block_given? ? yield : message
self.push(message, :#{name}) if #{number} >= level
flush if #{number} >= level
end
self
end
# ==== Returns
# Boolean:: True if this level will be logged by this logger.
def #{name}?
#{number} >= level
end
LEVELMETHODS
end
##
# Padrino::Loggger::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
##
# Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
# "lilith.local - - GET / HTTP/1.1 500 -"
# %{%s - %s %s %s%s %s - %d %s %0.4f}
#
FORMAT = %{%s (%0.4fms) %s - %s %s%s%s %s - %d %s}
def initialize(app, uri_root)
@app = app
@uri_root = uri_root.sub(/\/$/,"")
end
def call(env)
env['rack.logger'] = Padrino.logger
env['rack.errors'] = Padrino.logger.log
began_at = Time.now
status, header, body = @app.call(env)
log(env, status, header, began_at)
[status, header, body]
end
private
def log(env, status, header, began_at)
now = Time.now
length = extract_content_length(header)
return if env['sinatra.static_file'] and !logger.log_static
logger.debug FORMAT % [
env["REQUEST_METHOD"],
now - began_at,
env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
env["REMOTE_USER"] || "-",
@uri_root || "",
env["PATH_INFO"],
env["QUERY_STRING"].empty? ? "" : "?" + env["QUERY_STRING"],
env["HTTP_VERSION"],
status.to_s[0..3],
length]
end
def extract_content_length(headers)
headers.each do |key, value|
if key.downcase == 'content-length'
return value.to_s == '0' ? '-' : value
end
end
'-'
end
end # Rack
end # Logger
end # Padrino
module Kernel #:nodoc:
##
# Define a logger available every where in our app
#
def logger
Padrino.logger
end
end # Kernel

View file

@ -0,0 +1,192 @@
module Padrino
##
# Represents a particular mounted padrino application
# Stores the name of the application (app folder name) and url mount path
#
# ==== Examples
#
# 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 #:nodoc:
end
attr_accessor :name, :uri_root, :app_file, :app_class, :app_root, :app_obj, :app_host
def initialize(name, options={})
@name = name.to_s
@app_class = options[:app_class] || @name.camelize
@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
#
# ==== Examples
#
# 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
#
# ==== Examples
#
# 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
#
# @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, Padrino.root('public', app_data.uri_root) unless File.exists?(app_obj.public)
app_obj.set :static, File.exist?(app_obj.public) if app_obj.nil?
app_obj.setup_application! # We need to initialize here the app.
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
#
#
def named_routes
app_obj.routes.map { |route|
name_array = "(#{route.named.to_s.split("_").map { |piece| %Q[:#{piece}] }.join(", ")})"
request_method = route.conditions[:request_method][0]
full_path = File.join(uri_root, route.original_path)
next if route.named.blank? || request_method == 'HEAD'
OpenStruct.new(:verb => request_method, :identifier => route.named, :name => name_array, :path => full_path)
}.compact
end
##
# Makes two Mounters equal if they have the same name and uri_root
#
def ==(other)
other.is_a?(Mounter) && self.app_class == other.app_class && self.uri_root == other.uri_root
end
##
# Returns 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")
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
##
# Returns the root to the mounted apps base directory
#
def mounted_root(*args)
Padrino.root(@mounted_root ||= "", *args)
end
##
# Returns the mounted padrino applications (MountedApp objects)
#
def mounted_apps
@mounted_apps ||= []
end
##
# Inserts a Mounter object into the mounted applications (avoids duplicates)
#
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
#
# Padrino.mount("blog_app").to("/blog")
#
def mount(name, options={})
Mounter.new(name, options)
end
end # Mounter
end # Padrino

View file

@ -0,0 +1,247 @@
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.
#
MTIMES = {}
LOADED_FILES = {}
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 ||= []
end
##
# Specified constants can be configured to be reloaded on every request.
# Default included constants are: [none]
#
def include_constants
@_include_constants ||= []
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!
MTIMES.clear
LOADED_CLASSES.each do |file, klasses|
klasses.each { |klass| remove_constant(klass) }
LOADED_CLASSES.delete(file)
end
LOADED_FILES.each do |file, dependencies|
dependencies.each { |dependency| $LOADED_FEATURES.delete(dependency) }
$LOADED_FEATURES.delete(file)
end
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] ||= mtime
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.map { |klass| klass.to_s.split("::")[0] }.uniq
klasses = klasses | Padrino.mounted_apps.map { |app| app.app_class }
Padrino::Reloader.exclude_constants.concat(klasses)
end
##
# A safe Kernel::require which issues the necessary hooks depending on results
#
def safe_load(file, options={})
force, file = options[:force], figure_path(file)
# Check if file was changed or if force a reload
reload = MTIMES[file] && File.mtime(file) > MTIMES[file]
return if !force && !reload && MTIMES[file]
# Removes all classes declared in the specified file
if klasses = LOADED_CLASSES.delete(file)
klasses.each { |klass| remove_constant(klass) }
end
# Remove all loaded fatures with our file
if features = LOADED_FILES[file]
features.each { |feature| $LOADED_FEATURES.delete(feature) }
end
# Duplicate objects and loaded features before load file
klasses = ObjectSpace.classes.dup
files = $LOADED_FEATURES.dup
# Now we can reload dependencies of our file
if features = LOADED_FILES.delete(file)
features.each { |feature| safe_load(feature, :force => true) }
end
# And finally load the specified file
begin
logger.devel "Loading #{file}" if !reload
logger.debug "Reloading #{file}" if reload
$LOADED_FEATURES.delete(file)
verbosity_was, $-v = $-v, nil
loaded = false
require(file)
loaded = true
MTIMES[file] = File.mtime(file)
rescue SyntaxError => e
logger.error "Cannot require #{file} because of syntax error: #{e.message}"
ensure
$-v = verbosity_was
new_constants = (ObjectSpace.classes - klasses).uniq
if loaded
# Store the file details
LOADED_CLASSES[file] = new_constants
LOADED_FILES[file] = ($LOADED_FEATURES - files - [file]).uniq
# Track only features in our Padrino.root
LOADED_FILES[file].delete_if { |feature| !in_root?(feature) }
else
logger.devel "Failed to load #{file}; removing partially defined constants"
new_constants.each { |klass| remove_constant(klass) }
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.compact.uniq.any? { |c| (const.to_s =~ %r{^#{Regexp.escape(c)}}) } &&
!include_constants.compact.uniq.any? { |c| (const.to_s =~ %r{^#{Regexp.escape(c)}}) }
begin
parts = const.to_s.split("::")
base = parts.size == 1 ? Object : parts[0..-2].join("::").constantize
object = parts[-1].to_s
base.send(:remove_const, object)
logger.devel "Removed constant: #{const}"
rescue NameError; end
end
private
##
# Return the mounted_apps providing the app location
# Can be an array because in one app.rb we can define multiple Padrino::Appplications
#
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) =~ %r{^#{Regexp.escape(Padrino.root)}}
end
##
# Searches Ruby files in your +Padrino.load_paths+ , Padrino::Application.load_paths
# and monitors them for any changes.
#
def 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
files.uniq.map { |file|
file = File.expand_path(file)
next if Padrino::Reloader.exclude.any? { |base| file =~ %r{^#{Regexp.escape(base)}} } || !File.exist?(file)
yield(file, File.mtime(file))
}.compact
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
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 # Reloader
end # Padrino

View file

@ -0,0 +1,79 @@
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 doman aliases)
#
# ==== Options
#
# :to:: The class of application that you want mount
# :path:: Map the app to the given path
# :host:: Map the app to the given host
#
# ==== Examples
#
# 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
#
class Router
def initialize(*mapping, &block)
@mapping = []
mapping.each { |m| map(m) }
instance_eval(&block) if block
end
def sort!
@mapping = @mapping.sort_by { |h, p, m, a| -p.size }
end
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
def call(env)
rPath = env["PATH_INFO"].to_s
script_name = env['SCRIPT_NAME']
hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
@mapping.each do |host, path, match, app|
next unless host.nil? || hHost =~ host
next unless rPath =~ 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: #{rPath}"]]
end
end # Router
end # Padrino

View file

@ -0,0 +1,70 @@
module Padrino
##
# Run the Padrino apps as a self-hosted server using:
# thin, mongrel, webrick in that order.
#
# ==== Examples
#
# Padrino.run! # with these defaults => host: "localhost", port: "3000", adapter: the first found
# Padrino.run!("localhost", "4000", "mongrel") # use => host: "0.0.0.0", port: "3000", adapter: "mongrel"
#
def self.run!(options={})
Padrino.load!
Server.start(Padrino.application, options)
end
##
# This module build a Padrino server
#
class Server < Rack::Server
# Server Handlers
Handlers = [:thin, :mongrel, :webrick]
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) || '0.0.0.0'
options[:Port] = options.delete(:port) || 3000
options[:AccessLog] = []
if options[:daemonize]
options[:pid] = options[:pid].blank? ? File.expand_path('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
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 has ended his set (crowd applauds)" unless options[:daemonize]
end
def app
@app
end
alias :wrapped_app :app
def options
@options
end
private
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 # Server
end # Padrino

View file

@ -0,0 +1,135 @@
##
# This file loads certain extensions required by Padrino from ActiveSupport.
#
require 'active_support/core_ext/kernel' # silence_warnings
require 'active_support/core_ext/module/aliasing' # alias_method_chain
require 'active_support/core_ext/class/attribute_accessors' # cattr_reader
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/inflections' # load default inflections
##
# This is a small version of active_support/core_ext/string/inflections.rb
# to prevent to load a lot of 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
# +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.
#
# Examples
# "Module".constantize # => Module
# "Class".constantize # => Class
def constantize
ActiveSupport::Inflector.constantize(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
# 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
# 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.
def classes
ObjectSpace.each_object(Module).select do |klass|
Class.class_eval { klass } rescue false
end
end
end
end
##
# FileSet helper method for iterating and interacting with files inside a directory
#
class FileSet
# 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 self.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 self.glob_require(glob_pattern, file_path=nil)
self.glob(glob_pattern, file_path) { |f| require f }
end
end
##
# YAML Engine Parsing Fix
# https://github.com/padrino/padrino-framework/issues/424
#
require 'yaml' unless defined?(YAML)
YAML::ENGINE.yamler = "syck" if defined?(YAML::ENGINE)
##
# Loads our locale configuration files
#
I18n.load_path += Dir["#{File.dirname(__FILE__)}/locale/*.yml"] if defined?(I18n)
##
# Used to know if this file has already been required
#
module SupportLite; end

View file

@ -0,0 +1,23 @@
module Padrino
##
# This module it's used for bootstrap with padrino rake
# thirdy party tasks, in your gem/plugin/extension you
# need only do this:
#
# ==== Examples
#
# 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 # Tasks
end # Padrino

View file

@ -0,0 +1,15 @@
##
# 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
VERSION = '0.10.0' unless defined?(Padrino::VERSION)
##
# Return the current Padrino version
#
def self.version
VERSION
end
end # Padrino

View file

@ -0,0 +1,38 @@
#!/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.3.0")
s.add_dependency("sinatra", "~> 1.2.6")
s.add_dependency("http_router", "~> 0.8.10")
s.add_dependency("thor", "~> 0.14.3")
s.add_dependency("activesupport", "~> 3.0.0")
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,27 @@
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" }
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: 72!' # 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 @@
C = "C"

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,2 @@
class F < E
end

View file

@ -0,0 +1,2 @@
class G
end

View file

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

View file

@ -0,0 +1,101 @@
ENV['PADRINO_ENV'] = 'test'
PADRINO_ROOT = File.dirname(__FILE__) unless defined? PADRINO_ROOT
require File.expand_path('../../../load_paths', __FILE__)
require 'padrino-core'
require 'test/unit'
require 'rack/test'
require 'rack'
require 'shoulda'
require 'phocus'
# 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
module Kernel
# Silences the output by redirecting to stringIO
# silence_logger { ...commands... } => "...output..."
def silence_logger(&block)
$stdout = log_buffer = StringIO.new
block.call
$stdout = STDOUT
log_buffer.string
end
alias :silence_stdout :silence_logger
def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, nil
yield
ensure
$VERBOSE = old_verbose
end
end
class Class
# Allow assertions in request context
include Test::Unit::Assertions
end
class Test::Unit::TestCase
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
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,83 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
class PadrinoTestApp < Padrino::Application; end
class PadrinoTestApp2 < Padrino::Application; end
class TestApplication < Test::Unit::TestCase
def setup
Padrino.clear!
end
def teardown
remove_views
end
context 'for application functionality' do
should 'check default options' do
assert File.identical?(__FILE__, PadrinoTestApp.app_file)
assert_equal :padrino_test_app, PadrinoTestApp.app_name
assert_equal :test, PadrinoTestApp.environment
assert_equal Padrino.root("views"), PadrinoTestApp.views
assert PadrinoTestApp.raise_errors
assert !PadrinoTestApp.logging
assert !PadrinoTestApp.sessions
assert !PadrinoTestApp.dump_errors
assert !PadrinoTestApp.show_exceptions
assert PadrinoTestApp.raise_errors
assert !Padrino.configure_apps
end
should 'check padrino specific options' do
assert !PadrinoTestApp.instance_variable_get(:@_configured)
PadrinoTestApp.send(:setup_application!)
assert_equal :padrino_test_app, PadrinoTestApp.app_name
assert_equal 'StandardFormBuilder', PadrinoTestApp.default_builder
assert PadrinoTestApp.instance_variable_get(:@_configured)
assert !PadrinoTestApp.reload?
assert !PadrinoTestApp.flash
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 "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.tap { |app| app.send(:default_configuration!)
app.get("/") { session[:foo] = "shared" } }
PadrinoTestApp2.tap { |app| app.send(:default_configuration!)
app.get("/") { session[:foo] } }
browser = Rack::Test::Session.new(Rack::MockSession.new(Padrino.application))
browser.get '/write'
browser.get '/read'
assert_equal 'shared', browser.last_response.body
end
# compare to: test_routing: allow global provides
should "set content_type to :html if none can be determined" do
mock_app do
provides :xml
get("/foo"){ "Foo in #{content_type}" }
get("/bar"){ "Foo in #{content_type}" }
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 html", body
end # content_type to :html
end # application functionality
end

View file

@ -0,0 +1,79 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
class TestCore < Test::Unit::TestCase
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, :bundle
assert_respond_to Padrino, :configure_apps
end
should 'validate global helpers' do
assert_equal :test, Padrino.env
assert_match /\/test/, Padrino.root
assert_equal nil, Padrino.bundle
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, nil # 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_raise(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,44 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
class TestDependencies < Test::Unit::TestCase
context 'when we require a dependency that have another dependency' do
should 'raise an error without reloading it twice' do
silence_warnings do
assert_raise(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
silence_warnings 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
silence_warnings 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,266 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
class TestFilters < Test::Unit::TestCase
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
end

View file

@ -0,0 +1,91 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
class TestPadrinoLogger < Test::Unit::TestCase
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
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
end
end

View file

@ -0,0 +1,176 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
class TestMounter < Test::Unit::TestCase
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_file => "/path/to/test.rb")
mounter.to("/test")
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", mounter.name
assert_equal "Test", mounter.app_class
assert_equal "/path/to/test.rb", mounter.app_file
assert_equal "/test", 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_file => __FILE__)
mounter.to("/test")
assert_equal "test", mounter.name
assert_equal "Test", mounter.app_class
assert_equal __FILE__, mounter.app_file
assert_equal "/test", 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_file => __FILE__).to("/")
assert_equal "test", mounter.name
assert_equal "Test", mounter.app_class
assert_equal Test, 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_file => __FILE__).to('/me')
assert_equal "test", mounter.name
assert_equal "Test", mounter.app_class
assert_equal Test, 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
assert_raise(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_raise(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" }
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" }
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 11, Padrino.mounted_apps[0].routes.size
assert_equal 7, Padrino.mounted_apps[1].routes.size
assert_equal 5, 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
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", body
get '/demo_2'
assert_equal "Im Demo 2", body
end
should "not clobber the public setting when mounting an app" do
class ::PublicApp < Padrino::Application
set :root, "/root"
set :public, 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
end
end

View file

@ -0,0 +1,66 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require File.expand_path(File.dirname(__FILE__) + '/fixtures/apps/complex')
class TestComplexReloader < Test::Unit::TestCase
context 'for complex reload functionality' do
should 'correctly instantiate Complex(1-2)Demo fixture' do
Padrino.clear!
Padrino.mount("complex_1_demo").to("/complex_1_demo")
Padrino.mount("complex_2_demo").to("/complex_2_demo")
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
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)
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
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,97 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require File.expand_path(File.dirname(__FILE__) + '/fixtures/apps/simple')
class TestSimpleReloader < Test::Unit::TestCase
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 'correctly reload SimpleDemo fixture' do
@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 1.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 1, @app.filters[:after].size
assert_equal 1, @app.middleware.size # [Padrino::Reloader::Rack]
assert_equal 4, @app.routes.size # GET+HEAD of "/" + GET+HEAD of "/rand" = 4
assert_equal 2, @app.extensions.size # [Padrino::Routing, Padrino::Rendering]
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 1, @app.filters[:after].size
assert_equal 1, @app.middleware.size
assert_equal 4, @app.routes.size # GET+HEAD of "/" = 2
assert_equal 2, @app.extensions.size # [Padrino::Routing, Padrino::Rendering]
assert_equal 0, @app.templates.size
end
end
end

View file

@ -0,0 +1,437 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
require 'i18n'
class TestRendering < Test::Unit::TestCase
def setup
Padrino::Application.send(:register, Padrino::Rendering)
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" }
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 a <%= 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 a 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 :foo, "html file", :format => :html
mock_app do
get('/content_type_test', :provides => [:html, :xml]) { render :foo }
end
get "/content_type_test"
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_raise 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 :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 :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 "/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 '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 405, 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 405, 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
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')
class TestRouter < Test::Unit::TestCase
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

File diff suppressed because it is too large Load diff

View file

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

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,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.
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>relative_time_ago</tt>, <tt>time_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 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,51 @@
require 'padrino-core/support_lite' unless defined?(SupportLite)
require 'cgi'
require 'i18n'
require 'enumerator'
require 'active_support/core_ext/string/conversions' # to_date
require 'active_support/core_ext/float/rounding' # round
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__)
# Load our locales
I18n.load_path += Dir["#{File.dirname(__FILE__)}/padrino-helpers/locale/*.yml"]
module Padrino
##
# 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.
#
module Helpers
##
# Register these helpers:
#
# Padrino::Helpers::OutputHelpers
# Padrino::Helpers::TagHelpers
# Padrino::Helpers::AssetTagHelpers
# Padrino::Helpers::FormHelpers
# Padrino::Helpers::FormatHelpers
# Padrino::Helpers::RenderHelpers
# Padrino::Helpers::NumberHelpers
#
# for Padrino::Application
#
class << self
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
end
alias :included :registered
end
end # Helpers
end # Padrino

View file

@ -0,0 +1,288 @@
module Padrino
module Helpers
module AssetTagHelpers
##
# Creates a div to display the flash of given type if it exists
#
# ==== Examples
# # Generates: <div class="notice">flash-notice</div>
# flash_tag(:notice, :id => 'flash-notice')
#
def flash_tag(kind, options={})
flash_text = flash[kind]
return '' if flash_text.blank?
options.reverse_merge!(:class => kind)
content_tag(:div, flash_text, options)
end
##
# Creates a link element with given name, url and options
#
# ==== Examples
#
# 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!
options = parse_js_attributes(options) # parses remote, method and confirm options
anchor = "##{CGI.escape options.delete(:anchor).to_s}" if options[:anchor]
if block_given?
url = args[0] ? args[0] + anchor.to_s : anchor || 'javascript:void(0);'
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
name, url = args[0], (args[1] ? args[1] + anchor.to_s : anchor || 'javascript:void(0);')
return name unless parse_conditions(url, options)
options.reverse_merge!(:href => url)
content_tag(:a, name, options)
end
end
##
# Creates a form containing a single button that submits to the url.
#
# ==== Examples
#
# # Generates:
# # <form class="form" action="/admin/accounts/destroy/2" method="post">
# # <input type="hidden" value="delete" name="_method" />
# # <input type="submit" value="Delete" />
# # </form>
# button_to 'Delete', url(:accounts_destroy, :id => account), :method => :delete, :class => :form
#
def button_to(*args, &block)
name, url = args[0], args[1]
options = args.extract_options!
desired_method = options[:method]
options.delete(:method) if options[:method].to_s !~ /get|post/i
options.reverse_merge!(:method => 'post', :action => url)
options[:enctype] = "multipart/form-data" if options.delete(:multipart)
options["data-remote"] = "true" if options.delete(:remote)
inner_form_html = hidden_form_method_field(desired_method)
inner_form_html += block_given? ? capture_html(&block) : submit_tag(name)
content_tag('form', inner_form_html, options)
end
##
# Creates a link tag that browsers and news readers can use to auto-detect an RSS or ATOM feed.
#
# === Options
#
# :rel:: Specify the relation of this link, defaults to "alternate"
# :type:: Override the auto-generated mime type
# :title:: Specify the title of the link, defaults to the type
#
# === Examples
#
# # Generates: <link type="application/atom+xml" rel="alternate" href="/blog/posts.atom" title="ATOM" />
# feed_tag :atom, url(:blog, :posts, :format => :atom), :title => "ATOM"
# # Generates: <link type="application/rss+xml" rel="alternate" href="/blog/posts.rss" title="rss" />
# feed_tag :rss, url(:blog, :posts, :format => :rss)
#
def feed_tag(mime, url, options={})
full_mime = (mime == :atom) ? 'application/atom+xml' : 'application/rss+xml'
content_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
#
# ==== Examples
#
# # 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', '@')
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
#
# ==== Examples
#
# # 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
#
# ==== Examples
#
# 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
#
# ==== Examples
#
# 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+
# like app/public/stylesheets for inclusion. You can provide also a full path.
#
# ==== Examples
#
# stylesheet_link_tag 'style', 'application', 'layout'
#
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")
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+
# like app/public/javascript for inclusion. You can provide also a full path.
#
# ==== Examples
#
# javascript_include_tag 'application', :extjs
#
def javascript_include_tag(*sources)
options = sources.extract_options!.symbolize_keys
options.reverse_merge!(:type => 'text/javascript', :content => "")
sources.flatten.map { |source|
tag(:script, options.reverse_merge(:src => asset_path(:js, source)))
}.join("\n")
end
##
# Returns the path to the image, either relative or absolute. We search it in your +appname.public+
# like app/public/images for inclusion. You can provide also a full path.
#
# ==== Examples
#
# # Generates: /images/foo.jpg?1269008689
# image_path("foo.jpg")
#
def image_path(src)
asset_path(:images, src)
end
##
# Returns the path to the specified asset (css or javascript)
#
# ==== Examples
#
# # 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)
return source if source =~ /^http/
asset_folder = case kind
when :css then 'stylesheets'
when :js then 'javascripts'
else kind.to_s
end
source = source.to_s.gsub(/\s/, '%20')
ignore_extension = (asset_folder.to_s == kind.to_s) # don't append extension
source << ".#{kind}" unless ignore_extension or source =~ /\.#{kind}/
result_path = source if source =~ %r{^/} # absolute path
result_path ||= uri_root_path(asset_folder, source)
timestamp = asset_timestamp(result_path)
"#{result_path}#{timestamp}"
end
private
##
# Returns the uri root of the application.
#
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
#
def asset_timestamp(file_path)
return nil if file_path =~ /\?/ || (self.class.respond_to?(:asset_stamp) && !self.class.asset_stamp)
public_path = Padrino.root("public", file_path) if Padrino.respond_to?(:root)
stamp = Time.now.to_i unless public_path && File.exist?(public_path)
stamp ||= File.mtime(public_path).to_i
"?#{stamp}"
end
##
# Parses link_to options for given correct conditions
#
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
##
# Parses link_to options for given js declarations (remote, method, confirm)
# Not destructive on options; returns updated options
#
def parse_js_attributes(options)
options = options.dup
options["data-remote"] = "true" if options.delete(:remote)
if link_confirm = options.delete(:confirm)
options["data-confirm"] = link_confirm
end
if link_method = options.delete(:method)
options["data-method"] = link_method
options["rel"] = "nofollow"
end
options
end
end # AssetTagHelpers
end # Helpers
end # Padrino

View file

@ -0,0 +1,220 @@
module Padrino
module Helpers
module FormBuilder #:nodoc:
class AbstractFormBuilder #:nodoc:
attr_accessor :template, :object
def initialize(template, object, options={})
@template = template
@object = build_object(object)
@options = options
raise "FormBuilder template must be initialized!" unless template
raise "FormBuilder object must be not be nil value. If there's no object, use a symbol instead! (i.e :user)" unless object
end
# f.error_messages
def error_messages(*params)
params.unshift object
@template.error_messages_for(*params)
end
# f.error_message_on(field)
def error_message_on(field, options={})
@template.error_message_on(object, field, options)
end
# f.label :username, :caption => "Nickname"
def label(field, options={})
options.reverse_merge!(:caption => "#{field_human_name(field)}: ")
@template.label_tag(field_id(field), options)
end
# f.hidden_field :session_id, :value => "45"
def hidden_field(field, options={})
options.reverse_merge!(:value => field_value(field), :id => field_id(field))
@template.hidden_field_tag field_name(field), options
end
# f.text_field :username, :value => "(blank)", :id => 'username'
def text_field(field, options={})
options.reverse_merge!(:value => field_value(field), :id => field_id(field))
options.merge!(:class => field_error(field, options))
@template.text_field_tag field_name(field), options
end
# f.text_area :summary, :value => "(enter summary)", :id => 'summary'
def text_area(field, options={})
options.reverse_merge!(:value => field_value(field), :id => field_id(field))
options.merge!(:class => field_error(field, options))
@template.text_area_tag field_name(field), options
end
# f.password_field :password, :id => 'password'
def password_field(field, options={})
options.reverse_merge!(:value => field_value(field), :id => field_id(field))
options.merge!(:class => field_error(field, options))
@template.password_field_tag field_name(field), options
end
# f.select :color, :options => ['red', 'green'], :include_blank => true
# f.select :color, :collection => @colors, :fields => [:name, :id]
def select(field, options={})
options.reverse_merge!(:id => field_id(field), :selected => field_value(field))
options.merge!(:class => field_error(field, options))
@template.select_tag field_name(field), options
end
# f.check_box :remember_me, :value => 'true', :uncheck_value => '0'
def check_box(field, options={})
unchecked_value = options.delete(:uncheck_value) || '0'
options.reverse_merge!(:id => field_id(field), :value => '1')
options.reverse_merge!(:checked => true) if values_matches_field?(field, options[:value])
html = @template.hidden_field_tag(options[:name] || field_name(field), :value => unchecked_value, :id => nil)
html << @template.check_box_tag(field_name(field), options)
end
# f.radio_button :gender, :value => 'male'
def radio_button(field, options={})
options.reverse_merge!(:id => field_id(field, options[:value]))
options.reverse_merge!(:checked => true) if values_matches_field?(field, options[:value])
@template.radio_button_tag field_name(field), options
end
# f.file_field :photo, :class => 'avatar'
def file_field(field, options={})
options.reverse_merge!(:id => field_id(field))
options.merge!(:class => field_error(field, options))
@template.file_field_tag field_name(field), options
end
# f.submit "Update", :class => 'large'
def submit(caption="Submit", options={})
@template.submit_tag caption, options
end
# f.image_submit "buttons/submit.png", :class => 'large'
def image_submit(source, options={})
@template.image_submit_tag source, options
end
# Supports nested fields for a child model within a form
# f.fields_for :addresses
# f.fields_for :addresses, address
# f.fields_for :addresses, @addresses
def fields_for(child_association, instance_or_collection=nil, &block)
default_collection = self.object.send(child_association)
include_index = default_collection.respond_to?(:each)
nested_options = { :parent => self, :association => child_association }
nested_objects = instance_or_collection ? Array(instance_or_collection) : Array(default_collection)
result = nested_objects.each_with_index.map do |child_instance, index|
nested_options[:index] = include_index ? index : nil
@template.fields_for(child_instance, { :nested => nested_options }, &block)
end.join("\n")
end
protected
# Returns the known field types for a formbuilder
def self.field_types
[:hidden_field, :text_field, :text_area, :password_field, :file_field, :radio_button, :check_box, :select]
end
# Returns true if the value matches the value in the field
# field_has_value?(:gender, 'male')
def values_matches_field?(field, value)
value.present? && (field_value(field).to_s == value.to_s || field_value(field).to_s == 'true')
end
# Add a :invalid css class to the field if it contain an error
def field_error(field, options)
error = @object.errors[field] rescue nil
error.blank? ? options[:class] : [options[:class], :invalid].flatten.compact.join(" ")
end
# Returns the human name of the field. Look that use builtin I18n.
def field_human_name(field)
I18n.translate("#{object_model_name}.attributes.#{field}", :count => 1, :default => field.to_s.humanize, :scope => :models)
end
# Returns the name for the given field
# field_name(:username) => "user[username]"
# field_name(:number) => "user[telephone_attributes][number]"
# field_name(:street) => "user[addresses_attributes][0][street]"
def field_name(field=nil)
result = []
if root_form?
result << object_model_name
elsif nested_form?
parent_form = @options[:nested][:parent]
attributes_name = "#{@options[:nested][:association]}_attributes"
nested_index = @options[:nested][:index]
fragment = [parent_form.field_name, "[#{attributes_name}", "]"]
fragment.insert(2, "][#{nested_index}") if nested_index
result << fragment
end
result << "[#{field}]" unless field.blank?
result.flatten.join
end
# Returns the id for the given field
# field_id(:username) => "user_username"
# field_id(:gender, :male) => "user_gender_male"
# field_name(:number) => "user_telephone_attributes_number"
# field_name(:street) => "user_addresses_attributes_0_street"
def field_id(field=nil, value=nil)
result = []
if root_form?
result << object_model_name
elsif nested_form?
parent_form = @options[:nested][:parent]
attributes_name = "#{@options[:nested][:association]}_attributes"
nested_index = @options[:nested][:index]
fragment = [parent_form.field_id, "_#{attributes_name}"]
fragment.push("_#{nested_index}") if nested_index
result << fragment
end
result << "_#{field}" unless field.blank?
result << "_#{value}" unless value.blank?
result.flatten.join
end
# Returns the child object if it exists
def nested_object_id
nested_form? && object.respond_to?(:new_record?) && !object.new_record? && object.id
end
# Returns true if this form object is nested in a parent form
def nested_form?
@options[:nested] && @options[:nested][:parent] && @options[:nested][:parent].respond_to?(:object)
end
# Returns the value for the object's field
# field_value(:username) => "Joey"
def field_value(field)
@object && @object.respond_to?(field) ? @object.send(field) : ""
end
# explicit_object is either a symbol or a record
# Returns a new record of the type specified in the object
def build_object(object_or_symbol)
object_or_symbol.is_a?(Symbol) ? @template.instance_variable_get("@#{object_or_symbol}") || object_class(object_or_symbol).new : object_or_symbol
end
# Returns the object's models name
# => user_assignment
def object_model_name(explicit_object=object)
explicit_object.is_a?(Symbol) ? explicit_object : explicit_object.class.to_s.underscore.gsub(/\//, '_')
end
# Returns the class type for the given object
def object_class(explicit_object)
explicit_object.is_a?(Symbol) ? explicit_object.to_s.camelize.constantize : explicit_object.class
end
# Returns true if this form is the top-level (not nested)
def root_form?
!nested_form?
end
end # AbstractFormBuilder
end # FormBuilder
end # Helpers
end # Padrino

View file

@ -0,0 +1,43 @@
require File.expand_path(File.dirname(__FILE__) + '/abstract_form_builder') unless defined?(AbstractFormBuilder)
module Padrino
module Helpers
module FormBuilder #:nodoc:
class StandardFormBuilder < AbstractFormBuilder #:nodoc:
##
# StandardFormBuilder
#
# text_field_block(:username, { :class => 'long' }, { :class => 'wide-label' })
# text_area_block(:summary, { :class => 'long' }, { :class => 'wide-label' })
# password_field_block(:password, { :class => 'long' }, { :class => 'wide-label' })
# file_field_block(:photo, { :class => 'long' }, { :class => 'wide-label' })
# check_box_block(:remember_me, { :class => 'long' }, { :class => 'wide-label' })
# select_block(:color, :options => ['green', 'black'])
#
(self.field_types - [ :hidden_field, :radio_button ]).each do |field_type|
class_eval <<-EOF
def #{field_type}_block(field, options={}, label_options={})
label_options.reverse_merge!(:caption => options.delete(:caption)) if options[:caption]
field_html = label(field, label_options)
field_html << #{field_type}(field, options)
@template.content_tag(:p, field_html)
end
EOF
end
# submit_block("Update")
def submit_block(caption, options={})
submit_html = self.submit(caption, options)
@template.content_tag(:p, submit_html)
end
# image_submit_block("submit.png")
def image_submit_block(source, options={})
submit_html = self.image_submit(source, options)
@template.content_tag(:p, submit_html)
end
end # StandardFormBuilder
end # FormBuilder
end # Helpers
end # Padrino

View file

@ -0,0 +1,446 @@
module Padrino
module Helpers
module FormHelpers
##
# Constructs a form for object using given or default form_builder
#
# ==== Examples
#
# form_for :user, '/register' do |f| ... end
# form_for @user, '/register', :id => 'register' do |f| ... end
#
def form_for(object, url, settings={}, &block)
form_html = capture_html(builder_instance(object, settings), &block)
form_tag(url, settings) { form_html }
end
##
# Constructs form fields for an object using given or default form_builder
# Used within an existing form to allow alternate objects within one form
#
# ==== Examples
#
# fields_for @user.assignment do |assignment| ... end
# fields_for :assignment do |assigment| ... end
#
def fields_for(object, settings={}, &block)
instance = builder_instance(object, settings)
fields_html = capture_html(instance, &block)
fields_html << instance.hidden_field(:id) if instance.send(:nested_object_id)
concat_content fields_html
end
##
# Constructs a form without object based on options
#
# ==== Examples
#
# form_tag '/register' do ... end
#
def form_tag(url, options={}, &block)
desired_method = options[:method]
data_method = options.delete(:method) if options[:method].to_s !~ /get|post/i
options.reverse_merge!(:method => "post", :action => url)
options[:enctype] = "multipart/form-data" if options.delete(:multipart)
options["data-remote"] = "true" if options.delete(:remote)
options["data-method"] = data_method if data_method
options["accept-charset"] ||= "UTF-8"
inner_form_html = hidden_form_method_field(desired_method)
inner_form_html += capture_html(&block)
concat_content content_tag(:form, inner_form_html, options)
end
##
# Returns the hidden method field for 'put' and 'delete' forms
# Only 'get' and 'post' are allowed within browsers;
# 'put' and 'delete' are just specified using hidden fields with form action still 'put'.
#
# ==== Examples
#
# # Generate: <input name="_method" value="delete" />
# hidden_form_method_field('delete')
#
def hidden_form_method_field(desired_method)
return '' if desired_method.blank? || desired_method.to_s =~ /get|post/i
hidden_field_tag(:_method, :value => desired_method)
end
##
# Constructs a field_set to group fields with given options
#
# ==== Examples
#
# field_set_tag("Office", :class => 'office-set')
#
def field_set_tag(*args, &block)
options = args.extract_options!
legend_text = args[0].is_a?(String) ? args.first : nil
legend_html = legend_text.blank? ? '' : content_tag(:legend, legend_text)
field_set_content = legend_html + capture_html(&block)
concat_content content_tag(:fieldset, field_set_content, options)
end
##
# Constructs list html for the errors for a given symbol
#
# ==== Options
#
# :header_tag:: Used for the header of the error div (default: "h2").
# :id:: The id of the error div (default: "errorExplanation").
# :class:: The class of the error div (default: "errorExplanation").
# :object:: The object (or array of objects) for which to display errors,
# if you need to escape the instance variable convention.
# :object_name:: The object name to use in the header, or any text that you prefer.
# If +:object_name+ is not set, the name of the first object will be used.
# :header_message:: The message in the header of the error div. Pass +nil+
# or an empty string to avoid the header message altogether. (Default: "X errors
# prohibited this object from being saved").
# :message:: The explanation message after the header message and before
# the error list. Pass +nil+ or an empty string to avoid the explanation message
# altogether. (Default: "There were problems with the following fields:").
#
# ==== Examples
#
# error_messages_for :user
#
def error_messages_for(*objects)
options = objects.extract_options!.symbolize_keys
objects = objects.map {|object_name| object_name.is_a?(Symbol) ? instance_variable_get("@#{object_name}") : object_name }.compact
count = objects.inject(0) {|sum, object| sum + object.errors.size }
unless count.zero?
html = {}
[:id, :class, :style].each do |key|
if options.include?(key)
value = options[key]
html[key] = value unless value.blank?
else
html[key] = 'field-errors' unless key == :style
end
end
options[:object_name] ||= objects.first.class
I18n.with_options :locale => options[:locale], :scope => [:models, :errors, :template] do |locale|
header_message = if options.include?(:header_message)
options[:header_message]
else
object_name = options[:object_name].to_s.underscore.gsub(/\//, ' ')
object_name = I18n.t(:name, :default => object_name.humanize, :scope => [:models, object_name], :count => 1)
locale.t :header, :count => count, :model => object_name
end
message = options.include?(:message) ? options[:message] : locale.t(:body)
error_messages = objects.map { |object|
object_name = options[:object_name].to_s.underscore.gsub(/\//, ' ')
object.errors.map { |f, msg|
field = I18n.t(f, :default => f.to_s.humanize, :scope => [:models, object_name, :attributes])
content_tag(:li, "%s %s" % [field, msg])
}
}.join
contents = ''
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
contents << content_tag(:p, message) unless message.blank?
contents << content_tag(:ul, error_messages)
content_tag(:div, contents, html)
end
else
''
end
end
##
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
#
# ==== Options
#
# :tag:: The tag that enclose your error. (Default 'div')
# :prepend:: Text to add before error.
# :append:: Text to add after error.
#
# ==== Examples
#
# # => <span class="error">can't be blank</div>
# error_message_on :post, :title
# error_message_on @post, :title
#
# # => <div class="custom" style="border:1px solid red">can't be blank</div>
# error_message_on :post, :title, :tag => :id, :class => :custom, :style => "border:1px solid red"
#
# # => <div class="error">This title can't be blank (or it won't work)</div>
# error_message_on :post, :title, :prepend => "This title", :append => "(or it won't work)"
#
def error_message_on(object, field, options={})
object = object.is_a?(Symbol) ? instance_variable_get("@#{object}") : object
error = object.errors[field] rescue nil
if error
options.reverse_merge!(:tag => :span, :class => :error)
tag = options.delete(:tag)
# Array(error).first is necessary because some orm give us an array others directly a value
error = [options.delete(:prepend), Array(error).first, options.delete(:append)].compact.join(" ")
content_tag(tag, error, options)
else
''
end
end
##
# Constructs a label tag from the given options
#
# ==== Examples
#
# label_tag :username, :class => 'long-label'
# label_tag :username, :class => 'long-label' do ... end
#
def label_tag(name, options={}, &block)
options.reverse_merge!(:caption => "#{name.to_s.humanize}: ", :for => name)
caption_text = options.delete(:caption)
caption_text << "<span class='required'>*</span> " if options.delete(:required)
if block_given? # label with inner content
label_content = caption_text + capture_html(&block)
concat_content(content_tag(:label, label_content, options))
else # regular label
content_tag(:label, caption_text, options)
end
end
##
# Constructs a hidden field input from the given options
#
# ==== Examples
#
# hidden_field_tag :session_key, :value => "__secret__"
#
def hidden_field_tag(name, options={})
options.reverse_merge!(:name => name)
input_tag(:hidden, options)
end
##
# Constructs a text field input from the given options
#
# ==== Examples
#
# text_field_tag :username, :class => 'long'
#
def text_field_tag(name, options={})
options.reverse_merge!(:name => name)
input_tag(:text, options)
end
##
# Constructs a text area input from the given options
#
# ==== Examples
#
# text_area_tag :username, :class => 'long', :value => "Demo?"
#
def text_area_tag(name, options={})
options.reverse_merge!(:name => name, :rows => "", :cols => "")
content_tag(:textarea, options.delete(:value).to_s, options)
end
##
# Constructs a password field input from the given options
#
# ==== Examples
#
# password_field_tag :password, :class => 'long'
#
def password_field_tag(name, options={})
options.reverse_merge!(:name => name)
input_tag(:password, options)
end
##
# Constructs a select from the given options
#
# ==== Examples
#
# options = [['caption', 'value'], ['Green', 'green1'], ['Blue', 'blue1'], ['Black', "black1"]]
# options = ['option', 'red', 'yellow' ]
# select_tag(:favorite_color, :options => ['red', 'yellow'], :selected => 'green1')
# select_tag(:country, :collection => @countries, :fields => [:name, :code], :include_blank => 'None')
#
# # Optgroups can be generated using :grouped_options => (Hash or nested Array)
# grouped_options = [['Friends',['Yoda',['Obiwan',1]]],['Enemies',['Palpatine',['Darth Vader',3]]]]
# grouped_options = {'Friends' => ['Yoda',['Obiwan',1]],'Enemies' => ['Palpatine',['Darth Vader',3]]}
# select_tag(:color, :grouped_options => [['warm',['red','yellow']],['cool',['blue', 'purple']]])
#
# # Optgroups can be generated using :grouped_options => (Hash or nested Array)
# grouped_options = [['Friends',['Yoda',['Obiwan',1]]],['Enemies',['Palpatine',['Darth Vader',3]]]]
# grouped_options = {'Friends' => ['Yoda',['Obiwan',1]],'Enemies' => ['Palpatine',['Darth Vader',3]]}
# select_tag(:color, :grouped_options => [['warm',['red','yellow']],['cool',['blue', 'purple']]])
#
def select_tag(name, options={})
options.reverse_merge!(:name => name)
collection, fields = options.delete(:collection), options.delete(:fields)
options[:options] = options_from_collection(collection, fields) if collection
prompt = options.delete(:include_blank)
select_options_html = if options[:options]
options_for_select(options.delete(:options), options.delete(:selected))
elsif options[:grouped_options]
grouped_options_for_select(options.delete(:grouped_options), options.delete(:selected), prompt)
end
select_options_html = select_options_html.unshift(blank_option(prompt)) if select_options_html.is_a?(Array)
options.merge!(:name => "#{options[:name]}[]") if options[:multiple]
content_tag(:select, select_options_html, options)
end
##
# Constructs a check_box from the given options
#
# ==== Examples
#
# check_box_tag :remember_me, :value => 'Yes'
#
def check_box_tag(name, options={})
options.reverse_merge!(:name => name, :value => '1')
input_tag(:checkbox, options)
end
##
# Constructs a radio_button from the given options
#
# ==== Examples
#
# radio_button_tag :remember_me, :value => 'true'
#
def radio_button_tag(name, options={})
options.reverse_merge!(:name => name)
input_tag(:radio, options)
end
##
# Constructs a file field input from the given options
#
# ==== Examples
#
# file_field_tag :photo, :class => 'long'
#
def file_field_tag(name, options={})
options.reverse_merge!(:name => name)
input_tag(:file, options)
end
##
# Constructs a submit button from the given options
#
# ==== Examples
#
# submit_tag "Create", :class => 'success'
#
def submit_tag(caption="Submit", options={})
options.reverse_merge!(:value => caption)
input_tag(:submit, options)
end
##
# Constructs a button input from the given options
#
# ==== Examples
#
# button_tag "Cancel", :class => 'clear'
#
def button_tag(caption, options = {})
options.reverse_merge!(:value => caption)
input_tag(:button, options)
end
# Constructs a submit button from the given options
#
# ==== Examples
#
# submit_tag "Create", :class => 'success'
#
def image_submit_tag(source, options={})
options.reverse_merge!(:src => image_path(source))
input_tag(:image, options)
end
##
# Returns an array of option items for a select field based on the given collection
# fields is an array containing the fields to display from each item in the collection
#
def options_from_collection(collection, fields)
collection.map { |item| [ item.send(fields.first), item.send(fields.last) ] }
end
#
# Returns the options tags for a select based on the given option items
#
def options_for_select(option_items, selected_value=nil)
return '' if option_items.blank?
option_items.map do |caption, value|
value ||= caption
content_tag(:option, caption, :value => value, :selected => option_is_selected?(value, caption, selected_value))
end
end
#
# Returns the optgroups with options tags for a select based on the given :grouped_options items
#
def grouped_options_for_select(collection,selected=nil,prompt=false)
if collection.is_a?(Hash)
collection.map do |key, value|
content_tag :optgroup, :label => key do
options_for_select(value, selected)
end
end
elsif collection.is_a?(Array)
collection.map do |optgroup|
content_tag :optgroup, :label => optgroup.first do
options_for_select(optgroup.last, selected)
end
end
end
end
#
# Returns the blank option serving as a prompt if passed
#
def blank_option(prompt)
return unless prompt
case prompt
when String then content_tag(:option, prompt, :value => '')
when Array then content_tag(:option, prompt.first, :value => prompt.last)
else content_tag(:option, '', :value => '')
end
end
private
##
# Returns the FormBuilder class to use based on all available setting sources
# If explicitly defined, returns that, otherwise returns defaults.
#
# configured_form_builder_class(nil) => StandardFormBuilder
#
def configured_form_builder_class(explicit_builder=nil)
default_builder = self.respond_to?(:settings) && self.settings.default_builder
configured_builder = explicit_builder || default_builder || 'StandardFormBuilder'
configured_builder = "Padrino::Helpers::FormBuilder::#{configured_builder}".constantize if configured_builder.is_a?(String)
configured_builder
end
##
# Returns an initialized builder instance for the given object and settings
#
# builder_instance(@account, :nested => { ... }) => <FormBuilder>
#
def builder_instance(object, settings={})
builder_class = configured_form_builder_class(settings.delete(:builder))
builder_class.new(self, object, settings)
end
##
# Returns whether the option should be selected or not
#
def option_is_selected?(value, caption, selected_values)
Array(selected_values).any? do |selected|
[value.to_s, caption.to_s].include?(selected.to_s)
end
end
end # FormHelpers
end # Helpers
end # Padrino

View file

@ -0,0 +1,260 @@
module Padrino
module Helpers
module FormatHelpers
##
# Returns escaped text to protect against malicious content
#
def escape_html(text)
Rack::Utils.escape_html(text)
end
alias h escape_html
alias sanitize_html escape_html
##
# Returns escaped text to protect against malicious content
# Returns blank if the text is empty
#
def h!(text, blank_text = '&nbsp;')
return blank_text if text.nil? || text.empty?
h text
end
##
# Strips all HTML tags from the html
#
def strip_tags(html)
html.gsub(/<\/?[^>]*>/, "") if html
end
##
# Returns text transformed into HTML using simple formatting rules. Two or more consecutive newlines(\n\n) are considered
# as a paragraph and wrapped in <p> or your own tags. One newline (\n) is considered as a linebreak and a <br /> tag is appended.
# This method does not remove the newlines from the text.
#
# ==== Examples
#
# simple_format("hello\nworld") # => "<p>hello<br/>world</p>"
# simple_format("hello\nworld", :tag => :div, :class => :foo) # => "<div class="foo">hello<br/>world</div>"
#
def simple_format(text, options={})
t = options.delete(:tag) || :p
start_tag = tag(t, options.merge(:open => true))
text = text.to_s.dup
text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
text.gsub!(/\n\n+/, "</#{t}>\n\n#{start_tag}") # 2+ newline -> paragraph
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
text.insert 0, start_tag
text << "</#{t}>"
end
##
# Attempts to pluralize the singular word unless count is 1. If plural is supplied, it will use that when count is > 1,
# otherwise it will use the Inflector to determine the plural form
#
# ==== Examples
#
# pluralize(2, 'person') => '2 people'
#
def pluralize(count, singular, plural = nil)
"#{count || 0} " + ((count == 1 || count == '1') ? singular : (plural || singular.pluralize))
end
##
# Truncates a given text after a given :length if text is longer than :length (defaults to 30).
# The last characters will be replaced with the :omission (defaults to "…") for a total length not exceeding :length.
#
# ==== Examples
#
# truncate("Once upon a time in a world far far away", :length => 8) => "Once upon..."
#
def truncate(text, options={})
options.reverse_merge!(:length => 30, :omission => "...")
if text
len = options[:length] - options[:omission].length
chars = text
(chars.length > options[:length] ? chars[0...len] + options[:omission] : text).to_s
end
end
##
# Truncates words of a given text after a given :length if number of words in text is more than :length (defaults to 30).
# The last words will be replaced with the :omission (defaults to "…") for a total number of words not exceeding :length.
#
# ==== Examples
#
# truncate_words("Once upon a time in a world far far away", :length => 8) => "Once upon a time in a world far..."
#
def truncate_words(text, options={})
options.reverse_merge!(:length => 30, :omission => "...")
if text
words = text.split()
words[0..(options[:length]-1)].join(' ') + (words.length > options[:length] ? options[:omission] : '')
end
end
##
# Wraps the text into lines no longer than line_width width.
# This method breaks on the first whitespace character that does not exceed line_width (which is 80 by default).
#
# ==== Examples
#
# word_wrap('Once upon a time', :line_width => 8) => "Once upon\na time"
#
def word_wrap(text, *args)
options = args.extract_options!
unless args.blank?
options[:line_width] = args[0] || 80
end
options.reverse_merge!(:line_width => 80)
text.split("\n").map do |line|
line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line
end * "\n"
end
##
# Highlights one or more words everywhere in text by inserting it into a :highlighter string.
#
# The highlighter can be customized by passing :+highlighter+ as a single-quoted string
# with \1 where the phrase is to be inserted (defaults to <strong class="highlight">\1</strong>)
#
# ==== Examples
#
# # => Lorem ipsum <strong class="highlight">dolor</strong> sit amet
# highlight('Lorem ipsum dolor sit amet', 'dolor')
#
# # => Lorem ipsum <span class="custom">dolor</span> sit amet
# highlight('Lorem ipsum dolor sit amet', 'dolor', :highlighter => '<span class="custom">\1</span>')
#
def highlight(text, words, *args)
options = args.extract_options!
options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
if text.blank? || words.blank?
text
else
match = Array(words).map { |p| Regexp.escape(p) }.join('|')
text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter])
end
end
##
# Reports the approximate distance in time between two Time or Date objects or integers as seconds.
# Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs
# Distances are reported based on the following table:
#
# 0 <-> 29 secs # => less than a minute
# 30 secs <-> 1 min, 29 secs # => 1 minute
# 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
# 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
# 89 mins, 29 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
# 23 hrs, 59 mins, 29 secs <-> 47 hrs, 59 mins, 29 secs # => 1 day
# 47 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
# 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month
# 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
# 1 yr <-> 1 yr, 3 months # => about 1 year
# 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
# 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
# 2 yrs <-> max time or date # => (same rules as 1 yr)
#
# With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds:
# 0-4 secs # => less than 5 seconds
# 5-9 secs # => less than 10 seconds
# 10-19 secs # => less than 20 seconds
# 20-39 secs # => half a minute
# 40-59 secs # => less than a minute
# 60-89 secs # => 1 minute
#
# ==== Examples
#
# from_time = Time.now
# distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
# distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
# distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
# distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds
# distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
# distance_of_time_in_words(from_time, from_time + 60.hours) # => about 3 days
# distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute
# distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
# distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
#
# to_time = Time.now + 6.years + 19.days
# distance_of_time_in_words(from_time, to_time, true) # => about 6 years
# distance_of_time_in_words(to_time, from_time, true) # => about 6 years
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
#
def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
distance_in_minutes = (((to_time.to_i - from_time.to_i).abs)/60).round
distance_in_seconds = ((to_time.to_i - from_time.to_i).abs).round
I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
case distance_in_minutes
when 0..1
return distance_in_minutes == 0 ?
locale.t(:less_than_x_minutes, :count => 1) :
locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds
case distance_in_seconds
when 0..4 then locale.t :less_than_x_seconds, :count => 5
when 5..9 then locale.t :less_than_x_seconds, :count => 10
when 10..19 then locale.t :less_than_x_seconds, :count => 20
when 20..39 then locale.t :half_a_minute
when 40..59 then locale.t :less_than_x_minutes, :count => 1
else locale.t :x_minutes, :count => 1
end
when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
when 45..89 then locale.t :about_x_hours, :count => 1
when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
when 1440..2529 then locale.t :x_days, :count => 1
when 2530..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
when 43200..86399 then locale.t :about_x_months, :count => 1
when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
else
distance_in_years = distance_in_minutes / 525600
minute_offset_for_leap_year = (distance_in_years / 4) * 1440
remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600)
if remainder < 131400
locale.t(:about_x_years, :count => distance_in_years)
elsif remainder < 394200
locale.t(:over_x_years, :count => distance_in_years)
else
locale.t(:almost_x_years, :count => distance_in_years + 1)
end
end
end
end
##
# Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
#
# ==== Examples
#
# time_ago_in_words(3.minutes.from_now) # => 3 minutes
# time_ago_in_words(Time.now - 15.hours) # => 15 hours
# time_ago_in_words(Time.now) # => less than a minute
#
# from_time = Time.now - 3.days - 14.minutes - 25.seconds # => 3 days
#
def time_ago_in_words(from_time, include_seconds = false)
distance_of_time_in_words(from_time, Time.now, include_seconds)
end
##
# Used in xxxx.js.erb files to escape html so that it can be passed to javascript from Padrino
#
# js_escape_html("<h1>Hey</h1>")
#
def js_escape_html(html_content)
return '' unless html_content
javascript_mapping = { '\\' => '\\\\', '</' => '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'" }
html_content.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { javascript_mapping[$1] }
end
end # FormatHelpers
end # Helpers
end # Padrino

View file

@ -0,0 +1,103 @@
cz:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: ","
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: " "
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%n %u"
unit: "Kč"
# These three are to override number.format and are optional
# separator: ","
# delimiter: " "
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "půl minutou"
less_than_x_seconds:
one: "asi před sekundou"
other: "asi před %{count} sekundami"
x_seconds:
one: "sekundou"
other: "%{count} sekundami"
less_than_x_minutes:
one: "před necelou minutou"
other: "před ani ne %{count} minutami"
x_minutes:
one: "minutou"
other: "%{count} minutami"
about_x_hours:
one: "asi hodinou"
other: "asi %{count} hodinami"
x_days:
one: "24 hodinami"
other: "%{count} dny"
about_x_months:
one: "asi měsícem"
other: "asi %{count} měsíci"
x_months:
one: "měsícem"
other: "%{count} měsíci"
about_x_years:
one: "asi rokem"
other: "asi %{count} roky"
over_x_years:
one: "více než před rokem"
other: "více než %{count} roky"
almost_x_years:
one: "skoro rokem"
other: "almost %{count} roků"
models:
errors:
template:
header:
one: "Při ukládání %{model} došlo k chybě a nebylo jej možné uložit"
other: "Při ukládání %{model} došlo ke %{count} chybám a nebylo možné jej uložit"
body: "Následující pole obsahují chybně vyplněné údaje:"

View file

@ -0,0 +1,91 @@
da:
number:
format:
separator: ","
delimiter: "."
precision: 3
currency:
format:
format: "%u %n"
unit: "DKK"
separator: ","
delimiter: "."
precision: 2
precision:
format:
# separator:
delimiter: ""
# precision:
human:
format:
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
percentage:
format:
# separator:
delimiter: ""
# precision:
datetime:
distance_in_words:
half_a_minute: "et halvt minut"
less_than_x_seconds:
one: "mindre end et sekund"
other: "mindre end %{count} sekunder"
x_seconds:
one: "et sekund"
other: "%{count} sekunder"
less_than_x_minutes:
one: "mindre end et minut"
other: "mindre end %{count} minutter"
x_minutes:
one: "et minut"
other: "%{count} minutter"
about_x_hours:
one: "cirka en time"
other: "cirka %{count} timer"
x_days:
one: "en dag"
other: "%{count} dage"
about_x_months:
one: "cirka en måned"
other: "cirka %{count} måneder"
x_months:
one: "en måned"
other: "%{count} måneder"
about_x_years:
one: "cirka et år"
other: "cirka %{count} år"
over_x_years:
one: "mere end et år"
other: "mere end %{count} år"
almost_x_years:
one: "næsten et år"
other: "næsten %{count} years"
prompts:
second: "Sekund"
minute: "Minut"
hour: "Time"
day: "Dag"
month: "Måned"
year: "År"
models:
errors:
template:
header:
one: "En fejl forhindrede %{model} i at blive gemt"
other: "%{count} fejl forhindrede denne %{model} i at blive gemt"
body: "Der var problemer med følgende felter:"

View file

@ -0,0 +1,78 @@
de:
number:
format:
precision: 2
separator: ','
delimiter: '.'
currency:
format:
unit: '€'
format: '%n%u'
separator:
delimiter:
precision:
percentage:
format:
delimiter: ""
precision:
format:
delimiter: ""
human:
format:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
datetime:
distance_in_words:
half_a_minute: 'eine halbe Minute'
less_than_x_seconds:
zero: 'weniger als 1 Sekunde'
one: 'weniger als 1 Sekunde'
other: 'weniger als %{count} Sekunden'
x_seconds:
one: '1 Sekunde'
other: '%{count} Sekunden'
less_than_x_minutes:
zero: 'weniger als 1 Minute'
one: 'weniger als 1 Minute'
other: 'weniger als %{count} Minuten'
x_minutes:
one: '1 Minute'
other: '%{count} Minuten'
about_x_hours:
one: 'etwa 1 Stunde'
other: 'etwa %{count} Stunden'
x_days:
one: '1 Tag'
other: '%{count} Tage'
about_x_months:
one: 'etwa 1 Monat'
other: 'etwa %{count} Monate'
x_months:
one: '1 Monat'
other: '%{count} Monate'
about_x_years:
one: 'etwa 1 Jahr'
other: 'etwa %{count} Jahre'
over_x_years:
one: 'mehr als 1 Jahr'
other: 'mehr als %{count} Jahre'
models:
errors:
template:
header:
one: "1 Fehler verhindert, dass Objekt %{model} gesichert werden kann"
other: "%{count} Fehler verhindern, dass Objekt %{model} gesichert werden kann"
body: "Es existieren Probleme mit den folgenden Feldern:"

View file

@ -0,0 +1,103 @@
en:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: "."
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ","
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%u%n"
unit: "$"
# These three are to override number.format and are optional
separator: "."
delimiter: ","
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "half a minute"
less_than_x_seconds:
one: "less than 1 second"
other: "less than %{count} seconds"
x_seconds:
one: "1 second"
other: "%{count} seconds"
less_than_x_minutes:
one: "less than a minute"
other: "less than %{count} minutes"
x_minutes:
one: "1 minute"
other: "%{count} minutes"
about_x_hours:
one: "about 1 hour"
other: "about %{count} hours"
x_days:
one: "1 day"
other: "%{count} days"
about_x_months:
one: "about 1 month"
other: "about %{count} months"
x_months:
one: "1 month"
other: "%{count} months"
about_x_years:
one: "about 1 year"
other: "about %{count} years"
over_x_years:
one: "over 1 year"
other: "over %{count} years"
almost_x_years:
one: "almost 1 year"
other: "almost %{count} years"
models:
errors:
template:
header:
one: "1 error prohibited this %{model} from being saved"
other: "%{count} errors prohibited this %{model} from being saved"
body: "There were problems with the following fields:"

View file

@ -0,0 +1,103 @@
es:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: "."
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ","
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 2
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%u%n"
unit: "$"
# These three are to override number.format and are optional
separator: "."
delimiter: ","
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "medio minuto"
less_than_x_seconds:
one: "menos de un segundo"
other: "menos de %{count} segundos"
x_seconds:
one: "1 segundo"
other: "%{count} segundos"
less_than_x_minutes:
one: "menos de un minuto"
other: "menos de %{count} minutos"
x_minutes:
one: "1 minuto"
other: "%{count} minutos"
about_x_hours:
one: "hace 1 hora"
other: "hace %{count} horas"
x_days:
one: "1 día"
other: "%{count} días"
about_x_months:
one: "hace 1 mes"
other: "hace %{count} meses"
x_months:
one: "1 mes"
other: "%{count} meses"
about_x_years:
one: "hace 1 año"
other: "hace %{count} años"
over_x_years:
one: "hace más de 1 año"
other: "hace más de %{count} años"
almost_x_years:
one: "casi 1 año"
other: "casi %{count} años"
models:
errors:
template:
header:
one: "1 error impidió que %{model} se guardara"
other: "%{count} erroress impidieron que %{model} se guardara"
body: "Hay problemas con los siguientes campos:"

View file

@ -0,0 +1,79 @@
fr:
number:
format:
precision: 2
separator: ','
delimiter: '.'
currency:
format:
unit: '€'
format: '%n%u'
separator:
delimiter:
precision:
percentage:
format:
delimiter: ""
precision:
format:
delimiter: ""
human:
format:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
datetime:
distance_in_words:
half_a_minute: "une demi minute"
less_than_x_seconds:
one: "infèrieur à une seconde"
other: "infèrieur à %{count} secondes"
x_seconds:
one: "1 seconde"
other: "%{count} secondes"
less_than_x_minutes:
one: "infèrieur à 1 minute"
other: "infèrieur à %{count} minutes"
x_minutes:
one: "1 minute"
other: "%{count} minutes"
about_x_hours:
one: "environ 1 heure"
other: "environ %{count} heures"
x_days:
one: "1 jour"
other: "%{count} jours"
about_x_months:
one: "environ 1 mois"
other: "environ %{count} mois"
x_months:
one: "1 mois"
other: "%{count} mois"
about_x_years:
one: "environ 1 ans"
other: "environ %{count} ans"
over_x_years:
one: "plus d'un an"
other: "plus de %{count} ans"
almost_x_years:
one: "près d'un ans"
other: "près de %{count} years"
models:
errors:
template:
header:
one: "1 erreur a empêché ce %{model} d'être sauvé"
other: "%{count} erreurs ont empêché ce %{model} d'être sauvé"
body: "Il y avait des problèmes avec les champs suivants:"

View file

@ -0,0 +1,103 @@
hu:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: ","
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: "."
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 2
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%n %u"
unit: "Ft"
# These three are to override number.format and are optional
separator: ","
delimiter: "."
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "byte"
other: "byte"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "fél perc"
less_than_x_seconds:
one: "kevesebb, mint egy másodperc"
other: "kevesebb, mint %{count} másodperc"
x_seconds:
one: "1 másodperc"
other: "%{count} másodperc"
less_than_x_minutes:
one: "kevesebb, mint egy perc"
other: "kevesebb, mint %{count} perc"
x_minutes:
one: "1 perc"
other: "%{count} perc"
about_x_hours:
one: "nagyjából 1 óra"
other: "nagyjából %{count} óra"
x_days:
one: "1 nap"
other: "%{count} nap"
about_x_months:
one: "nagyjából 1 hónap"
other: "nagyjából %{count} hónap"
x_months:
one: "1 hónap"
other: "%{count} hónap"
about_x_years:
one: "nagyjából 1 év"
other: "nagyjából %{count} év"
over_x_years:
one: "több, mint 1 év"
other: "több, mint %{count} év"
almost_x_years:
one: "majdnem 1 év"
other: "majdnem %{count} év"
models:
errors:
template:
header:
one: "1 hiba akadályozza a(z) %{model} mentését"
other: "%{count} hiba akadályozza a(z) %{model} mentését"
body: "A következő mezőkkel van baj:"

View file

@ -0,0 +1,85 @@
it:
number:
format:
separator: ","
delimiter: "."
precision: 3
currency:
format:
format: "%n %u"
unit: "€"
separator: "."
delimiter: ","
precision: 2
percentage:
format:
delimiter: ""
# precision:
precision:
format:
# separator:
delimiter: ""
# precision:
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
datetime:
distance_in_words:
half_a_minute: "mezzo minuto"
less_than_x_seconds:
one: "meno di un secondo"
other: "meno di %{count} secondi"
x_seconds:
one: "1 secondo"
other: "%{count} secondi"
less_than_x_minutes:
one: "meno di un minuto"
other: "meno di %{count} minuti"
x_minutes:
one: "1 minuto"
other: "%{count} minuti"
about_x_hours:
one: "circa un'ora"
other: "circa %{count} ore"
x_days:
one: "1 giorno"
other: "%{count} giorni"
about_x_months:
one: "circa un mese"
other: "circa %{count} mesi"
x_months:
one: "1 mese"
other: "%{count} mesi"
about_x_years:
one: "circa un anno"
other: "circa %{count} anni"
over_x_years:
one: "oltre un anno"
other: "oltre %{count} anni"
models:
errors:
template:
header:
one: "Non posso salvare questo %{model}: 1 errore"
other: "Non posso salvare questo %{model}: %{count} errori."
body: "Per favore ricontrolla i seguenti campi:"

View file

@ -0,0 +1,103 @@
ja:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: "."
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ","
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 2
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%u%n"
unit: "¥"
# These three are to override number.format and are optional
separator: "."
delimiter: ","
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "30秒"
less_than_x_seconds:
one: "1 秒以内"
other: "%{count} 秒以内"
x_seconds:
one: "1 秒"
other: "%{count} 秒"
less_than_x_minutes:
one: "1分以内"
other: "%{count} 分以内"
x_minutes:
one: "1 分"
other: "%{count} 分"
about_x_hours:
one: "およそ 1 時間"
other: "およそ %{count} 時間"
x_days:
one: "1 日"
other: "%{count} 日"
about_x_months:
one: "およそ 1 ヶ月"
other: "およそ %{count} ヶ月"
x_months:
one: "1 ヶ月"
other: "%{count} ヶ月"
about_x_years:
one: "およそ 1 年"
other: "およそ %{count} 年"
over_x_years:
one: "1 年以上"
other: "%{count} 年以上"
almost_x_years:
one: "ほぼ 1 年"
other: "ほぼ %{count} 年"
models:
errors:
template:
header:
one: "1 つのエラーにより、 %{model} をセーブできませんでした"
other: "%{count} つのエラーにより、 %{model} をセーブできませんでした"
body: "以下のフィールドにエラーが存在します:"

View file

@ -0,0 +1,78 @@
nl:
number:
format:
precision: 2
separator: ','
delimiter: '.'
currency:
format:
unit: '€'
format: '%u %n'
separator:
delimiter:
precision:
percentage:
format:
delimiter: ""
precision:
format:
delimiter: ""
human:
format:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "byte"
other: "bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
datetime:
distance_in_words:
half_a_minute: 'een halve minuut'
less_than_x_seconds:
zero: 'minder dan 1 seconde'
one: 'minder dan 1 seconde'
other: 'minder dan %{count} seconden'
x_seconds:
one: '1 seconde'
other: '%{count} seconden'
less_than_x_minutes:
zero: 'minder dan 1 minuut'
one: 'minder dan 1 minuut'
other: 'minder dan %{count} minuten'
x_minutes:
one: '1 minuut'
other: '%{count} minuten'
about_x_hours:
one: 'ongeveer 1 uur'
other: 'ongeveer %{count} uur'
x_days:
one: '1 dag'
other: '%{count} dagen'
about_x_months:
one: 'ongeveer 1 dag'
other: 'ongeveer %{count} dagen'
x_months:
one: '1 maand'
other: '%{count} maanden'
about_x_years:
one: 'ongeveer 1 jaar'
other: 'ongeveer %{count} jaar'
over_x_years:
one: 'meer dan 1 jaar'
other: 'meer dan %{count} jaar'
models:
errors:
template:
header:
one: "1 fout verhindert het opslaan van dit %{model} object"
other: "%{count} fouten verhinderen het opslaan van dit %{model} object"
body: "Controleer alstublieft de volgende velden:"

View file

@ -0,0 +1,91 @@
"no":
number:
format:
separator: ","
delimiter: "."
precision: 3
currency:
format:
format: "%u %n"
unit: "NOK"
separator: ","
delimiter: "."
precision: 2
precision:
format:
# separator:
delimiter: ""
# precision:
human:
format:
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
percentage:
format:
# separator:
delimiter: ""
# precision:
datetime:
distance_in_words:
half_a_minute: "et halvt minutt"
less_than_x_seconds:
one: "mindre enn et sekund"
other: "mindre enn %{count} sekunder"
x_seconds:
one: "ett sekund"
other: "%{count} sekunder"
less_than_x_minutes:
one: "mindre enn et minutt"
other: "mindre enn %{count} minutter"
x_minutes:
one: "ett minutt"
other: "%{count} minutter"
about_x_hours:
one: "cirka en time"
other: "cirka %{count} timer"
x_days:
one: "en dag"
other: "%{count} dager"
about_x_months:
one: "cirka en måned"
other: "cirka %{count} måneder"
x_months:
one: "en måned"
other: "%{count} måneder"
about_x_years:
one: "cirka et år"
other: "cirka %{count} år"
over_x_years:
one: "mer enn ett år"
other: "mer enn %{count} år"
almost_x_years:
one: "nesten et år"
other: "nesten %{count} år"
prompts:
second: "Sekund"
minute: "Minutt"
hour: "Time"
day: "Dag"
month: "Måned"
year: "År"
models:
errors:
template:
header:
one: "En feil forhindrer %{model} å bli lagret"
other: "%{count} feil forhindrer denne %{model} i å bli lagret"
body: "Det er problemer med følgende felter:"

View file

@ -0,0 +1,95 @@
pl:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
separator: ","
delimiter: " "
precision: 2
# Used in number_to_currency()
currency:
format:
format: "%n %u"
unit: "PLN"
separator: ","
delimiter: " "
precision: 2
# Used in number_to_percentage()
percentage:
format:
delimiter: ""
# Used in number_to_precision()
precision:
format:
delimiter: ""
# Used in number_to_human_size()
human:
format:
delimiter: ""
precision: 1
storage_units:
format: "%n %u"
units:
byte:
one: "bajt"
other: "bajty"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "pół minuty"
less_than_x_seconds:
one: "mniej niż sekundę"
few: "mniej niż %{count} sekundy"
other: "mniej niż %{count} sekund"
x_seconds:
one: "sekundę"
few: "%{count} sekundy"
other: "%{count} sekund"
less_than_x_minutes:
one: "mniej niż minutę"
few: "mniej niż %{count} minuty"
other: "mniej niż %{count} minut"
x_minutes:
one: "minutę"
few: "%{count} minuty"
other: "%{count} minut"
about_x_hours:
one: "około godziny"
other: "około %{count} godzin"
x_days:
one: "1 dzień"
other: "%{count} dni"
about_x_months:
one: "około miesiąca"
other: "około %{count} miesięcy"
x_months:
one: "1 miesiąc"
few: "%{count} miesiące"
other: "%{count} miesięcy"
about_x_years:
one: "około roku"
other: "około %{count} lat"
almost_x_years:
one: "prawie rok"
few: "prawie %{count} lata"
other: "prawie %{count} lat"
over_x_years:
one: "ponad rok"
few: "ponad %{count} lata"
other: "ponad %{count} lat"
models:
errors:
template:
header:
one: "%{model} nie został zachowany z powodu jednego błędu"
other: "%{model} nie został zachowany z powodu %{count} błędów"
body: "Błędy dotyczą następujących pól:"

View file

@ -0,0 +1,103 @@
pt_br:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: ","
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: "."
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%u%n"
unit: "R$"
# These three are to override number.format and are optional
separator: "."
delimiter: ","
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "meio minuto"
less_than_x_seconds:
one: "menos de 1 segundo"
other: "menos de %{count} segundos"
x_seconds:
one: "1 segundo"
other: "%{count} segundos"
less_than_x_minutes:
one: "menos de 1 minuto"
other: "menos de %{count} minutos"
x_minutes:
one: "1 minuto"
other: "%{count} minutos"
about_x_hours:
one: "cerca de 1 hora"
other: "cerca de %{count} horas"
x_days:
one: "1 dia"
other: "%{count} dias"
about_x_months:
one: "cerca de 1 mês"
other: "cerca de %{count} meses"
x_months:
one: "1 mês"
other: "%{count} meses"
about_x_years:
one: "cerca de 1 ano"
other: "cerca de %{count} anos"
over_x_years:
one: "mais de 1 ano"
other: "mais de %{count} anos"
almost_x_years:
one: "quase 1 ano"
other: "quase %{count} anos"
models:
errors:
template:
header:
one: "Não posso salvar %{model}: 1 erro"
other: "Não posso salvar %{model}: %{count} erros."
body: "Por favor, verifique novamente os seguintes campos:"

View file

@ -0,0 +1,103 @@
ru:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: ","
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ""
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%n %u"
unit: "руб"
# These three are to override number.format and are optional
separator: ","
delimiter: " "
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Байт"
other: "Байт"
kb: "Кб"
mb: "Мб"
gb: "Гб"
tb: "Тб"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "пол минуты"
less_than_x_seconds:
one: "менее секунды"
other: "менее %{count} секунд"
x_seconds:
one: "1 секунда"
other: "%{count} секунд"
less_than_x_minutes:
one: "менее минуты"
other: "менее %{count} минут"
x_minutes:
one: "1 минута"
other: "%{count} минут"
about_x_hours:
one: "около часа"
other: "около %{count} часов"
x_days:
one: "1 день"
other: "%{count} дней"
about_x_months:
one: "около месяца"
other: "около %{count} месяцев"
x_months:
one: "1 месяц"
other: "%{count} месяцев"
about_x_years:
one: "около года"
other: "около %{count} лет"
over_x_years:
one: "более года"
other: "более %{count} лет"
almost_x_years:
one: "почти год"
other: "почти %{count} года"
models:
errors:
template:
header:
one: "Данные «%{model}» не могут быть сохранены из-за 1 ошибки"
other: "Данные «%{model}» не могут быть сохранены из-за %{count} ошибок"
body: "Допущены ошибки в следующих полях:"

View file

@ -0,0 +1,103 @@
tr:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: "."
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ","
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%n%u"
unit: "TL"
# These three are to override number.format and are optional
separator: "."
delimiter: ","
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Byte"
other: "Bytes"
kb: "KB"
mb: "MB"
gb: "GB"
tb: "TB"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "30 saniye"
less_than_x_seconds:
one: "1 saniyeden az"
other: "%{count} saniyeden az"
x_seconds:
one: "1 saniye"
other: "%{count} saniye"
less_than_x_minutes:
one: "bir dakikadan az"
other: "%{count} dakikadan az"
x_minutes:
one: "1 dakika"
other: "%{count} dakika"
about_x_hours:
one: "yaklaşık 1 saat"
other: "yaklaşık %{count} saat"
x_days:
one: "1 gün"
other: "%{count} gün"
about_x_months:
one: "yaklaşık 1 ay"
other: "yaklaşık %{count} ay"
x_months:
one: "1 ay"
other: "%{count} ay"
about_x_years:
one: "yaklaşık 1 yıl"
other: "yaklaşık %{count} yıl"
over_x_years:
one: "1 yıldan fazla"
other: "%{count} yıldan fazla"
almost_x_years:
one: "neredeyse 1 yıl"
other: "neredeyse %{count} yıl"
models:
errors:
template:
header:
one: "1 hata oluştu; %{model} kaydedilemedi"
other: "%{count} hata oluştu; %{model} kaydedilemedi"
body: "Hata oluşan alanlar:"

View file

@ -0,0 +1,103 @@
uk:
number:
# Used in number_with_delimiter()
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format:
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: ","
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ""
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
# Used in number_to_currency()
currency:
format:
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
format: "%n %u"
unit: "грн"
# These three are to override number.format and are optional
separator: ","
delimiter: " "
precision: 2
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_precision()
precision:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
# precision:
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
# separator:
delimiter: ""
precision: 1
storage_units:
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u"
units:
byte:
one: "Байт"
other: "Байт"
kb: "Кб"
mb: "Мб"
gb: "Гб"
tb: "Тб"
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
datetime:
distance_in_words:
half_a_minute: "півхвилини"
less_than_x_seconds:
one: "менше секунди"
other: "менше %{count} секунд"
x_seconds:
one: "1 секунда"
other: "%{count} секунд"
less_than_x_minutes:
one: "менше хвилини"
other: "менше %{count} хвилин"
x_minutes:
one: "1 хвилина"
other: "%{count} хвилин"
about_x_hours:
one: "близько години"
other: "близько %{count} годин"
x_days:
one: "1 день"
other: "%{count} днів"
about_x_months:
one: "близько місяця"
other: "близько %{count} місяців"
x_months:
one: "1 місяць"
other: "%{count} месяців"
about_x_years:
one: "близько року"
other: "близько %{count} років"
over_x_years:
one: "більше року"
other: "більше %{count} років"
almost_x_years:
one: "майже рік"
other: "майжу %{count} роки"
models:
errors:
template:
header:
one: "Дані «%{model}» не можуть бути збережені через 1 помилки"
other: "Дані «%{model}» не можуть бути збережені через %{count} помилок"
body: "Допущені помилки в наступних полях:"

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