備忘録的な何か

技術ブログ的な何かです

compactしないで、nilを含む配列からnilではない最初の要素を取得する方法

表題どおり。

arr = [nil,"hoge","fuga"]という配列があり、nilではない最初の要素を取得したい場合、私は以下のようにしていた。

 

arr.compact.first #=>"hoge"

 

コードゴルフ的に言えば18byteなので、あまりいただけない。(firstではなく、[0]でアクセスするのは、から配列の時に例外が起きてしまうのでNG)

 

ということで、findを使って、取得する。

arr.find{|x|x} #=>"hoge"

 

実は、falseが配列にあるとその要素もskipしてしまうが、nilまたはfalse以外の最初の要素を取得するということで。

tapを使って配列じゃなくてもeachする方法

処理対象がEnumerableな要素かそうでないかわからない時ってよくありますよね。

ActiveRecordなんかもそうですが、1要素の時は単体のオブジェクトがきて、複数要素の時はEnumerableが来るapiってわりとあります。(scanとかもそう。)

 

で、tapを使えばそれを解決できますよという話。

http://melborne.github.io/2012/10/29/rubys-new-control-structure-by-tap-and-break/

上記サイトがすべてを物語っていますが、tapの中でbreakを使うと、その値が返り値になることを利用します。

 

hoge.tap{|s| break [s] unless s.respond_to?(:each)}.each{|x| puts x}

#hogeは配列かどうかわからないが配列として扱いたい

 

 

まあ、tapを使わなくても普通に定義できます。

module Kernel

  def wrap_a

    self.respond_to?(:each) ? self : [self]

  end

end

1.wrap_a.each{|x| puts x} # 1をeach

ActiveRecordのjoinsで全カラムを取得する方法 (Rails小ネタ)

ActiveRecordではjoinsメソッドを利用して、inner joinすることができる。

 

例:

Blog.joins(:article) #=> SELECT "blogs".* FROM "blogs" INNER JOIN "articles" ON "articles"."blog_id" = "blogs"."id"

 

ここで、問題になるのは"blogs".*である。articlesの情報はとれていない。

Includes Vs Joins in Rails - Nikhil Lingutla

よって、上記で解説されているように、.selectをチェーンしてやらないといけない。

 

極めて単純な解決策としては、以下のscopeを作成してやれば良い。

  scope :joins_get_all_columns, ->(*tables) {

    joins(*tables).select("*")

  }

作成されるSQLは下記。

Blog.joins_get_all_columns(:article) #=> SELECT * FROM "blogs" INNER JOIN "articles" ON "articles"."blog_id" = "blogs"."id" 

 

この方法には、同じカラム名があると、どちらかで上書きされる問題がある。

もう少し頑張って、下記のようなスコープを作成すれば、カラム名の重複は避けられると思われる。

  scope :joins_get_all_columns, ->(*tables) {

    select_stmt = [%{"#{self.table_name}".*}] # joinするテーブルは通常通りのカラム名

    select_stmt << tables.map{|t|

      ar = ActiveRecord::Base.const_get(t.to_s.classify.singularize)

      table_name = ar.table_name

      table_name_single = table_name.singularize

      ar.attribute_names.map{|col|

        %{"#{table_name}".#{col} as #{table_name_single}_#{col}} # "table名_column名"

      }

    }

    joins(*tables).select(select_stmt.flatten.join(","))

  }

作成されるSQLは下記。

Blog.joins_get_all_columns(:article) #=> SELECT "blogs".*,"articles".id as article_id,"articles".content as article_content,"articles".created_at as article_created_at,"articles".updated_at as article_updated_at FROM "blogs" INNER JOIN "articles" ON "articles"."blog_id" = "blogs"."id" 

 

最後に、LEFT OUTER JOIN対応版。

  scope :joins_get_all_columns, ->(*tables,outer) {

    outer_join= outer && []

    my_table = self.table_name

    select_stmt = [%{"#{my_table}".*}]

    select_stmt << tables.map{|t|

      ar = ActiveRecord::Base.const_get(t.to_s.classify.singularize)

      table_name = ar.table_name

      table_name_single = table_name.singularize

      outer_join << %{LEFT OUTER JOIN "#{table_name}" ON "#{table_name}"."#{my_table.singularize}_id" = "#{my_table}"."id"} if outer

      ar.attribute_names.map{|col|

        %{"#{table_name}"."#{col}" as "#{table_name_single}_#{col}"}

      }

    }

    joins(outer_join || tables).select(select_stmt.flatten.join(","))

  }

Blog.joins_get_all_columns(:article,false) #=> inner join

Blog.joins_get_all_columns(:article,true) #=> left outer join

 

Railsでアプリを作ってると、Railsをいじるのが楽しくなって脱線してしまうから困る。

 

Rubyでまとめて動的クラス定義

リファクタリングRubyエディションを読んでいたら、def_eachというイカしたメソッドがあった。まとめて、似たようなメソッド定義をするものだ。

 

ここで、まとめて似たようなクラス定義をする関数もあったらいいなと思ったので作ってみた。役立つか微妙だが*。

hierarchy階層に、super_classを親クラスとしたクラスが定義される。

 

  def def_class_each(classes,hierarchy,super_class,&block)

    hierarchy ||= Object

    super_class ||= Object

    classes.each do |clas|

      next if hierarchy.const_defined? clas,false

      c = hierarchy.const_set clas, Class.new(super_class)

      c.class_exec(clas, &block)

    end

  end

 

こんな感じで使う。

 

      def_class_each [:Hoge,:Fuga],nil,nil do |clas|

        define_method("my") do |*args|

          "This class is #{clas}."

        end

      end

      puts Hoge.new.my #=> This class is Hoge.

      puts Fuga.new.my #=> This class is Fuga.

 

 

*ActiveRecordでもクラス定義はクラスファイルを吐いていることから、あんまりクラス定義は動的にしないものなのかなぁとか思ったりもする。

redcar(Ruby)のオレオレ改造をしてみた。

JRubyテキストエディタRedcarというのがある。

https://github.com/redcar/redcar

 

便利なのだが、個人的に以下の不満があった。

(1) Ruby1.8系でしか動かない。Evalする時にRuby1.9系のHashの構文とか使えない

(2) 日本語ファイル名がNG。日本語ファイル(UTF-8)以外は文字化けする。

(3)ドラッグアンドドロップでファイルが読み込めない。

(4) 内部でEvalする機能のOutputがシェルに出たりしてイマイチ。あと、手軽にEvalしたい。

(5) クリップボード履歴機能があるが、RedcarにFocusがあるときしか履歴をためてくれない

(6) 前回状態(開いてたタブ)を復帰できない。

 

ということで、オレオレ改造をしてみた。

https://www.dropbox.com/s/62ge9h0gpetyzx4/redcar_v_ore.zip

 

ひどい改造の仕方なので、ソースは見ると辛い気持ちになるかもしれません。(テストもろくにしてない。例えば、1.9でlambda{}が動かなくなった箇所をProc.new{}に全置換したり..)

 

あと、Windowsでしか動きません!!さらに、Javaは32bitしか動きません。

64bitOSの方は、32bitのJavaの方にPathを通してください。(もしくは、JRubyの-d32オプション。試してないけど。)

JRubyの1.7系はインスト必須。redcar.batで起動できます。

 

前置きが長くてあれですが、以下の機能を追加しました。

(1) Ruby1.9系,2.0系で動く。(試していないが、1.8系では動かない)

(2) 日本語ファイルが読み込める。但し、保存すると強制的にUTF-8になる。

(3) ドラッグアンドドロップでファイルが読める。

(4) 簡単に内部でEvalできる。これ一押し!右クリック→Evalもしくは、EvalSelection(選択範囲)で、指定したコードをEvalできる。トップレベルでevalしてるので、ローカル変数も保持できる。irbの変化版みたいなもので、内部からredcarをいじくり回せる。

例えば、all_tab.each{|x| puts [x.title,x.document.path,x.text]*"\n"}

をEvalすれば、開いている全タブのタイトル、ファイルパス、テキストを表示できる。

(all_tabとtextは私が作った簡単なメソッド(エイリアス))

 

他に、Redcar::Top::OpenNewEditTabCommand.new.run

とすれば、新しいTabが開く。

ということで、色々と夢がひろがりんぐな機能となっている。

 

(5) 起動していれば、大体のコピーした履歴は残る。

(6) 前回開いていたファイルを次回起動時に開いてくれる。

(7) なんとなく不安定動きをする。いや、かなり不安定。

 

で、このオレオレRedcarの何がいいかというと、ある程度作ったスクリプトを、evalで試すみたいなことができる。irbだと対話形式なので、やりにくい場合に便利。

 

ということで、全体的にあんまりいい出来ではないけれど、結構頑張ったのでこっそりブログの中だけで公開します...。自分の環境固有のコードを入れてて、他の環境だと動かなかったりするかも。(デフォルトフォントをメイリオにしてたり。)

 

Excel VBA To Ruby Converter

手抜きプログラムシリーズ。

 

Excel VBAが吐き出したコードをRubyのWin32ole対応のコードに変換するスクリプトを作成した。やっぱ、ExcelやるならRubyでしょ的な。

https://www.dropbox.com/s/jw51apxtrgjle3k/vba2ruby.rb

 

基本的な文法(SubとかWithとかForとか)は変換できるが、LEFTとかMIDとかの関数の変換は面倒なので対応していない。

 

実装は、オープンクラスを結構している&超絶適当な正規表現置換でお察しな感じ。

簡単に説明すると、

・withはinstance_evalで解決。

・他構文は頑張って変換。(ここ参考。http://slingfive.com/pages/code/scriptConverter/)

・大文字変数は、snakecaseに変換。

・名前付き引数はHashに変換。

・全体を WIN32OLE.connect('#{path}').Application.instance_eval do |_| で囲むことで、Applicationオブジェクトのメソッドをグローバル化。

・後は、ほそぼそした対応。手前に、_.付けたり、()つけたり。

 

どんな感じになるかというと、

require './vba2ruby.rb'

WIN32OLE.convert_vba_to_ruby(s,"C:\\hoge.xls") #sはvbaコード

を実行すると、

 

    Range("C9").Select

    ActiveCell.FormulaR1C1 = "fd"

    Range("C9").Select

    With Selection.Font

        .Color = -16776961

        .TintAndShade = 0

    End With

WIN32OLE.connect('C:\hoge.xls').Application.instance_eval do |_|

    Range("C9").Select

    ActiveCell().FormulaR1C1 = "fd"

    Range("C9").Select

    with Selection().Font do |_1|

        _1.Color = -16776961

        _1.TintAndShade = 0

    end

end

となる。単純な変換とは行かなくて、()がついたり、_1がついたりしている。(大文字メソッドは括弧を抜いてしまうと定数と判断されてしまう...)

あと、connectなので、対象のエクセル(上記だと、C:\\hoge.xls)を開いてる状態で実行する必要あり。

 

eval(WIN32OLE.convert_vba_to_ruby(s,"C:\\hoge.xls"))で直接変換結果を実行できる。

 

postgreSQL RC 9.3 インストール

http://www.enterprisedb.com/products-services-training/pgdevdownload

に行く。

 

Linuxの場合、Linux版のをダウンロードする。

 

chmod 755 postgresql-9.3.0-rc1-linux.run

で権限を付けて、

 

./ postgresql-9.3.0-rc1-linux.run

すれば、Windowsのインストーラっぽいのが出てくるので、Nextを連打すればインスト完了。pgadmin3は自動的にインストールされている模様。

 

終わったら、拡張パッケージを入れるか聞いてくるので、phppgadminくらいは入れておく。

 

偉そうに書いてるけど、PostgreSQL使ったことないので、ご容赦ください。