jsmin.rb を Class 化する
ハイパフォーマンスWebサイト ―高速サイトを実現する14のルール
- 作者: Steve Souders,スティーブサウダーズ,武舎広幸,福地太郎,武舎るみ
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/04/11
- メディア: 大型本
- 購入: 32人 クリック: 676回
- この商品を含むブログ (125件) を見る
これを読みつついろいろいじっていたところ、http://www.crockford.com/javascript/jsmin.rb があったので使ってみました。確かに便利なのですが、折角なので rake で deploy する際に使用できるようにクラス化してました。そうすれば rake ファイル中でこんな事ができるようになります。
# -*- ruby -*- require 'fileutils' require 'jsmin' def minify puts "Minify JavaScript files by jsmin" Dir.glob('*.js').each do |js| JsMin.new(js, File.join(dirs[:minified], js)).minify end end ...
で、以下がそのコードです。
# # jsmin.rb 2008-06-08 # Author: UEDA Hiroyuki <BSDmad@gmail.com> # # This work is originated from jsmin.rb by Uladzislau Latynski. # The purpose of translation from jsmin.rb to jsmin.rb ^^;) is # for using the feature as a class. As a result, you can deploy # minified JavaScript files with this class as follows: # # (snip...) # File.glob('*.js').each do |file| # JsMin.new(file, 'output/' + file).minify # end # (snip...) # # # jsmin.rb 2007-07-20 # Author: Uladzislau Latynski # This work is a translation from C to Ruby of jsmin.c published by # Douglas Crockford. Permission is hereby granted to use the Ruby # version under the same conditions as the jsmin.c on which it is # based. # # /* jsmin.c # 2003-04-21 # # Copyright (c) 2002 Douglas Crockford (www.crockford.com) # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies # of the Software, and to permit persons to whom the Software is furnished to do # so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # The Software shall be used for Good, not Evil. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. class JsMin EOF = -1 def initialize(src, dst = $stdout) @src = src @dst = dst @input = nil @output = nil @the_a = "" @the_b = "" end def minify begin @input = File.open(@src, 'r') @output = File.open(@dst, 'w') jsmin rescue p $! p $@ ensure @input.close if @input @output.close if @output end end private def isAlphanum(c) return false if !c || c == EOF return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || c[0] > 126) end def get c = @input.getc return EOF if(!c) c = c.chr return c if (c >= " " || c == "\n" || c.unpack("c") == EOF) return "\n" if (c == "\r") return " " end # Get the next character without getting it. def peek lookaheadChar = @input.getc @input.ungetc(lookaheadChar) return lookaheadChar.chr end # mynext -- get the next character, excluding comments. # peek() is used to see if a '/' is followed by a '/' or '*'. def mynext c = get if (c == "/") if (peek == "/") while(true) c = get if (c <= "\n") return c end end end if(peek == "*") get while(true) case get when "*" if (peek == "/") get return " " end when EOF raise "Unterminated comment" end end end end return c end # action -- do something! What you do is determined by the argument: 1 # Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B. # (Delete A). 3 Get the next B. (Delete B). action treats a string as a # single character. Wow! action recognizes a regular expression if it is # preceded by ( or , or =. def action(a) result = "" if (a==1) result += @the_a end if (a==1 || a==2) @the_a = @the_b if (@the_a == "\'" || @the_a == "\"") while (true) result += @the_a @the_a = get break if (@the_a == @the_b) raise "Unterminated string literal" if (@the_a <= "\n") if (@the_a == "\\") result += @the_a @the_a = get end end end end if(a==1 || a==2 || a==3) @the_b = mynext if (@the_b == "/" && (@the_a == "(" || @the_a == "," || @the_a == "=" || @the_a == ":" || @the_a == "[" || @the_a == "!" || @the_a == "&" || @the_a == "|" || @the_a == "?" || @the_a == "{" || @the_a == "}" || @the_a == ";" || @the_a == "\n")) result = result + @the_a + @the_b while (true) @the_a = get if (@the_a == "/") break elsif (@the_a == "\\") result += @the_a @the_a = get elsif (@the_a <= "\n") raise "Unterminated RegExp Literal" end result += @the_a end @the_b = mynext end end @output.print result end # jsmin -- Copy the input to the output, deleting the characters which are # insignificant to JavaScript. Comments will be removed. Tabs will be # replaced with spaces. Carriage returns will be replaced with linefeeds. # Most spaces and linefeeds will be removed. def jsmin @the_a = "\n" action(3) while (@the_a != EOF) case @the_a when " " if (isAlphanum(@the_b)) action(1) else action(2) end when "\n" case (@the_b) when "{","[","(","+","-" action(1) when " " action(3) else if (isAlphanum(@the_b)) action(1) else action(2) end end else case (@the_b) when " " if (isAlphanum(@the_a)) action(1) else action(3) end when "\n" case (@the_a) when "}","]",")","+","-","\"","\\", "'", '"' action(1) else if (isAlphanum(@the_a)) action(1) else action(3) end end else action(1) end end end end end