いきなりはまった。select_date
かなり久しぶりにRails触って、いきなりハマった・・
<%= select_date(@selected_date, :use_month_numbers => true) %>
ActionView::TemplateError (can't convert Symbol into String)
Railsのバージョンが2.2.2でロケールをjaにしていたせいみたい。(知ったかぶり)
id:masaaki008:20090128:1233130816
ついでにはてな記法もきれいさっぱり忘れていた。
やっぱマメに書かなきゃと少し反省・・
OSXのJVMはClientとServerで違っていた・・
常識?かも知れないのですが、自分は今の今まで知らなかったのでメモ・・
昨日ちょっといろいろトラブっていて環境調査をしていて気がつきました。
$ java -version java version "1.5.0_13" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-241) Java HotSpot(TM) Client VM (build 1.5.0_13-121, mixed mode, sharing)
OSX Tiger Server (XServe G5)での表示
$ java -version java version "1.5.0_13" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-241) Java HotSpot(TM) Client VM (build 1.5.0_13-121, mixed mode)
よーく目を凝らしてみると、Server版にはsharingの文字がありません。
どういう事か調べてみたら、sharingがつくモードはクライアント版のJVMだと言う事が解りました。
という事は、OSX Serverで何も考えずにjavaを起動すると、それは「-server」オプションをつけて起動したのと同じ事という訳ですね。*1
いったいその違いはどこで設定されているのか調べてみると、/Library/Java/Home/lib/jvm.cfg にどのVMを起動するかの設定が書かれていました。ぜんぜん知らなかったよ!(恥)
$ cat /Library/Java/Home/lib/jvm.cfg # # @(#)jvm.cfg 1.6 01/12/03 # # Copyright 2002 Sun Microsystems, Inc. All rights reserved. # SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. # # # # # List of JVMs that can be used as an option to java, javac, etc. # Order is important -- first in this list is the default JVM. # NOTE that this both this file and its format are UNSUPPORTED and # WILL GO AWAY in a future release. # # You may also select a JVM in an arbitrary location with the # "-XXaltjvm=<jvm_dir>" option, but that too is unsupported # and may not be available in a future release. # -client KNOWN -jvm ALIASED_TO -client -hotspot ALIASED_TO -client -server KNOWN -classic WARN
$ cat /Library/Java/Home/lib/jvm.cfg # # @(#)jvm.cfg 1.6 01/12/03 # # Copyright 2002 Sun Microsystems, Inc. All rights reserved. # SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. # # # # # List of JVMs that can be used as an option to java, javac, etc. # Order is important -- first in this list is the default JVM. # NOTE that this both this file and its format are UNSUPPORTED and # WILL GO AWAY in a future release. # # You may also select a JVM in an arbitrary location with the # "-XXaltjvm=<jvm_dir>" option, but that too is unsupported # and may not be available in a future release. # -server KNOWN -jvm ALIASED_TO -client -hotspot ALIASED_TO -client -client KNOWN -classic WARN
よーく目を凝らしてみると、-serverと-clientの順序が入れ替わっている・・*2
という訳で、知らず知らずのうちにServer版のJVMを使っている可能性もあるね、と言うお話でした。
ActiveRecord#to_xmlの日本語対応版(?)を作る
ActiveRecordをXMLにシリアライズするのは非常にお気軽で良いのですが、日本語が数値参照形式になってしまいます。
katochan = Member.create(:name => '加藤茶', :email => 'katochan@hoge.com') puts katochan.to_xml
結果はこのとおり。
<?xml version="1.0" encoding="UTF-8"?> <member> <created-at type="datetime">2008-06-02T17:46:34+09:00</created-at> <id type="integer">1</id> <mail>katochan@hoge.com</mail> <name>加藤茶</name> <updated-at type="datetime">2008-06-02T17:46:34+09:00</updated-at> </member>
d:id:ehara_kw:20071221とd:id:eclipse-a:20070905を参考に、強引ですがこういう対処を取ってみました。
class ActiveRecord::Base def to_jp_xml(options, &block) return self.to_xml(options, &block).gsub(/&#(?:(\d*?)|(?:[xX]([0-9a-fA-F]{4})));/) { [$1.nil? ? $2.to_i(16) : $1.to_i].pack('U') } end end
to_xmlメソッドをオーバーライドしてもいいかな?と思ったけど、小心者なのであえて別メソッドにしています・・
2行追加してSingletonにする方法
オブジェクトをSingletonにしたいケースってたまにあると思いますけど、Javaだとこんな感じ。
public class Singleton { private static Singleton instance; private Singleton() { super(); System.out.println("Singletonオブジェクト生成"); } public static Singleton getInstance() { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } }
当たり前ですけど、いちいち実装しなければなりません。コンストラクタをプライベートにして、staticでインスタンスを持たせて、getInstanceメソッドでは念のためsynchronizedで囲って・・と、結構煩わしいですね。しかもそういう目に遭う度に同じようなコードを書かなくちゃならない。ただ、そう言うもんだと何の疑問も持っていませんでした。
同じような局面にRubyで出会った時、会社のRubistにあっさりと、それrequire 'singleton' で、と言われて目から鱗。
require 'singleton' class Singleton include Singleton def initialize puts 'Singletonオブジェクト生成' end end
require 'singleton'しておいて、include Singletonすればそのクラスのコンストラクタがprivateになり、クラスメソッドの#instanceが追加されるというもの。*1実質的には2行追加するのみ。
コンストラクタに引数がある場合どうすんだ?という疑問もありますけど、こういうフレキシビリティーは感動的ですらあります(笑)
ぼちぼち再開しようかと
気がつけば、最後に更新したのが2年ほど前・・
生きてます(笑) > Blog繋がりの人たち
最近はRuby(っていうか、Rails)を勉強しているので、メモして行きます。
- 作者: Bruce A. Tate,角谷信太郎
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2007/04/21
- メディア: 単行本(ソフトカバー)
- 購入: 3人 クリック: 53回
- この商品を含むブログ (119件) を見る
sipsコマンドは使えるかも
印刷屋としては、画像処理は避けて通れない道だったりします。
最近は何でもアプリケーションサーバで処理する傾向があって、ファイル容量も大きなものを扱う必要があったりなど、ちょっと悩みの種でもあります。
今の所、Javaの Java Image I/O API を使っていますが、Javaでデカイ画像を扱うのは勇気がいります。それに、CMYKの扱いが今ひとつだったりしますし。
Photoshopを使えば簡単にできるような処理も、サーバ上でそれをやる訳にも行かず(やって出来ない事は無いだろうけど・・)、どうしたものかと考えておりました。
そこで、せっかく画像処理に強いと言われる(笑)Mac OSX を運用プラットフォームにしている強みを生かして、何か簡単で良い方法が無いかと調べていた所・・・やっぱりありました。
sipsコマンドです。なんだ、Pantherからあったのか。知らんかった(笑)
sipsで出来る事
このページ(http://developer.apple.com/jp/technotes/tn2035.html)に日本語の解説が載っていました。
- 各種ラスタイメージ形式の読み書きと変換
- jpeg(Jfif および Xiff)、GIF、PNG
- 基本的画像処理
- 回転、反転、切り取り、パディング、再サンプリング、解像度変更
- カラーマネージメント操作
- プロファイルの埋め込み、抽出、調整
- 既知のメタデータタグの読み書き
- 可能な限りのオリジナルコンテンツの維持
印刷屋的には、おそらく一番最後の「可能な限りのオリジナルコンテンツの維持」ってのが重要な気がします。
これから書く事は、実は2003年12月のこの記事(http://journal.mycom.co.jp/column/osx/073/)と、ほとんどかぶってしまう事が判明しましたが、個人的なテストの結果としてメモしておきます。
画像のプロパティを取得する
- 取れるだけ取ってくる
- sips -g all imageFile
$ sips -g all test.jpg
/test.jpg
pixelWidth: 3136
pixelHeight: 2352
typeIdentifier: public.jpeg
format: jpeg
formatOptions: default
dpiWidth: 314.000
dpiHeight: 314.000
samplesPerPixel: 3
bitsPerSample: 8
hasAlpha: no
space: RGB
creation: 2006:01:09 11:49:37
make: OLYMPUS IMAGING CORP.
model: E-330
software: Version 1.0
description: OLYMPUS DIGITAL CAMERA
縦横のピクセル数、解像度、カラースペースはもとより、メタデータのたぐいも拾って来れますね。
$ sips -g pixelHeight -g pixelWidth test.jpg
/test.jpg
pixelHeight: 2352
pixelWidth: 3136
不要な情報がいらない場合は、-gオプションに欲しいプロパティ名をつければOKです。
- 複数画像のプロパティを取ってくる
- sips -g propertyName imageFile1 imageFile2...
$ sips -g pixelHeight -g pixelWidth test.jpg test2.jpg
/test.jpg
pixelHeight: 2352
pixelWidth: 3136
/test2.jpg
pixelHeight: 2304
pixelWidth: 1736
画像のフォーマットを変更する
$ sips -s format tiff --out out.tiff test.jpg
/test.jpg
/out.tiff
画像のフォーマットは、jpeg、tiff、png、gif、jp2、pict、bmp、qtif、psdとなっています。
ちなみに、TIFF変換した画像のプロパティを見てみると、ちゃんと「可能な限りのオリジナルコンテンツの維持」ってのが果たされているのが分かります。
$ sips -g all out.tiff
/out.tiff
pixelWidth: 3136
pixelHeight: 2352
typeIdentifier: public.tiff
format: tiff
formatOptions: default
dpiWidth: 314.000
dpiHeight: 314.000
samplesPerPixel: 3
bitsPerSample: 8
hasAlpha: no
space: RGB
creation: 2006:01:09 11:49:37
make: OLYMPUS IMAGING CORP.
model: E-330
software: Version 1.0
description: OLYMPUS DIGITAL CAMERA
- JPEG画像の品質を指定して保存する
- sips -s format jpeg -s formatOptions default | [low|normal|high|best|
] |packbits --out outImage imageFile
$ sips -s format jpeg -s formatOptions low --out outImage test.jpg
/test.jpg
/out.jpg
formatOptionは直接パーセント指定も出来るようです。
$ sips -s format jpeg -s formatOptions 50 --out outImage test.jpg
/test.jpg
/out.jpg
画像をリサイズする
$ sips --resampleWidth 300 --out out.jpg test.jpg
/test.jpg
/out.jpg
このリサイズは、縦横比を維持したまま、幅を300ピクセルにリサンプルします。また、高さの指定は --resampleHeight になります。
$ sips --resampleHeightWidth 300 200 --out out.jpg test.jpg
/test.jpg
/out.jpg
縦横比を変更したい場合はこのオプションを使います。
$ sips --resampleHeightWidthMax 100 --out out.jpg test.jpg
/test.jpg
/out.jpg
結構使えるオプションかも知れません。DTP系の言葉で言えば(?)縦横100ピクセルのボックスにフィットするように画像を縮小する言えばよいでしょうか。
ちなみに、これらのコマンドは実際の画像のピクセル数が指定より小さい場合、逆に拡大されてしまうので要注意です。
サーバー用途?
sipsコマンドは必要最低限な実装になっています。ただ、将来的にはフィルターやレイヤーを扱えるようにしたいとAppleさんも言ってます*1ので、そのうち何とかなるかもしれません。
アプリケーションサーバで使うとしたら、アップロードされた画像のフォーマットチェックとか、画像のリサイズ等に使えますね。
ただ、個人的に使えない理由の一つに、グレースケールに変換等の処理が出来ない事です。あー、これさえあれば今すぐにでも使うのになぁ。やっぱりQuartz2D APIを叩かないと駄目かなぁ。
(まとめ)JavaでXMLをフォーマットする
結果をまとめてみました
必要ない場合はXMLを読み込む所でDOMを作らないようにし、XMLをフォーマットするという事だけに焦点を絞ってコードを書き直してみました。
検証した環境
- MacOSX Tiger 10.4.7
- Eclipse 3.2
- JavaSE5.0
- Apache Xerces2 2.8.0(http://xerces.apache.org/xerces2-j/)
- Apache Xalan-Java Version 2.7.0(http://xml.apache.org/xalan-j/)
XSLスタイルシートでXSLする方法(id:toolkit:20060719)
- 備考
- JDKだけで稼動します。単なるXSLです。
import java.io.File; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; public class JDKXSLTTest01 { public static void main(String[] args) throws Exception { // フォーマットしたいXML File inXml = new File("in.xml"); // フォーマットしたXML File outXml = new File("out.xml"); // インデントをつけるスタイルシート File xsl = new File("style.xsl"); // XSLT付きのTransformerを用意する。 TransformerFactory transFactory = TransformerFactory.newInstance(); Source xslSource = new StreamSource(xsl); Transformer transformer = transFactory.newTransformer(xslSource); Source source = new StreamSource(inXml); Result result = new StreamResult(outXml); // outXmlに書き出す。 transformer.transform(source, result); } }
style.xsl
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt"> <xsl:output method="xml" encoding="UTF-8" indent="yes" xalan:indent-amount="2"/> <xsl:template match="/"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
Xercesを使って、DOM Level3 APIを使用する方法(id:toolkit:20060720)
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSSerializer; public class LSSerializerTest01 { public static void main(String[] args) throws Exception { // フォーマットしたいXML File inXml = new File("in.xml"); // フォーマットしたXML File outXml = new File("out.xml"); // フォーマットしたいXMLのDOMを作る DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory .newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); InputStream inStream = new FileInputStream(inXml); Document document = docBuilder.parse(inStream); inStream.close(); // DOMImplementationLSを取得する DOMImplementation domImpl = document.getImplementation(); DOMImplementationLS domImplLS = (DOMImplementationLS) domImpl .getFeature("LS", "3.0"); LSOutput lsOutput = domImplLS.createLSOutput(); LSSerializer lsSer = domImplLS.createLSSerializer(); OutputStream out = new FileOutputStream(outXml); lsOutput.setByteStream(out); // LSSerializerのDOMConfigurationにformat-pretty-printをセットする lsSer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); lsSer.write(document, lsOutput); out.close(); } }
Xalanを使って、OutputPropertiesFactory.S_KEY_INDENT_AMOUNTを指定する方法(id:toolkit:20060721)
- 備考
- JDKの実装ではうまく行かないので、Xalanが必要。OutputPropertiesFactoryは com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory でも、 org.apache.xml.serializer.OutputPropertiesFactory でもどちらでもうまく行く。
import java.io.File; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory; public class OutputPropertiesFactoryTest01 { public static void main(String[] args) throws Exception { // フォーマットしたいXML File inXml = new File("in.xml"); // フォーマットしたXML File outXml = new File("out.xml"); // Transformerを用意する。 TransformerFactory transFactory = TransformerFactory.newInstance(); Transformer transformer = transFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); // OutputPropertiesFactory.S_KEY_INDENT_AMOUNTをセットする transformer.setOutputProperty( OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "2"); Source source = new StreamSource(inXml); Result result = new StreamResult(outXml); // outXmlに書き出す。 transformer.transform(source, result); } }
個人的な感想としては、パフォーマンスの問題を抜きにして考えると、スタイルシートを使う方法が一番良いのかな?と思います。
今後もそのまま使える方法だし、XSLT出来るし。
いやぁ、しかし、いろんな方からツッコミいただき、ありがたい次第です。いろいろ勉強になりました。
オレンジニュース(http://secure.ddo.jp/~kaku/tdiary/)からリンク張られると、アクセスが急激にアップしてちょっとビビりますけど(笑)。