[Jifty-commit] jifty branch, master, updated. e52f12d49f342d861484709cafa8911445b2a52d

Jifty commits jifty-commit at lists.jifty.org
Thu Jan 7 18:09:50 EST 2010


The branch, master has been updated
       via  e52f12d49f342d861484709cafa8911445b2a52d (commit)
      from  d6ed667414e6270fd3aa2834535ade0b4906b322 (commit)

Summary of changes:
 Makefile.PL                                  |    4 +-
 lib/Jifty/JSON.pm                            |  153 ++++++++++----------------
 lib/Jifty/Plugin/REST/Dispatcher.pm          |    4 +-
 lib/Jifty/Plugin/SetupWizard/View.pm         |    4 +-
 lib/Jifty/Plugin/SinglePage.pm               |    2 +-
 lib/Jifty/Request.pm                         |    2 +-
 lib/Jifty/Script/Po.pm                       |    7 +-
 lib/Jifty/Web/Form/Element.pm                |   17 +--
 lib/Jifty/Web/Form/Field.pm                  |    7 +-
 lib/Jifty/Web/Menu.pm                        |    2 +-
 lib/Jifty/Web/PageRegion.pm                  |    2 +-
 share/web/templates/__jifty/webservices/json |    2 +-
 t/TestApp-Plugin-REST/t/03-format.t          |    4 +-
 t/TestApp/t/16-template-region.t             |    2 +-
 14 files changed, 85 insertions(+), 127 deletions(-)

- Log -----------------------------------------------------------------
commit e52f12d49f342d861484709cafa8911445b2a52d
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Jan 7 18:04:32 2010 -0500

    Update Jifty::JSON to use modern JSON.pm (2.xx)
    
    Jifty::JSON's pure Perl functions no longer worked with modern
    JSON.pm's, and JSON::Syck has been more or less deprecated in favor of
    modern JSON and JSON::XS.
    
    This deprecates the "singlequote" option that the previous version of
    Jifty::JSON supported.  Single quoted strings are a violation of the
    JSON spec.  Instead, HTML escape the entire blob of JSON if you plan to
    put it in an HTML attribute.  Jifty::JSON will croak if you attempt to
    use the "singlequote" option.

diff --git a/Makefile.PL b/Makefile.PL
index 30cd50e..140f816 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -53,6 +53,7 @@ requires('Hook::LexWrap');
 requires('IPC::PubSub' => '0.23' );
 requires('IPC::Run3');
 requires('Jifty::DBI' => '0.60' );            # Jifty::DBI::Collection Jifty::DBI::Handle Jifty::DBI::Record::Cachable Jifty::DBI::SchemaGenerator
+requires('JSON' => 2.17);
 requires('List::MoreUtils');
 requires('Locale::Maketext::Extract' => '0.35');
 requires('Locale::Maketext::Lexicon' => '0.77');
@@ -96,11 +97,10 @@ if (can_cc()) {
     requires('YAML' => 0.35); 	# Use YAML::Dump for the moment since YAML.pm segfaults on
 				# reading stupidly long (~20K characters) double-quoted
 				# strings, and we need to produce YAML.pm-readable output.
-    requires('JSON::Syck' => 0.29);
+    requires('JSON::XS' => 2.27);
 }
 else {
     requires('YAML' => 0.35) unless can_use('YAML::Syck' => 0.71);
-    requires('JSON' => 0.01) unless can_use('JSON::Syck' => 0.29);
 }
 
 feature 'Administrative Interface (web)' =>
diff --git a/lib/Jifty/JSON.pm b/lib/Jifty/JSON.pm
index 040a21e..711b0b3 100644
--- a/lib/Jifty/JSON.pm
+++ b/lib/Jifty/JSON.pm
@@ -4,7 +4,10 @@ use strict;
 package Jifty::JSON;
 
 use base 'Exporter';
-our @EXPORT_OK = qw/jsonToObj objToJson/;
+our @EXPORT_OK = qw/jsonToObj objToJson decode_json encode_json/;
+
+use Carp qw//;
+use JSON qw/ -support_by_pp -no_export /;
 
 =head1 NAME
 
@@ -12,128 +15,88 @@ Jifty::JSON -- Wrapper around L<JSON>
 
 =head1 SYNOPSIS
 
-  use Jifty::JSON qw/jsonToObj objToJson/;
+  use Jifty::JSON qw/decode_json encode_json/;
 
-  # Even though you might be using JSON::Syck, use the original JSON names
-  my $obj  = jsonToObj(q! { 'x': 1, 'y': 2, 'z': 3 } !);
-  my $json = objToJson($obj);
+  my $obj  = decode_json(q! { "x": "1", "y": "2", "z": "3" } !);
+  my $json = encode_json($obj);
 
 =head1 DESCRIPTION
 
-Provides a wrapper around the L<JSON> library.
+Provides a thin wrapper around the L<JSON> 2.xx library, which provides a
+frontend for L<JSON::XS> and L<JSON::PP>.
 
-The JSON specification at L<http://www.json.org/> states that only
-double-quotes are possible for specifying strings.  However, for the purposes
-of embedding Javascript-compatible objects in XHTML attributes (which use
-double-quotes), we sometimes want to provide strings in single quotes.
-This provides a version of L<JSON/objToJson> which allows
-single-quoted string output.
+This module used to wrap L<JSON::Syck> and L<JSON> 1.xx with special-casing for
+outputing JSON with single quoted values.  Single quotes make it easy to
+simply plop JSON into HTML attributes but are in violation of the JSON spec
+which mandates only double quoted strings.
 
-If the faster L<JSON::Syck> is available, it is preferred over the pure-perl
-L<JSON>, as it provides native support for single-quoted strings.
+The old behavior is now unsupported and it is recommended that you simply HTML
+escape your entire blob of JSON if you are sticking it in an HTML attribute.
+You can use L<Jifty-E<gt>web-E<gt>escape()|Jifty::Web/escape> to properly
+escape problematic characters for HTML.
 
-=head1 METHODS
+=head1 FUNCTIONS
 
-=cut
+=head2 decode_json JSON, [ARGUMENT HASHREF]
 
-BEGIN {
-    # Errors that happen here, stay here.
-    local $@;
-
-    # We're hacking, so tell the nannies to leave for a minute
-    no strict 'refs';
-    no warnings 'once';
-
-    # If a good version of JSON::Syck is available use that...
-    if (eval { require JSON::Syck; JSON::Syck->VERSION(0.05) }) {
-        *jsonToObj = *_jsonToObj_syck;
-        *objToJson = *_objToJson_syck;
-        $JSON::Syck::ImplicitUnicode = 1;
-    }
-
-    # Bummer, fallback to the pure Perl implementation
-    else {
-        require JSON;
-        *jsonToObj = *_jsonToObj_pp;
-        *objToJson = *_objToJson_pp;
-        $JSON::UTF8 = 1;
-    }
-}
+=head2 encode_json JSON, [ARGUMENT HASHREF]
 
-=head2 jsonToObj JSON, [ARGUMENTS]
+These functions are just like L<JSON>'s, except that you can pass options to
+them like you can with L<JSON>'s C<from_json> and C<to_json> functions.
 
-For completeness, C<Jifty::JSON> provides a C<jsonToObj>.  It is
-identical to L<JSON/jsonToObj>.
+By default they encode/decode using UTF8 (like L<JSON>'s functions of the same
+name), but you can turn that off by passing C<utf8 =E<gt> 0> in the
+options.  The L<allow_nonref|JSON/allow_nonref> flag is also enabled for
+backwards compatibility with earlier versions of this module.  It allows
+encoding/decoding of values that are not references.
+
+L<JSON> is imported with the C<-support_by_pp> flag in order to support all
+options that L<JSON::PP> provides when using L<JSON::XS> as the backend.  If
+you are concerned with speed, be careful what options you specify as it may
+cause the pure Perl backend to be used.  Read L<JSON/JSON::PP SUPPORT METHODS>
+for more information.
 
 =cut
 
-sub _jsonToObj_syck {
-    local $JSON::Syck::SingleQuote = 0;
-    JSON::Syck::Load($_[0]);
+sub decode_json {
+    JSON::from_json( $_[0], { utf8 => 1, allow_nonref => 1, %{$_[1] || {}} } );
 }
 
-sub _jsonToObj_pp {
-    return JSON::jsonToObj(@_);
+sub encode_json {
+    JSON::to_json( $_[0], { utf8 => 1, allow_nonref => 1, %{$_[1] || {}} } );
 }
 
-=head2 objToJson OBJECT, [ARGUMENTS]
+=head2 DEPRECATED jsonToObj JSON, [ARGUMENTS]
+
+=head2 DEPRECATED objToJson JSON, [ARGUMENTS]
 
-This method is identical to L<JSON/objToJson>, except it has an
-additional possible option.  The C<singlequote> option, if set to a
-true value in the C<ARGUMENTS> hashref, overrides L<JSON::Converter>'s
-string output method to output single quotes as delimters instead of
-double quotes.
+These functions are deprecated and provided for backwards compatibility.  They
+wrap the appropriate function above, but L<Carp/croak> if you try to set the
+C<singlequote> option.
 
 =cut
 
-sub _objToJson_syck {
-    my ($obj, $args) = @_;
-
-    local $JSON::Syck::SingleQuote = $args->{singlequote};
-    local $JSON::Syck::ImplicitUnicode = 1;
-        my $json = JSON::Syck::Dump($obj);
-        if (! $args->{singlequote}) {
-                $json =~ s/\n\n\n/\\n/gs;       # fix syck bug
-                $json =~ s/\n/\\n/gs;           # just to be safe
-                $json =~ s/\r/\\r/gs;
-        }
-        return $json;
+sub jsonToObj {
+    my $args = $_[1] || {};
+    Carp::croak("Attempted to set 'singlequote' option, but it is no longer supported.".
+                "  You may need to HTML escape the resulting JSON.".
+                "  Please read the POD of Jifty::JSON and fix your code.")
+        if exists $args->{'singlequote'};
+    decode_json( @_ );
 }
 
-# We should escape double-quotes somehow, so that we can guarantee
-# that double-quotes *never* appear in the JSON string that is
-# returned.
-sub _objToJson_pp {
-    my ($obj, $args) = @_;
-
-    # Unless we're asking for single-quoting, just do what JSON.pm
-    # does
-    return JSON::Converter::objToJson($obj)
-      unless delete $args->{singlequote};
-
-    # Otherwise, insert our own stringify sub
-    no warnings 'redefine';
-    my %esc = (
-        "\n" => '\n',
-        "\r" => '\r',
-        "\t" => '\t',
-        "\f" => '\f',
-        "\b" => '\b',
-        "'"  => '\\\'',
-        "\\" => '\\\\',
-    );
-    local *JSON::Converter::_stringfy = sub {
-        my $arg = shift;
-        $arg =~ s/([\\\n'\r\t\f\b])/$esc{$1}/eg;
-        $arg =~ s/([\x00-\x07\x0b\x0e-\x1f])/'\\u00' . unpack('H2',$1)/egs;
-        return "'" . $arg ."'";
-    };
-    return JSON::objToJson($obj, $args);
+sub objToJson {
+    my $args = $_[1] || {};
+    Carp::croak("Attempted to set 'singlequote' option, but it is no longer supported.".
+                "  You may need to HTML escape the resulting JSON.".
+                "  Please read the POD of Jifty::JSON and fix your code.")
+        if exists $args->{'singlequote'};
+    encode_json( @_ );
 }
 
 =head1 LICENSE
 
-Jifty is Copyright 2005-2006 Best Practical Solutions, LLC.
+Jifty is Copyright 2005-2010 Best Practical Solutions, LLC.
 Jifty is distributed under the same terms as Perl itself.
 
 =cut
diff --git a/lib/Jifty/Plugin/REST/Dispatcher.pm b/lib/Jifty/Plugin/REST/Dispatcher.pm
index cecc1f4..4dfec7d 100644
--- a/lib/Jifty/Plugin/REST/Dispatcher.pm
+++ b/lib/Jifty/Plugin/REST/Dispatcher.pm
@@ -223,7 +223,7 @@ sub output_format {
             format       => 'JSON',
             extension    => 'json',
             content_type => 'application/json; charset=UTF-8',
-            freezer      => \&Jifty::JSON::objToJson,
+            freezer      => \&Jifty::JSON::encode_json,
         };
     }
     elsif ($accept =~ /j(?:ava)?s|ecmascript/i) {
@@ -231,7 +231,7 @@ sub output_format {
             format       => 'JS',
             extension    => 'js',
             content_type => 'application/javascript; charset=UTF-8',
-            freezer      => sub { 'var $_ = ' . Jifty::JSON::objToJson( @_, { singlequote => 1 } ) },
+            freezer      => sub { 'var $_ = ' . Jifty::JSON::encode_json( @_ ) },
         };
     }
     elsif ($accept =~ qr{^(?:application/x-)?(?:perl|pl)$}i) {
diff --git a/lib/Jifty/Plugin/SetupWizard/View.pm b/lib/Jifty/Plugin/SetupWizard/View.pm
index 4229f4e..1cb102f 100644
--- a/lib/Jifty/Plugin/SetupWizard/View.pm
+++ b/lib/Jifty/Plugin/SetupWizard/View.pm
@@ -175,7 +175,7 @@ template '/__jifty/admin/setupwizard/language' => sub {
 template '/__jifty/admin/setupwizard/database' => sub {
     # XXX: We've got to add a sane way to unquote stuff in onfoo handlers...
     my $onchange = 'Jifty.update('
-                 . Jifty::JSON::objToJson({
+                 . Jifty::JSON::encode_json({
                     actions          => {},
                     action_arguments => {},
                     fragments        => [
@@ -187,7 +187,7 @@ template '/__jifty/admin/setupwizard/database' => sub {
                     ],
                     continuation     => undef,
 
-                 }, {singlequote => 1})
+                 })
                  . ', this)';
 
     $onchange =~ s/PLACEHOLDER/'+this.value+'/;
diff --git a/lib/Jifty/Plugin/SinglePage.pm b/lib/Jifty/Plugin/SinglePage.pm
index 7a0ed97..9b00526 100644
--- a/lib/Jifty/Plugin/SinglePage.pm
+++ b/lib/Jifty/Plugin/SinglePage.pm
@@ -74,7 +74,7 @@ sub _sp_link {
             $self->_push_onclick($args, {
                 region       => $self->region_name,
                 replace_with => $url,
-                beforeclick  => qq{SPA.historyChange('$complete_url', { 'continuation':{}, 'actions':{}, 'fragments':[{'mode':'Replace','args':@{[ Jifty::JSON::objToJson($args->{parameters})]},'region':'__page','path':'$url'}],'action_arguments':{}}, true);},
+                beforeclick  => qq{SPA.historyChange('$complete_url', { 'continuation':{}, 'actions':{}, 'fragments':[{'mode':'Replace','args':@{[ Jifty::JSON::encode_json($args->{parameters})]},'region':'__page','path':'$url'}],'action_arguments':{}}, true);},
                 args         => { %{$args->{parameters}}} });
         }
         elsif (exists $args->{submit} && !$args->{onclick}) {
diff --git a/lib/Jifty/Request.pm b/lib/Jifty/Request.pm
index 89af7d8..b83950b 100644
--- a/lib/Jifty/Request.pm
+++ b/lib/Jifty/Request.pm
@@ -128,7 +128,7 @@ sub fill {
     # Check it for something appropriate
     if ($data) {
         if ($ct =~ m{^text/x-json}) {
-            return $self->from_data_structure(eval{Jifty::JSON::jsonToObj($data)}, $cgi);
+            return $self->from_data_structure(eval{Jifty::JSON::decode_json($data)}, $cgi);
         } elsif ($ct =~ m{^text/x-yaml}) {
             return $self->from_data_structure(eval{Jifty::YAML::Load($data)}, $cgi);
         }
diff --git a/lib/Jifty/Script/Po.pm b/lib/Jifty/Script/Po.pm
index 4f8a44c..47451ac 100644
--- a/lib/Jifty/Script/Po.pm
+++ b/lib/Jifty/Script/Po.pm
@@ -95,10 +95,9 @@ sub _js_gen {
 
         no strict 'refs';
         print $fh
-            Jifty::JSON::objToJson( { map { my $text = ${"Jifty::I18N::".$lang."::Lexicon"}{$_};
-                                            defined $text ? ( $_ => $text ) : () }
-                                      keys %{$LMExtract->lexicon} },
-                                    { singlequote => 1 } );
+            Jifty::JSON::encode_json( { map { my $text = ${"Jifty::I18N::".$lang."::Lexicon"}{$_};
+                                              defined $text ? ( $_ => $text ) : () }
+                                        keys %{$LMExtract->lexicon} } );
     }
 }
 
diff --git a/lib/Jifty/Web/Form/Element.pm b/lib/Jifty/Web/Form/Element.pm
index 4d11c6e..397e9fd 100644
--- a/lib/Jifty/Web/Form/Element.pm
+++ b/lib/Jifty/Web/Form/Element.pm
@@ -553,15 +553,13 @@ sub javascript_attrs {
                      @{ $trigger_structure->{value} };
 
         if ( @$fragments or ( !$actions || %$actions ) ) {
-            my $update_json = Jifty::JSON::objToJson({
+            my $update_json = Jifty::JSON::encode_json({
                     actions      => $actions,
                     action_arguments => $trigger_structure->{action_arguments},
                     fragments    => $fragments,
                     continuation => $self->continuation,
                     preload_key  => $trigger_structure->{preload_key},
-                },
-                { singlequote => 1 },
-            );
+                });
 
             my $update = "Jifty.update($update_json, this);";
 
@@ -575,10 +573,7 @@ sub javascript_attrs {
         }
 
         if ($trigger_structure->{confirm}) {
-            my $text = Jifty::JSON::objToJson(
-                $trigger_structure->{confirm},
-                {singlequote => 1},
-            );
+            my $text = Jifty::JSON::encode_json( $trigger_structure->{confirm} );
             $string = "if(!confirm($text)){ Jifty.stopEvent(event); return false; }" . $string;
         }
 
@@ -644,10 +639,10 @@ sub key_binding_javascript {
                     : $self->label;
     if ($key) {
         return "Jifty.KeyBindings.add("
-                . Jifty::JSON::objToJson( uc($key), { singlequote => 1 } ).","
+                . Jifty::JSON::encode_json( uc $key ).","
                 . "'click', "
-                . Jifty::JSON::objToJson( $self->id, { singlequote => 1 } ).","
-                . Jifty::JSON::objToJson( $label, { singlequote => 0 } )
+                . Jifty::JSON::encode_json( $self->id ).","
+                . Jifty::JSON::encode_json( $label )
                 . ");";
     }
 }
diff --git a/lib/Jifty/Web/Form/Field.pm b/lib/Jifty/Web/Form/Field.pm
index 70879d9..a789692 100644
--- a/lib/Jifty/Web/Form/Field.pm
+++ b/lib/Jifty/Web/Form/Field.pm
@@ -432,6 +432,7 @@ sub render_inline_javascript {
     );
     
     if($javascript =~ /\S/) {
+        $javascript = Jifty->web->escape( $javascript );
         Jifty->web->out(qq{<script type="text/javascript">$javascript</script>
 });
     }
@@ -450,6 +451,7 @@ sub render_preload_javascript {
     my $javascript = $self->preload_javascript;
 
     if($javascript =~ /\S/) {
+        $javascript = Jifty->web->escape( $javascript );
         Jifty->web->out(qq{<script type="text/javascript">$javascript</script>
 });
     }
@@ -747,12 +749,11 @@ sub preload_javascript {
 
         my @preloaded;
 
-        my $preload_json = Jifty::JSON::objToJson(
+        my $preload_json = Jifty::JSON::encode_json(
             {
                 fragments   => $trigger_structure->{fragments},
                 preload_key => $trigger_structure->{preload_key},
-            },
-            { singlequote => 1 },
+            }
         );
 
         push @javascript, "Jifty.preload($preload_json, this);";
diff --git a/lib/Jifty/Web/Menu.pm b/lib/Jifty/Web/Menu.pm
index 6d41336..ea6dc1c 100644
--- a/lib/Jifty/Web/Menu.pm
+++ b/lib/Jifty/Web/Menu.pm
@@ -447,7 +447,7 @@ sub render_as_yui_menu {
     );
 
     my $showjs = $args{'show'} ? "menu.show();" : "";
-    my $json   = Jifty::JSON::objToJson( $args{'options'} );
+    my $json   = Jifty->web->escape( Jifty::JSON::encode_json( $args{'options'} ) );
 
     # Bind to a button to show the menu
     my $binding = (defined $args{'button'} and length $args{'button'}) ? 1 : 0;
diff --git a/lib/Jifty/Web/PageRegion.pm b/lib/Jifty/Web/PageRegion.pm
index 5db344f..504eb5d 100644
--- a/lib/Jifty/Web/PageRegion.pm
+++ b/lib/Jifty/Web/PageRegion.pm
@@ -320,7 +320,7 @@ sub make_body {
     if ( $self->region_wrapper ) {
          $buffer->append(qq|<script type="text/javascript">\n|
             . qq|new Region('| . $self->qualified_name . qq|',|
-            . Jifty::JSON::objToJson( \%arguments, { singlequote => 1 } ) . qq|,| 
+            . Jifty->web->escape(Jifty::JSON::encode_json( \%arguments )) . qq|,| 
             . qq|'| . $self->path . qq|',|
             . ( $self->parent ? qq|'| . $self->parent->qualified_name . qq|'| : q|null|)
             . qq|,| . (Jifty->web->form->is_open ? '1' : 'null')
diff --git a/share/web/templates/__jifty/webservices/json b/share/web/templates/__jifty/webservices/json
index bacde46..1471053 100644
--- a/share/web/templates/__jifty/webservices/json
+++ b/share/web/templates/__jifty/webservices/json
@@ -1,5 +1,5 @@
 % $r->content_type("text/x-json");
-<% Jifty::JSON::objToJson(\%results) |n%>
+<% Jifty::JSON::encode_json(\%results) |n%>
 
 <%INIT>
 my %results = Jifty->web->response->results;
diff --git a/t/TestApp-Plugin-REST/t/03-format.t b/t/TestApp-Plugin-REST/t/03-format.t
index 14ca829..adfb307 100644
--- a/t/TestApp-Plugin-REST/t/03-format.t
+++ b/t/TestApp-Plugin-REST/t/03-format.t
@@ -35,11 +35,11 @@ sub result_of {
 
     my %loaders = (
         yml  => \&Jifty::YAML::Load,
-        json => \&Jifty::JSON::jsonToObj,
+        json => \&Jifty::JSON::decode_json,
         js   => sub {
             my $js = shift;
             $js =~ s/.*? = //; # variable assignment
-            return Jifty::JSON::jsonToObj($js);
+            return Jifty::JSON::decode_json($js);
         },
     );
 
diff --git a/t/TestApp/t/16-template-region.t b/t/TestApp/t/16-template-region.t
index 7c6984b..6c9dca9 100644
--- a/t/TestApp/t/16-template-region.t
+++ b/t/TestApp/t/16-template-region.t
@@ -10,7 +10,7 @@ my @tests = (
         text => q|list!
 <span>1</span>
 <span>2</span><script type="text/javascript">
-new Region('special',{'id':3},'/foo/item',null,null);
+new Region('special',{&#34;id&#34;:3},'/foo/item',null,null);
 </script><div id="region-special" class="jifty-region">
 <span>3</span></div>|
     },

-----------------------------------------------------------------------


More information about the Jifty-commit mailing list