use latest padrino

This commit is contained in:
Thomas Reynolds 2013-06-04 09:56:33 -07:00
parent 39d3fa01ad
commit 69c36e691f
209 changed files with 2754 additions and 719 deletions

View file

@ -413,8 +413,7 @@ module Middleman
@_out_buf = _buf_was @_out_buf = _buf_was
end end
# concat_safe_content concat_safe_content render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
concat_content render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
ensure ensure
@current_engine = engine_was @current_engine = engine_was
end end

View file

@ -1,6 +1,6 @@
if defined?(::Padrino::Helpers) if !defined?(::Padrino::Helpers)
require 'vendored-middleman-deps/padrino-core-0.10.7/lib/padrino-core/support_lite' require 'vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/support_lite'
require 'vendored-middleman-deps/padrino-helpers-0.10.7/lib/padrino-helpers' require 'vendored-middleman-deps/padrino-helpers-0.11.2/lib/padrino-helpers'
end end
module Middleman module Middleman

View file

@ -5,9 +5,9 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/object/to_query'
if defined?(::Padrino::Helpers) if !defined?(::Padrino::Helpers)
require 'vendored-middleman-deps/padrino-core-0.10.7/lib/padrino-core/support_lite' require 'vendored-middleman-deps/padrino-core-0.11.2/lib/padrino-core/support_lite'
require 'vendored-middleman-deps/padrino-helpers-0.10.7/lib/padrino-helpers' require 'vendored-middleman-deps/padrino-helpers-0.11.2/lib/padrino-helpers'
end end
app.helpers ::Padrino::Helpers::OutputHelpers app.helpers ::Padrino::Helpers::OutputHelpers
@ -18,7 +18,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
app.helpers ::Padrino::Helpers::RenderHelpers app.helpers ::Padrino::Helpers::RenderHelpers
app.helpers ::Padrino::Helpers::NumberHelpers app.helpers ::Padrino::Helpers::NumberHelpers
# app.helpers ::Padrino::Helpers::TranslationHelpers # app.helpers ::Padrino::Helpers::TranslationHelpers
# app.helpers ::Padrino::Helpers::Breadcrumbs app.helpers ::Padrino::Helpers::Breadcrumbs
app.config.define_setting :relative_links, false, 'Whether to generate relative links instead of absolute ones' app.config.define_setting :relative_links, false, 'Whether to generate relative links instead of absolute ones'
end end

View file

@ -1,25 +0,0 @@
require File.expand_path('../../tasks', __FILE__)
require 'rake'
require 'rake/dsl_definition'
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

@ -1,8 +1,19 @@
require 'sinatra/base' require 'sinatra/base'
require 'padrino-core/support_lite' unless defined?(SupportLite) require 'padrino-core/version'
require 'padrino-core/support_lite'
require 'padrino-core/application'
require 'padrino-core/caller'
require 'padrino-core/command'
require 'padrino-core/loader'
require 'padrino-core/logger'
require 'padrino-core/mounter'
require 'padrino-core/reloader'
require 'padrino-core/router'
require 'padrino-core/server'
require 'padrino-core/tasks'
require 'padrino-core/module'
FileSet.glob_require('padrino-core/application/*.rb', __FILE__)
FileSet.glob_require('padrino-core/*.rb', __FILE__)
# The Padrino environment (falls back to the rack env or finally develop) # The Padrino environment (falls back to the rack env or finally develop)
PADRINO_ENV = ENV["PADRINO_ENV"] ||= ENV["RACK_ENV"] ||= "development" unless defined?(PADRINO_ENV) PADRINO_ENV = ENV["PADRINO_ENV"] ||= ENV["RACK_ENV"] ||= "development" unless defined?(PADRINO_ENV)
@ -80,7 +91,14 @@ module Padrino
# end # end
# #
def configure_apps(&block) def configure_apps(&block)
@_global_configuration = block if block_given? return unless block_given?
@@_global_configurations ||= []
@@_global_configurations << block
@_global_configuration = lambda do |app|
@@_global_configurations.each do |configuration|
app.class_eval(&configuration)
end
end
end end
## ##
@ -148,5 +166,41 @@ module Padrino
def use(m, *args, &block) def use(m, *args, &block)
middleware << [m, args, block] middleware << [m, args, block]
end end
##
# Registers a gem with padrino. This relieves the caller from setting up
# loadpaths by himself and enables Padrino to look up apps in gem folder.
#
# The name given has to be the proper gem name as given in the gemspec.
#
# @param [String] name
# The name of the gem being registered.
#
# @param [Module] main_module
# The main module of the gem.
#
# @returns The root path of the loaded gem
def gem(name, main_module)
_,spec = Gem.loaded_specs.find { |spec_name, spec| spec_name == name }
gems << spec
modules << main_module
spec.full_gem_path
end
##
# Returns all currently known padrino gems.
#
# @returns [Gem::Specification]
def gems
@gems ||= []
end
##
# All loaded Padrino modules.
#
# @returns [<Padrino::Module>]
def modules
@modules ||= []
end
end # self end # self
end # Padrino end # Padrino

View file

@ -1,3 +1,8 @@
require 'padrino-core/application/rendering'
require 'padrino-core/application/routing'
require 'padrino-core/application/flash'
require 'padrino-core/application/showexceptions'
module Padrino module Padrino
class ApplicationSetupError < RuntimeError # @private class ApplicationSetupError < RuntimeError # @private
end end
@ -173,17 +178,21 @@ module Padrino
set :method_override, true set :method_override, true
set :sessions, false set :sessions, false
set :public_folder, Proc.new { Padrino.root('public', uri_root) } set :public_folder, Proc.new { Padrino.root('public', uri_root) }
set :views, Proc.new { File.join(root, "views") } set :views, Proc.new { File.join(root, 'views') }
set :images_path, Proc.new { File.join(public, "images") } set :images_path, Proc.new { File.join(public_folder, 'images') }
set :protection, false set :protection, true
# Haml specific
set :haml, { :ugly => (Padrino.env == :production) } if defined?(Haml)
# Padrino specific # Padrino specific
set :uri_root, '/' set :uri_root, '/'
set :app_name, settings.to_s.underscore.to_sym set :app_name, settings.to_s.underscore.to_sym
set :default_builder, 'StandardFormBuilder' set :default_builder, 'StandardFormBuilder'
set :flash, defined?(Sinatra::Flash) || defined?(Rack::Flash)
set :authentication, false set :authentication, false
# Padrino locale # Padrino locale
set :locale_path, Proc.new { Dir[File.join(settings.root, '/locale/**/*.{rb,yml}')] } set :locale_path, Proc.new { Dir[File.join(settings.root, '/locale/**/*.{rb,yml}')] }
# Authenticity token
set :protect_from_csrf, false
set :allow_disabled_csrf, false
# Load the Global Configurations # Load the Global Configurations
class_eval(&Padrino.apps_configuration) if Padrino.apps_configuration class_eval(&Padrino.apps_configuration) if Padrino.apps_configuration
end end
@ -241,27 +250,42 @@ module Padrino
# Also initializes the application after setting up the middleware # Also initializes the application after setting up the middleware
def setup_default_middleware(builder) def setup_default_middleware(builder)
setup_sessions builder setup_sessions builder
setup_flash builder
builder.use Padrino::ShowExceptions if show_exceptions? builder.use Padrino::ShowExceptions if show_exceptions?
builder.use Padrino::Logger::Rack, uri_root if Padrino.logger && logging? builder.use Padrino::Logger::Rack, uri_root if Padrino.logger && logging?
builder.use Padrino::Reloader::Rack if reload? builder.use Padrino::Reloader::Rack if reload?
builder.use Rack::MethodOverride if method_override? builder.use Rack::MethodOverride if method_override?
builder.use Rack::Head builder.use Rack::Head
register Padrino::Flash
setup_protection builder setup_protection builder
setup_csrf_protection builder
setup_application! setup_application!
end end
# TODO Remove this in a few versions (rack-flash deprecation) # sets up csrf protection for the app
# Move register Sinatra::Flash into setup_default_middleware def setup_csrf_protection(builder)
# Initializes flash using sinatra-flash or rack-flash if protect_from_csrf? && !sessions?
def setup_flash(builder) raise(<<-ERROR)
register Sinatra::Flash if flash? && defined?(Sinatra::Flash) `protect_from_csrf` is activated, but `sessions` are not. To enable csrf
if defined?(Rack::Flash) && !defined?(Sinatra::Flash) protection, use:
logger.warn %Q{
[Deprecation] In Gemfile, 'rack-flash' should be replaced with 'sinatra-flash'! enable :sessions
Rack-Flash is not compatible with later versions of Rack and should be replaced.
} or deactivate protect_from_csrf:
builder.use Rack::Flash, :sweep => true if flash?
disable :protect_from_csrf
ERROR
end
if protect_from_csrf?
if allow_disabled_csrf?
builder.use Rack::Protection::AuthenticityToken,
:reaction => :report,
:report_key => 'protection.csrf.failed',
:logger => logger
else
builder.use Rack::Protection::AuthenticityToken,
:logger => logger
end
end end
end end
end # self end # self

View file

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

View file

@ -7,6 +7,16 @@ module Padrino
# locale enabled rendering, among other features. # locale enabled rendering, among other features.
# #
module Rendering module Rendering
##
# A SafeTemplate assumes that its output is safe.
#
# @api private
module SafeTemplate
def render(*)
super.html_safe
end
end
## ##
# Exception responsible for when an expected template did not exist. # Exception responsible for when an expected template did not exist.
# #
@ -25,19 +35,34 @@ module Padrino
] unless defined?(IGNORE_FILE_PATTERN) ] unless defined?(IGNORE_FILE_PATTERN)
## ##
# Default rendering options used in the #render-method. # Default options used in the #resolve_template-method.
# #
DEFAULT_RENDERING_OPTIONS = { :strict_format => false, :raise_exceptions => true } unless defined?(DEFAULT_RENDERING_OPTIONS) DEFAULT_RENDERING_OPTIONS = { :strict_format => false, :raise_exceptions => true } unless defined?(DEFAULT_RENDERING_OPTIONS)
class << self class << self
##
# Default engine configurations for Padrino::Rendering
#
# @return {Hash<Symbol,Hash>}
# The configurations, keyed by engine.
def engine_configurations
@engine_configurations ||= {}
end
## ##
# Main class that register this extension. # Main class that register this extension.
# #
def registered(app) def registered(app)
app.send(:include, InstanceMethods) included(app)
app.extend(ClassMethods) engine_configurations.each do |engine, configs|
app.set engine, configs
end
end
def included(base)
base.send(:include, InstanceMethods)
base.extend(ClassMethods)
end end
alias :included :registered
end end
## ##
@ -152,11 +177,8 @@ module Padrino
# * Use render 'path/to/template.haml' (with explicit 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
# * Use render 'path/to/template', :layout => false, :engine => 'haml' # * 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) def render(engine, data=nil, options={}, locals={}, &block)
# If engine is a hash then render data converted to json
content_type(:json, :charset => 'utf-8') and return engine.to_json if engine.is_a?(Hash)
# If engine is nil, ignore engine parameter and shift up all arguments # If engine is nil, ignore engine parameter and shift up all arguments
# render nil, "index", { :layout => true }, { :localvar => "foo" } # render nil, "index", { :layout => true }, { :localvar => "foo" }
@ -175,10 +197,10 @@ module Padrino
root = settings.respond_to?(:root) ? settings.root : "" root = settings.respond_to?(:root) ? settings.root : ""
# Use @layout if it exists # Use @layout if it exists
options[:layout] = @layout if options[:layout].nil? layout_was = options[:layout]
options[:layout] = @layout if options[:layout].nil? || options[:layout] == true
# Resolve layouts similar to in Rails # Resolve layouts similar to in Rails
if (options[:layout].nil? || options[:layout] == true) && !settings.templates.has_key?(:layout) if options[:layout].nil? && !settings.templates.has_key?(:layout)
layout_path, layout_engine = *resolved_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] = 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] = false unless layout_engine == engine # TODO allow different layout engine
@ -186,10 +208,12 @@ module Padrino
elsif options[:layout].present? elsif options[:layout].present?
options[:layout] = settings.fetch_layout_path(options[:layout] || @layout) options[:layout] = settings.fetch_layout_path(options[:layout] || @layout)
end end
# Default to original layout value if none found
options[:layout] ||= layout_was
# Cleanup the template # Cleanup the template
@current_engine, engine_was = engine, @current_engine @current_engine, engine_was = engine, @current_engine
@_out_buf, _buf_was = "", @_out_buf @_out_buf, _buf_was = ActiveSupport::SafeBuffer.new, @_out_buf
# Pass arguments to Sinatra render method # Pass arguments to Sinatra render method
super(engine, data, options.dup, locals, &block) super(engine, data, options.dup, locals, &block)
@ -290,3 +314,7 @@ module Padrino
end # InstanceMethods end # InstanceMethods
end # Rendering end # Rendering
end # Padrino end # Padrino
require 'padrino-core/application/rendering/extensions/haml'
require 'padrino-core/application/rendering/extensions/erubis'
require 'padrino-core/application/rendering/extensions/slim'

View file

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

View file

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

View file

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

View file

@ -11,6 +11,9 @@ class Sinatra::Request
def controller def controller
route_obj && route_obj.controller route_obj && route_obj.controller
end end
def action
route_obj && route_obj.action
end
end end
## ##
@ -28,8 +31,8 @@ class HttpRouter
@route = path.route @route = path.route
@params ||= {} @params ||= {}
@params.update(env['router.params']) @params.update(env['router.params'])
@block_params = if path.route.is_a?(HttpRouter::RegexRoute) @block_params = if match_data = env['router.request'].extra_env['router.regex_match']
params_list = env['router.request'].extra_env['router.regex_match'].to_a params_list = match_data.to_a
params_list.shift params_list.shift
@params[:captures] = params_list @params[:captures] = params_list
params_list params_list
@ -47,7 +50,6 @@ class HttpRouter
(@route.before_filters - settings.filters[:before]).each { |block| instance_eval(&block) } (@route.before_filters - settings.filters[:before]).each { |block| instance_eval(&block) }
@layout = path.route.use_layout if path.route.use_layout @layout = path.route.use_layout if path.route.use_layout
@route.custom_conditions.each { |block| pass if block.bind(self).call == false } if @route.custom_conditions @route.custom_conditions.each { |block| pass if block.bind(self).call == false } if @route.custom_conditions
@block_params = @block_params[0, @route.dest.arity] if @route.dest.arity > 0
halt_response = catch(:halt) { route_eval { @route.dest[self, @block_params] } } halt_response = catch(:halt) { route_eval { @route.dest[self, @block_params] } }
@_response_buffer = halt_response.is_a?(Array) ? halt_response.last : halt_response @_response_buffer = halt_response.is_a?(Array) ? halt_response.last : halt_response
successful = true successful = true
@ -62,7 +64,9 @@ class HttpRouter
# @private # @private
class Route class Route
attr_accessor :use_layout, :controller, :cache, :cache_key, :cache_expires_in VALID_HTTP_VERBS.replace %w[GET POST PUT PATCH DELETE HEAD OPTIONS LINK UNLINK]
attr_accessor :use_layout, :controller, :action, :cache, :cache_key, :cache_expires_in, :parent
def before_filters(&block) def before_filters(&block)
@_before_filters ||= [] @_before_filters ||= []
@ -84,6 +88,60 @@ class HttpRouter
@_custom_conditions @_custom_conditions
end end
def significant_variable_names
@significant_variable_names ||= if @original_path.is_a?(String)
@original_path.scan(/(^|[^\\])[:\*]([a-zA-Z0-9_]+)/).map{|p| p.last.to_sym}
elsif @original_path.is_a?(Regexp) and @original_path.respond_to?(:named_captures)
@original_path.named_captures.keys.map(&:to_sym)
else
[]
end
end
end
#Monkey patching the Request class. Using Rack::Utils.unescape rather than
#URI.unescape which can't handle utf-8 chars
class Request
def initialize(path, rack_request)
@rack_request = rack_request
@path = Rack::Utils.unescape(path).split(/\//)
@path.shift if @path.first == ''
@path.push('') if path[-1] == ?/
@extra_env = {}
@params = []
@acceptable_methods = Set.new
end
end
class Node::Path
def to_code
path_ivar = inject_root_ivar(self)
"#{"if !callback && request.path.size == 1 && request.path.first == '' && (request.rack_request.head? || request.rack_request.get?) && request.rack_request.path_info[-1] == ?/
catch(:pass) do
response = ::Rack::Response.new
response.redirect(request.rack_request.path_info[0, request.rack_request.path_info.size - 1], 302)
return response.finish
end
end" if router.redirect_trailing_slash?}
#{"if request.#{router.ignore_trailing_slash? ? 'path_finished?' : 'path.empty?'}" unless route.match_partially}
catch(:pass) do
if callback
request.called = true
callback.call(Response.new(request, #{path_ivar}))
else
env = request.rack_request.dup.env
env['router.request'] = request
env['router.params'] ||= {}
#{"env['router.params'].merge!(Hash[#{param_names.inspect}.zip(request.params)])" if dynamic?}
@router.rewrite#{"_partial" if route.match_partially}_path_info(env, request)
response = @router.process_destination_path(#{path_ivar}, env)
return response unless router.pass_on_response(response)
end
end
#{"end" unless route.match_partially}"
end
end end
end end
@ -98,7 +156,7 @@ module Padrino
def apply?(request) def apply?(request)
detect = @args.any? do |arg| detect = @args.any? do |arg|
case arg case arg
when Symbol then request.route_obj && (request.route_obj.named == arg or request.route_obj.named == [@scoped_controller, arg].flatten.join("_").to_sym) when Symbol then request.route_obj && (request.route_obj.name == arg or request.route_obj.name == [@scoped_controller, arg].flatten.join("_").to_sym)
else arg === request.path_info else arg === request.path_info
end end
end || @options.any? do |name, val| end || @options.any? do |name, val|
@ -166,7 +224,7 @@ module Padrino
# Class methods responsible for enhanced routing for controllers. # Class methods responsible for enhanced routing for controllers.
module ClassMethods module ClassMethods
## ##
# Method for organize in a better way our routes. # Method to organize our routes in a better way.
# #
# @param [Array] args # @param [Array] args
# Controller arguments. # Controller arguments.
@ -237,8 +295,8 @@ module Padrino
# get :index, :map => "/:lang" do; "params[:lang] == :de"; end # get :index, :map => "/:lang" do; "params[:lang] == :de"; end
# end # end
# #
# In a controller before and after filters are scoped and didn't affect other controllers or main app. # In a controller, before and after filters are scoped and don't affect other controllers or the main app.
# In a controller layout are scoped and didn't affect others controllers and main app. # In a controller, layouts are scoped and don't affect other controllers or the main app.
# #
# @example # @example
# controller :posts do # controller :posts do
@ -449,7 +507,8 @@ module Padrino
# #
def recognize_path(path) def recognize_path(path)
responses = @router.recognize(Rack::MockRequest.env_for(path)) responses = @router.recognize(Rack::MockRequest.env_for(path))
[responses[0].path.route.named, responses[0].params] responses = responses[0] if responses[0].is_a?(Array)
[responses[0].path.route.name, responses[0].params]
end end
## ##
@ -470,10 +529,11 @@ module Padrino
params[:format] = params[:format].to_s unless params[:format].nil? params[:format] = params[:format].to_s unless params[:format].nil?
params = value_to_param(params) params = value_to_param(params)
end end
url = if params_array.empty? url =
compiled_router.url(name, params) if params_array.empty?
compiled_router.path(name, params)
else else
compiled_router.url(name, *(params_array << params)) compiled_router.path(name, *(params_array << params))
end end
url[0,0] = conform_uri(uri_root) if defined?(uri_root) 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[0,0] = conform_uri(ENV['RACK_BASE_URI']) if ENV['RACK_BASE_URI']
@ -556,8 +616,17 @@ module Padrino
# Do padrino parsing. We dup options so we can build HEAD request correctly # Do padrino parsing. We dup options so we can build HEAD request correctly
route_options = options.dup route_options = options.dup
route_options[:provides] = @_provides if @_provides route_options[:provides] = @_provides if @_provides
# CSRF protection is always active except when explicitly switched off
if allow_disabled_csrf
unless route_options[:csrf_protection] == false
route_options[:csrf_protection] = true
end
end
path, *route_options[:with] = path if path.is_a?(Array) path, *route_options[:with] = path if path.is_a?(Array)
path, name, options, route_options = *parse_route(path, route_options, verb) action = path
path, name, route_parents, options, route_options = *parse_route(path, route_options, verb)
options.reverse_merge!(@_conditions) if @_conditions options.reverse_merge!(@_conditions) if @_conditions
# Sinatra defaults # Sinatra defaults
@ -572,20 +641,22 @@ module Padrino
# HTTPRouter route construction # HTTPRouter route construction
route = router.add(path, route_options) route = router.add(path, route_options)
route.name(name) if name route.name = name if name
route.action = action
priority_name = options.delete(:priority) || :normal priority_name = options.delete(:priority) || :normal
priority = ROUTE_PRIORITY[priority_name] or raise("Priority #{priority_name} not recognized, try #{ROUTE_PRIORITY.keys.join(', ')}") 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.cache = options.key?(:cache) ? options.delete(:cache) : @_cache
route.send(verb.downcase.to_sym) route.parent = route_parents ? (route_parents.count == 1 ? route_parents.first : route_parents) : route_parents
route.host(options.delete(:host)) if options.key?(:host) route.add_request_method(verb.downcase.to_sym)
route.user_agent(options.delete(:agent)) if options.key?(:agent) route.host = options.delete(:host) if options.key?(:host)
route.user_agent = options.delete(:agent) if options.key?(:agent)
if options.key?(:default_values) if options.key?(:default_values)
defaults = options.delete(:default_values) defaults = options.delete(:default_values)
route.default(defaults) if defaults route.add_default_values(defaults) if defaults
end end
options.delete_if do |option, args| options.delete_if do |option, args|
if route.send(:significant_variable_names).include?(option) if route.significant_variable_names.include?(option)
route.matching(option => Array(args).first) route.add_match_with(option => Array(args).first)
true true
end end
end end
@ -646,8 +717,8 @@ module Padrino
if @_use_format or format_params = options[:provides] if @_use_format or format_params = options[:provides]
process_path_for_provides(path, format_params) process_path_for_provides(path, format_params)
options[:matching] ||= {} # options[:add_match_with] ||= {}
options[:matching][:format] = /[^\.]+/ # options[:add_match_with][:format] = /[^\.]+/
end end
absolute_map = map && map[0] == ?/ absolute_map = map && map[0] == ?/
@ -657,14 +728,14 @@ module Padrino
if map.blank? and !absolute_map if map.blank? and !absolute_map
controller_path = controller.join("/") controller_path = controller.join("/")
path.gsub!(%r{^\(/\)|/\?}, "") path.gsub!(%r{^\(/\)|/\?}, "")
path = File.join(controller_path, path) path = File.join(controller_path, path) unless @_map
end end
# Here we build the correct name route # Here we build the correct name route
end end
# Now we need to parse our 'parent' params and parent scope # Now we need to parse our 'parent' params and parent scope
if !absolute_map and parent_params = options.delete(:parent) || @_parents if !absolute_map and parent_params = options.delete(:parent) || @_parents
parent_params = Array(@_parents) + Array(parent_params) parent_params = (Array(@_parents) + Array(parent_params)).uniq
path = process_path_for_parent_params(path, parent_params) path = process_path_for_parent_params(path, parent_params)
end end
@ -692,7 +763,7 @@ module Padrino
# Merge in option defaults # Merge in option defaults
options.reverse_merge!(:default_values => @_defaults) options.reverse_merge!(:default_values => @_defaults)
[path, name, options, route_options] [path, name, parent_params, options, route_options]
end end
## ##
@ -756,19 +827,22 @@ module Padrino
def provides(*types) def provides(*types)
@_use_format = true @_use_format = true
condition do condition do
mime_types = types.map { |t| mime_type(t) } mime_types = types.map { |t| mime_type(t) }.compact
url_format = params[:format].to_sym if params[:format] url_format = params[:format].to_sym if params[:format]
accepts = request.accept.map { |a| a.split(";")[0].strip } accepts = request.accept.map { |a| a.to_str }
# per rfc2616-sec14: # per rfc2616-sec14:
# Assume */* if no ACCEPT header is given. # Assume */* if no ACCEPT header is given.
catch_all = (accepts.delete "*/*" || accepts.empty?) catch_all = (accepts.delete "*/*" || accepts.empty?)
matching_types = accepts.empty? ? mime_types.slice(0,1) : (accepts & mime_types) matching_types = accepts.empty? ? mime_types.slice(0,1) : (accepts & mime_types)
if matching_types.empty? && types.include?(:any)
matching_types = accepts
end
if !url_format && matching_types.first if !url_format && matching_types.first
type = ::Rack::Mime::MIME_TYPES.find { |k, v| v == matching_types.first }[0].sub(/\./,'').to_sym type = ::Rack::Mime::MIME_TYPES.find { |k, v| v == matching_types.first }[0].sub(/\./,'').to_sym
accept_format = CONTENT_TYPE_ALIASES[type] || type accept_format = CONTENT_TYPE_ALIASES[type] || type
elsif catch_all elsif catch_all && !types.include?(:any)
type = types.first type = types.first
accept_format = CONTENT_TYPE_ALIASES[type] || type accept_format = CONTENT_TYPE_ALIASES[type] || type
end end
@ -793,6 +867,21 @@ module Padrino
matched_format matched_format
end end
end end
##
# Implements CSRF checking when `allow_disabled_csrf` is set to true.
#
# This condition is always on, except when it is explicitly switched
# off.
#
# @example
# post("/", :csrf_protection => false)
#
def csrf_protection(on = true)
if on
condition { halt 403 if request.env['protection.csrf.failed'] }
end
end
end end
## ##
@ -836,7 +925,7 @@ module Padrino
else else
path_params << params path_params << params
end end
@route.url(*path_params) @route.path(*path_params)
end end
## ##
@ -905,13 +994,19 @@ module Padrino
end end
def dispatch! def dispatch!
invoke do
static! if settings.static? && (request.get? || request.head?) static! if settings.static? && (request.get? || request.head?)
route! route!
end
rescue ::Exception => boom rescue ::Exception => boom
filter! :before filter! :before if boom.kind_of? ::Sinatra::NotFound
handle_exception!(boom) invoke { @boom_handled = handle_exception!(boom) }
ensure ensure
@boom_handled or begin
filter! :after unless env['sinatra.static_file'] filter! :after unless env['sinatra.static_file']
rescue ::Exception => boom
invoke { handle_exception!(boom) } unless @env['sinatra.error']
end
end end
def route!(base=settings, pass_block=nil) def route!(base=settings, pass_block=nil)
@ -922,6 +1017,7 @@ module Padrino
match[1].each { |k,v| response[k] = v } match[1].each { |k,v| response[k] = v }
status match[0] status match[0]
route_missing if match[0] == 404 route_missing if match[0] == 404
route_missing if allow = response['Allow'] and allow.include?(request.env['REQUEST_METHOD'])
end end
end end
else else

View file

@ -6,17 +6,18 @@ module Padrino
class Base < Thor class Base < Thor
include Thor::Actions include Thor::Actions
class_option :chdir, :type => :string, :aliases => "-c", :desc => "Change to dir before starting" 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 :environment, :type => :string, :aliases => "-e", :required => true, :default => :development, :desc => "Padrino Environment."
class_option :help, :type => :boolean, :desc => "Show help usage" class_option :help, :type => :boolean, :desc => "Show help usage"
desc "start", "Starts the Padrino application" desc "start", "Starts the Padrino application (alternatively use 's')."
map "s" => :start
method_option :server, :type => :string, :aliases => "-a", :desc => "Rack Handler (default: autodetect)" method_option :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 :host, :type => :string, :aliases => "-h", :required => true, :default => '127.0.0.1', :desc => "Bind to HOST address."
method_option :port, :type => :numeric, :aliases => "-p", :required => true, :default => 3000, :desc => "Use PORT" method_option :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 :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 :pid, :type => :string, :aliases => "-i", :desc => "File to store pid."
method_option :debug, :type => :boolean, :desc => "Set debugging flags" method_option :debug, :type => :boolean, :desc => "Set debugging flags."
def start def start
prepare :start prepare :start
require File.expand_path("../adapter", __FILE__) require File.expand_path("../adapter", __FILE__)
@ -24,12 +25,8 @@ module Padrino
Padrino::Cli::Adapter.start(options) Padrino::Cli::Adapter.start(options)
end end
desc "s", "Starts the Padrino application" desc "stop", "Stops the Padrino application (alternatively use 'st')."
def s map "st" => :stop
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' method_option :pid, :type => :string, :aliases => "-p", :desc => "File to store pid", :default => 'tmp/pids/server.pid'
def stop def stop
prepare :stop prepare :stop
@ -37,7 +34,7 @@ module Padrino
Padrino::Cli::Adapter.stop(options) Padrino::Cli::Adapter.stop(options)
end end
desc "rake", "Execute rake tasks" desc "rake", "Execute rake tasks."
method_option :environment, :type => :string, :aliases => "-e", :required => true, :default => :development 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 :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 :trace, :type => :boolean, :aliases => "-t", :desc => "Turn on invoke/execute tracing, enable full backtrace."
@ -51,12 +48,15 @@ module Padrino
ARGV.concat(args) ARGV.concat(args)
puts "=> Executing Rake #{ARGV.join(' ')} ..." puts "=> Executing Rake #{ARGV.join(' ')} ..."
load File.expand_path('../rake.rb', __FILE__) load File.expand_path('../rake.rb', __FILE__)
require File.expand_path('config/boot.rb') Rake.application.init
PadrinoTasks.init(true) Rake.application.instance_variable_set(:@rakefile, __FILE__)
load File.expand_path('Rakefile')
Rake.application.top_level
end end
desc "console", "Boots up the Padrino application irb console" desc "console", "Boots up the Padrino application irb console (alternatively use 'c')."
def console map "c" => :console
def console(*args)
prepare :console prepare :console
require File.expand_path("../../version", __FILE__) require File.expand_path("../../version", __FILE__)
ARGV.clear ARGV.clear
@ -68,12 +68,8 @@ module Padrino
IRB.start IRB.start
end end
desc "c", "Boots up the Padrino application irb console" desc "generate", "Executes the Padrino generator with given options (alternatively use 'gen' or 'g')."
def c(*args) map ["gen", "g"] => :generate
invoke(:console, args)
end
desc "generate", "Executes the Padrino generator with given options."
def generate(*args) def generate(*args)
# Build Padrino g as an alias of padrino-gen # Build Padrino g as an alias of padrino-gen
begin begin
@ -90,23 +86,30 @@ module Padrino
end end
end end
desc "g", "Executes the Padrino generator with given options." desc "version", "Show current Padrino version."
def g(*args) map ["-v", "--version"] => :version
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 def version
require 'padrino-core/version' require 'padrino-core/version'
puts "Padrino v. #{Padrino.version}" puts "Padrino v. #{Padrino.version}"
end end
desc "runner", "Run a piece of code in the Padrino application environment (alternatively use 'run' or 'r')."
map ["run", "r"] => :runner
def runner(*args)
prepare :runner
code_or_file = args.shift
abort "Please specify code or file" if code_or_file.nil?
require File.expand_path('config/boot.rb')
if File.exist?(code_or_file)
eval(File.read(code_or_file), nil, code_or_file)
else
eval(code_or_file)
end
end
private private
def prepare(task) def prepare(task)
if options.help? if options.help?

View file

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

View file

@ -1,11 +1,17 @@
# Load rake tasks from common rake task definition locations # Load rake tasks from common rake task definition locations
Dir["lib/tasks/**/*.rake"]. Dir["{lib/tasks/**,tasks/**,test,spec}/*.rake"].each do |file|
concat(Dir["tasks/**/*.rake"]). begin
concat(Dir["{test,spec}/*.rake"]).each { |rake| load(rake) } load(file)
rescue LoadError => e
warn "#{file}: #{e.message}"
end
end
# Loads the Padrino applications mounted within the project # Loads the Padrino applications mounted within the project
# setting up the required environment for Padrino # setting up the required environment for Padrino
task :environment do task :environment do
require File.expand_path('config/boot.rb', Rake.application.original_dir)
Padrino.mounted_apps.each do |app| Padrino.mounted_apps.each do |app|
app.app_obj.setup_application! app.app_obj.setup_application!
end end
@ -46,14 +52,3 @@ namespace :routes do
list_app_routes(app, args) if app list_app_routes(app, args) if app
end end
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

@ -60,6 +60,8 @@ module Padrino
# #
def load! def load!
return false if loaded? return false if loaded?
t = Time.now
@_called_from = first_caller @_called_from = first_caller
Padrino.set_encoding Padrino.set_encoding
Padrino.set_load_paths(*load_paths) # We set the padrino load paths Padrino.set_load_paths(*load_paths) # We set the padrino load paths
@ -71,6 +73,8 @@ module Padrino
Padrino.after_load.each(&:call) # Run after hooks Padrino.after_load.each(&:call) # Run after hooks
Padrino::Reloader.run! Padrino::Reloader.run!
Thread.current[:padrino_loaded] = true Thread.current[:padrino_loaded] = true
Padrino.logger.devel "Loaded Padrino in #{Time.now - t} seconds"
end end
## ##
@ -162,10 +166,8 @@ module Padrino
begin begin
Padrino::Reloader.safe_load(file, options.dup) Padrino::Reloader.safe_load(file, options.dup)
files.delete(file) files.delete(file)
rescue LoadError => e rescue NameError, LoadError => e
errors << e Padrino.logger.devel "Problem while loading #{file}: #{e.to_s}"
failed << file
rescue NameError => e
errors << e errors << e
failed << file failed << file
rescue Exception => e rescue Exception => e
@ -190,11 +192,7 @@ module Padrino
# Padrino.dependency_paths << "#{Padrino.root}/uploaders/*.rb" # Padrino.dependency_paths << "#{Padrino.root}/uploaders/*.rb"
# #
def dependency_paths def dependency_paths
@_dependency_paths_was = [ @_dependency_paths ||= (dependency_paths_was + Array(module_paths))
"#{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 end
## ##
@ -207,5 +205,21 @@ module Padrino
$:.concat(paths); load_paths.concat(paths) $:.concat(paths); load_paths.concat(paths)
$:.uniq!; load_paths.uniq! $:.uniq!; load_paths.uniq!
end end
private
def module_paths
Padrino.modules.map(&:dependency_paths).flatten!
end
def dependency_paths_was
[
"#{root}/config/database.rb",
"#{root}/lib/**/*.rb",
"#{root}/shared/lib/**/*.rb",
"#{root}/models/**/*.rb",
"#{root}/shared/models/**/*.rb",
"#{root}/config/apps.rb"
]
end
end # self end # self
end # Padrino end # Padrino

View file

@ -5,8 +5,8 @@ de:
# Wenn keine Formate angegeben werden, wird "default" benutzt. # Wenn keine Formate angegeben werden, wird "default" benutzt.
# Du kannst auch weitere Formate hinzufügen, wenn Du möchtest. # Du kannst auch weitere Formate hinzufügen, wenn Du möchtest.
default: "&d.&m.%Y" default: "&d.&m.%Y"
short: "%b %d" short: "%d. %b"
long: "%B %d, %Y" long: "%d. %B %Y"
only_day: "%e" only_day: "%e"
day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag] day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
@ -20,9 +20,9 @@ de:
time: time:
formats: formats:
default: "%a, %d %b %Y %H:%M:%S %z" default: "%a, %d. %b %Y %H:%M:%S %z"
short: "%d %b %H:%M" short: "%d. %b %H:%M"
long: "%B %d, %Y %H:%M" long: "%d. %B %Y %H:%M"
am: "am" am: "am"
pm: "pm" pm: "pm"
@ -31,4 +31,4 @@ de:
array: array:
words_connector: ", " words_connector: ", "
two_words_connector: " und " two_words_connector: " und "
last_word_connector: ", und " last_word_connector: " und "

View file

@ -12,7 +12,7 @@ fr:
day_names: [Dimanche, Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi] day_names: [Dimanche, Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi]
abbr_day_names: [Lun, Mar, Mer, Jeu, Ven, Sam, Dim] 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] 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] abbr_month_names: [~, Jan, Fev, Mar, Avr, Mai, Jun, Jul, Aou, Sep, Oct, Nov, Dec]
order: order:
- day - day
- month - month

View file

@ -12,7 +12,7 @@ ru:
day_names: [Воскресенье, Понедельник, Вторник, Среда, Четверг, Пятница, Суббота] day_names: [Воскресенье, Понедельник, Вторник, Среда, Четверг, Пятница, Суббота]
abbr_day_names: [Вс, Пн, Вт, Ср, Чт, Пт, Сб] abbr_day_names: [Вс, Пн, Вт, Ср, Чт, Пт, Сб]
month_names: [~, Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь] month_names: [~, Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь]
abbr_month_names: [~, Янв, Феб, Мар, Апр, Май, Июн, Июл, Авг, Сен, Окт, Ноя, Дек] abbr_month_names: [~, Янв, Фев, Мар, Апр, Май, Июн, Июл, Авг, Сен, Окт, Ноя, Дек]
order: order:
- year - year
- month - month

View file

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

View file

@ -13,8 +13,7 @@ module Padrino
# logger.warn "bar" # logger.warn "bar"
# #
def self.logger def self.logger
Padrino::Logger.setup! if Thread.current[:padrino_logger].nil? Padrino::Logger.logger
Thread.current[:padrino_logger]
end end
## ##
@ -35,8 +34,7 @@ module Padrino
# Padrino.logger = Buffered.new(STDOUT) # Padrino.logger = Buffered.new(STDOUT)
# #
def self.logger=(value) def self.logger=(value)
value.extend(Padrino::Logger::Extensions) unless (Padrino::Logger::Extensions === value) Padrino::Logger.logger = value
Thread.current[:padrino_logger] = value
end end
## ##
@ -95,7 +93,7 @@ module Padrino
# #
# @example # @example
# logger.bench 'GET', started_at, '/blog/categories' # logger.bench 'GET', started_at, '/blog/categories'
# # => DEBUG - GET (0.056ms) - /blog/categories # # => DEBUG - GET (0.0056s) - /blog/categories
# #
def bench(action, began_at, message, level=:debug, color=:yellow) def bench(action, began_at, message, level=:debug, color=:yellow)
@_pad ||= 8 @_pad ||= 8
@ -103,7 +101,7 @@ module Padrino
duration = Time.now - began_at duration = Time.now - began_at
color = :red if duration > 1 color = :red if duration > 1
action = colorize(action.to_s.upcase.rjust(@_pad), color) action = colorize(action.to_s.upcase.rjust(@_pad), color)
duration = colorize('%0.4fms' % duration, :bold, color) duration = colorize('%0.4fs' % duration, :bold, color)
push "#{action} (#{duration}) #{message}", level push "#{action} (#{duration}) #{message}", level
end end
@ -208,7 +206,6 @@ module Padrino
end end
include Extensions include Extensions
include Colorize
attr_accessor :level attr_accessor :level
attr_accessor :auto_flush attr_accessor :auto_flush
@ -216,8 +213,7 @@ module Padrino
attr_reader :log attr_reader :log
attr_reader :init_args attr_reader :init_args
attr_accessor :log_static attr_accessor :log_static
attr_reader :colorize_logging
@@mutex = {}
## ##
# Configuration for a given environment, possible options are: # Configuration for a given environment, possible options are:
@ -233,6 +229,7 @@ module Padrino
# :format_datetime:: Format of datetime. Defaults to: "%d/%b/%Y %H:%M:%S" # :format_datetime:: Format of datetime. Defaults to: "%d/%b/%Y %H:%M:%S"
# :format_message:: Format of message. Defaults to: ""%s - - [%s] \"%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 # :log_static:: Whether or not to show log messages for static files. Defaults to: false
# :colorize_logging:: Whether or not to colorize log messages. Defaults to: true
# #
# @example # @example
# Padrino::Logger::Config[:development] = { :log_level => :debug, :stream => :to_file } # Padrino::Logger::Config[:development] = { :log_level => :debug, :stream => :to_file }
@ -259,6 +256,17 @@ module Padrino
} }
Config.merge!(PADRINO_LOGGER) if PADRINO_LOGGER Config.merge!(PADRINO_LOGGER) if PADRINO_LOGGER
@@mutex = Mutex.new
def self.logger
@_logger || setup!
end
def self.logger=(logger)
logger.extend(Padrino::Logger::Extensions)
@_logger = logger
end
## ##
# Setup a new logger # Setup a new logger
# #
@ -266,6 +274,7 @@ module Padrino
# A {Padrino::Logger} instance # A {Padrino::Logger} instance
# #
def self.setup! def self.setup!
self.logger = begin
config_level = (PADRINO_LOG_LEVEL || Padrino.env || :test).to_sym # need this for PADRINO_LOG_LEVEL config_level = (PADRINO_LOG_LEVEL || Padrino.env || :test).to_sym # need this for PADRINO_LOG_LEVEL
config = Config[config_level] config = Config[config_level]
@ -284,7 +293,8 @@ module Padrino
else config[:stream] # return itself, probabilly is a custom stream. else config[:stream] # return itself, probabilly is a custom stream.
end end
Thread.current[:padrino_logger] = Padrino::Logger.new(config.merge(:stream => stream)) Padrino::Logger.new(config.merge(:stream => stream))
end
end end
## ##
@ -311,16 +321,20 @@ module Padrino
# @option options [Symbol] :log_static (false) # @option options [Symbol] :log_static (false)
# Whether or not to show log messages for static files. # Whether or not to show log messages for static files.
# #
# @option options [Symbol] :colorize_logging (true)
# Whether or not to colorize log messages. Defaults to: true
#
def initialize(options={}) def initialize(options={})
@buffer = [] @buffer = []
@auto_flush = options.has_key?(:auto_flush) ? options[:auto_flush] : true @auto_flush = options.has_key?(:auto_flush) ? options[:auto_flush] : true
@level = options[:log_level] ? Padrino::Logger::Levels[options[:log_level]] : Padrino::Logger::Levels[:debug] @level = options[:log_level] ? Padrino::Logger::Levels[options[:log_level]] : Padrino::Logger::Levels[:debug]
@log = options[:stream] || $stdout @log = options[:stream] || $stdout
@log.sync = true @log.sync = true
@mutex = @@mutex[@log] ||= Mutex.new
@format_datetime = options[:format_datetime] || "%d/%b/%Y %H:%M:%S" @format_datetime = options[:format_datetime] || "%d/%b/%Y %H:%M:%S"
@format_message = options[:format_message] || "%s - %s %s" @format_message = options[:format_message] || "%s - %s %s"
@log_static = options.has_key?(:log_static) ? options[:log_static] : false @log_static = options.has_key?(:log_static) ? options[:log_static] : false
@colorize_logging = options.has_key?(:colorize_logging) ? options[:colorize_logging] : true
colorize! if @colorize_logging
end end
## ##
@ -328,8 +342,9 @@ module Padrino
# #
def flush def flush
return unless @buffer.size > 0 return unless @buffer.size > 0
@mutex.synchronize do @@mutex.synchronize do
@log.write(@buffer.slice!(0..-1).join('')) @log.write(@buffer.join(''))
@buffer.clear
end end
end end
@ -360,7 +375,9 @@ module Padrino
# #
def <<(message = nil) def <<(message = nil)
message << "\n" unless message[-1] == ?\n message << "\n" unless message[-1] == ?\n
@@mutex.synchronize {
@buffer << message @buffer << message
}
flush if @auto_flush flush if @auto_flush
message message
end end
@ -425,4 +442,3 @@ module Kernel # @private
Padrino.logger Padrino.logger
end end
end # Kernel end # Kernel

View file

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

View file

@ -22,10 +22,12 @@ module Padrino
# @option options [Symbol] :app_file (Automatically detected) # @option options [Symbol] :app_file (Automatically detected)
# @option options [Symbol] :app_obj (Detected) # @option options [Symbol] :app_obj (Detected)
# @option options [Symbol] :app_root (Directory of :app_file) # @option options [Symbol] :app_root (Directory of :app_file)
# @option options [Symbol] :gem The gem to load the app from (Detected from name)
# #
def initialize(name, options={}) def initialize(name, options={})
@name = name.to_s @name = name.to_s
@app_class = options[:app_class] || @name.camelize @app_class = options[:app_class] || @name.camelize
@gem = options[:gem] || @app_class.split("::").first.underscore
@app_file = options[:app_file] || locate_app_file @app_file = options[:app_file] || locate_app_file
@app_obj = options[:app_obj] || app_constant || locate_app_object @app_obj = options[:app_obj] || app_constant || locate_app_object
ensure_app_file! || ensure_app_object! ensure_app_file! || ensure_app_object!
@ -104,11 +106,12 @@ module Padrino
# #
def named_routes def named_routes
app_obj.routes.map { |route| app_obj.routes.map { |route|
name_array = "(#{route.named.to_s.split("_").map { |piece| %Q[:#{piece}] }.join(", ")})" name_array = "(#{route.name.to_s.split("_").map { |piece| %Q[:#{piece}] }.join(", ")})"
request_method = route.conditions[:request_method][0] request_method = route.request_methods.first
full_path = File.join(uri_root, route.original_path) next if route.name.blank? || request_method == 'HEAD'
next if route.named.blank? || request_method == 'HEAD' original_path = route.original_path.is_a?(Regexp) ? route.original_path.inspect : route.original_path
OpenStruct.new(:verb => request_method, :identifier => route.named, :name => name_array, :path => full_path) full_path = File.join(uri_root, original_path)
OpenStruct.new(:verb => request_method, :identifier => route.name, :name => name_array, :path => full_path)
}.compact }.compact
end end
@ -158,6 +161,13 @@ module Padrino
candidates << app_constant.app_file if app_constant.respond_to?(:app_file) && File.exist?(app_constant.app_file.to_s) 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.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.mounted_root(name.downcase, "app.rb")
simple_name = name.split("::").last.downcase
mod_name = name.split("::")[0..-2].join("::")
Padrino.modules.each do |mod|
if mod.name == mod_name
candidates << mod.root(simple_name, "app.rb")
end
end
candidates << Padrino.root("app", "app.rb") candidates << Padrino.root("app", "app.rb")
candidates.find { |candidate| File.exist?(candidate) } candidates.find { |candidate| File.exist?(candidate) }
end end

View file

@ -33,7 +33,7 @@ module Padrino
# Specified constants can be excluded from the code unloading process. # Specified constants can be excluded from the code unloading process.
# #
def exclude_constants def exclude_constants
@_exclude_constants ||= [] @_exclude_constants ||= Set.new
end end
## ##
@ -41,7 +41,7 @@ module Padrino
# Default included constants are: [none] # Default included constants are: [none]
# #
def include_constants def include_constants
@_include_constants ||= [] @_include_constants ||= Set.new
end end
## ##
@ -74,15 +74,9 @@ module Padrino
# Remove files and classes loaded with stat # Remove files and classes loaded with stat
# #
def clear! def clear!
MTIMES.clear clear_modification_times
LOADED_CLASSES.each do |file, klasses| clear_loaded_classes
klasses.each { |klass| remove_constant(klass) } clear_loaded_files_and_features
LOADED_CLASSES.delete(file)
end
LOADED_FILES.each do |file, dependencies|
dependencies.each { |dependency| $LOADED_FEATURES.delete(dependency) }
$LOADED_FEATURES.delete(file)
end
end end
## ##
@ -103,9 +97,12 @@ module Padrino
# We lock dependencies sets to prevent reloading of protected constants # We lock dependencies sets to prevent reloading of protected constants
# #
def lock! def lock!
klasses = ObjectSpace.classes.map { |klass| klass._orig_klass_name.split('::')[0] }.uniq klasses = ObjectSpace.classes do |klass|
klass._orig_klass_name.split('::')[0]
end
klasses = klasses | Padrino.mounted_apps.map { |app| app.app_class } klasses = klasses | Padrino.mounted_apps.map { |app| app.app_class }
Padrino::Reloader.exclude_constants.concat(klasses) Padrino::Reloader.exclude_constants.merge(klasses)
end end
## ##
@ -113,57 +110,46 @@ module Padrino
# #
def safe_load(file, options={}) def safe_load(file, options={})
began_at = Time.now began_at = Time.now
force, file = options[:force], figure_path(file) force = options[:force]
file = figure_path(file)
reload = should_reload?(file)
m_time = modification_time(file)
# Check if file was changed or if force a reload return if !force && m_time && !reload
reload = MTIMES[file] && File.mtime(file) > MTIMES[file]
return if !force && !reload && MTIMES[file]
# Removes all classes declared in the specified file remove_loaded_file_classes(file)
if klasses = LOADED_CLASSES.delete(file) remove_loaded_file_features(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 # Duplicate objects and loaded features before load file
klasses = ObjectSpace.classes.dup klasses = ObjectSpace.classes
files = $LOADED_FEATURES.dup files = Set.new($LOADED_FEATURES.dup)
# Now we can reload dependencies of our file reload_deps_of_file(file)
if features = LOADED_FILES.delete(file)
features.each { |feature| safe_load(feature, :force => true) }
end
# And finally load the specified file # And finally load the specified file
begin begin
logger.devel :loading, began_at, file if !reload logger.devel :loading, began_at, file if !reload
logger.debug :reload, began_at, file if reload logger.debug :reload, began_at, file if reload
$LOADED_FEATURES.delete(file)
verbosity_was, $-v = $-v, nil $LOADED_FEATURES.delete(file) if files.include?(file)
Padrino::Utils.silence_output
loaded = false loaded = false
require(file) require(file)
loaded = true loaded = true
MTIMES[file] = File.mtime(file) update_modification_time(file)
rescue SyntaxError => e rescue SyntaxError => e
logger.error "Cannot require #{file} due to a syntax error: #{e.message}" logger.error "Cannot require #{file} due to a syntax error: #{e.message}"
ensure ensure
$-v = verbosity_was Padrino::Utils.unsilence_output
new_constants = (ObjectSpace.classes - klasses).uniq new_constants = ObjectSpace.new_classes(klasses)
if loaded if loaded
# Store the file details process_loaded_file(:file => file,
LOADED_CLASSES[file] = new_constants :constants => new_constants,
LOADED_FILES[file] = ($LOADED_FEATURES - files - [file]).uniq :files => files)
# Track only features in our Padrino.root
LOADED_FILES[file].delete_if { |feature| !in_root?(feature) }
else else
logger.devel "Failed to load #{file}; removing partially defined constants" logger.devel "Failed to load #{file}; removing partially defined constants"
new_constants.each { |klass| remove_constant(klass) } unload_constants(new_constants)
end end
end end
end end
@ -183,8 +169,8 @@ module Padrino
# Removes the specified class and constant. # Removes the specified class and constant.
# #
def remove_constant(const) def remove_constant(const)
return if exclude_constants.compact.uniq.any? { |c| const._orig_klass_name.index(c) == 0 } && return if exclude_constants.any? { |c| const._orig_klass_name.index(c) == 0 } &&
!include_constants.compact.uniq.any? { |c| const._orig_klass_name.index(c) == 0 } !include_constants.any? { |c| const._orig_klass_name.index(c) == 0 }
begin begin
parts = const.to_s.sub(/^::(Object)?/, 'Object::').split('::') parts = const.to_s.sub(/^::(Object)?/, 'Object::').split('::')
object = parts.pop object = parts.pop
@ -195,6 +181,101 @@ module Padrino
end end
private private
###
# Clear instance variables that keep track of
# loaded features/files/mtimes
#
def clear_modification_times
MTIMES.clear
end
def clear_loaded_classes
LOADED_CLASSES.each do |file, klasses|
klasses.each { |klass| remove_constant(klass) }
LOADED_CLASSES.delete(file)
end
end
def clear_loaded_files_and_features
LOADED_FILES.each do |file, dependencies|
dependencies.each { |dependency| $LOADED_FEATURES.delete(dependency) }
$LOADED_FEATURES.delete(file)
end
end
###
# Macro for mtime query
#
def modification_time(file)
MTIMES[file]
end
###
# Macro for mtime update
#
def update_modification_time(file)
MTIMES[file] = File.mtime(file)
end
###
# Tracks loaded file features/classes/constants
#
def process_loaded_file(*args)
options = args.extract_options!
new_constants = options[:constants]
files = options[:files]
file = options[:file]
# Store the file details
LOADED_CLASSES[file] = new_constants
LOADED_FILES[file] = Set.new($LOADED_FEATURES) - files - [file]
# Track only features in our Padrino.root
LOADED_FILES[file].delete_if { |feature| !in_root?(feature) }
end
###
# Unloads all constants in new_constants
#
def unload_constants(new_constants)
new_constants.each { |klass| remove_constant(klass) }
end
###
# Safe load dependencies of a file
#
def reload_deps_of_file(file)
if features = LOADED_FILES.delete(file)
features.each { |feature| safe_load(feature, :force => true) }
end
end
##
# Check if file was changed or if force a reload
#
def should_reload?(file)
MTIMES[file] && File.mtime(file) > MTIMES[file]
end
##
# Removes all classes declared in the specified file
#
def remove_loaded_file_classes(file)
if klasses = LOADED_CLASSES.delete(file)
klasses.each { |klass| remove_constant(klass) }
end
end
##
# Remove all loaded fatures with our file
#
def remove_loaded_file_features(file)
if features = LOADED_FILES[file]
features.each { |feature| $LOADED_FEATURES.delete(feature) }
end
end
## ##
# Return the mounted_apps providing the app location # Return the mounted_apps providing the app location
# Can be an array because in one app.rb we can define multiple Padrino::Appplications # Can be an array because in one app.rb we can define multiple Padrino::Appplications
@ -218,15 +299,21 @@ module Padrino
# and monitors them for any changes. # and monitors them for any changes.
# #
def rotation def rotation
files = Padrino.load_paths.map { |path| Dir["#{path}/**/*.rb"] }.flatten files_for_rotation.uniq.map do |file|
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 do |file|
file = File.expand_path(file) file = File.expand_path(file)
next if Padrino::Reloader.exclude.any? { |base| file.index(base) == 0 } || !File.exist?(file) next if Padrino::Reloader.exclude.any? { |base| file.index(base) == 0 } || !File.exist?(file)
yield file, File.mtime(file) yield file, File.mtime(file)
end.compact end.compact
end end
##
# Creates an array of paths for use in #rotation
#
def files_for_rotation
files = Padrino.load_paths.map { |path| Dir["#{path}/**/*.rb"] }.flatten
files = files | Padrino.mounted_apps.map { |app| app.app_file }
files = files | Padrino.mounted_apps.map { |app| app.app_obj.dependencies }.flatten
end
end # self end # self
## ##

View file

@ -4,8 +4,8 @@ module Padrino
# thin, mongrel, or webrick in that order. # thin, mongrel, or webrick in that order.
# #
# @example # @example
# Padrino.run! # with these defaults => host: "localhost", port: "3000", adapter: the first found # Padrino.run! # with these defaults => host: "127.0.0.1", port: "3000", adapter: the first found
# Padrino.run!("localhost", "4000", "mongrel") # use => host: "0.0.0.0", port: "3000", adapter: "mongrel" # Padrino.run!("0.0.0.0", "4000", "mongrel") # use => host: "0.0.0.0", port: "4000", adapter: "mongrel"
# #
def self.run!(options={}) def self.run!(options={})
Padrino.load! Padrino.load!
@ -17,17 +17,17 @@ module Padrino
# #
class Server < Rack::Server class Server < Rack::Server
# Server Handlers # Server Handlers
Handlers = [:thin, :mongrel, :trinidad, :webrick] Handlers = [:thin, :puma, :mongrel, :trinidad, :webrick]
# Starts the application on the available server with specified options. # Starts the application on the available server with specified options.
def self.start(app, opts={}) def self.start(app, opts={})
options = {}.merge(opts) # We use a standard hash instead of Thor::CoreExt::HashWithIndifferentAccess options = {}.merge(opts) # We use a standard hash instead of Thor::CoreExt::HashWithIndifferentAccess
options.symbolize_keys! options.symbolize_keys!
options[:Host] = options.delete(:host) || '0.0.0.0' options[:Host] = options.delete(:host) || '127.0.0.1'
options[:Port] = options.delete(:port) || 3000 options[:Port] = options.delete(:port) || 3000
options[:AccessLog] = [] options[:AccessLog] = []
if options[:daemonize] if options[:daemonize]
options[:pid] = options[:pid].blank? ? File.expand_path('tmp/pids/server.pid') : opts[:pid] options[:pid] = File.expand_path(options[:pid].blank? ? 'tmp/pids/server.pid' : opts[:pid])
FileUtils.mkdir_p(File.dirname(options[:pid])) FileUtils.mkdir_p(File.dirname(options[:pid]))
end end
options[:server] = detect_rack_handler if options[:server].blank? options[:server] = detect_rack_handler if options[:server].blank?

View file

@ -9,6 +9,7 @@ require 'active_support/core_ext/object/blank' # present?
require 'active_support/core_ext/array/extract_options' # extract_options require 'active_support/core_ext/array/extract_options' # extract_options
require 'active_support/inflector/methods' # constantize require 'active_support/inflector/methods' # constantize
require 'active_support/inflector/inflections' # pluralize require 'active_support/inflector/inflections' # pluralize
require 'active_support/core_ext/string/output_safety' # SafeBuffer and html_safe
require 'active_support/inflections' # load default inflections require 'active_support/inflections' # load default inflections
require 'yaml' unless defined?(YAML) # load yaml for i18n require 'yaml' unless defined?(YAML) # load yaml for i18n
require 'win32console' if RUBY_PLATFORM =~ /(win|m)32/ # ruby color support for win require 'win32console' if RUBY_PLATFORM =~ /(win|m)32/ # ruby color support for win
@ -110,13 +111,49 @@ end
module ObjectSpace module ObjectSpace
class << self class << self
##
# Returns all the classes in the object space. # Returns all the classes in the object space.
def classes # Optionally, a block can be passed, for example the following code
ObjectSpace.each_object(Module).select do |klass| # would return the classes that start with the character "A":
# Why? Ruby, when you remove a costant dosen't remove it from #
# rb_tables, this mean that here we can find classes that was # ObjectSpace.classes do |klass|
# removed. # if klass.to_s[0] == "A"
klass.name rescue false # klass
# end
# end
#
def classes(&block)
rs = Set.new
ObjectSpace.each_object(Class).each do |klass|
if block
if r = block.call(klass)
# add the returned value if the block returns something
rs << r
end
else
rs << klass
end
end
rs
end
##
# Returns a list of existing classes that are not included in "snapshot"
# This method is useful to get the list of new classes that were loaded
# after an event like requiring a file.
# Usage:
#
# snapshot = ObjectSpace.classes
# # require a file
# ObjectSpace.new_classes(snapshot)
#
def new_classes(snapshot)
self.classes do |klass|
if !snapshot.include?(klass)
klass
end
end end
end end
end end
@ -205,3 +242,19 @@ I18n.load_path += Dir["#{File.dirname(__FILE__)}/locale/*.yml"] if defined?(I18n
# Used to determine if this file has already been required # Used to determine if this file has already been required
# #
module SupportLite; end module SupportLite; end
module Padrino
class Utils
###
# Silences output verbosity level so load
# errors are not visible when safe_load(file)
#
def self.silence_output
@verbosity_level, $-v = $-v, nil
end
def self.unsilence_output
$-v = @verbosity_level
end
end
end

View file

@ -6,7 +6,7 @@
# #
module Padrino module Padrino
# The version constant for the current version of Padrino. # The version constant for the current version of Padrino.
VERSION = '0.10.7' unless defined?(Padrino::VERSION) VERSION = '0.11.2' unless defined?(Padrino::VERSION)
# #
# The current Padrino version. # The current Padrino version.

View file

@ -30,9 +30,14 @@ Gem::Specification.new do |s|
# s.post_install_message << " as shown here:\e[0m https://gist.github.com/1d36a35794dbbd664ea4" # 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.post_install_message << "\n\e[32m" + ("*" * 20) + "\n\e[0m"
s.add_dependency("tilt", "~> 1.3.0") s.add_dependency("tilt", "~> 1.3.7")
s.add_dependency("sinatra", "~> 1.3.1") if ENV["SINATRA_EDGE"]
s.add_dependency("http_router", "~> 0.10.2") s.add_dependency("sinatra")
s.add_dependency("thor", "~> 0.15.2") else
s.add_dependency("activesupport", "~> 3.2.0") s.add_dependency("sinatra", "~> 1.4.2")
end
s.add_dependency("http_router", "~> 0.11.0")
s.add_dependency("thor", "~> 0.17.0")
s.add_dependency("activesupport", ">= 3.1.0")
s.add_dependency("rack-protection", ">= 1.5.0")
end end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
gem 'minitest' gem 'minitest'
require 'minitest/spec'
require 'minitest/autorun' require 'minitest/autorun'
require 'mocha' # Load mocha after minitest require 'minitest/spec'
require 'mocha/setup'
begin begin
require 'ruby-debug' require 'ruby-debug'

View file

@ -1,17 +1,13 @@
require File.expand_path(File.dirname(__FILE__) + '/helper') require File.expand_path(File.dirname(__FILE__) + '/helper')
require 'haml'
class PadrinoPristine < Padrino::Application; end class PadrinoPristine < Padrino::Application; end
class PadrinoTestApp < Padrino::Application; end class PadrinoTestApp < Padrino::Application; end
class PadrinoTestApp2 < Padrino::Application; end class PadrinoTestApp2 < Padrino::Application; end
describe "Application" do describe "Application" do
def setup before { Padrino.clear! }
Padrino.clear! after { remove_views }
end
def teardown
remove_views
end
context 'for application functionality' do context 'for application functionality' do
@ -19,7 +15,7 @@ describe "Application" do
assert File.identical?(__FILE__, PadrinoPristine.app_file) assert File.identical?(__FILE__, PadrinoPristine.app_file)
assert_equal :padrino_pristine, PadrinoPristine.app_name assert_equal :padrino_pristine, PadrinoPristine.app_name
assert_equal :test, PadrinoPristine.environment assert_equal :test, PadrinoPristine.environment
assert_equal Padrino.root("views"), PadrinoPristine.views assert_equal Padrino.root('views'), PadrinoPristine.views
assert PadrinoPristine.raise_errors assert PadrinoPristine.raise_errors
assert !PadrinoPristine.logging assert !PadrinoPristine.logging
assert !PadrinoPristine.sessions assert !PadrinoPristine.sessions
@ -29,6 +25,19 @@ describe "Application" do
assert !Padrino.configure_apps assert !Padrino.configure_apps
end end
should 'check haml options on production' do
assert defined?(Haml), 'Haml not defined'
assert_equal :test, PadrinoPristine.environment
assert !PadrinoPristine.haml[:ugly]
Padrino.stub :env, :production do
PadrinoPristine.send :default_configuration!
assert_equal :production, Padrino.env
assert_equal :production, PadrinoPristine.environment
assert PadrinoPristine.haml[:ugly]
PadrinoPristine.environment = :test
end
end
should 'check padrino specific options' do should 'check padrino specific options' do
assert !PadrinoPristine.instance_variable_get(:@_configured) assert !PadrinoPristine.instance_variable_get(:@_configured)
PadrinoPristine.send(:setup_application!) PadrinoPristine.send(:setup_application!)
@ -36,7 +45,6 @@ describe "Application" do
assert_equal 'StandardFormBuilder', PadrinoPristine.default_builder assert_equal 'StandardFormBuilder', PadrinoPristine.default_builder
assert PadrinoPristine.instance_variable_get(:@_configured) assert PadrinoPristine.instance_variable_get(:@_configured)
assert !PadrinoPristine.reload? assert !PadrinoPristine.reload?
assert !PadrinoPristine.flash
end end
should 'set global project settings' do should 'set global project settings' do
@ -48,18 +56,27 @@ describe "Application" do
assert_equal PadrinoTestApp.session_secret, PadrinoTestApp2.session_secret assert_equal PadrinoTestApp.session_secret, PadrinoTestApp2.session_secret
end end
should 'be able to configure_apps multiple times' do
Padrino.configure_apps { set :foo1, "bar" }
Padrino.configure_apps { set :foo1, "bam" }
Padrino.configure_apps { set :foo2, "baz" }
PadrinoTestApp.send(:default_configuration!)
assert_equal "bam", PadrinoTestApp.settings.foo1, "should have foo1 assigned to bam"
assert_equal "baz", PadrinoTestApp.settings.foo2, "should have foo2 assigned to baz"
end
should "have shared sessions accessible in project" do should "have shared sessions accessible in project" do
Padrino.configure_apps { enable :sessions; set :session_secret, 'secret' } Padrino.configure_apps { enable :sessions; set :session_secret, 'secret' }
Padrino.mount("PadrinoTestApp").to("/write") Padrino.mount("PadrinoTestApp").to("/write")
Padrino.mount("PadrinoTestApp2").to("/read") Padrino.mount("PadrinoTestApp2").to("/read")
PadrinoTestApp.tap { |app| app.send(:default_configuration!) PadrinoTestApp.send :default_configuration!
app.get("/") { session[:foo] = "shared" } } PadrinoTestApp.get('/') { session[:foo] = "shared" }
PadrinoTestApp2.tap { |app| app.send(:default_configuration!) PadrinoTestApp2.send(:default_configuration!)
app.get("/") { session[:foo] } } PadrinoTestApp2.get('/') { session[:foo] }
browser = Rack::Test::Session.new(Rack::MockSession.new(Padrino.application)) @app = Padrino.application
browser.get '/write' get '/write'
browser.get '/read' get '/read'
assert_equal 'shared', browser.last_response.body assert_equal 'shared', body
end end
# compare to: test_routing: allow global provides # compare to: test_routing: allow global provides

View file

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

View file

@ -275,4 +275,74 @@ describe "Filters" do
get '/foo' get '/foo'
assert_equal 'before', test assert_equal 'before', test
end end
should "call before filters only once" do
once = ''
mock_app do
error 500 do
'error 500'
end
before do
once += 'before'
end
get :index do
raise Exception, 'Oops'
end
end
get '/'
assert_equal 'before', once
end
should 'catch exceptions in before filters' do
doodle = nil
mock_app do
after do
doodle = 'Been after'
end
before do
raise StandardError, "before"
end
get :index do
doodle = 'Been now'
end
error 500 do
"We broke #{env['sinatra.error'].message}"
end
end
get '/'
assert_equal 'We broke before', body
assert_equal nil, doodle
end
should 'catch exceptions in after filters if no exceptions caught before' do
doodle = ''
mock_app do
after do
doodle += ' and after'
raise StandardError, "after"
end
get :foo do
doodle = 'Been now'
raise StandardError, "now"
end
get :index do
doodle = 'Been now'
end
error 500 do
"We broke #{env['sinatra.error'].message}"
end
end
get '/foo'
assert_equal 'We broke now', body
assert_equal 'Been now', doodle
doodle = ''
get '/'
assert_equal 'We broke after', body
assert_equal 'Been now and after', doodle
end
end end

View file

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

View file

@ -152,3 +152,30 @@ describe "alternate logger: stdlib logger" do
assert_match /\e\[1m200\e\[0m OK/, @log.string assert_match /\e\[1m200\e\[0m OK/, @log.string
end end
end end
describe "options :colorize_logging" do
def access_to_mock_app
mock_app do
enable :logging
get("/"){ "Foo" }
end
get "/"
end
context 'default' do
should 'use colorize logging' do
Padrino::Logger.setup!
access_to_mock_app
assert_match /\e\[1m200\e\[0m OK/, Padrino.logger.log.string
end
end
context 'set value is false' do
should 'not use colorize logging' do
Padrino::Logger::Config[:test][:colorize_logging] = false
Padrino::Logger.setup!
access_to_mock_app
assert_match /200 OK/, Padrino.logger.log.string
end
end
end

View file

@ -113,11 +113,13 @@ describe "Mounter" do
class ::OneApp < Padrino::Application class ::OneApp < Padrino::Application
get("/test") { "test" } get("/test") { "test" }
get(:index, :provides => [:js, :json]) { "index" } get(:index, :provides => [:js, :json]) { "index" }
get(%r{/foo|/baz}) { "regexp" }
controllers :posts do controllers :posts do
get(:index) { "index" } get(:index) { "index" }
get(:new, :provides => :js) { "new" } get(:new, :provides => :js) { "new" }
get(:show, :provides => [:js, :html], :with => :id) { "show" } get(:show, :provides => [:js, :html], :with => :id) { "show" }
post(:create, :provides => :js, :with => :id) { "create" } post(:create, :provides => :js, :with => :id) { "create" }
get(:regexp, :map => %r{/foo|/baz}) { "regexp" }
end end
end end
class ::TwoApp < Padrino::Application class ::TwoApp < Padrino::Application
@ -133,9 +135,9 @@ describe "Mounter" do
Padrino.mount("one_app").to("/") Padrino.mount("one_app").to("/")
Padrino.mount("two_app").to("/two_app") Padrino.mount("two_app").to("/two_app")
assert_equal 11, Padrino.mounted_apps[0].routes.size assert_equal 15, Padrino.mounted_apps[0].routes.size
assert_equal 7, Padrino.mounted_apps[1].routes.size assert_equal 7, Padrino.mounted_apps[1].routes.size
assert_equal 5, Padrino.mounted_apps[0].named_routes.size assert_equal 6, Padrino.mounted_apps[0].named_routes.size
assert_equal 5, Padrino.mounted_apps[1].named_routes.size assert_equal 5, Padrino.mounted_apps[1].named_routes.size
first_route = Padrino.mounted_apps[0].named_routes[3] first_route = Padrino.mounted_apps[0].named_routes[3]
@ -148,6 +150,10 @@ describe "Mounter" do
assert_equal "(:users, :create)", another_route.name assert_equal "(:users, :create)", another_route.name
assert_equal "POST", another_route.verb assert_equal "POST", another_route.verb
assert_equal "/two_app/users/create", another_route.path assert_equal "/two_app/users/create", another_route.path
regexp_route = Padrino.mounted_apps[0].named_routes[5]
assert_equal "posts_regexp", regexp_route.identifier.to_s
assert_equal "(:posts, :regexp)", regexp_route.name
assert_equal "/\\/foo|\\/baz/", regexp_route.path
end end
should 'correctly instantiate a new padrino application' do should 'correctly instantiate a new padrino application' do
@ -173,5 +179,21 @@ describe "Mounter" do
assert res.ok? assert res.ok?
assert_equal File.read(__FILE__), res.body assert_equal File.read(__FILE__), res.body
end end
should "load apps from gems" do
spec_file = Padrino.root("fixtures", "app_gem", "app_gem.gemspec")
spec = Gem::Specification.load(spec_file)
spec.activate
def spec.full_gem_path
Padrino.root("fixtures", "app_gem")
end
require Padrino.root("fixtures", "app_gem", "lib", "app_gem")
Padrino.mount("AppGem::App").to("/from_gem")
mounter = Padrino.mounted_apps[0]
assert_equal AppGem::App, mounter.app_obj
assert_equal Padrino.root('public'), mounter.app_obj.public_folder
end
end end
end end

View file

@ -78,20 +78,20 @@ describe "SimpleReloader" do
last_body = body last_body = body
assert_equal 2, @app.filters[:before].size # one is ours the other is default_filter for content type 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.errors.size
assert_equal 1, @app.filters[:after].size assert_equal 2, @app.filters[:after].size # app + content-type + padrino-flash
assert_equal 0, @app.middleware.size assert_equal 0, @app.middleware.size
assert_equal 4, @app.routes.size # GET+HEAD of "/" + GET+HEAD of "/rand" = 4 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 3, @app.extensions.size # [Padrino::Routing, Padrino::Rendering, Padrino::Flash]
assert_equal 0, @app.templates.size assert_equal 0, @app.templates.size
@app.reload! @app.reload!
get "/rand" get "/rand"
assert_not_equal last_body, body 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 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.errors.size
assert_equal 1, @app.filters[:after].size assert_equal 2, @app.filters[:after].size
assert_equal 0, @app.middleware.size assert_equal 0, @app.middleware.size
assert_equal 4, @app.routes.size # GET+HEAD of "/" = 2 assert_equal 4, @app.routes.size # GET+HEAD of "/" = 2
assert_equal 2, @app.extensions.size # [Padrino::Routing, Padrino::Rendering] assert_equal 3, @app.extensions.size # [Padrino::Routing, Padrino::Rendering, Padrino::Flash]
assert_equal 0, @app.templates.size assert_equal 0, @app.templates.size
end end
end end

View file

@ -1,5 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + '/helper') require File.expand_path(File.dirname(__FILE__) + '/helper')
require 'i18n' require 'i18n'
require 'slim'
describe "Rendering" do describe "Rendering" do
def setup def setup
@ -28,7 +29,7 @@ describe "Rendering" do
"this is a <%= yield %>" "this is a <%= yield %>"
end end
get("/"){ render :erb, "sinatra layout" } get("/"){ render :erb, "sinatra layout", :layout => true }
end end
get "/" get "/"
@ -137,6 +138,7 @@ describe "Rendering" do
should 'use correct layout with each controller' do should 'use correct layout with each controller' do
create_layout :foo, "foo layout at <%= yield %>" create_layout :foo, "foo layout at <%= yield %>"
create_layout :bar, "bar layout at <%= yield %>" create_layout :bar, "bar layout at <%= yield %>"
create_layout :baz, "baz layout at <%= yield %>"
create_layout :application, "default layout at <%= yield %>" create_layout :application, "default layout at <%= yield %>"
mock_app do mock_app do
get("/"){ render :erb, "application" } get("/"){ render :erb, "application" }
@ -148,6 +150,10 @@ describe "Rendering" do
layout :bar layout :bar
get("/"){ render :erb, "bar" } get("/"){ render :erb, "bar" }
end end
controller :baz do
layout :baz
get("/"){ render :erb, "baz", :layout => true }
end
controller :none do controller :none do
get("/") { render :erb, "none" } get("/") { render :erb, "none" }
get("/with_foo_layout") { render :erb, "none with layout", :layout => :foo } get("/with_foo_layout") { render :erb, "none with layout", :layout => :foo }
@ -157,6 +163,8 @@ describe "Rendering" do
assert_equal "foo layout at foo", body assert_equal "foo layout at foo", body
get "/bar" get "/bar"
assert_equal "bar layout at bar", body assert_equal "bar layout at bar", body
get "/baz"
assert_equal "baz layout at baz", body
get "/none" get "/none"
assert_equal "default layout at none", body assert_equal "default layout at none", body
get "/none/with_foo_layout" get "/none/with_foo_layout"
@ -207,7 +215,7 @@ describe "Rendering" do
create_view :index, "<%= foo %>" create_view :index, "<%= foo %>"
mock_app do mock_app do
enable :logging enable :logging
get("/") { render "index", { :layout => true }, { :foo => "bar" } } get("/") { render "index", { :layout => nil }, { :foo => "bar" } }
end end
get "/" get "/"
assert_equal "bar", body assert_equal "bar", body
@ -396,7 +404,7 @@ describe "Rendering" do
assert_equal "Im Italian Js", body assert_equal "Im Italian Js", body
I18n.locale = :en I18n.locale = :en
get "/foo.pk" get "/foo.pk"
assert_equal 405, status assert_equal 404, status
end end
should 'resolve template content_type and locale with layout' do should 'resolve template content_type and locale with layout' do
@ -438,7 +446,7 @@ describe "Rendering" do
get "/bar.json" get "/bar.json"
assert_equal "Im a json", body assert_equal "Im a json", body
get "/bar.pk" get "/bar.pk"
assert_equal 405, status assert_equal 404, status
end end
should 'renders erb with blocks' do should 'renders erb with blocks' do
@ -457,5 +465,68 @@ describe "Rendering" do
assert ok? assert ok?
assert_equal 'THIS. IS. SPARTA!', body assert_equal 'THIS. IS. SPARTA!', body
end end
should 'render erb to a SafeBuffer' do
mock_app do
layout do
"this is a <%= yield %>"
end
get '/' do
render :erb, '<p><%= %q{<script lang="ronin">alert("https://github.com/ronin-ruby/ronin")</script>} %></p>', :layout => false
end
get '/with_layout' do
render :erb, '<span>span</span>', :layout => true
end
end
get '/'
assert ok?
assert_equal '<p>&lt;script lang=&quot;ronin&quot;&gt;alert(&quot;https://github.com/ronin-ruby/ronin&quot;)&lt;/script&gt;</p>', body
get '/with_layout'
assert ok?
assert_equal 'this is a <span>span</span>', body
end
should 'render haml to a SafeBuffer' do
mock_app do
layout do
"%p= yield"
end
get '/' do
render :haml, '%p= %s{<script lang="ronin">alert("https://github.com/ronin-ruby/ronin")</script>}', :layout => false
end
get '/with_layout' do
render :haml, "%div\n foo", :layout => true
end
end
get '/'
assert ok?
assert_equal '<p>&lt;script lang=&quot;ronin&quot;&gt;alert(&quot;https://github.com/ronin-ruby/ronin&quot;)&lt;/script&gt;</p>', body.strip
get 'with_layout'
assert ok?
assert_equal '<p><div>foo</div></p>', body.gsub(/\s+/, "")
end
should 'render slim to a SafeBuffer' do
mock_app do
layout do
"p= yield"
end
get '/' do
render :slim, 'p = %q{<script lang="ronin">alert("https://github.com/ronin-ruby/ronin")</script>}', :layout => false
end
get "/with_layout" do
render :slim, 'div foo', :layout => true
end
end
get '/'
assert ok?
assert_equal '<p>&lt;script lang=&quot;ronin&quot;&gt;alert(&quot;https://github.com/ronin-ruby/ronin&quot;)&lt;/script&gt;</p>', body.strip
get '/with_layout'
assert ok?
assert_equal '<p><div>foo</div></p>', body.strip
end
end end
end end

View file

@ -1,3 +1,4 @@
#encoding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/helper') require File.expand_path(File.dirname(__FILE__) + '/helper')
class FooError < RuntimeError; end class FooError < RuntimeError; end
@ -57,12 +58,12 @@ describe "Routing" do
should 'accept regexp routes' do should 'accept regexp routes' do
mock_app do mock_app do
get(%r{/fob|/baz}) { "regexp" } get(%r./fob|/baz.) { "regexp" }
get("/foo") { "str" } get("/foo") { "str" }
get %r{/([0-9]+)/} do |num| get %r./([0-9]+)/. do |num|
"Your lucky number: #{num} #{params[:captures].first}" "Your lucky number: #{num} #{params[:captures].first}"
end end
get /\/page\/([0-9]+)|\// do |num| get %r./page/([0-9]+)|/. do |num|
"My lucky number: #{num} #{params[:captures].first}" "My lucky number: #{num} #{params[:captures].first}"
end end
end end
@ -72,8 +73,8 @@ describe "Routing" do
assert_equal "regexp", body assert_equal "regexp", body
get "/baz" get "/baz"
assert_equal "regexp", body assert_equal "regexp", body
get "/1234/" get "/321/"
assert_equal "Your lucky number: 1234 1234", body assert_equal "Your lucky number: 321 321", body
get "/page/99" get "/page/99"
assert_equal "My lucky number: 99 99", body assert_equal "My lucky number: 99 99", body
end end
@ -100,6 +101,24 @@ describe "Routing" do
assert_equal "no access", body assert_equal "no access", body
end end
should 'parse routes that are encoded' do
mock_app do
get('/щч') { 'success!' }
end
get(URI.escape('/щч'))
assert_equal 'success!', body
end
should 'encode params using UTF-8' do
skip unless ''.respond_to?(:encoding) # for 1.8.7
mock_app do
get('/:foo') { params[:foo].encoding.name }
end
get '/bar'
assert_equal 'UTF-8', body
end
should 'match correctly similar paths' do should 'match correctly similar paths' do
mock_app do mock_app do
get("/my/:foo_id"){ params[:foo_id] } get("/my/:foo_id"){ params[:foo_id] }
@ -138,9 +157,9 @@ describe "Routing" do
post("/main"){ "hello" } post("/main"){ "hello" }
end end
assert_equal 3, app.routes.size, "should generate GET, HEAD and PUT" assert_equal 3, app.routes.size, "should generate GET, HEAD and PUT"
assert_equal ["GET"], app.routes[0].conditions[:request_method] assert_equal "GET", app.routes[0].request_methods.first
assert_equal ["HEAD"], app.routes[1].conditions[:request_method] assert_equal "HEAD", app.routes[1].request_methods.first
assert_equal ["POST"], app.routes[2].conditions[:request_method] assert_equal "POST", app.routes[2].request_methods.first
end end
should 'generate basic urls' do should 'generate basic urls' do
@ -192,13 +211,13 @@ describe "Routing" do
get "/b.js" get "/b.js"
assert_equal "/b.js", body assert_equal "/b.js", body
get "/b.ru" get "/b.ru"
assert_equal 405, status assert_equal 404, status
get "/c.js" get "/c.js"
assert_equal "/c.json", body assert_equal "/c.json", body
get "/c.json" get "/c.json"
assert_equal "/c.json", body assert_equal "/c.json", body
get "/c.ru" get "/c.ru"
assert_equal 405, status assert_equal 404, status
get "/d" get "/d"
assert_equal "/d.js?foo=bar", body assert_equal "/d.js?foo=bar", body
get "/d.js" get "/d.js"
@ -207,6 +226,14 @@ describe "Routing" do
assert_equal 404, status assert_equal 404, status
end end
should 'allow regex url with format' do
mock_app do
get(/.*/, :provides => :any) { "regexp" }
end
get "/anything"
assert_equal "regexp", body
end
should 'use padrino url method' do should 'use padrino url method' do
mock_app do mock_app do
end end
@ -255,7 +282,7 @@ describe "Routing" do
end end
get "/a.xml", {}, {} get "/a.xml", {}, {}
assert_equal 405, status assert_equal 404, status
end end
should "not set content_type to :html if Accept */* and html not in provides" do should "not set content_type to :html if Accept */* and html not in provides" do
@ -276,15 +303,6 @@ describe "Routing" do
assert_equal 'json', body assert_equal 'json', body
end end
should "set content_type to :json if render => :json" do
mock_app do
get("/foo"){ render :foo => :bar }
end
get '/foo'
assert_equal 'application/json;charset=utf-8', content_type
end
should 'set and get content_type' do should 'set and get content_type' do
mock_app do mock_app do
get("/foo"){ content_type(:json); content_type.to_s } get("/foo"){ content_type(:json); content_type.to_s }
@ -303,6 +321,7 @@ describe "Routing" do
end end
should "allow .'s in param values" do should "allow .'s in param values" do
skip
mock_app do mock_app do
get('/id/:email', :provides => [:json]) { |email, format| [email, format] * '/' } get('/id/:email', :provides => [:json]) { |email, format| [email, format] * '/' }
end end
@ -343,7 +362,7 @@ describe "Routing" do
end end
get "/a.xml", {}, {"HTTP_ACCEPT" => "text/html"} get "/a.xml", {}, {"HTTP_ACCEPT" => "text/html"}
assert_equal 405, status assert_equal 404, status
end end
should "generate routes for format simple" do should "generate routes for format simple" do
@ -371,20 +390,32 @@ describe "Routing" do
assert_equal "mini", body assert_equal "mini", body
end end
should "should inject the action name into the request" do
mock_app do
controller :posts do
get('/omnomnom(/:id)') { request.action.inspect }
controller :mini do
get([:a, :b, :c]) { request.action.inspect }
end
end
end
get "/posts/omnomnom"
assert_equal "\"/omnomnom(/:id)\"", body
get "/mini/a/b/c"
assert_equal ":a", body
end
should "support not_found" do should "support not_found" do
mock_app do mock_app do
not_found do not_found { 'whatever' }
response.status = 404
'whatever'
end
get :index, :map => "/" do get :index, :map => "/" do
'index' 'index'
end end
end end
get '/something' get '/wrong'
assert_equal 'whatever', body
assert_equal 404, status assert_equal 404, status
assert_equal 'whatever', body
get '/' get '/'
assert_equal 'index', body assert_equal 'index', body
assert_equal 200, status assert_equal 200, status
@ -393,7 +424,7 @@ describe "Routing" do
should "should inject the route into the request" do should "should inject the route into the request" do
mock_app do mock_app do
controller :posts do controller :posts do
get(:index) { request.route_obj.named.to_s } get(:index) { request.route_obj.name.to_s }
end end
end end
get "/posts" get "/posts"
@ -741,7 +772,27 @@ describe "Routing" do
assert_equal 404, status assert_equal 404, status
end end
should "match params and format" do
app = mock_app do
get '/:id', :provides => [:json, :html] do |id, _|
id
end
get 'format/:id', :provides => [:json, :html] do |id, format|
format
end
end
get '/123.html'
assert_equal '123', body
get 'format/123.html'
assert_equal 'html', body
end
should 'respect priorities' do should 'respect priorities' do
skip
route_order = [] route_order = []
mock_app do mock_app do
get(:index, :priority => :normal) { route_order << :normal; pass } get(:index, :priority => :normal) { route_order << :normal; pass }
@ -815,6 +866,55 @@ describe "Routing" do
assert_equal "show 3 1 2", body assert_equal "show 3 1 2", body
end end
should "respect parent precedence: controllers parents go before route parents" do
mock_app do
controllers :project do
get(:index, :parent => :user) { "index #{params[:user_id]}" }
end
controllers :bar, :parent => :foo do
get(:index) { "index on foo #{params[:foo_id]} @ bar" }
get(:index, :parent => :baz) { "index on foo #{params[:foo_id]} @ baz #{params[:baz_id]} @ bar" }
end
end
get "/user/1/project"
assert_equal "index 1", body
get "/foo/1/bar"
assert_equal "index on foo 1 @ bar", body
get "/foo/1/baz/2/bar"
assert_equal "index on foo 1 @ baz 2 @ bar", body
end
should "keep a reference to the parent on the route" do
mock_app do
controllers :project do
get(:index, :parent => :user) { "index #{params[:user_id]}" }
get(:index, :parent => [:user, :section]) { "index #{params[:user_id]} #{params[:section_id]}" }
get(:edit, :with => :id, :parent => :user) { "edit #{params[:id]} #{params[:user_id]}"}
get(:show, :with => :id, :parent => [:user, :product]) { "show #{params[:id]} #{params[:user_id]} #{params[:product_id]}"}
end
controllers :bar, :parent => :foo do
get(:index) { "index on foo/bar" }
get(:index, :parent => :baz) { "index on foo/baz/bar" }
end
end
# get "/user/1/project"
assert_equal :user, @app.routes[0].parent
# get "/user/1/section/3/project"
assert_equal [:user, :section], @app.routes[2].parent
# get "/user/1/project/edit/2"
assert_equal :user, @app.routes[4].parent
# get "/user/1/product/2/project/show/3"
assert_equal [:user, :product], @app.routes[6].parent
# get "/foo/1/bar"
assert_equal :foo, @app.routes[8].parent
# get "/foo/1/baz/2/bar"
assert_equal [:foo, :baz], @app.routes[10].parent
end
should "apply parent to controller" do should "apply parent to controller" do
mock_app do mock_app do
controller :project, :parent => :user do controller :project, :parent => :user do
@ -1041,6 +1141,24 @@ describe "Routing" do
assert_equal 'js', body assert_equal 'js', body
end end
should "set content_type to :html if Accept */* and provides of :any" do
mock_app do
get("/foo", :provides => :any) { content_type.to_s }
end
get '/foo', {}, { 'HTTP_ACCEPT' => '*/*' }
assert_equal 'html', body
end
should "set content_type to :js if Accept includes both application/javascript, */*;q=0.5 and provides of :any" do
mock_app do
get("/foo", :provides => :any) { content_type.to_s }
end
get '/foo', {}, { 'HTTP_ACCEPT' => 'application/javascript, */*;q=0.5' }
assert_equal 'js', body
end
should 'allows custom route-conditions to be set via route options and halt' do should 'allows custom route-conditions to be set via route options and halt' do
protector = Module.new do protector = Module.new do
def protect(*args) def protect(*args)
@ -1323,11 +1441,11 @@ describe "Routing" do
get "/.json" get "/.json"
assert_equal "This is the get index.json", body assert_equal "This is the get index.json", body
get "/.js" get "/.js"
assert_equal 405, status assert_equal 404, status
post "/.json" post "/.json"
assert_equal "This is the post index.json", body assert_equal "This is the post index.json", body
post "/.js" post "/.js"
assert_equal 405, status assert_equal 404, status
end end
should "allow controller level mapping" do should "allow controller level mapping" do
@ -1349,6 +1467,33 @@ describe "Routing" do
assert_equal "1, 2", body assert_equal "1, 2", body
end end
should "replace name of named controller with mapping path" do
mock_app do
controller :ugly, :map => "/pretty/:id" do
get(:url3) { "#{params[:id]}" }
get(:url4, :map => 'test-:id2') { "#{params[:id]}, #{params[:id2]}" }
end
controller :voldemort, :map => "" do
get(:url5) { "okay" }
end
end
url = @app.url(:ugly, :url3, :id => 1)
assert_equal "/pretty/1/url3", url
get url
assert_equal "1", body
url = @app.url(:ugly, :url4, 3, 5)
assert_equal "/pretty/3/test-5", url
get url
assert_equal "3, 5", body
url = @app.url(:voldemort, :url5)
assert_equal "/url5", url
get url
assert_equal 'okay', body
end
should 'use absolute and relative maps' do should 'use absolute and relative maps' do
mock_app do mock_app do
controller :one do controller :one do
@ -1544,8 +1689,8 @@ describe "Routing" do
end end
should "have overideable format" do should "have overideable format" do
mock_app do
::Rack::Mime::MIME_TYPES[".other"] = "text/html" ::Rack::Mime::MIME_TYPES[".other"] = "text/html"
mock_app do
before do before do
params[:format] ||= :other params[:format] ||= :other
end end
@ -1553,6 +1698,7 @@ describe "Routing" do
end end
get "/format_test" get "/format_test"
assert_equal "other", body assert_equal "other", body
::Rack::Mime::MIME_TYPES.delete('.other')
end end
should 'invokes handlers registered with ::error when raised' do should 'invokes handlers registered with ::error when raised' do
@ -1626,13 +1772,33 @@ describe "Routing" do
assert_match /not found/, body assert_match /not found/, body
end end
should 'render a custom 404 page' do should 'render a custom 404 page using not_found' do
mock_app do mock_app do
error(404) { "not found" } not_found { "custom 404 not found" }
end end
get "/" get "/"
assert_equal 404, status assert_equal 404, status
assert_match /not found/, body assert_equal "custom 404 not found", body
end
should 'render a custom error page using error method' do
skip
mock_app do
error(404) { "custom 404 error" }
end
get "/"
assert_equal 404, status
assert_equal "custom 404 error", body
end
should 'render a custom 403 page' do
mock_app do
error(403) { "custom 403 not found" }
get("/") { status 403 }
end
get "/"
assert_equal 403, status
assert_equal "custom 403 not found", body
end end
should 'recognize paths' do should 'recognize paths' do
@ -1692,4 +1858,12 @@ describe "Routing" do
get @app.url(:index, :page => 10) get @app.url(:index, :page => 10)
assert_equal "/paginate/66", body assert_equal "/paginate/66", body
end end
should 'not route get :users, :with => :id to /users//' do
mock_app do
get(:users, :with => :id) { 'boo' }
end
get '/users//'
assert_equal 404, status
end
end end

View file

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

View file

@ -1,9 +0,0 @@
==content_tag :p, "Test 1", :class => 'test', :id => "test1"
== content_tag :p, "Test 2"
==content_tag(:p, :class => 'test', :id => 'test3') do
span Test 3
==content_tag(:p) do
span Test 4

View file

@ -1,15 +0,0 @@
- @user = MarkupUser.new
== form_for @user , '/demo1', :id => 'demo-fields-for' do |f|
== f.text_field :gender
== fields_for @user.permission do |permission|
== permission.check_box :can_edit
== permission.check_box :can_delete
== f.fields_for :telephone do |child_form|
== child_form.label :number
== child_form.text_field :number
== f.fields_for :addresses do |child_form|
== child_form.label :name
== child_form.text_field :name
- unless child_form.object.new_record?
== child_form.check_box '_destroy'
== child_form.label '_destroy', :caption => 'Remove'

View file

@ -1,59 +0,0 @@
== form_for MarkupUser.new, '/demo', :id => 'demo' do |f|
== f.error_messages(:header_message => "custom MarkupUser cannot be saved!")
== f.hidden_field :session_id
p
== f.label :username, :caption => "Login: ", :class => 'user-label'
== f.text_field :username, :class => 'user-text', :value => "John"
p
== f.label :password
== f.password_field :password, :class => 'user-password', :value => "secret"
p
== f.label :age
== f.number_field :age, :class => 'numeric'
p
== f.label :telephone
== f.telephone_field :telephone, :class => 'numeric'
p
== f.label :email, :caption => 'Email Address: '
== f.email_field :email, :class => 'string'
p
== f.label :webpage, :caption => 'Your Web Page: '
== f.url_field :webpage, :class => 'string'
p
== f.label :search
== f.search_field :search, :class => 'string'
p
== f.label :photo
== f.file_field :photo, :class => 'user-photo'
p
== f.label :about, :caption => "About Me: "
== f.text_area :about, :class => 'user-about'
p
== f.label :gender, :caption => "Your gender: "
== f.radio_button :gender, :value => 'male'
== f.radio_button :gender, :value => 'female'
p
== f.label :country, :caption => "Your country"
== f.select :country, :options => ['USA', 'Canada', 'Mexico'], :selected => 'USA', :class => 'selector'
p
== f.label :remember_me
== f.check_box :remember_me, :value => "1"
p
== f.submit "Create", :class => 'success', :id => 'demo-button'
p
== f.image_submit "buttons/post.png", :class => 'success', :id => 'image-button'
== form_for MarkupUser.new, '/another_demo', :id => 'demo2', :method => 'get' do |f|
== f.error_messages :header_message => "custom MarkupUser cannot be saved!"
== f.hidden_field :session_id
== f.text_field_block :username, { :class => 'input' }, { :caption => 'Nickname: ', :class => 'label' }
== f.password_field_block :code, { :class => 'input' }
== f.text_area_block :about, { :class => 'textarea' }
== f.file_field_block :photo, { :class => 'upload' }
== f.check_box_block :remember_me, { :class => 'checker' }
== f.select_block :state, :options => ['California', 'Texas'], :class => 'selector'
== f.submit_block "Create", { :class => 'button' }
== f.image_submit_block "buttons/ok.png", { :class => 'image' }
== form_for :markup_user, '/third_demo', :id => 'demo3', :method => 'get' do |f|
== f.text_field_block :username

View file

@ -1,70 +0,0 @@
== form_tag '/simple', :class => 'simple-form' do
== error_messages_for nil
== field_set_tag do
== hidden_field_tag :session_id, :value => "__secret__"
== label_tag :username
== text_field_tag :username
== label_tag :password
== password_field_tag :password
== label_tag :email
== email_field_tag :email
== label_tag :age
== number_field_tag :age
== label_tag :telephone
== telephone_field_tag :telephone
== label_tag :webpage
== url_field_tag :webpage
== label_tag :search
== search_field_tag :search
== label_tag :color
== select_tag :color, :options => ['green', 'orange', 'purple']
== label_tag :gender
== radio_button_tag :gender, :value => 'male'
== radio_button_tag :gender, :value => 'female'
== check_box_tag :remember_me
== submit_tag
== form_tag '/advanced', :id => 'advanced', :class => 'advanced-form', :method => 'get' do
== error_messages_for MarkupUser.new, :header_message => "There are problems with saving user!"
== hidden_field_tag :session_id, :value => "__secret__"
== field_set_tag "Advanced", :class => 'advanced-field-set' do
p
== label_tag :username, :class => 'first', :caption => "Nickname"
== text_field_tag :username, :value => params[:username], :id => 'the_username'
p
== label_tag :password, :class => 'first'
== password_field_tag :password, :value => params[:password]
p
== label_tag :email, :caption => 'Email Address'
== email_field_tag :email, :class => 'string'
p
== label_tag :age, :class => 'age'
== number_field_tag :age, :class => 'numeric'
p
== label_tag :telephone, :class => 'telephone'
== telephone_field_tag :telephone, :class => 'numeric'
p
== label_tag :webpage, :caption => 'Your Home Page'
== url_field_tag :webpage, :class => 'string'
p
== label_tag :search
== search_field_tag :search, :class => 'string'
p
== label_tag :about, :class => 'about', :caption => "About Me"
== text_area_tag :about, :class => 'large'
p
== label_tag :gender, :class => 'gender'
== radio_button_tag :gender, :value => 'male', :checked => true
== radio_button_tag :gender, :value => 'female'
p
== label_tag :photo, :class => 'photo'
== file_field_tag :photo, :class => 'upload'
p
== label_tag :fav_color
== select_tag :fav_color, :options => [ ['green', '1'], ['orange', '2'], ['purple', '3'] ], :selected => '2'
p
== check_box_tag :remember_me, :value => "1", :checked => true
== field_set_tag(:class => 'buttons') do
== submit_tag "Login"
== button_tag "Cancel"
== image_submit_tag "buttons/submit.png"

View file

@ -1,4 +0,0 @@
== link_to "Test 1 No Block", '/test1', :class => 'test', :id => 'test1'
== link_to("/test2", :class => 'test', :id => 'test2') do
span Test 2 With Block

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