Hashie::Mashおかしい

(解決した、下に書いた)

これ実行すると

#!/usr/bin/env ruby
require 'rubygems'
require 'hashie'

arr = Array.new
3.times do |i|
  arr.push Hashie::Mash.new(:count => i, :text => 'test'*i)
end

arr.each do |i|
  p i
  puts "count : #{i.count}"
  puts "text : #{i.text}"
end


countだけ同じ値がでるんだけど、なんで・・・?

#<Hashie::Mash count=0 text="">
count : 2
text :
#<Hashie::Mash count=1 text="test">
count : 2
text : test
#<Hashie::Mash count=2 text="testtest">
count : 2
text : testtest


Hash#countで保持しているkeyの数が返ってくるらしい。
hashieはmethod_missingで実装されているので、missingじゃないの呼び出してた。


このへん全部使えない

1.8.7 :002 > h.methods
 => ["find", "[]=", "inspect", "convert_value", "merge", "tap", "reject!", "clone", "take", "store", "__send__", "object_id", "public_methods", "reject", "instance_variable_defined?", "minmax", "hashie_inspect", "freeze", "equal?", "indexes", "member?", "each", "values", "delete", "sort", "extend", "partition", "each_cons", "send", "any?", "each_with_index", "hashie_stringify_keys!", "default", "methods", "values_at", "initializing_reader", "detect", "hashie_stringify_keys", "hash", "take_while", "default=", "default_proc", "dup", "replace", "instance_variables", "to_enum", "collect", "clear", "stringify_keys", "min_by", "invert", "sort_by", "eql?", "indices", "regular_reader", "size", "has_key?", "group_by", "enum_cons", "instance_eval", "id", "one?", "enum_with_index", "singleton_methods", "find_index", "deep_merge!", "taint", "empty?", "shift", "drop", "each_value", "instance_variable_get", "frozen?", "enum_for", "map", "shallow_merge", "display", "instance_of?", "max_by", "method", "grep", "to_a", "regular_writer", "update", "first", "to_json", "has_value?", "instance_exec", "type", "fetch", "none?", "reverse_each", "stringify_keys!", "rehash", "protected_methods", "find_all", "delete_if", "regular_dup", "method_missing", "==", "min", "to_mash", "===", "drop_while", "each_key", "instance_variable_set", "shallow_update", "each_slice", "key?", "inject", "kind_of?", "minmax_by", "respond_to?", "keys", "count", "to_s", "convert_key", "value?", "class", "to_hash", "index", "zip", "private_methods", "=~", "tainted?", "__id__", "select", "deep_merge", "length", "max", "hash_inspect", "untaint", "nil?", "each_pair", "entries", "deep_update", "cycle", "merge!", "reduce", "enum_slice", "include?", "is_a?", "[]", "all?"]

idとかclassとかcountとかindexとかtypeとかmapとかmaxとかlength使えなかったとはワロタ


こうすると値を取り出せる

1.8.7 :007 > h.count = 30
 => 30
1.8.7 :008 > h['count']
 => 30

gemを全て新しいRubyに入れなおす

rvmで、新しいrubyに乗り換える時によくやる。

ruby-1.8.7-p352 → 1.8.7-p358にしたとする。

rvm get latest
rvm reload
rvm list known
rvm install 1.8.7-p358


gem listを保存しておいて

gem list | ruby -lane 'puts $_.split(/\s/)[0]' > ~/gems.txt


あたらしい方に入れなおす

rvm use 1.8.7-p358
cat ~/gems | xargs gem install --no-rdoc --no-ri
gem list
rvm use 1.8.7-p358 --default


そういえばrdoc、gem serverコマンドで起動するやつで見ると見やすい。

herokuセットアップ、sinatraを動かす

herokuでsinatra動かした。

サインアップ

https://heroku.com でサインアップ、メールが来るのでconfirm

herokuコマンドインストール

gem install heroku
heroku login

で ~/.ssh/id_rsa.pub がアップロードされて認証された。自動的すぎてすごい。

アプリを作る

普通のsinatraアプリを作ってみる。

% ls
Gemfile      Gemfile.lock Procfile     web.rb

の4つのファイルが最終的にできる。


web.rb

require 'rubygems'
require 'sinatra'

get '/' do
  'hello work'
end

Gemfile

source :rubygems
gem 'sinatra'
gem 'thin'

Procfile を作る。中身はこれ

web: bundle exec ruby web.rb -p $PORT

config.ruを使うならこうらしい

web: bundle exec rackup config.ru -p $PORT

ローカルで動かしてみる

Procfileで動かすためにforemanってのを使うらしい。

gem install foreman
bundle install
foreman start

localhost:5000でsinatraが起動する

herokuにデプロイ

gitリポジトリにしてherokuにpushする。

git init
git add Gemfile Gemfile.lock web.rb Procfile
git commit -m 'first commit'

Gemfile.lockもcommitする。

heroku create --stack cedar
git push heroku master

remoteにherokuが追加されているので、pushする

herokuで動いた

http://simple-mountain-9295.herokuapp.com みたいな長い名前で起動するので、 https://api.heroku.com/myapps/ で名前を変更した。

こうなった。
http://shokai.herokuapp.com/

mini_magickとImageMagickで画像を切り取る

capybara-webkitで撮影した縦長のwebページスクリーンショット(PNG)を、1:1.41のA4縦長で印刷できるサイズ(JPEG)に分割する。

brew install imagemagick
gem install mini_magick

mini_magick版

#!/usr/bin/env ruby
require 'rubygems'
require 'mini_magick'

source = ARGV.shift
img = MiniMagick::Image.open source

puts "#{img[:width]}x#{img[:height]}"
w = img[:width]
h = (w*1.41).to_i

0.upto(img[:height]/h) do |i|
  puts fname = "#{i}.jpg"
  img = MiniMagick::Image.open source
  img.format('jpg')
  img.quality('95')
  img.crop("#{w}x#{h}+0+#{i*h}")
  img.write(fname)
end

結果

1236x8026
0.jpg
1.jpg
2.jpg
3.jpg
4.jpg
ruby crop.rb ../capybara-screenshot/shokai.png  7.73s user 1.82s system 89% cpu 10.663 total

7.73秒かかった。
ImageMagickはファイルにあわせて画像フォーマットを決めてくれるのに、mini_magickではformatメソッドで指定しないと元画像のままPNG形式で保存されてしまっていた。
exiftoolで確認すべき。


ImageMagick

#!/usr/bin/env ruby
img = ARGV.shift

width, height = `identify '#{img}'`.split(/\s/).select{|i|
  i =~ /^\d+x\d+$/
}.first.split('x').map{|i| i.to_i}

puts "#{width}x#{height}"
w = width
h = (w*1.41).to_i

0.upto(height/h) do |i|
  puts fname = "#{i}.jpg"
  puts cmd = "convert -quality 95 -crop #{w}x#{h}+0+#{h*i} #{img} #{fname}"
  system cmd
end

結果

0.jpg
convert -crop 1236x1742+0+0 ../capybara-screenshot/shokai.png 0.jpg
1.jpg
convert -crop 1236x1742+0+1742 ../capybara-screenshot/shokai.png 1.jpg
2.jpg
convert -crop 1236x1742+0+3484 ../capybara-screenshot/shokai.png 2.jpg
3.jpg
convert -crop 1236x1742+0+5226 ../capybara-screenshot/shokai.png 3.jpg
4.jpg
convert -crop 1236x1742+0+6968 ../capybara-screenshot/shokai.png 4.jpg
ruby crop_imagemagick.rb ../capybara-screenshot/shokai.png  1.97s user 0.52s system 74% cpu 3.089 total

1.97秒だった。
ImageMagickを直に使ったほうが圧倒的に速い。

感想

  • ImageMagickを直に使ったほうが速い。しかし・・
  • 画像のサイズやフォーマットなどを読み取る場合、identifyコマンドを使うのだが文字列で返ってくるのでparseが面倒。mini_magickやrmagickを使ったほうが楽。
  • 画像のサイズを変えたり、色調を補正するぐらいならImageMagickをそのまま使ったほうが楽だし、高速。
    • 特に細かいオプションを設定したりする場合、それをラッパーから指定する方法を調べるのがめんどい。
  • 複数の画像を重ねあわせたりするならmini_magickなどを使ったほうが書きやすい。

使い分け

ちょっと画像を加工→ImageMagickを直に使う
画像のプロパティを読み取り、細かくに加工(というか装飾)→mini_magick
がいいのでは。

画像をpdfファイルにまとめる

今までhomebrewで入れれるpdfjamを使っていたのだが、macportsにしかないpdflatexに依存しているのであまり使いたくない。
prawn(https://github.com/prawnpdf/prawn)を使うとRubyだけで作れるので、乗り換えることにした。


prawnpngを入れると遅くなるので、jpegを使ったほうがいい。

gem install prawn

join-images.rb

#!/usr/bin/env ruby
require 'rubygems'
require 'prawn'

puts out_fname = ARGV.pop
p imgs = ARGV

Prawn::Document.generate(out_fname) do
  imgs.each do |img|
    image(img,
          :fit => [bounds.absolute_right - bounds.absolute_left,
                   bounds.absolute_top - bounds.absolute_bottom]
          )
    start_new_page
  end
end

結合する

ruby join-images.rb *.jpg out.pdf

フチ無し、画像をページぴったりにする

join-images-fit.rb

#!/usr/bin/env ruby
require 'rubygems'
require 'prawn'

puts out_fname = ARGV.pop
p imgs = ARGV

Prawn::Document.generate(out_fname) do
  imgs.each do |img|
    image img, :at => [-1*bounds.absolute_left, bounds.absolute_top], :fit => [bounds.absolute_right+bounds.absolute_left, bounds.absolute_top+bounds.absolute_bottom]
    start_new_page
  end
end

capybara-webkitでwebページのスクリーンショットを撮る

capybara使うと簡単にキャプチャできる。もともとjsのheadless testの為のツールなのでjsも実行されるし良い。


Macの場合

brew install qt
gem install capybara capybara-webkit
#!/usr/bin/env ruby
require 'rubygems'
require 'capybara-webkit'

driver = Capybara::Driver::Webkit.new 'web_capture'
driver.visit 'http://shokai.org/blog/'
driver.render 'out.png'

MacGUIでログインしていなくてもキャプチャできる。モニタほぼ使ってないサーバー状態のMacで使いたかったのでありがたい。
webkit2png(http://d.hatena.ne.jp/shokai/20120214/1329201906)だとGUIでログインしないとwindowが見つからないというエラーでキャプチャできなかったが、capybara-webkitならxvfbも必要ない。

Ubuntuの場合

sudo apt-get install qt4-dev-tools xvfb
gem install capybara capybara-webkit headless

GUIが入っていなくてもheadlessでxvfbを作れば良い。

#!/usr/bin/env ruby
require 'rubygems'
require 'capybara-webkit'
require 'headless'

Headless.ly do
  driver = Capybara::Driver::Webkit.new 'web_capture'
  driver.visit 'http://shokai.org/blog/'
  driver.render 'out.png'
end

フォントが入っていないからか、日本語が表示できない


フォントを入れたら日本語でた!! id:myatsmoto に教えてもらった。

sudo apt-get install x-ttcidfont-conf ttf-kochi-mincho-naga10 ttf-kochi-gothic-naga10

画像のサイズを求める方法

ImageMagick

imagemagickについてくるidentifyコマンドを使う

identify ファイル名

結果

ファイル名 PNG 1200x4702 1200x4702+0+0 8-bit DirectClass 890KB 0.000u 0:00.000

mini_magick

ImageMagickRubyラッパー。

gem install mini_magick
#!/usr/bin/env ruby
require 'rubygems'
require 'mini_magick'

ARGV.each do |arg|
  puts arg
  img = MiniMagick::Image.open(arg)
  puts "#{img[:width]}x#{img[:height]}"
end

exiftool

MacのhomebrewやDebian系のaptでインストールできる、exiftoolコマンドを使う

exiftool ファイル名 | grep "Image Size"

結果

Image Size                      : 1200x4702

JRuby

JRubyをでワンライナー

jruby -rjava -e 'img=javax.imageio.ImageIO.read(java.io.File.new ARGV.shift); puts "#{img.width}x#{img.height}"' ファイル名

結果

1200x4702