HTTPSサーバに接続
HTTPSのサイトに接続して内容を取得する、というrubyスクリプトを書く必要があり、調べてみたら、次のサイトがとても参考になりました。
参考というか、ほぼそのまま使える情報だったのですが、もうちょっと具体的にメモしておきます。
httpsのサイト
httpsで公開されているサイトということで、今回は https://www.amazon.com/ につないでみたいと思います。
サイト証明書を取得する
まず、証明書が必要になるので、サイトに接続し、ブラウザの機能で取得します。
Firefoxの場合、ウィンドウ下部に下図のような南京錠アイコンが出ています。
この南京錠アイコンをダブルクリックするとページ情報のセキュリティページが見られます。そこの「証明書を表示...」ボタンを押します。「証明書ビューア」が開きますので、「詳細」タブを押し、その下部にある「エクスポート...」ボタンを押して証明書をエクスポートします。
ファイルの種類を「証明書パスを含む X.509 証明書 (PEM)」にして出力します。今回は、"www_amazon_com.crt"と名付けてスクリプトのあるディレクトリに保存します。
ソースを準備
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)"
任意の底を持つ対数
ちょっとしたアプリを作ろうとしていたら、いつの間にかもともと作ろうとしてた言語から離れていて、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を書いてみます。
また併せて、RubyのCGIライブラリを使ってヘッダとかも簡単に書く方法をメモしておきます。
まずは、リファレンスマニュアルの真似をしてコーディングしてみます。
#!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" ) }
ブラウザ上では変わりませんが、ソースを見てみると読みやすくなっています。
もっともシンプルなアップローダ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について、とても参考になる記述が多い本が手元にあったので、順を追って読み進めています。
- 作者: 前田修吾,まつもとゆきひろ,やまだあきら,永井秀利
- 出版社/メーカー: オーム社
- 発売日: 2002/04/01
- メディア: 単行本
- 購入: 3人 クリック: 45回
- この商品を含むブログ (27件) を見る
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