Permalink
Browse files

Added named params to get-opts(), finished List and Date modifiers.

  • Loading branch information...
1 parent 8caffee commit 1ebcb20187144d25dbe70b7e822ca00d0746fecb @supernovus committed Oct 10, 2010
Showing with 254 additions and 42 deletions.
  1. +28 −20 README
  2. +38 −12 lib/Flower.pm
  3. +93 −8 lib/Flower/Utils/Date.pm
  4. +30 −2 lib/Flower/Utils/List.pm
  5. +65 −0 t/06-utils-date.t
View
48 README
@@ -64,20 +64,28 @@ Zope or PHPTAL.
Inspired by Petal::Utils, Flower includes a namespace called Flower::Utils::
If you load modules in the Petal::Utils:: namespace, it will add additional
-modifiers. The following sets are planned for inclusion (at the current time
-there are only a couple available, with a subset of the modifiers):
+modifiers. The following sets are currently included with Flower:
- Text, same as the :text set from Petal::Utils
- - Logic**, same as the :logic set from Petal::Utils
- - List*, same as the :list set from Petal::Utils, plus group: which
- turns a flat Array into a nested Array, where each inner Array has a
- set number of elements in it.
- - Hash**, same as the :hash set from Petal::Utils
- - Date**, similar to the :date set from Petal::Utils, using on DateTime::Utils
- Not exactly the same, it offers strftime: and rfc: and works on
- DateTime and Date objects in addition to epoch timestamps.
- If you pass a DateTime or Date object without one of these filters you
- will get its standard stringification form (ISO by default.)
+ lc: make the string lowercase.
+ uc: make the string uppercase.
+ ucfirst: make the first letter of the string uppercase.
+ substr: extract a portion of the string.
+ printf: format a string or object in a specified way.
+ - List, similar to the :list set from Petal::Utils.
+ group: turns a flat Array into a nested Array, where each inner
+ Array has a set number of elements in it.
+ sort: sort the array using the default sort algorithm.
+ limit: only return the first number of items from the list.
+ shuffle: shuffle the list into a random order.
+ pick: pick a set number of items randomly from the list.
+ reverse: reverse the contents of the list.
+ - Date, similar to the :date set from Petal::Utils, using on DateTime::Utils
+ date: Builds a DateTime object using specified paramters.
+ time: Builds a DateTime object from a Unix epoch.
+ strftime: Displays a date/time string in a specified format.
+ rfc: A modifier specifically for use with strftime, as the format.
+ now: A modifier specifically for use with strftime, as the object.
- Perl, makes a 'perl:' modifier available, which is the counterpart
to Zope's 'python:' or PHPTAL's 'php:' modifiers.
There are some extra, more dangerous modifiers included, but they
@@ -86,16 +94,16 @@ there are only a couple available, with a subset of the modifiers):
dump: modifier spits out the .perl representation of the object.
what: modifier spits out the class name of the object.
-Sets with no marks are fully implemented and included with Flower.
-Sets marked with * have been partially implemented and are included with
-the current version of Flower. Sets marked with ** are not yet started, and
-therefore aren't included with Flower.
+In addition, the following sets are planned for inclusion very soon:
+
+ - Logic, similar to the :logic set from Petal::Utils
+ - Hash, same as the :hash set from Petal::Utils
The syntax for the Flower::Utils methods is based on the modifiers from
-Petal::Utils, but extended to use the Flower-specific get-opts method, includes
-non whitespace, strings wrapped in 'quotes' and nested queries wrapped in
-((special markers)). The get-opts method is also used when passing parameters
-to object method calls in queries themselves.
+Petal::Utils, but extended to use the Flower-specific get-opts method (the
+same method that is used to parse method call parameters in queries.)
+As is obvious, the syntax is not always the same, and not all of the
+modifiers are the same.
Full documentation for the usage of Flower and the Flower::Utils modifiers
will be included in the docs/ folder in an upcoming release, until then
View
@@ -330,24 +330,50 @@ method process-query($data is copy, :$forcexml, :$noxml, :$noescape, :$bool) {
return $data;
}
-## get-args now supports parameters in the form of ((param name)) for
-## when you have queries with spaces in them that shouldn't be treated
+## get-args now supports parameters in the form of {{param name}} for
+## when you have nested queries with spaces in them that shouldn't be treated
## as strings, like 'a string' does.
-method get-args($string, :$query, *@defaults) {
- my @result = $string.comb(/ [ \'.*?\' | '(('.*?'))' | \S+ ] /);
- @result>>.=subst(/^'(('/, '');
- @result>>.=subst(/'))'$/, '');
+## It also supports named parameters in the form of :param(value).
+## If the :query option is set, all found parameters will be looked up using
+## the query() method (with default options.)
+## If you specify the :named option, it will always include the %named
+## parameter, even if it's empty.
+method get-args($string, :$query, :$named, *@defaults) {
+ my @result =
+ $string.comb(/ [ '{{'.*?'}}' | ':'\w+'('.*?')' | \'.*?\' | \S+ ] /);
+ @result>>.=subst(/^'{{'/, '');
+ @result>>.=subst(/'}}'$/, '');
+ my %named;
+ ## Our nice for loop has been replaced now that we support named
+ ## parameters. Oh well, such is life.
+ loop (my $i=0; $i < @result.elems; $i++) {
+ my $param = @result[$i];
+ if $param ~~ /^ ':' (\w+) '(' (.*?) ')' $/ {
+ my $key = ~$0;
+ my $val = ~$1;
+ if $query { $val = self.query($val); }
+ %named{$key} = $val;
+ @result.splice($i, 1);
+ if $i < @result.elems {
+ $i--;
+ }
+ }
+ else {
+ if $query {
+ @result[$i] = self.query($param);
+ }
+ }
+ }
+
my $results = @result.elems - 1;
my $defs = @defaults.elems;
+
if $results < $defs {
@result.push: @defaults[$results..$defs-1];
}
- if $query {
- for @result -> $result is rw {
- if defined $result {
- $result = self.query($result);
- }
- }
+ ## Named params are always last.
+ if ($named || (%named.elems > 0)) {
+ @result.push: %named;
}
return @result;
}
View
@@ -4,18 +4,103 @@ use DateTime::Utils;
our sub all() {
my %modifiers = {
- date => &make_date,
-# time => &date_time,
-# strftime => &strf_time,
-# rfc => &timestamp_rfc,
+ 'date' => &make_date,
+ 'time' => &date_time,
+ 'strftime' => &strf_time,
+ 'rfc' => &format_rfc,
+ 'now' => &datetime_now,
};
return %modifiers;
}
-our sub date($parent, $query, *%opts) {
- my ($year, $month, $day, $hour, $minute, $second) =
- $parent.get-args(:query, $query, Nil xx 5);
- my $dt = DateTime.new($year, $month, $day, $hour, $minute, $second);
+## date: modifier, Creates a DateTime object with the given spec.
+## Usage: date: year [month] [day] [hour] [minute] [second] :tz(timezone)
+## The named paramter 'tz' must be specified in the common ISO offset format,
+## '-0800' would represent a timezone that is 8 hours behind UTC.
+## '+0430' would represent a timezone that is 4 hours and 30 minutes ahead.
+
+our sub make_date ($parent, $query, *%opts) {
+ my ($year, $month, $day, $hour, $minute, $second, %params) =
+ $parent.get-args(:query, :named, $query, 1, 1, 0, 0, 0);
+ if defined $year {
+ my $timezone = 0;
+ if %params.exists('tz') && %params<tz> ~~ Str {
+ $timezone = iso-offset(%params<tz>);
+ }
+ my $dt = DateTime.new(
+ :year($year.Int), :month($month.Int), :day(+$day.Int),
+ :hour($hour.Int), :minute($minute.Int), :second($second.Int),
+ :timezone($timezone)
+ );
+ return $parent.process-query($dt, |%opts);
+ }
+}
+
+## time: modifier, Creates a DateTime object based on an epoch integer/string.
+
+our sub date_time ($parent, $query, *%opts) {
+ my $epoch = $parent.query($query);
+ my $dt = DateTime.new($epoch.Int);
return $parent.process-query($dt, |%opts);
}
+## strftime: modifier, formats a DateTime object.
+## Usage: strftime: format [date] [timezone]
+## If date is not specified, it will be right now.
+## For now with a specified timezone, use the now:
+## modifier extension (see below).
+## The date parameter can be a DateTime object, Date object
+## or epoch integer/string.
+## If you don't specify a timezone, then for Date objects
+## or epoch integers, UTC will be used. DateTime objects will
+## use their existing timezones.
+
+our sub strf_time ($parent, $query, *%opts) {
+ my ($format, $date, $timezone) =
+ $parent.get-args(:query, $query, DateTime.now(), Nil);
+ if defined $format && defined $date {
+ if defined $timezone {
+ $timezone = iso-offset($timezone);
+ }
+ my $return;
+ if $date ~~ DateTime {
+ if defined $timezone {
+ $date.=in-timezone($timezone);
+ }
+ $return = strftime($format, $date);
+ }
+ else {
+ if !defined $timezone { $timezone = 0; }
+ if $date ~~ Date {
+ $return = strftime($format, DateTime.new(:$date, :$timezone));
+ }
+ elsif $date ~~ Int {
+ $return = strftime($format, DateTime.new($date, :$timezone));
+ }
+ }
+ }
+}
+
+## rfc: special modifier extension for strftime.
+## The only valid use for this, is in strftime: modifier queries.
+## Don't use it on its own, as it assumes it's being parsed by strftime.
+## Example:
+## <div tal:content="strftime: rfc: {{date: 2010 10 10 :tz('-0800')}}"/>
+## Will return <div>Sun, 10 Oct 2010 00:00:00 -0800</div>
+
+our sub format_rfc ($parent, $query, *%opts) {
+ return '%a, %d %b %Y %T %z';
+}
+
+## now: special modifier extension for strftime.
+## Returns a datetime object representing 'right now'.
+## It's in UTC by default, but that's okay, because the only
+## real use for this is in strftime: where you may want to
+## add a timezone, example:
+## <div tal:content="strftime: rfc: now: '-0800'"/>
+## Will return the current time in RFC format, in the -0800 timezone.
+
+our sub datetime_now ($parent, $query, *%opts) {
+ return DateTime.now();
+}
+
View
@@ -2,8 +2,12 @@ module Flower::Utils::List;
our sub all() {
my %modifiers = {
- group => &array_group,
- 'sort' => &array_sort,
+ group => &array_group,
+ 'sort' => &array_sort,
+ 'reverse' => &array_reverse,
+ limit => &array_limit,
+ shuffle => &array_pick,
+ pick => &array_pick,
};
return %modifiers;
}
@@ -35,3 +39,27 @@ our sub array_group ($parent, $query, *%opts) {
}
}
+our sub array_limit ($parent, $query, %*opts) {
+ my ($subquery, $num) = $parent.get-args($query, 1);
+ my $array = $parent.query($subquery);
+ if $array ~~ Array {
+ my $count = $num - 1;
+ return $array[0..$count];
+ }
+}
+
+our sub array_pick ($parent, $query, %*opts) {
+ my ($subquery, $num) = $parent.get-args($query, *);
+ my $array = $parent.query($subquery);
+ if $array ~~ Array {
+ return $array.pick($num);
+ }
+}
+
+our sub array_reverse ($parent, $query, %*opts) {
+ my $array = $parent.query($query);
+ if $array ~~ Array {
+ return $array.reverse;
+ }
+}
+
View
@@ -0,0 +1,65 @@
+#!/usr/bin/env perl6
+
+BEGIN { @*INC.unshift: './lib' }
+
+use Test;
+use Flower;
+
+plan 6;
+
+my $xml = '<?xml version="1.0"?>';
+
+my %date = {
+ 'date' => Date.new(2010,10,9),
+ 'datetime' => DateTime.new(
+ :year(2010), :month(10), :day(11),
+ :hour(13), :minute(17), :second(14),
+ :timezone('16200') # +0430
+ ),
+};
+
+## test 1
+
+my $template = "<date tal:content=\"date: '2010' '10' '10'\"/>";
+my $flower = Flower.new(:template($template));
+
+$flower.load-modifiers('Date');
+
+is $flower.parse(), $xml~'<date>2010-10-10T00:00:00Z</date>', 'date: modifier';
+
+## test 2
+
+$template = '<date tal:content="time: \'1286666133\'"/>';
+$flower.=another(:template($template));
+
+is $flower.parse(), $xml~'<date>2010-10-09T23:15:33Z</date>', 'time: modifier';
+
+## test 3
+
+$template = '<date tal:content="strftime: \'%Y_%m_%d-%T\' date/datetime"/>';
+$flower.=another(:template($template));
+
+is $flower.parse(:date(%date)), $xml~'<date>2010_10_11-13:17:14</date>', 'strftime: modifier on a datetime object';
+
+## test 4
+
+$template = '<date tal:content="strftime: \'%b %d, %Y\' date/date"/>';
+$flower.=another(:template($template));
+
+is $flower.parse(:date(%date)), $xml~'<date>Oct 09, 2010</date>', 'strftime: modifier on a date object';
+
+## test 5
+
+$template = "<date tal:content=\"strftime: rfc: \{\{date: '2010' '10' '10' :tz('-0800')}}\"/>";
+$flower.=another(:template($template));
+
+is $flower.parse(), $xml~'<date>Sun, 10 Oct 2010 00:00:00 -0800</date>', 'strftime: with rfc: modifier';
+
+## test 6
+
+$template = '<date tal:content="strftime: \'%Y-%m-%d\' now:"/>';
+my $now = Date.today();
+$flower.=another(:template($template));
+
+is $flower.parse(), $xml~'<date>'~$now~'</date>', 'strftime: with now: modifier';
+

0 comments on commit 1ebcb20

Please sign in to comment.