Home > Programming > Ruby

Ruby Archive

RSpecでMacro?を書く。

  • 2010-01-08 (Fri)
  • Ruby

RSpecを使ってSpecを書いていると何度も同じことを書くことが多いです。

たとえばパラメータに応じてページタイトルを変えたい場合とか、validationがきちんと行われているかとか。。。
毎回、 it “should have hoge” do … end と書いているのは大変です。
RSpecではいくつかの方法を使用して、もっと簡単に書くことができます。

クラスメソッドを使う

一つのspecファイルで何度も使うものであればクラスメソッドにします。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  def self.it_should_have_tag(selector, content)
    it "should have '#{content}' at '#{selector}'" do
      response.should(have_tag(selector, content))
    end
  end
end

と書いておけば、以下のような書き方が可能になります。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  def self.it_should_have_tag(selector, content)
    it "should have '#{content}' at '#{selector}'" do
      response.should(have_tag(selector, content))
    end
  end

  it_should_have_tag('tr>td', 'MyString')
end

it_should_have_tag(’tr>td’, ‘MyString’) の部分だけで適切なspecに展開されます。
1行で簡潔に書けますので、理解する時間も短くてすみます。

自分はページタイトル、meta description、meta keywords、RSSのスペックに以下のコードを使用しています。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  def self.it_should_have_tag(selector, content)
    it "should have '#{content}' at '#{selector}'" do
      response.should(have_tag(selector, content))
    end
  end

  def self.it_should_have_page_title(title)
    it_should_have_tag('head>title', title)
  end

  def self.it_should_have_meta_description(meta_description)
    it_should_have_tag('head>meta[name=description][content=?]', meta_description)
  end

  def self.it_should_have_meta_keywords(meta_keywords)
    it_should_have_tag('head>meta[name=keywords][content=?]', meta_keywords)
  end

  def self.it_should_have_rss_link(link)
    it_should_have_tag('head>link[rel=alternate][type=application/rss+xml][href=?]', link)
  end
end

モジュールに追い出す

さらによく使う項目、上記のhtmlに関するspecなどはたくさんのところに書かれるであろうことが推測されます。
そういう場合には、モジュールに追い出してしまうということが可能だということを同僚のquekさんに教えていただきました。1

$RAILS_ROOT/spec/support フォルダにモジュールのファイルを書きます。
上記の例であれば

[$RAILS_ROOT/spec/support/tag_macros.rb]

module TagMacros
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def it_should_have_tag(selector, content)
      it "should have '#{content}' at '#{selector}'" do
        response.should(have_tag(selector, content))
      end
    end

    def it_should_have_page_title(title)
      it_should_have_tag('head>title', title)
    end

    def it_should_have_meta_description(meta_description)
      it_should_have_tag('head>meta[name=description][content=?]', meta_description)
    end

    def it_should_have_meta_keywords(meta_keywords)
      it_should_have_tag('head>meta[name=keywords][content=?]', meta_keywords)
    end

    def it_should_have_rss_link(link)
      it_should_have_tag('head>link[rel=alternate][type=application/rss+xml][href=?]', link)
    end
  end
end

使用する際には include TagMacros を書いておきます。

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

describe "/articles" do
  include TagMacros

  it_should_have_tag('tr>td', 'MyString')
end

自分のような include 〜 を書くのもものぐさだという人は、 $RAILS_ROOT/spec/spec_helper.rb に config.include メソッドを使用すれば、自動的に読み込まれるようになります。

[$RAILS_ROOT/spec/spec_helper.rb]

Spec::Runner.configure do |config|
  # ...
  config.include(TagMacros, :type => [:views])
  # ...
end

上記のように :type を指定すれば view だけで読み込まれるというような指定も可能です。

参考

Railscasts – RSpec Matchers & Macros
Writing Macros in RSpec • Blog Archive • Ben Mabey

P.S.
参考にしたサイトではMacroと書いてありますが、どうもこれをMacroと読んでいいものなのかどうかかなり謎なので、タイトルはMacro?と?付きにしてみましたwww

  1. quekさんによると、下の「参考」のところに書いた Railscasts に同様のことが書いてあり、そのアドレスを過去の自分は社内のチケットに書いていたらしいのですが、モジュールに出すことは全く知りませんでしたwww []

ssl_requirement pluginでssl_required(:all)と書きたい!

  • 2009-08-27 (Thu)
  • Ruby

Ruby on RailsでSSLで通信する必要があるページには、DHH謹製らしい?ssl_requirement pluginを使用すると、以下のようにに簡単にhttpsにリダイレクトしてくれるようにできます。

class Hoge < ApplicationController
  ssl_required(:index)  # インデックスページだけSSLで通信する。

  def index
    # some crazy codes ...
  end

  def show
    # some rotten codes ...
  end
end

残念ながら、このssl_requiredはアクションに:allとか書いてコントローラ全部を対象にするというようなことができません。
allというアクションを定義する場合もあるからこうなっているのかもしれません。

コントローラ全体にSSL通信をさせるようにするための方法については、コントローラ全体をssl強制に – 車輪の再発明というサイトに、trueを返すssl_required?メソッドを定義する方法が書かれています。

現在書いているRailsアプリでも同様のことが行われているものの、いかんせんたびたび見る気がしてきて、クラスメソッドにしようかと思いました。
でも、すでにssl_requiredというメソッドがあるところに、さらなる同じようなメソッドを作るのは避けたいし、やっぱりssl_required(:all)と書きたいと思いました。

そこでalias_method_chainを使用して、ApplicationControllerをこんな風にしてみました。

class ApplicationController < ActionController::Base
  include SslRequirement

  # some codes...

  class << self
    def ssl_required_with_all(*actions)
      if actions.include?(:all)
        class_eval do
          def ssl_required?
            true
          end
        end
      else
        class_eval do
          ssl_required_without_all(*actions)
        end
      end
    end
    alias_method_chain(:ssl_required, :all)
  end
end

まともにclass_evalとかがわかっていないので、かなり怪しいです!!!

Apple Push Notification Serviceを利用した、iPhone クライアントと、Rubyによるサーバの作成。

[とりあえず表示しましたが、これからコードをアップロードしたり改変していきます。]

先週から、システム部で何か動くものを作って発表するという企画?が始まりました。
プログラムプレゼンテーションを始めてみたら意外に良かった

第一弾は自分だったので、1週間しかなかったので、ちょこっとやればできそうで今までやってみたかったiPhone OS 3.0の目玉機能の一つ、Push Notification Serviceの実装をやってみることにしてみました。

iPhoneクライアントはほぼObjective-Cしかありませんのでそれを使い、サーバはRubyで実装しました。

以下が発表したスライドを少し改変したものになります。

コードは今回はbitbucket.orgにアップロードすることにしました!
コードはこちらになります。
http://bitbucket.org/milkcocoa/apns-test/

ただしサーバのファイルに関しては

  • デバイスIDとかの部分はxでマスクしています。
  • キーに関してはスライドで指定した方法で記述してください。

[ポイントを書く。]

mislav-will_paginate pluginを使用したページをRSpecでテストする。

  • 2009-07-19 (Sun)
  • Ruby

Webアプリケーションでは、たくさんの項目を一覧表示する場合、1ページに全部見せるのではなくpaginate処理をして数ページに分ける場合があります。
たとえばGoogleの検索結果は通常、1ページに10件しか表示されません。

そういうページをRuby on Railsで作成する場合には、mislav-will_paginate pluginというプラグインを使用するということは割とよく知られていることです。

先日もそういうページを作成する必要があり、作成していましたが、RSpecでテストを作成しようとしたところやり方がわからず詰まりました。
そのため、その方法をここに記しておこうと思います。

ドキュメントを読もう!

実はGitHubのpluginのWikiページに最低限必要なことは書いてありました。
気がついていればもっと早く解決していたところでした。
「I’m getting “undefined method `total_pages’” error when rendering in the view!」という部分ですね。

ポイントはpaginateやtotal_pagesというメソッドをstub!で定義してあげることです。

実際のコード

  • Controller: ArticlesController
  • Model: Article
    • title: string
    • body: text

Controllerのテスト(の一部)

[$RAILS_ROOT/spec/controllers/articles_controller_spec.rb]

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe ArticlesController do
  describe "handling GET /articles" do
    before(:each) do
      article = mock_model(Article)
      article.stub!(:find).and_return([article])

      @articles = [article]
      @articles.stub!(:total_pages).and_return(1)
      @articles.stub!(:paginate).and_return(@articles)
    end

    def do_get
      get(:index)
    end

    it "should be successful" do
      do_get
      response.should be_success
    end

    it "should render index template" do
      do_get
      response.should render_template('index')
    end

    it "should find all articles" do
      Article.should_receive(:paginate).with(:page => nil, :per_page => 100).and_return(@articles)
      do_get
    end

    it "should assign the found articles for the view" do
      do_get
      assigns[:articles].should == @articles
    end
  end
end

ちなみに29行目の:page => nilになっているのは、たぶんassingsとかでparameterを指定していないからこうなるのだと思います。
まだshould_receiveをきちんと理解していないで、まともな説明が書けません。

Viewのテスト(の一部)

[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]

require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')

describe "/articles/index.html.erb" do
  include ArticlesHelper

  before(:each) do
    article_98 = mock_model(Article)
    article_98.stub!(:id).and_return(1)
    article_98.stub!(:title).and_return('MyString')
    article_98.stub!(:body).and_return('MyText')

    article_99 = mock_model(Article)
    article_99.stub!(:id).and_return(2)
    article_99.stub!(:title).and_return('MyString')
    article_99.stub!(:body).and_return('MyText')

    @articles = [article_98, article_99]
    @articles.stub!(:total_pages).and_return(1)
    @articles.stub!(:paginate).and_return(@articles)

    assigns[:articles] = @articles
  end

  it "should render list of articles" do
    render "/articles/index.html.erb"
    response.should have_tag("tr>td", "MyString", 2)
    response.should have_tag("tr>td", "MyText", 2)
  end
end

RubyでArrayの部分集合を作成する。

  • 2009-07-10 (Fri)
  • Ruby

Arrayの部分集合を作成する必要があったので、作ってみました。

class Array
  def subset
    rec = Proc.new do |univ|
      # subset!でポインタが循環しないようにcloneを使う。
      (ret = []) << univ.clone
      univ.each do |e|
        ret += rec.call(univ - [e])
      end
      ret
    end

    rec.call(self).uniq
  end

  def subset!
    self.replace(self.subset)
  end
end

他には同じサイズの配列を作成して、その要素を使用するかどうかtrueかfalseのフラグを立てていくという手法もあるようです。

irbで実行するとこうなります。

>> a = %w(1 2 3 4)
=> ["1", "2", "3", "4"]
>> a.subset
=> [["1", "2", "3", "4"], ["2", "3", "4"], ["3", "4"], ["4"], [], ["3"], ["2", "4"], ["2"], ["2", "3"], ["1", "3", "4"], ["1", "4"], ["1"], ["1", "3"], ["1", "2", "4"], ["1", "2"], ["1", "2", "3"]]

2009/7/20 追記

twitterですばらしい方法を教えていただきました!

ただしRuby 1.8.7で追加されたcombinationというメソッドを使用しているので1.8.7以降限定です。
cf. Ruby 1.8.7で使えるようになったRuby 1.9のメソッドたち – (rubikitch loves (Emacs Ruby CUI))

class Array
  def subset
    (0..self.length).inject([]) do |ret, n|
      ret.push(*self.combination(n))
    end
  end
end

irbでの実行結果。

>> a = %w(1 2 3 4)
=> ["1", "2", "3", "4"]
>> a.subset
=> [[], ["1"], ["2"], ["3"], ["4"], ["1", "2"], ["1", "3"], ["1", "4"], ["2", "3"], ["2", "4"], ["3", "4"], ["1", "2", "3"], ["1", "2", "4"], ["1", "3", "4"], ["2", "3", "4"], ["1", "2", "3", "4"]]

すごくすっきりしました!

レセプトオンライン送信で請求ができたかを確認するRubyスクリプト

すごいニッチなところをすみませんwww。

レセプトオンライン送信について知っている人だけをまずは対象にしています。

複数の店舗のレセプトを一括してオンライン請求するときにきちんと請求済みになっているかどうか1 を(念のため)確認するのは2(社保、国保) × 店舗数分やらないといけないので大変です。
特にEeePCを使っていると遅いので、だいぶん大変度が増します。
ということで、Rubyでオートマティックに確認するスクリプトを書いてみました。
神奈川県の事例しか知らないので神奈川県限定です。
スクリプト中の神奈川県に関する部分を修正すれば、ほかの県でももしかしたら利用できるかもしれません。
またHTMLを解析して、やっていますのでサイトの構成やHTML等が変わってしまった場合には利用ができません。

共有はしますが、ノーサポートです。
私はこのスクリプトを使用して生じた一切の責任を負いません。

ライセンスをどうしようかと思いましたが、どのみちスクリプトですから改変しても公開せざるを得ないと思うので、GPLv3にしておきます。2

動作環境は

  • Windows3
  • Ruby 1.8.7
  • RubyGems 1.3.2
    RubyGemsによってインストールした

    • WWW::Mechanize 0.9.2
    • Term::ANSIColor 1.0.3
    • Win32Console 1.2.0
    • およびそれらの依存ライブラリ(Nokogiri等)

です。
これらのインストールに関する質問はお答えできませんので、あらかじめご了承を。

さらにopenssl(UNIX系だと普通にあると思われる4 )を用いて配布されているp12形式の証明書ファイルからcerファイルとkeyファイル(この二つをいまいちわかっていない)を作成します。

パスワードの入力を求められますが、これは証明書発行時に発行されたパスワードになります。

openssl pkcs12 -in hoge.p12 -clcerts -out hoge.cer -nokeys
openssl pkcs12 -in hoge.p12 -clcerts -out hoge.key -nocerts -nodes

さらに店舗情報としてYAMLファイルを作成します。
名前は適当につけていただいて、ファイルの中身は

- name: hoge
  id: 0120444444
  password: hogepass
  cert: hoge.cer
  key: hoge.key
- name: piyo
  id: 0120888888
  password: piyopass
  cert: piyo.cer
  key: piyo.key

とかしてください。

nameは任意の区別できる名前にします。
id, passwordはその薬局がログインするときのIDとパスワードです。
certとkeyは上記で作成したcertファイルとkeyファイルのパスを指定します。
後のオプションはきっとわかると思います。
このときWindowsでパス区切りに使われるバックスラッシュ(\)はうまく動いてくれなかったように思えるので、スラッシュでパスを区切りました。

あとは

ruby receipt_check.rb hoge.yaml

とでも打ち込めば、請求済みであれば緑色の字で[OK]、請求済みになっていない場合には[Failed]と表示されます。

ruby receipt_check.rb -t hoge.yaml

のように -t オプションをつけると、確認試験で実行します。

さらに自分は上記コマンドを呼び出すバッチファイルを作成し、それをcmd.exeで呼び出すリンクファイルを作ってリンクファイルをクリックするだけで確認できるようにしてあります。

TODOはそこにも書いてありますが、エラー処理を全くしていません。
そのため運用時間外などに実行すると、運用時間外ですとか知らせることなく、思い切りスクリプトのエラーが出ます。
もしやる気があれば、未請求になっていた場合にはスクリプトで請求できるようにしたり、もうスクリプトだけで請求するということも考えています(こちらは実現が難しそう)。

あと、できるだけ修正はしたいと思いますが、最終的なデバッグはレセプトオンライン送信の端末でしかできないので、修正まで非常に時間がかかる場合、修正しない場合があります。

ということで、以上を理解した方のみダウンロードはこちらから。

  1. 送信してから、請求するボタンを押さないといけません []
  2. 正直ここらは返るかもしれません []
  3. 今から考えると確認するだけならWindowsである必要はありませんね。 []
  4. MacでやったのでWindowsだとわからない []

Ultraviolet Syntax Highlighting Engineをさくらインターネットのサーバにインストールする

UltravioletはRubyによって書かれた様々なSyntax(文法)に対応したSyntax Highlighting Engineです。
今回はこれをさくらインターネットのサーバにインストールしてみましょう。

RubyGemsを使ってインストールをするのですが、以前RubyGemsのインストール方法については書きました。
cocoa*life – ブログをさくらインターネットへ移行しました。

ということで、RubyGemsのインストールが終わっている状態で書き始めます。

Ultravioletのインストール

正規表現ライブラリOniguruma(鬼車)のインストール

まずは正規表現ライブラリであるOnigurumaをインストールします。
普通にconfigureしてmakeしてinstallすれば良いだけです。

wget http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.9.1.tar.gz
tar zxf onig-5.9.1.tar.gz
cd onig-5.9.1
./configure --prefix=$HOME/local
make
make install

RubyGemsを使ってUltravioletのインストール

一直線に答えを書いてしまってもいいのですが、問題解決法を書くのも忘れてしまう自分のためになると思うので、書きます。

gem install -r ultraviolet

とすればいいと思いきや、こんなエラーが出て止まります。

oregexp.c:2:23: oniguruma.h: No such file or directory

どうも標準ではない/home/hoge/local/以下にインストールしてあるので、oniguruma.hなどが見つからないようです。
さらに、エラーの最後を見ていると

Stop in /home/hoge/local/gems/oniguruma-1.1.0/ext.

なんて書いてあって、 どうも依存関係によりまず他のライブラリをインストールしているよう。
調べてみるとOniguruma for Rubyというものらしい。

そこでまず~/local/gems/oniguruma-1.1.0というフォルダを見てみると、Rakefileがある。
さらに中身を読んでみると、30 〜 36行目に以下のような記述があります。

if ENV['PLATFORM'] =~ /win32/
   p.lib_files = ["win/oregexp.so"]
   p.spec_extras[:require_paths] = ["win", "lib", "ext" ]
   p.spec_extras[:platform] = Gem::Platform::WIN32
else
   p.spec_extras[:extensions] = ["ext/extconf.rb"]
end

どうも、ext/extconf.rbがくさそうだという気がしたので、ext/extconf.rbを見てみます。

require 'mkmf'
have_library("onig")
$CFLAGS='-Wall'
create_makefile( "oregexp" )

たった4行!
ですがcreate_makefileでMakefileを作成しているというのは読み取れますので、何かありそうです。
そしてどうも、require ’mkmf’というのが鍵を握っている様な気がしました。
mkmf – Rubyリファレンスマニュアル

Ruby の拡張ライブラリのための Makefile を作成するライブラリです。

mkmf – Rubyリファレンスマニュアル

ということで、ビンゴでしょうか?
さらに読み進めると 

–with-opt-dir=directory
ヘッダファイル、ライブラリファイルを探索するディレクトリ directory/include、directory/lib をそれぞれ追加します。  

mkmf – Rubyリファレンスマニュアル

と書いてあるので、これを指定できればいいということになります。
ということで、

gem install -r oniguruma -- --with-opt-dir=$HOME/local
gem install -r ultraviolet

として、これでめでたくインストールが完了します。1

RubyGemsに慣れている人であれば当たり前のことなのかもしれませんが、自分は初めてだったので良い経験になりました。

Ultravioletの使い方

uvというコマンドがUltravioletのコマンドになりますが、uv –helpでヘルプを見てみると

uv -t amy -h ~/.bashrc > bashrc.html

なんてのがサンプルとして書いてあります。
amyというのはテーマの名前のようです。

テーマはUltraviolet Theme Galleryで見ることができます。
-sでSyntaxを指定することができますが、ある程度は自動的に認識されるのかもしれません。

uv -l syntax

で利用可能なSyntax一覧を表示することが可能です。 

自分は.zshrcをやってみました。
Syntaxが認識されないので、-s shell-unix-genericというのを指定しました。

uv -t blackboard -s shell-unix-generic -h ~/.zshrc > zshrc.html

生成されたzshrc.htmlはcssを参照しているところがあるのですが、そのcssは
/home/hoge/local/gems/ultraviolet-0.10.2/render/xhtml/files/cssにあります。

Rubyからもライブラリとして使うことができるようです。
Usageのところを見ると書いてあります。

  1. 試してはいないけれど、gem install -r ultraviolet — –with-opt-dir=$HOME/localでうまくいくのかなぁ? []

RSRubyでRubyのArrayをRのMatrixに変換する

  • 2008-09-03 (Wed)
  • Ruby

題名の通りです。

以前ご紹介したように、RSRubyはRubyでRのスクリプトを実行するためのものです。
RのオブジェクトをRubyのオブジェクトに変換することは簡単です。
というのはeval_Rをした結果がArrayとして扱うのが適当であればArrayに、Hashとして扱うのが適当であれば、Hashに変換してくれます。

問題はRubyのArrayをRの中で使いたいときです。

たとえば
[1, 2, 3, 4, 5]
というArrayをRの行列にしたい場合。

ruby_data = [1, 2, 3, 4, 5]
r = RSRuby::instance
r.eval_R <<-RCOMMANDS
r_data <- matrix(c(#{ruby_data.join(",")}), 1, #{ruby_data.size})
<<-RCOMMANDS

などとしてうまくjoinを使ってやればできます。

Ruby de R

RでRubyではなく、RubyでR。
Rubyが本当に便利なので、できる限りRubyで作業をしたいなぁと思っています。

そんなことを助けてくれるのにrurubyというライブラリがあります。

MacBookでのインストールは

sudo gem install rsruby -- --with-R-dir=/Library/Frameworks/R.framework/Resources

としました。

このままでは $R_HOMEが見つからないと怒られるようです。
私はzshを使っているので、~/.zshrcに

export R_HOME=/Library/Frameworks/R.framework/Resources

を追加しておきました。

使い方はとりあえずeval_Rコマンドを使えばRのコマンドはなんでも実行できるので、特にRubyのデータ構造を使う必要がなければこれだけでもいけます。
ちなみにヒアドキュメントを使えばいいと

require 'rubygems'
require 'rsruby'

r = RSRuby.instance
r.eval_R <<-RCOMMAND
  # 何かしらのRのコマンド
RCOMMAND

これで使いなれたRubyの正規表現が使えるので自分としてはとても嬉しい。

鯖の方に移らなきゃ

Crêpeで使うデータ更新用のサーバーを作らないといけないのですが、今までやったことがないことなので(スタンドアロンアプリケーションしか作ったことがない)、何からやらなきゃいけないかがわかりません。

まずは、言語およびフレームワークの選択?
PHPを使うか、 Ruby on Railsを使うか、それとも単純にRubyだけで・・・。

とりあえず昔買ったRails本を引っ張り出して斜め読みをしています。
いつの間にか第2版が出ていたようで、というか第2版でもRails 2系とは違うらしいという驚くべき開発スピードなんですが(汗)。

RailsによるアジャイルWebアプリケーション開発 第2版
著者: Dave Thomas, David Heinemeier Hansson, Leon Breedt, Mike Clark, Andreas Schwarz, James Duncan Davidson, Justin Gehtland
出版社: オーム社
発売日: 2007年10月20日

ちなみに本国?の方では第3版の準備がされているようです。1

Agile Web Development with Rails, Third Edition

2ヶ月前はSQLもさっぱりだったし、Objective-Cもさっぱりだったし(最近ようやく参照カウンタに慣れてきた)、ある程度頑張ればそこからはある程度わかるようになるでしょうと思っているのですが・・・。

SQLがわかったあとなので、少しは頭に入ってきていますが、まだ全体的な繋がりがよく見えない。
ちょろちょろとテストアプリを作ってみて掴んでいくしかないな。
でも、ここでの開発の経験はあとに生かせるはず!!!

  1. 上記サイトでBeta版のPDFが出ているのだから、日本語版も2, 3ヶ月遅れでできないかしら?なんてわがまま放題なことを発言してみます。 []

Home > Programming > Ruby

Feeds
Meta
Get Adobe Flash playerPlugin by wpburn.com wordpress themes

Return to page top