HTTPSサーバに接続

HTTPSのサイトに接続して内容を取得する、というrubyスクリプトを書く必要があり、調べてみたら、次のサイトがとても参考になりました。

参考というか、ほぼそのまま使える情報だったのですが、もうちょっと具体的にメモしておきます。

httpsのサイト

httpsで公開されているサイトということで、今回は https://www.amazon.com/ につないでみたいと思います。

サイト証明書を取得する

まず、証明書が必要になるので、サイトに接続し、ブラウザの機能で取得します。
Firefoxの場合、ウィンドウ下部に下図のような南京錠アイコンが出ています。

この南京錠アイコンをダブルクリックするとページ情報のセキュリティページが見られます。そこの「証明書を表示...」ボタンを押します。「証明書ビューア」が開きますので、「詳細」タブを押し、その下部にある「エクスポート...」ボタンを押して証明書をエクスポートします。
ファイルの種類を「証明書パスを含む X.509 証明書 (PEM)」にして出力します。今回は、"www_amazon_com.crt"と名付けてスクリプトのあるディレクトリに保存します。

ソースを準備

アクセスする以下のようなrubyスクリプトを準備します。

require 'net/https'

https = Net::HTTP.new( 'www.amazon.com', 443 )
https.ca_file = './www_amazon_com.crt'
req = Net::HTTP::Get.new('/')

https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
https.verify_depth = 15

https.start{ |h|
  response = h.request( req )
  
  print "Content-type: text/html\n\n"
  print response.body
}

以上で完了です。

ファイルのエントロピーを求める

仕事の合間にちょっと時間が空いたらいろいろ勉強することにしているのですが、昨日はこのページを読むことにした。

圧縮アルゴリズムの基礎、みたいな感じのページです。
その中に、ファイルの平均情報量(エントロピー)を計算するpythonコードがあったので、それをrubyで書き直してみた。

#! ruby -Ku

#---------- 関数定義 ----------#

# 任意の底 a を持つ x の対数を計算.
def logA( x, a )
  return (Math.log(x)/Math.log(a))
end

# ファイルの内容を読んでエントロピーを計算します.
def entropy( name )
  p "check entropy [#{name}]"
  
  aCount = Array.new
  f = File.open( name, "rb" )
  loop{
    c = f.getc
    break if c == nil
    aCount[ c ] = 0 if aCount[ c ] == nil
    aCount[ c ] = aCount[ c ] + 1
  }
  f.close
  
  # 1st path.
  total = 0
  aCount.each{ |c|
    total = total + c if c != nil
  }
  
  # 2nd path.
  e = 0.0
  ap = 0
  total = total.to_f
  aCount.each{ |c|
    next if c == 0 || c == nil
    ap = c.to_f / total
    e += -ap * logA(ap, 2)
  }
  p " .. entropy  : #{e}"
  p " .. min size : #{e*total/8}(bytes)"
end


#---------- 実行 ----------#
entropy( "cantrbry/alice29.txt" )

同じファイルを処理してみたらほぼ同じ結果になったので、とりあえず間違っていないみたい。

C:\study-comp>ruby comp.rb
"check entropy [cantrbry/alice29.txt]"
" .. entropy  : 4.56768021217727"
" .. min size : 86836.7394737285(bytes)"

このアルゴリズムは、1文字ずつのエントロピーを計算しているわけで、そういう意味での平均情報量。

任意の底を持つ対数

ちょっとしたアプリを作ろうとしていたら、いつの間にかもともと作ろうとしてた言語から離れていて、rubyで書いていた。そこで対数を計算する必要があって、特になにも考えずに書いていたら、なんと、rubyには「任意の底を持つ対数を計算する関数」が用意されていないのですね!
プログラミング言語 Ruby リファレンスマニュアル」を参考にrubyで実装されている関数についてメモしておきます。

Math.log(x)
x の自然対数(底はMath.E)を返します。
Math.log10(x)
x の常用対数(底は10)を返します。

任意の底を持つ対数を計算するコード

以下のページを参考にして、テストしてみたらうまくいきました。

# 任意の底 a を持つ x の対数を計算.
def logA( x, a )
  return (Math.log(x)/Math.log(a))
end

p logA( 2, 2 ) # => 1.0
p logA( 4, 2 ) # => 2.0

大学受験とかの時なら一瞬で分かったことだろうに、すっかり忘れていたよ。

まさに、ヤク毛刈り

他の言語のlog実装

調べているときに見つけたサイトで、いろいろな言語でのlog実装が一覧になっていた。

任意の底の対数を計算する関数は意外に実装されていないのですね。

CGIにGETメソッドで渡されたデータをすべて表示してみる

GETメソッドで渡されたデータをすべて表示するCGIを書いてみます。
また併せて、RubyCGIライブラリを使ってヘッダとかも簡単に書く方法をメモしておきます。

まずは、リファレンスマニュアルの真似をしてコーディングしてみます。

#!ruby

require 'cgi'
cgi = CGI.new( "html3" )	# html3.2

cgi.out {
  cgi.html {
    cgi.head{ cgi.title{"[TEST CGI]"} } +
    cgi.body{
      cgi_get_data = "GET .. "
      cgi.keys.each {|k|
        cgi_get_data += "[\"#{k}\"=>\"#{cgi[k]}\"]"
      }
      
      # ↓これを出力.
      cgi_get_data
    }
  }
}

例えば、上記のコードをtest.cgiとして保存して、

http://example.com/test.cgi??t=dddd&s=Button

のように実行したら、以下のように表示されるはずです。

GET .. ["s"=>"Button"]["t"=>"dddd"]

CGIライブラリの内容を把握しつつコーディング

上記のコードでは、いまいちどういうデータの流れが起きているのか分かりにくいのですが、cgi.out や cgi.html、cgi.bodyなどは、ブロックにStringを渡す感じで書くようです。
ブロック内に式があるときには最後のStringが評価されるので、上記コードのhtml_bodyを生成しているコードでは、最後のcgi_get_dataが出力されるわけです。

次に、いちいち変数に代入しつつ、CGI.pretty()というクラスメソッドで、最終出力のHTMLも読みやすいコードにしてみます。

#!ruby

require 'cgi'
cgi = CGI.new( "html3" )	# html3.2

html_head = cgi.head{ cgi.title{"[TEST CGI]"} }

html_body = cgi.body{
  cgi_get_data = "GET .. "
  cgi.keys.each {|k|
    cgi_get_data += "[\"#{k}\"=>\"#{cgi[k]}\"]"
  }
  
  # ↓これを出力.
  cgi_get_data
}

html_html =  cgi.html {
  html_head +
  html_body
}

cgi.out {
  CGI.pretty( html_html, "\t" )
}

ブラウザ上では変わりませんが、ソースを見てみると読みやすくなっています。

参考URL

リファレンスマニュアルはちょっと分かりにくい。

もっともシンプルなアップローダCGI

「そういえば、アップローダってどういうCGIなの?」と、ちょっと考えて、ちょっと調べて、ちょっと書いてみた。rubyで。
こういうのがあると、ちょっとしたデータのやりとりに便利だし、いいかもしれない。
実際に動作したコードを公開。
ただし、以下のコードは、セキュリティに関する思考はゼロなので、使用するときには注意してください。

<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>uploader</title>
</head>

<body>
<form action="./fu.cgi" method="POST" enctype="multipart/form-data">
<input type="FILE" name="up_fname" size="30">
<input type="submit" value="送信">
</form>
</body>

</html>
#!ruby

# fu.cgi
#
# 超シンプルなアップローダ.
#
# ### 実際に使うときに修正する場所 ### #
# - 1行目のrubyへのパス.
# - file_nameに書いている保存先のパス.
# - 本当はLockとか使った方がいい.
# - エラー処理をきちんと.
# - スパム対策を何か.

require "cgi"
cgi = CGI.new

print "Content-type: text/html\n\n"
print "<html><body>"

# 保存先のファイル名を生成.
file_name = "./" +
  cgi['up_fname'].original_filename.split(/(\\|\/)/)[-1]

open(file_name, "w") {|f|
  print "<p>write to[" + file_name + "]</p>"
  
  f.binmode
  f.write cgi['up_fname'].read
}

print "</body></html>"

ちなみに、ruby 1.8では、original_filenameがパス無しのファイル名なので、file_nameの最後に処理しているsplit等はいらなさそうです。

Ruby/Tk:ウィンドウを増やすテスト

ボタンをクリックするたびにウィンドウを追加する方法を調べてみました。

参考書

Ruby/Tkについて、とても参考になる記述が多い本が手元にあったので、順を追って読み進めています。

Rubyアプリケーションプログラミング

Rubyアプリケーションプログラミング

TkToplevel

上述した参考書「Rubyアプリケーションプログラミング」のp300にルートウィジェット等の説明があります。ここにトップレベウィジェット(TkToplevel)について次のように書かれています。

トップレベウィジェットは、「新たなウィンドウとして作成される」という重要な点を除き、フレームウィジェットと同じです。新たなルートウィジェットといってもよいでしょう。

つまりこれを使えば新しくウィンドウを作ることは簡単に出来そうだ。
また、TkToplevelクラスについては、ソース(自分の環境の場合、C:/ruby/lib/ruby/1.8/tk/toplevel.rb)を見ると簡単な使い方は想像できる。

class TkToplevel<TkWindow
  # -- 略 -- #
  def initialize(parent=nil, screen=nil, classname=nil, keys=nil)
    # -- 略 -- #
      @classname = keys['class']
      @colormap  = keys['colormap']
      @container = keys['container']
      @screen    = keys['screen']
      @use       = keys['use']
      @visual    = keys['visual']
  # -- 略 -- #
end

ウィンドウのタイトルは変更できないのかな?

コード

以上のようにあれこれ調べて、実際に動作に成功したコードを以下に載せておきます。
ウィンドウそれぞれについて何か処理を行いたくなるのは目に見えているので、簡単なクラスを定義しています。

続きを読む

Ruby/TkでGUIをテスト

ちょっとかんが得ているアイディアをテストするために、GUIプログラムを作りたくなり、あれこれ考えてRuby+Tkという選択肢を試してみることにしました。

環境メモ

後述する作業を行った環境は次のようになります。

インストール

RubyにおいてTkへのブリッジ部分は最近のRubyなら最初から入っているので、特に問題ありませんでした。
WindowsにはTkが入っていないのでこれをインストールする必要があります。ActiveTclをインストールします。今回試したのは8.4.18.0というバージョン(8.5.1.0を試したのですがなんかうまくいかなかった)。ダウンロードしたインストーラを実行したらインストールが完了します。私の場合、C:\Tclというパスにインストールしましたが、インストール後、自動的に環境変数PATHにC:\Tcl\binと設定されていたので、どこにインストールしても問題はないでしょう。
インストール作業はこれで終わり。

テスト

こちら(Ruby/Tk チュートリアル)のサンプルコードをそのまま試してみました。

#!/usr/bin/env ruby
require 'tk'
TkButton.new {
  text "Hello, World!"
  command { print "Hello, World!\n" }
  pack
}
Tk.mainloop

ウィンドウに表示されたボタンを押すと、コンソールに文字列を出力するプログラムです。

問題なく実行されましたー!