diff --git a/CHANGES b/CHANGES index 4ae1f3f8e..837edcb4e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,31 @@ +1.3003 06.02.2011 + + [ API CHANGES ] + * Remove load_plugin from the core's DSL (was deprecated). + + [ BUG FIXES ] + * Eliminate test warnings on Windows. + (Gabor Szabo) + * GH#271 - use correct VERSION_FROM in scaffolded application. + (Sawyer X) + * GH#260 - send_file doesn't clobber existing headers + (Alexis Sukrieh) + * logger unicode bugfix in the formated date + (jahmed) + * GH#281 - Don't crash if splat is used with no captures + (David Precious) + * Possible to given "template" a view name with the extenstion. + (Alexis Sukrieh) + + [ ENHANCEMENTS ] + * New setting log_path to allow for alternalte logging path in logger + "file". + (Natal Ngétal) + * GH#289: Add more aliases on the ENV, provide more smart accessors to Plack env + entries + (Franck Cuny) + + Dancer 1.3002 [ API CHANGES ] @@ -10,6 +38,9 @@ Dancer 1.3002 * status is kept even when halt is used in a before filter (Alexis Sukrieh) + * Proper handling of temporary file creation using File::Temp module + instead of homebrew solution. (jahmed) + * Logger::Abstract unicode bug fix. (jahmed) [ ENHANCEMENTS ] diff --git a/MANIFEST b/MANIFEST index 98faebb7f..06fd01df4 100644 --- a/MANIFEST +++ b/MANIFEST @@ -209,6 +209,7 @@ t/10_template/03_simple.t t/10_template/05_template_toolkit.t t/10_template/06_before_template_hook.t t/10_template/index.txt +t/10_template/template.t t/10_template/views/index.tt t/11_logger/000_create_fake_env.t t/11_logger/01_abstract.t @@ -263,6 +264,7 @@ t/18_main_dsl/000_create_fake_env.t t/18_main_dsl/01_config.t t/18_main_dsl/uri_for.t t/19_dancer/01_script.t +t/19_dancer/02_script_version_from.t t/lib/EasyMocker.pm t/lib/Forum.pm t/lib/LinkBlocker.pm diff --git a/lib/Dancer.pm b/lib/Dancer.pm index d8f56173f..061184c8f 100644 --- a/lib/Dancer.pm +++ b/lib/Dancer.pm @@ -7,7 +7,7 @@ use Cwd 'abs_path', 'realpath'; use vars qw($VERSION $AUTHORITY @EXPORT); -$VERSION = '1.3002'; +$VERSION = '1.3003'; $AUTHORITY = 'SUKRIA'; use Dancer::Config; @@ -61,7 +61,6 @@ use base 'Exporter'; layout load load_app - load_plugin logger mime_type options @@ -174,7 +173,10 @@ sub session { : Dancer::Session->write(@_); } } -sub splat { @{Dancer::SharedData->request->params->{splat}} } +sub splat { + my $splat = Dancer::SharedData->request->params->{splat}; + return ref $splat ? @$splat : (); +} sub status { Dancer::Response->status(@_) } sub template { Dancer::Helpers::template(@_) } sub true {1} @@ -211,10 +213,6 @@ sub load_app { Dancer::App->set_running_app('main'); } -sub load_plugin { - goto &Dancer::Plugin::load_plugin; -} - # When importing the package, strict and warnings pragma are loaded, # and the appdir detection is performed. sub import { @@ -612,16 +610,6 @@ Note that a package loaded using load_app B import Dancer with the C<:syntax> option, in order not to change the application directory (which has been previously set for the caller script). -=head2 load_plugin - -Loads a plugin in the current namespace. As with load_app, the method takes -care to set the libdir to the current C<./lib> directory: - - package MyWebApp; - use Dancer; - - load_plugin 'Dancer::Plugin::Database'; - =head2 mime_type Returns all the user-defined mime-types when called without parameters. diff --git a/lib/Dancer/Logger/Abstract.pm b/lib/Dancer/Logger/Abstract.pm index 58f93e664..f6fa4d952 100644 --- a/lib/Dancer/Logger/Abstract.pm +++ b/lib/Dancer/Logger/Abstract.pm @@ -90,7 +90,7 @@ sub format_message { ? $r->env->{'HTTP_X_REAL_IP'} || $r->env->{'REMOTE_ADDR'} : '-'; }, - t => sub { POSIX::strftime( "%d/%b/%Y %H:%M:%S", localtime ) }, + t => sub { Encode::decode(setting('charset'), POSIX::strftime( "%d/%b/%Y %H:%M:%S", localtime )) }, P => sub { $$ }, L => sub { $level }, D => sub { diff --git a/lib/Dancer/Logger/File.pm b/lib/Dancer/Logger/File.pm index 880293f57..eb0220bd7 100644 --- a/lib/Dancer/Logger/File.pm +++ b/lib/Dancer/Logger/File.pm @@ -11,8 +11,9 @@ use IO::File; sub logdir { my $appdir = setting('appdir'); + my $altpath = setting('log_path'); my $logroot = $appdir || File::Spec->tmpdir(); - return path($logroot, 'logs'); + return ($altpath ? $altpath : path($logroot, 'logs')); } sub init { @@ -72,7 +73,9 @@ file. =head2 logdir Returns the log directory, decided by "logs" either in "appdir" setting or in a -temp directory. +temp directory. It's also possible to specify a logs directory with the log_path option. + + setting log_path => $dir; =head2 _log diff --git a/lib/Dancer/Renderer.pm b/lib/Dancer/Renderer.pm index 1a309a07a..e02f35ca4 100644 --- a/lib/Dancer/Renderer.pm +++ b/lib/Dancer/Renderer.pm @@ -164,11 +164,11 @@ sub get_file_response_for_path { my $fh = open_file('<', $static_file); binmode $fh; - return Dancer::Response->new( - status => $status, - headers => ['Content-Type' => get_mime_type($static_file)], - content => $fh - ); + Dancer::Response->status($status); + Dancer::Response->content_type( + get_mime_type($static_file)); + Dancer::Response::set_current_content($fh); + return Dancer::Response->current; } return; } diff --git a/lib/Dancer/Request.pm b/lib/Dancer/Request.pm index 6835b9d8b..0fdaa85c1 100644 --- a/lib/Dancer/Request.pm +++ b/lib/Dancer/Request.pm @@ -25,7 +25,7 @@ Dancer::Request->attributes( # query 'env', 'path', 'method', 'content_type', 'content_length', - 'body', 'id', 'request_uri', + 'body', 'id', 'uploads', 'headers', 'path_info', 'ajax', @http_env_keys, @@ -33,8 +33,18 @@ Dancer::Request->attributes( # aliases sub agent { $_[0]->user_agent } -sub remote_address { $_[0]->{env}->{'REMOTE_ADDR'} } -sub forwarded_for_address { $_[0]->{env}->{'X_FORWARDED_FOR'} } +sub remote_address { $_[0]->address } +sub forwarded_for_address { $_[0]->env->{'X_FORWARDED_FOR'} } +sub address { $_[0]->env->{REMOTE_ADDR} } +sub remote_host { $_[0]->env->{REMOTE_HOST} } +sub protocol { $_[0]->env->{SERVER_PROTOCOL} } +sub port { $_[0]->env->{SERVER_PORT} } +sub request_uri { $_[0]->env->{REQUEST_URI} } +sub user { $_[0]->env->{REMOTE_USER} } +sub script_name { $_[0]->env->{SCRIPT_NAME} } +sub scheme { $_[0]->env->{'psgi.url_scheme'} } +sub secure { $_[0]->scheme eq 'https' } + sub is_head { $_[0]->{method} eq 'HEAD' } sub is_post { $_[0]->{method} eq 'POST' } sub is_get { $_[0]->{method} eq 'GET' } @@ -45,7 +55,7 @@ sub header { $_[0]->{headers}->header($_[1]) } # public interface compat with CGI.pm objects sub request_method { method(@_) } sub Vars { params(@_) } -sub input_handle { $_[0]->{env}->{'psgi.input'} || $_[0]->{env}->{'PSGI.INPUT'} } +sub input_handle { $_[0]->env->{'psgi.input'} || $_[0]->env->{'PSGI.INPUT'} } sub new { my ($class, $env) = @_; @@ -102,7 +112,7 @@ sub base { SERVER_NAME HTTP_HOST SERVER_PORT SCRIPT_NAME psgi.url_scheme ); - my ($server, $host, $port, $path, $scheme) = @{$self->{env}}{@env_names}; + my ($server, $host, $port, $path, $scheme) = @{$self->env}{@env_names}; $scheme ||= $self->{'env'}{'PSGI.URL_SCHEME'}; # Windows @@ -212,8 +222,8 @@ sub _init { $self->_build_headers(); $self->_build_request_env(); $self->_build_path() unless $self->path; - $self->_build_method() unless $self->method; $self->_build_path_info() unless $self->path_info; + $self->_build_method() unless $self->method; $self->{_http_body} = HTTP::Body->new($self->content_type, $self->content_length); @@ -250,17 +260,17 @@ sub _build_request_env { # Don't refactor that, it's called whenever a request object is needed, that # means at least once per request. If refactored in a loop, this will cost 4 # times more than the following static map. - $self->{user_agent} = $self->{env}{HTTP_USER_AGENT}; - $self->{host} = $self->{env}{HTTP_HOST}; - $self->{accept_language} = $self->{env}{HTTP_ACCEPT_LANGUAGE}; - $self->{accept_charset} = $self->{env}{HTTP_ACCEPT_CHARSET}; - $self->{accept_encoding} = $self->{env}{HTTP_ACCEPT_ENCODING}; - $self->{keep_alive} = $self->{env}{HTTP_KEEP_ALIVE}; - $self->{connection} = $self->{env}{HTTP_CONNECTION}; - $self->{accept} = $self->{env}{HTTP_ACCEPT}; - $self->{accept_type} = $self->{env}{HTTP_ACCEPT_TYPE}; - $self->{referer} = $self->{env}{HTTP_REFERER}; - $self->{x_requested_with} = $self->{env}{HTTP_X_REQUESTED_WITH}; + $self->{user_agent} = $self->env->{HTTP_USER_AGENT}; + $self->{host} = $self->env->{HTTP_HOST}; + $self->{accept_language} = $self->env->{HTTP_ACCEPT_LANGUAGE}; + $self->{accept_charset} = $self->env->{HTTP_ACCEPT_CHARSET}; + $self->{accept_encoding} = $self->env->{HTTP_ACCEPT_ENCODING}; + $self->{keep_alive} = $self->env->{HTTP_KEEP_ALIVE}; + $self->{connection} = $self->env->{HTTP_CONNECTION}; + $self->{accept} = $self->env->{HTTP_ACCEPT}; + $self->{accept_type} = $self->env->{HTTP_ACCEPT_TYPE}; + $self->{referer} = $self->env->{HTTP_REFERER}; + $self->{x_requested_with} = $self->env->{HTTP_X_REQUESTED_WITH}; } sub _build_headers { @@ -294,16 +304,13 @@ sub _build_path { my ($self) = @_; my $path = ""; - $path .= $self->{env}{'SCRIPT_NAME'} - if defined $self->{env}->{'SCRIPT_NAME'}; - $path .= $self->{env}->{'PATH_INFO'} - if defined $self->{env}->{'PATH_INFO'}; + $path .= $self->script_name if defined $self->script_name; + $path .= $self->env->{PATH_INFO} if defined $self->env->{PATH_INFO}; # fallback to REQUEST_URI if nothing found # we have to decode it, according to PSGI specs. - if (defined $self->{env}->{REQUEST_URI}) { - $self->{request_uri} = $self->{env}->{REQUEST_URI}; - $path ||= $self->_url_decode($self->{request_uri}); + if (defined $self->request_uri) { + $path ||= $self->_url_decode($self->request_uri); } croak "Cannot resolve path" if not $path; @@ -312,7 +319,7 @@ sub _build_path { sub _build_path_info { my ($self) = @_; - my $info = $self->{env}->{'PATH_INFO'}; + my $info = $self->env->{PATH_INFO}; if (defined $info) { # Empty path info will be interpreted as "root". @@ -326,7 +333,7 @@ sub _build_path_info { sub _build_method { my ($self) = @_; - $self->{method} = $self->{env}->{REQUEST_METHOD} + $self->{method} = $self->env->{REQUEST_METHOD} || $self->{request}->request_method(); } @@ -351,7 +358,7 @@ sub _parse_get_params { return $self->{_query_params} if defined $self->{_query_params}; $self->{_query_params} = {}; - my $source = $self->{env}{QUERY_STRING} || ''; + my $source = $self->env->{QUERY_STRING} || ''; foreach my $token (split /[&;]/, $source) { my ($key, $val) = split(/=/, $token); next unless defined $key; @@ -402,7 +409,7 @@ sub _has_something_to_read { # taken from Miyagawa's Plack::Request::BodyParser sub _read { my ($self,) = @_; - my $remaining = $self->{env}->{CONTENT_LENGTH} - $self->{_read_position}; + my $remaining = $self->content_length - $self->{_read_position}; my $maxlength = $self->{_chunk_size}; return if ($remaining <= 0); @@ -509,6 +516,42 @@ While this method returns the method string as provided by the environment, it's better to use one of the following boolean accessors if you want to inspect the requested method. +=head2 address() + +Return the IP address of the client. + +=head2 remote_host() + +Return the remote host of the client. + +=head2 protocol() + +Return the protocol (HTTP/1.0 or HTTP/1.1) used for the request. + +=head2 port() + +Return the port of the server. + +=head2 request_uri + +Return the raw, undecoded request URI path. + +=head2 user + +Return remote user if defined. + +=head2 script_name + +Return script_name from the environment. + +=head2 scheme + +Return the scheme of the request + +=head2 secure + +Return true of false, indicating wether the connection is secure + =head2 is_get() Return true if the method requested by the client is 'GET' diff --git a/lib/Dancer/Response.pm b/lib/Dancer/Response.pm index 132c439ac..b4d9037bb 100644 --- a/lib/Dancer/Response.pm +++ b/lib/Dancer/Response.pm @@ -67,6 +67,12 @@ sub set { $CURRENT = shift; } +# FIXME this whole class needs to be rewritten +sub set_current_content { + $CURRENT->{content} = $_[0] + if defined $CURRENT; +} + sub content { my $current = _get_object(shift); my $content = shift; @@ -144,6 +150,7 @@ sub halted { $current && $current->{halted} } + sub header { my $current = _get_object(shift); my $header = shift; diff --git a/lib/Dancer/Session/YAML.pm b/lib/Dancer/Session/YAML.pm index 009acc380..cfd3ec82e 100644 --- a/lib/Dancer/Session/YAML.pm +++ b/lib/Dancer/Session/YAML.pm @@ -10,6 +10,7 @@ use Dancer::ModuleLoader; use Dancer::Config 'setting'; use Dancer::FileUtils qw(path open_file); use File::Copy; +use File::Temp qw(tempfile); # static @@ -72,10 +73,11 @@ sub destroy { sub flush { my $self = shift; - my $sessionfh = open_file('>', tmp_yaml_file($self->id)); - print {$sessionfh} YAML::Dump($self); - close $sessionfh; - move(tmp_yaml_file($self->id), yaml_file($self->id)); + my ( $fh, $tmpname ) = + tempfile( $self->id . '.XXXXXXXX', DIR => setting('session_dir') ); + print {$fh} YAML::Dump($self); + close $fh; + move($tmpname, yaml_file($self->id)); return $self; } diff --git a/lib/Dancer/Template/Abstract.pm b/lib/Dancer/Template/Abstract.pm index 4c0105013..f0f585520 100644 --- a/lib/Dancer/Template/Abstract.pm +++ b/lib/Dancer/Template/Abstract.pm @@ -16,7 +16,8 @@ sub default_tmpl_ext { "tt" } sub _template_name { my ( $self, $view ) = @_; my $def_tmpl_ext = $self->config->{extension} || $self->default_tmpl_ext(); - $view .= ".$def_tmpl_ext" if $view !~ /\.${def_tmpl_ext}$/; + $view .= ".$def_tmpl_ext" if $view !~ /\.\Q$def_tmpl_ext\E$/; + return $view; } sub view { @@ -129,13 +130,14 @@ The base class provides a plain init() method that only returns true. =item B Template class that inherits this class should override this method to return a default template -extension, example: for Template::Toolkit it returns "tt" and for HTML::Mason it returns "mason". +extension, example: for Template::Toolkit it returns "tt" and for HTML::Mason it returns "mason". So when you call C