OSDN Git Service

add JavaScriptLoader plugin
authorhylom <hylom@users.sourceforge.jp>
Tue, 21 Nov 2017 12:46:47 +0000 (21:46 +0900)
committerhylom <hylom@users.sourceforge.jp>
Tue, 21 Nov 2017 12:46:47 +0000 (21:46 +0900)
src/newslash_web/lib/Newslash/Plugin/JavaScriptLoader.pm [new file with mode: 0644]
src/newslash_web/lib/Newslash/Web.pm
src/newslash_web/templates/common/header.html.tt2

diff --git a/src/newslash_web/lib/Newslash/Plugin/JavaScriptLoader.pm b/src/newslash_web/lib/Newslash/Plugin/JavaScriptLoader.pm
new file mode 100644 (file)
index 0000000..e645c87
--- /dev/null
@@ -0,0 +1,191 @@
+package Newslash::Plugin::JavaScriptLoader;
+use Mojo::Base 'Mojolicious::Plugin';
+
+use File::Spec;
+use File::Basename qw(fileparse);
+use Digest::MD5 qw(md5_hex);
+use IPC::Open3;
+use Symbol qw(gensym);
+use IO::Select;
+use POSIX ":sys_wait_h";
+use Mojo::Util qw(dumper);
+use Encode;
+
+use constant KVS_PREFIX => "JSL:";
+use constant DEFAULT_MEM_EXPIRE => 60;
+use constant DEFAULT_KVS_EXPIRE => 60;
+
+sub _kvs {
+    return shift->{app}->kvs;
+}
+
+sub _cache {
+    return shift->{app}->cache;
+}
+
+sub set_cache {
+    my ($self, $key, $value) = @_;
+    my $mem_expire = $self->{conf}->{mem_expire};
+    my $kvs_expire = $self->{conf}->{kvs_expire};
+    $mem_expire = DEFAULT_MEM_EXPIRE if !defined $mem_expire;
+    $kvs_expire = DEFAULT_KVS_EXPIRE if !defined $kvs_expire;
+
+    $self->_kvs->set(KVS_PREFIX . $key, $value, $kvs_expire);
+    $self->_cache->set(KVS_PREFIX . $key, $value, $mem_expire);
+}
+
+sub get_cache {
+    my ($self, $key) = @_;
+    my $expire = $self->{conf}->{mem_expire};
+    $expire = DEFAULT_MEM_EXPIRE if !defined $expire;
+
+    my $value = $self->_cache->get(KVS_PREFIX . $key);
+    return $value if defined $value;
+
+    $value = $self->_kvs->get(KVS_PREFIX . $key);
+    if (defined $value) {
+        $self->_cache->set(KVS_PREFIX . $key, $value, $expire);
+        return $value;
+    }
+
+    # no cache found
+    return;
+}
+
+sub get_content {
+    my ($self, $basename) = @_;
+    my $content = $self->get_cache($basename);
+    return $content if defined $content;
+
+    $content = $self->_load_js($basename);
+    if (defined $content) {
+        $self->set_cache($basename, $content);
+        $self->set_cache($content->{path}, $content);
+        return $content
+    }
+    return;
+}
+
+sub cached_basedir {
+    return "/js_cache";
+}
+
+sub _load_js {
+    my ($self, $basename) = @_;
+
+    # check if file exists
+    my $abs_path = $self->_basename_to_absolute_path($basename);
+    return if (!-f $abs_path || !-r $abs_path);
+
+    # load file
+    open( my $fh, $abs_path ) or return;
+    my $js_body = do { local($/); <$fh> } ;
+    close($fh);
+
+    my $md5;
+    if (utf8::is_utf8($js_body)) {
+        $md5 = md5_hex(Encode::encode('utf8', $js_body));
+    }
+    else {
+        $md5 = md5_hex($js_body);
+    }
+    my $path;
+    if ($self->{mode} eq "development") {
+        my $basedir = $self->{conf}->{source_directory};
+        $basedir =~ s/^public//g;
+        $path = "$basedir/$basename";
+    }
+    else {
+        my ($base, $dir, $ext) = fileparse($basename, qw/\.[^.]*/);
+        $dir = "" if $dir eq "./";
+        my $basedir = $self->cached_basedir;
+        $path = "$basedir/$dir${base}.$md5$ext";
+    }
+
+    my $result = { md5 => $md5,
+                   content => $js_body,
+                   path => $path,
+                   type => "text/javascript",
+                 };
+    return $result;
+}
+
+sub _basename_to_absolute_path {
+    my ($self, $basename) = @_;
+    my $base_dir = $self->{conf}->{base_directory};
+    if (!$base_dir) {
+        $base_dir = $self->{app}->home;
+    }
+    if (!$base_dir) { # fallback
+        $base_dir = "./";
+    }
+    my $src_dir = $self->{conf}->{source_directory};
+    my $path = File::Spec->catdir($base_dir, $src_dir, $basename);
+    $path = File::Spec->rel2abs($path);
+    return $path;
+}
+
+sub _split_md5 {
+    my ($self, $path) = @_;
+    if ($path =~ m/^(.+)\.([0-9a-f]+)(\.\w+)$/) {
+        return ($1 . $3, $2);
+    }
+    return ($path, "");
+}
+
+sub register {
+    my ($self, $app, $conf) = @_;
+
+    $self->{conf} = {%$conf, %{$app->config->{JavaScriptLoader}}};
+    $self->{app} = $app;
+    $self->{mode} = $self->{conf}->{mode} || $app->mode;
+
+    if (!$self->{conf}->{source_directory}) {
+        $app->log->warn("JavaScriptLoader: no source_directory given!");
+    }
+
+    if ($app->config->{TT2Renderer}) {
+        my $tt2r = $app->config->{TT2Renderer}->{self};
+        $tt2r->add_NS_function('load_js', sub {
+                                   my ($c, $name) = @_;
+                                   my $content = $self->get_content($name);
+                                   if ($content) {
+                                       return "<script src='$content->{path}'></script>";
+                                   }
+                                   $self->{app}->log->warn("JavaScriptLoader: cannot load $name");
+                                   return '';
+                               });
+    }
+
+    my $r = $app->routes;
+    $r->any('/js_cache/*content_path' => sub {
+                my $c = shift;
+                my $path = $c->stash('content_path');
+                if ($path) {
+                    my $content = $self->get_cache($path);
+                    if (!$content) {
+                        # no cache, so try to generate
+                        my ($basename, $md5) = $self->_split_md5($path);
+                        $content = $self->get_content($basename);
+
+                        # check md5
+                        if ($content && $content->{md5} ne $md5) {
+                            $content = "";
+                        }
+                    }
+                    if ($content) {
+                        $c->res->headers->content_type($content->{type});
+                        $c->res->body(encode_utf8($content->{content}));
+                        $c->rendered(200);
+                        return;
+                    }
+                }
+                $c->rendered(404);
+            });
+
+    $app->helper(javascript_loader => sub { state $javascript_loader = $self; });
+}
+
+
+
+1;
index 09643ec..d598b90 100644 (file)
@@ -120,6 +120,9 @@ sub startup {
     # quasi-static content
     $app->plugin('Newslash::Plugin::QuasiStaticContent');
 
+    # javascript loader
+    $app->plugin('Newslash::Plugin::JavaScriptLoader');
+
     # user authorization
     $app->plugin('Newslash::Plugin::UserAuth');
 
index 059018f..96c9e48 100644 (file)
   <script src="/vue/vue-resource.js"></script>
   [%- END -%]
 
+  [%- IF production -%]
   <script src="/js/escape-html.js" ></script>
   <script src="/js/base.js"></script>
   <script src="/js/ns-util.js"></script>
-  <script src="[% NS.static_content('js/siteconfig.js'); %]" ></script>
   <script src="/js/vue-newslash.js"></script>
+  [%- ELSE %]
+  [% NS.load_js("escape-html.js") %]
+  [% NS.load_js("base.js") %]
+  [% NS.load_js("ns-util.js") %]
+  [% NS.load_js("vue-newslash.js") %]
+  [%- END %]
+  <script src="[% NS.static_content('js/siteconfig.js'); %]" ></script>
 </head>