ラベル ruby の投稿を表示しています。 すべての投稿を表示
ラベル ruby の投稿を表示しています。 すべての投稿を表示

2015年12月31日木曜日

MRIをでバックビルドしてgdbでデバッグ

Untitled Document.md

MRIのビルド

./configure時の–enable-debug-envオプションは、コンパイラによる最適化を抑止してgdbでソースをおえるようにしてくれているはず。 システムのrubyと競合しないように–prefixでインストール先を指定する。

./configure --enable-debug-env --prefix /home/sato/ruby/ CFLAGS="-g3 -O0"
make
make install

環境変数を設定

さっきインストールしたrubyの実行ファイルが呼び出されるように環境変数を設定してやる。

RUBYPATH=$HOME/ruby/bin
export RUBYPATH
export PATH=$RUBYPATH:$PATH
export RUBYLIB=$RUBYPATH/lib/ruby

gdb用のヘルパーをホームディレクトリに入れておく

rubyの内部のデータは殆どがVALUEポインタで表現され、後でキャストしてから使用される。 この時、キャスト後が何のデータであるかがわからなければ、gdbのprintで中を覗くことができない。 このため、rubyのソースにバンドルされているgdb用のヘルパーを使う。

以下からダウンロード
https://github.com/ruby/ruby/blob/trunk/.gdbinit

cp .gdbinit ~/

定義されたヘルパーの一覧はhelp user-definedで見ることができる。

(gdb) help user-defined
User-defined commands.
The commands in this class are those defined by the user.
Use the "define" command to define a command.

List of commands:


******略******

rb_p -- User-defined
rb_ps -- Dump all threads and their callstacks
rb_ps_thread -- User-defined
rb_ps_vm -- Dump all threads in a (rb_vm_t*) and their callstacks
rbi -- User-defined
rp --   Print a Ruby's VALUE
rp_class --   Print the content of a Class/Module
rp_id --   Print an ID
rp_imemo --   Print the content of a memo
rp_string --   Print the content of a String
ruby_gdb_init -- User-defined
sdr -- User-defined

重要なのはrpで、これでVALUEの中身を知ることができる。

irbを起動しgdbをアタッチ

irb

まず、起動したirbのプロセスIDを調べる。

$ ps x|grep irb
1742 pts/1    Sl+    0:00 irb

別のターミナルから起動したirbにアタッチする。

gdb /home/sato/ruby/bin/irb -p 1742

ブレークポイントを設定してみる。

  (gdb) b proc_curry
  Breakpoint 3 at 0x7f60aa2bff64: file proc.c, line 2755.
  (gdb) c
  Continuing

proc_curryはProc#curryのC側での実装。 以下の関数がそれにあたる。

  rb_define_method(rb_cMethod, "curry", rb_method_curry, -1);

  static VALUE
  proc_curry(int argc, const VALUE *argv, VALUE self)
  {
      int sarity, max_arity, min_arity = rb_proc_min_max_arity(self, &max_arity);
      VALUE arity;

      rb_scan_args(argc, argv, "01", &arity);
      if (NIL_P(arity)) {
    arity = INT2FIX(min_arity);
      }
      else {
    sarity = FIX2INT(arity);
    if (rb_proc_lambda_p(self)) {
        rb_check_arity(sarity, min_arity, max_arity);
    }
      }

      return make_curry_proc(self, rb_ary_new(), arity);
  }

この状態で、irb側から以下のようにProc#curryを呼び出す

  Proc.new {|x,y|x+y}.curry

gdbがbreakする。

  Breakpoint 1, proc_curry (argc=0, argv=0x7f48ea86d258, self=139951178000880) at proc.c:2755
  warning: Source file is more recent than executable.
  2755        int sarity, max_arity, min_arity = rb_proc_min_max_arity(self, &max_arity);

コードの中の現在位置を確認

  (gdb) l
  2750      *     p b.curry[]                  #=> :foo
  2751      */
  2752    static VALUE
  2753    proc_curry(int argc, const VALUE *argv, VALUE self)
  2754    {
  2755        int sarity, max_arity, min_arity = rb_proc_min_max_arity(self, &max_arity);
  2756        VALUE arity;
  2757
  2758        rb_scan_args(argc, argv, "01", &arity);
  2759        if (NIL_P(arity)) {

selfのVALUEポインタを見てみる。

  (gdb) rp self
  T_DATA(proc): $4 = (struct RTypedData *) 0x7f48ec4029f0
  (gdb) print *(struct RTypedData *) self
  $5 = {basic = {flags = 12, klass = 139951178175800}, type = 0x7f48eae51ca0 <proc_data_type>,
  typed_flag = 1, data = 0x7f48ec85bd80}
  (gdb) print (*(struct RTypedData *) self)->basic
  $6 = {flags = 12, klass = 139951178175800}
  (gdb) print (*(struct RTypedData *) self)->basic->klass
  $7 = 139951178175800

2015年10月23日金曜日

Ruby Net::HTTPの使い方と(直接)使うべきでない時

Untitled Document.md

rubyで単純なHTTP Getをするのであれば、openuriが圧倒的に便利ファイルと同じように、http getリクエストを扱えてしまう。
しかし、比較的複雑なリクエスト(例:POST、ヘッダに何か入れる)の場合、net/httpを使う。

最も単純なGET

Net::HTTPでのgetは以下のようになる。 この程度ならopenuriを使った方が良い。

require "net/http"

# レスポンスをブロックの外で使いたい時には事前に定義しておこう
# ブロックがクロージャであることを忘れずに
response=nil

uri = URI.parse("http://example.net/")
Net::HTTP.start(uri.host, uri.port) do |http|
  response = http.get("/index.html")
  puts response.code # ステータスコード
  puts response.message # メッセージ
  puts response.body # レスポンスボディ
end

puts response.message # => OK

以下のようにインスタンスを作成してからコネクションを開く事もできる。
しかしこの場合には、最後にクローズしないとだめ。忘れるとシツレイ。
startにブロックを渡せば、ファイルオープンと同じように最後にクローズを勝手にやってくれるので安心。

http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new("/index.html")
http.close

HTTPSへのGET

以下のようにstartの引数に:use_ssl=>trueを渡してやる。

require "net/http"

uri = URI.parse("https://example.net/")
Net::HTTP.start(uri.host, uri.port,:use_ssl=>true) do |http|
  response = http.get("/index.html")
  puts response.body
end

POSTの場合

require "net/http"

uri = URI.parse("http://example.net/")
Net::HTTP.start(uri.host, uri.port) do |http|
  response = http.post("/search.cgi",'query=foo',header=nil)
  puts response.code
  puts response.message
  puts response.body
end

素晴らしいNet::HTTP解説のリンク

チートシート
http://www.rubyinside.com/nethttp-cheat-sheet-2940.html

RestでJSONリクエストを投げる例。
https://www.socialtext.net/open/very_simple_rest_in_ruby_part_3_post_to_create_a_new_workspace
http://altarf.net/computer/ruby/2890

restを作る時に便利なgem
https://github.com/rest-client/rest-client

以下に該当する場合にはNet::HTTPを使うべきでない。

単純なGETしかしない

→openuriを使う
http://ruby-doc.org/stdlib-2.1.0/libdoc/open-uri/rdoc/OpenURI.html

クローラーを作りたい

→anemoneを使う
https://github.com/chriskite/anemone

Amazonの商品検索、操作APIを使いたい

→asinを使う
https://github.com/phoet/asin

AWSのRESTを使いたい

→aws-sdk-rubyを使う
https://github.com/aws/aws-sdk-ruby

なんかRESTを使いたい。

→rest-clientを使う
https://github.com/rest-client/rest-client

2015年9月27日日曜日

rubyのテクニック覚書

rubyの勉強をしていくうちに、好ましい記述法や技術が分かってきた。

正規表現でマッチした文字を取り出したいだけ時にはmatchメソッドや=^メソッドではなくString型の[]メソッドが最も短く書ける

p "@@@@@hoge@@@@@"[/[a-z]+/] #=> "hoge"

パーセント記法はとても便利、場合によってはかなりみやすくなる。

p %s|symbol|  #=> :symbol
p %w|abc def gh| #=> ["abc", "def", "gh"]
p %x|ruby -v| #=> "ruby 2.1.4p265 (2014-10-27 revision 48166) [x86_64-linux]\n"
p %r|[A-Z]| #=> /[A-Z]/

ある配列から他の配列を作りたいときなどに、以下のようにeachの前で結果を入れるための配列を初期化しておくのは冗長

array=[1,2,3,4,5,6]
array2=[]
array.each{|i| array2 << i * 2}
p array2 #=> [2, 4, 6, 8, 10, 12]

以下のようにmapメソッドで関数型言語の流儀で書くべき。

array=[1,2,3,4,5,6]
array2 = array.map{|i| i * 2}
p array2 #=> [2, 4, 6, 8, 10, 12]

条件によって要素を外したい、条件に合う要素のみの配列を作りたい場合にはreject,selectメソッドがよい。
さらに、配列からハッシュを作りたいときなどには、以下のようにreduceメソッドがよい。

array=[1,2,3,4,5,6]
hash = array.reduce({}){|h,i| h[i]= i*2; h }
p hash #=> {1=>2, 2=>4, 3=>6, 4=>8, 5=>10, 6=>12}

case文は===演算子で含有関係が評価されため、単純な一致の場合以外にも使える。
Integerの場合にはRangeがそれを含むかどうかを評価できる。

i = 10
case i
when 1..50
  puts 'small'
when 51..10
  puts 'big'
end

Stringの場合には正規表現が使える。

str = 'dojadf@ss'
case str
when /@/
  puts "include @"
when /[a-z]+/
  puts "only small letter"
end

2015年8月7日金曜日

Rails runnerの簡単な使い方

RailsのrunnerはRailsの環境の上で任意の処理を書くことができる。 Active Record経由でバッチ処理をするのには最適な機能。
実行の方法は2つある。
  1. Railsで作成したアプリのメソッドを直に指定してやる
  2. ファイルを読み込ませる
紹介されている例は(1)が圧倒的に多く、 lib/配下にTasks::XXXのようなモジュールを作りautoload_pathsを指定してRailsアプリにこのモジュールを読み込ませてやる様な事がされている。 (2)の場合、Webアプリでautoload_pathsやrequireをして処理を記載するファイルを読み込む必要がない。
個人的には、Webアプリと独立したバッチ処理を長々とWebアプリにメソッドとして追加するのは違和感があるので(2)の方が良い気がする。 それに、autoload_pathsを変更しなくていいので簡単。
例:
予めRailsルートはいかにrunner/を作っておく。
mkdir runner
あとは適当なファイルを作って呼び出すだけ。
runner/runner.rb
# Railsでつくったアプリが読み込まれてから実行されるので、何もrequireしなくてもActive Record経由でモデルにアクセスできる
# この場合はTagという名前のモデルが存在する環境
p Tag.count
成功。
$ rails runner runner/runner.rb
21931

2015年8月4日火曜日

AmazonAPIとruby gem asin

Amazonの商品情報の問い合わせにはrubyのgemのasinが便利。
https://github.com/phoet/asin

上記ではcurbを使ってアクセスしているが何故かうまくいかなかった。
かわりにhttpiを使うと動いた。
直接、ハッシュを返してくれるので楽。
require 'asin'
require 'httpi'

# Setting for amazon API
include ASIN::Client

ASIN::Configuration.configure do |config|
config.secret        = 'SECRET'
config.key           = 'SECRET'
config.associate_tag = 'SECRET'
config.host = 'ecs.amazonaws.jp'
end
HTTPI.adapter = :net_http
client = ASIN::Client.instance

# Define amazon api response group
RESPONSE_GROUP = [:Medium,:Reviews]

# Search!!
api_responses = client.search({:Title => "我輩は猫である", :SearchIndex => :Books, :ResponseGroup => RESPONSE_GROUP})
p api_responses
アマゾンAPIの仕様

2015年5月5日火曜日

rubyでのファイル名に適したTimeの文字列表現

ファイルを作るスクリプトを何度も起動する必要がある場合いちいちファイル名を指定ルルのは面倒。 現在時刻から文字列を作成してファイル名にしておくといちいち指定する必要がなくて便利. しかしTime.now.to_s現在時刻の文字表現を取得すると、ファイル名にスペースが入って気持ちが悪い。 さらに、頭が年度だとタブでのファイル名保管が面倒

2.1.4 :013 > Time.now.to_s
 => "2015-05-06 00:20:39 +0900"

こんなかんじがよい

2.1.4 :008 > Time.now.strftime("m%M-h%H-d%d-%b-%Y")
=> "m53-h16-d06-May-2015"

2015年4月3日金曜日

rubyの@とselfのちがい

下によると、@hoge='hoge'はインスタンス変数を直接書き換えてself.hoge='hoge'はセッターのメソッド経由で書き換えるらしい。

https://www.ruby-forum.com/topic/3546147

2014年10月15日水曜日

rubyでBag-of-Wordsの計算

Mecabという日本語の形態素解析機があります。

Mecab公式

Bag-of-Wordsをつかっていろいろやってみたかったので、いろいろライブラリを探していました。
Mecabを使ったBag-of-Wordsの計算はRでRmecabというライブラリが提供されていますが、Rでは大きなテキストに対応できないそうで、私の環境では数十M程度のテキストファイルでメモリ関連のエラーが頻発しました。公式サイトでも大きなファイルには対応できないと書いてあります。

RMecab公式

そこで、rubyでBag-of-Wordsを計算してみました。
(Mecabを使って作ったのでTororoっていう名前にしました。)

Tororo

今はまだBag-of-Wordsの計算ぐらいにしか使えませんが、将来的にはMecabのラッパーとして、rubyからの日本語構文解析全般に使えるようにしたいです。 (あとgemの形にしたい) そのうち、READMEをもっとまともに書いて使える用にします。

githubのOVERVIEWに書かれている以下の文言は、これからこうしたい!という意気込みです。
Parse Japanese text with Mecab.
Simple ruby interface.

2014年10月4日土曜日

rubyは文字列のインクリメントができる!

rubyは文字列のインクリメントができます。
こういう細かいところに配慮されているっていうところが素晴らしい
こういった動作はCであれば'x'を格納するアドレスをずらしていって実装するしかなかった。
str='x'
4.times{
   puts str
  str=str.next
}

実行結果

x
y
z
aa

ちゃんと桁上げまでされます。

2014年9月27日土曜日

rubyからMecabを使う

ruby-mecabのインストール

別途、Mecabのインストールが必要 参考:  Mecab公式
wget https://mecab.googlecode.com/files/mecab-ruby-0.996.tar.gz
tar zxf mecab-ruby-0.996.tar.gz 
cd mecab-ruby-0.996

ruby extconf.rb
su
make;make install

使ってみる。

require 'MeCab'
require 'csv'

class Parser
  @targger
  def parse(str)
    node=@targger.parseToNode(str)

    while node do
   #助動詞とかの情報はCSV形式だよ。
   #必要なのは1行目のみ
     feature=CSV.parse(node.feature)[0]
      puts node.surface+' '+feature[0].to_s
      node=node.next
    end
  end

  def initialize
    @targger=MeCab::Tagger.new()
  end

end

obj=Parser.new
obj.parse('今宵は月が綺麗ですね')

実行結果

[sato@localhost konbu]$ ruby parser.rb 
 BOS/EOS
今宵 名詞
は 助詞
月 名詞
が 助詞
綺麗 名詞
です 助動詞
ね 助詞
 BOS/EOS

これは便利!

2014年9月23日火曜日

RubyMineの最重要ショートカット

rubyやRoR用IDEのRubyMineの重要ショートカット
これぐらい知っておけば十分
  • command+/
  • コメントアウトを付加/削除
  • control+space
  • 入力補完
  • command+J
  • Live Templates呼びだし
  • F4
  • 選択したクラスやメソッド等にジャンプbr>
  • command+shift+.
  • <%= %>を挿入
  • option+enter
  • Quick Fix呼びだし
  • Ctrl+Alt+U
  • Model dependency diagrambr
  • Alt+enter
  • 正規表現の簡易チェック

rubyプロジェクトのフォルダ構成のベストプラクティス(?)

rubyのプロジェクトのフォルダ構成はこんな形がよいみたい
つーかgemはみんなこんな感じ

crawler/
  lib/
   crawler.rb #動作用モジュールCrawler
   crawler/
    database.rb #必要なクラス
    webpage.rb
    database_connection.yml #DBの接続情報
  bin/
   exec_crawler.rb #動作させるときに実際実行されるファイル


上記の例だと、exec_crawler.rbを実際に叩いて使う
exec_crawler.rbはcrawler.rbにあるメソッドを使う。
exec_crawler.rbが行うのはコマンドライン引数の解釈など
lib/をインクルードするとAPI的に使える。

exec_crawler.rbではこんな感じでロードパスにlib/を追加してreuireする
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
require 'crawler'
include Cralwer
この場合、カレントディレクトリがbin/の状態で実行するのでlib/内でのファイルの読み込みはこんな感じ
YAML::load_file(File.join(__dir__, 'database_connection.yml'))

2014年9月21日日曜日

rubyからPostgreSQLへのアクセス その1

pgというgemでPostgreSQLへrubyからアクセスできます。
rubyには史上最強のO/R mapperであるActiveRecordが存在するため、PostgreSQLとのインターフェイスとしてはそちらを先に想定すべきです。
しかし、ちょっとSQLを投げたいだけの場合にはむしろこちらのほうが便利です。


基本:行数を表示するだけ


$LOAD_PATH << "."
require 'pg'

#接続する
db = PG::connect("127.0.0.1",5432 ,"","","mydb","sato","password")

#クエリの定義
sql="select count(*) from tbl";

#クエリの実行
result=@db.exec(sql)

#行数の表示
p result[0]["count"].to_i

注意していただきたいのはSELETしてきた結果の取り出しです。
pgでは結果はレコードを表すハッシュの配列として帰ってきます。
id,name.ageというテーブルがある場合、結果として帰ってくるのは次のような配列です。
result[0]['id']=>1
result[0]['name']=>sato
result[0]['age']=>26


result[1]には次の結果の行が入っています。
select max(id),max(age) のようなクエリを使った場合結果として帰ってくる行の名前が
同名であるため、maxというキーが2つ帰ってきます。
これでハッシュが上書きされるため正常に取り出せません。
AS 句をうまく使いましょう

rubyのopenuriやnet/http でエラーが出ちゃうときの対処

rubyのopenuriやnet/http で503とかのエラーがでるとき、何度かリトライしたいということがあります。 単純にアクセス繰り返すとDOSってると思われてしまいます。 次のようにGETを行うコードのラッパーとなるメソッドを作り、これを呼ぶとよさそう アクセス前の待機時間を引数にとっておき、503エラーが出た場合などは同じメソッドを待機時間を延ばしてリトライします。 400秒待ってダメなような場合はあきらめます。
     def carefulGet(url,waittime)
    begin
      sleep waittime
      charset = nil
      html = open(url, :proxy => nil) do |f|
        charset = f.charset
        f.read
      end
      return html
    rescue => evar
      puts evar
      if waittime<400 then
        puts 'GET error!! retry '
       #ここで同じメソッドを待機時間を上げて再起呼び出し
    carefulGet(url,waittime+20)
      else
        puts 'GET error!! QUIT'
      end
    end
  end

2014年9月20日土曜日

CentOSでrubyにnokogiri

普通にgem install nokogiriだといろんな依存ライブラリが足りない

こんな感じで
yum install libxslt libxslt-devel libxml2 libxml2-devel -y
gem install nokogiri --use-system-libraries