Ruby で自分で作ったクラスをハッシュのキーに設定する場合の話

今回以下の様なクラスを作成して、

インスタンス変数(今回は @name )の値が同じ場合にハッシュで同一キーと判定してほしかった。

class HashKey
  def initialize(name)
    @name = name
  end
end

事前にいくつか必要なメソッドを見ていきます。

Object#equal? と Object#eql?

Object#equal?

  • 2つのオブジェクトが同一のものか調べる。
  • Object#object_id を比較する。
  • 再定義は付加
p("foo".equal?("bar")) #=> false
p("foo".equal?("foo")) #=> false

p(4.equal?(4)) #=> true
p(4.equal?(4.0)) #=> false

p(:foo.equal? :foo) #=> true

http://docs.ruby-lang.org/ja/2.0.0/method/Object/i/equal=3f.html

Object#eql?

  • デフォルトでは equal? と同じオブジェクトが同一のものか調べる。
  • Hash で二つのキー が等しいかどうかを判定するのに使われる。
  • 各クラスの性質に合わせて再定義すべき。
  • このメソッドを再定義した時には Object#hash メソッドも再定義しなければならない。

http://docs.ruby-lang.org/ja/2.0.0/method/Object/i/eql=3f.html

では、Object#hash を見てみます。

Object#hash

  • オブジェクトのハッシュ値を返す。
  • デフォルトでは Object#object_id と同じ値を返す。
  • A.eql?(B) ならば A.hash == B.hash
  • eql? を再定義した時には必ずこちらも合わせて再定義しなければならない。

つまり、eql? と hash が定義されていて、同一の値を判定させればいいみたいです。

上記を読んだ上で、実際にいくつかのパターンを試してみる

キーが文字列の場合

h2 = Hash.new
h2["key1"] = "fuga"
h2["key1"] = "piyo"
p h2 #=>{"key1"=>"piyo"}

同一のキーに値を設定した場合上書きになります。

自分で作ったクラスの場合

クラス定義

class HashKey
  def initialize(name)
    @name = name
  end
end
key1 = HashKey.new("hoge")

h1 = Hash.new
h1[key1] = "fuga"
h1[key1] = "piyo"
p h1 #=>{#<HashKey:0x007f9294058c90 @name="hoge">=>"piyo"}

同一の object_id なのでこちらも上書きになります。

同じインスタンス変数を持った別のオブジェクトの場合

key1 = HashKey.new("hoge")
key2 = HashKey.new("hoge")

h1 = Hash.new
h1[key1] = "fuga"
h1[key2] = "piyo"
p key1.eql? key2 #=>false
p h1 #=>{#<HashKey:0x007fdea2858c60 @name="hoge">=>"fuga", #<HashKey:0x007fdea2858c10 @name="hoge">=>"piyo"}

eql? で比較した場合に false になるので違うキーとして設定されます。

今回はこのパターンの時に同一のキーとしたかったんです。

では、冒頭に書いた eql? メソッドと hash メソッドを定義します。

class HashKey
  attr_accessor :name
  def initialize(name)
    @name = name
  end

  def eql? (other)
    @name == other.name
  end

  def hash
    @name.hash
  end
end
key1 = HashKey.new("hoge")
key2 = HashKey.new("hoge")

h1 = Hash.new
h1[key1] = "fuga"
h1[key2] = "piyo"
p key1.eql? key2 #=>true
p h1 #=>{#<HashKey:0x007fc24c1fc820 @name="hoge">=>"piyo"}

今回は同一のキーと判定されて 後から追加した値で上書きされました。

ちなみに hash メソッドを定義しなかった場合は

p key1.eql? key2 #=>true
p h1 #=>{#<HashKey:0x007fde91058a08 @name="hoge">=>"fuga", #<HashKey:0x007fde910589b8 @name="hoge">=>"piyo"}

となりました。

eql? メソッドは true になっているけど、hash の値が違うので同一のキーと判定されなかったみたいです。

参考にさせていただいたページ

http://d.hatena.ne.jp/shunsuk/20100412/1271073263

ありがとうございました。

Ruby の Date#cwday と Date#wday について

試したバージョン

  • ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin11.4.2]

Date クラスのメソッドには wday と cwday があって2つとも曜日を数値で返します。

実際に 2014 年 4 月 13 日 日曜日を試してみます。

[2] pry(main)> d = Date.new(2014, 4, 13)
=> #<Date: 2014-04-13 ((2456761j,0s,0n),+0s,2299161j)>
[3] pry(main)> d.cwday
=> 7
[4] pry(main)> d.wday
=> 0

このように wday の場合は 日曜日が 0 を返して、 cwday の場合は 7 を返します。

マニュアルにはこう書いてあります。

Date#cwday 暦週の日 (曜日) を返します (1-7、月曜は1)。
Date#wday  曜日を返します (0-6、日曜日は零)。

月曜日を週の始まりとする場合は cwday を使用して、 日曜日を週の始まりとする場合は wday を使用すればいいのかな。

参考

暦週日付は、暦週と暦年中の序数による日付です。
暦週は、暦年中の序数によって指定される特定の7日の期間であり、月曜から 始まります。
その年の第1暦週は、最初の木曜日を含む週とします。これは、 1月4日を含む週と同じです。

2014 年 4 月の wday, cwday, cweek を表示してみた

wday と cwday と cweek

vim で Another plugin set completefunc! Disabled neocomplcache. ってエラーが出た時

各バージョン、使用している vim plugin は以下

rails のコード書いてたら、以下のエラーが 表示された。

completefunc=syntaxcomplete#Complete<00><09>Last set from ~/.vim/bundle/neocomplcache/autoload/neocomplcache/init.vim
Another plugin set completefunc! Disabled neocomplcache.

https://twitter.com/yoneapp/status/383233456496340992 https://github.com/tpope/vim-rails/issues/283

vim-rails が関数を上書きしちゃってるんだなってのは分かったんだけど、 上の URL に書いてある設定

g:neocomplete#force_overwrite_completefunc

を追加しても解決されない。。

よく見たら、自分がインストールしていたプラグインneocomplcache でした。

http://d.hatena.ne.jp/senggonghaza/20131214/1387029004

こちらを参考にさせていただいて、

g:neocomplcache_force_overwrite_completefunc=1

を追加して解決。

どうも今後は neocomplete 使った方がいいみたいです。速いらしいし。

今度機会を見て、neocomplete に変えてみよう。

ちゃんと追加するコードを見ないと。。反省。

Ruby on Rails チュートリアル メモ #3

第4章

  • $ rails console (or rails c) Rails コンソールを起動
  • デフォルトでは開発 (development) 環境で立ち上がる
  • コメントは "#" から始まる
  • #{} 式展開
  • puts を使用して出力すると、改行文字 (¥n) が自動的に末尾に追加される
  • print を使用して出力すると、改行文字が追加されない
  • ダブルクォートもシングルクォートも同じ文字列を表現するが、シングルクォートの中では式展開は行われない
  • ダブルクォートでは式展開されるので # をバックスラッシュでエスケープする必要がある
  • シングルクォートは入力した文字をエスケープせずに「そのまま」保持するときに便利
  • オブジェクトは何をするか?オブジェクトとはメッセージに応答するもの
  • メッセージ = メソッド
  • メソッドの末尾に ? を付けることで、論理値(boolean) を返すことを示す
  • nil もオブジェクト
  • オブジェクトのうち、論理値が false になるのは false と nil
  • 0 も true
  • 関数は暗黙の戻り値を持つ。これは、最後に評価された式の値が自動的に返されることを意味する
  • モジュールは関連したメソッドをまとめる
  • クラスに include することでミックスインできる
  • 配列の内容を変更したい場合は、破壊的メソッドを使用する
  • 配列にはさまざまな共存させることができる
  • ブロック
    • { }
    • do end
  • ハッシュは並び順が保証されていない
    • Ruby 1.9 ではハッシュの順序が入力順と同じであることを保証しているが、ハッシュを特定の順序に依存してカウントさせるべきではない
  • キーと値はハッシュロケット(=>)によって表現する
  • ハッシュのキーはシンボルを使うのが普通
    • user = { "first_name" => "ka", "last_name" => "be" }
    • user = { :first_name => "ka", :last_name => "be" }
    • user = { first_name: "ka", :last_name: "be" } Ruby 1.9 から
  • Rails でよく使われているハッシュの使用法
  params = {}        # 'params' というハッシュを定義する ('parameters' の略)。
  params[:user] = { name: "Michael Hartl", email: "mhartl@example.com" }
  => {:name=>"Michael Hartl", :email=>"mhartl@example.com"}
  params
  => {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}}
  params[:user][:email]
  => "mhartl@example.com"
  • ハッシュの each メソッドでは、ブロックの変数がキーと値になっている
  • オブジェクトを表現する文字列を返す便利メソッド inspect
  • p メソッド = inspect メソッド + puts メソッド
  irb(main):010:0> p :name
  :name
  => :name
  irb(main):011:0> puts :name
  name
  => nil
  irb(main):012:0> puts :name.inspect
  :name
  => nil
  • Ruby は関数呼び出し時に丸かっこの省略可能
  • ハッシュが関数呼び出しの最後の引数である場合、波かっこの省略可能
  [1] pry(main)> def func(a, b)
  [1] pry(main)*   p a
  [1] pry(main)*   p b
  [1] pry(main)* end
  => nil
  [2] pry(main)> func('hoge', :a => 'fuga')
  "hoge"
  {:a=>"fuga"}
  => {:a=>"fuga"}
  • シンボルにハイフンを使用できない
  • ハッシュでハイフンを含むキーを設定する場合、ハッシュロケット記法を使用する
  • Ruby はプログラム内の改行の有無に左右されない
  • ハッシュのコンストラクタ (Hash.new) はデフォルト値を引数に取る(キーが存在しない場合のデフォルト値になる)
  • メソッドがクラス自身に対して呼び出される => クラスメソッド
  • インスタンスに対して呼び出すメソッド => インスタンスメソッド
  • superclass メソッドでクラス階層を調べられる
  • クラスの中では self はオブジェクト自身を指す
  • Ruby では組み込みの基本クラスの拡張が可能
  • 大いなる力には大いなる責任が伴います <= スパイダーマンの名台詞
  • Rails コンソールは、セッションごとにローカルの Rails 環境を読み込む
  • だから、コンソール内でコントロールを作成したりできる
  • Rails のアクションには戻り値がない(返される値は重要ではない)
  • home アクションは Web ページを作成するもの(値を返すものではない)
  • コントローラーのインスタンス変数はビューで自動的に使えるようになる
  • インスタンス変数は @ から始まり、未定義の場合は nil
  • require './example_user' でカレントディレクトリから example_user ファイルを探す

Ruby on Rails チュートリアル メモ #2

第3章

  • rails new のオプションに --skip-test-unit をつけることで、Test::Unit フレームワークと関連している test ディレクトリを作成しない
  • bundle install --without pruduction オブションは"remembered option"と呼ばれるもので、このオプションを一度実行するとコマンドに保存され、今後実行するときにオプションを追加する必要がない
  • 秘密トークン(secret_token)はハードコードせず、動的に生成する
  • config/initializers/secret_token.rb で秘密トークン作成する
  • .secret キーをリポジトリで公開しないように .gitignore に設定しておく
  • RSpec を使用する
    • rails g rspec:install
    • rspec のテストを使わない場合は、--no-test-framework を使用する
  • キャメルケースを使用してコントローラーを作成すれば、自動的に static_pages_controller.rb が作成される
  • ActiveSupport#underscore メソッドを使用してキャメルケースをスネークケースに変換している
  • コントローラーを作成する rails generate controller FooBars baz quux
  • コントローラーを取り消す rails destroy controller FooBars baz quux
  • DB を1つ前の状態に戻す rake db:rollback
  • DB を最初の状態に戻す rake db:migrate VERSION=0
  • マイグレーションはそれぞれに対してバージョン番号が付与される。VERSION の値を変更することで、指定したバージョンに戻すことができる
  • Rails 4.0 からは PUT メソッドよりも PATCH メソッドの使用が推奨されている
  • RSpec の リクエストspec = 結合テスト
  • RSpec を用いて結合テストを作成する rails generate integration_test static_pages spec/requests ディレクトリに static_pages_spec.rb が生成される
  • capybara を使用する場合 spec_helper.rb に config.include Capybara::DSL を追加
  • RSpec を使用して TDD で Green になるまで
    • No route matches [GET] "/static_pages/about" routes.rb にルート追加
    • The action 'about' could not be found for StaticPagesController StaticPages コントローラーに about メソッド追加
    • Missing template static_pages/about app/views/static_pagesディレクトリの中にabout.html.erb 追加
  • have_title メソッドは引数で与えられた内容に完全一致する必要はなく、部分一致でもよい
  • 「重複してはならない」 = Don't Repeat Yourself, DRY
  • 埋め込み Ruby = Embedded Ruby
  • <% provide(:title, 'Home') %>
    • :title ラベルに 'Home' 文字列を関連付ける
  • <% ... %>
    • 中に書かれた Ruby のコードを実行するだけで出力なし
  • <%= ... %>
    • 中に書かれた Ruby のコードを実行して、結果をテンプレートに挿入する
  • yield 関数を使用することで、結果をテンプレートに挿入する
  • application.html.erb = 共通の構造をまとめるレイアウトファイル
  • application.html.erb に記述されている <%= yield %> は各ページの内容をレイアウトに挿入する
  • <%= csrf_meta_tags %> csrf_meta_tags メソッドは CSRF を防ぐために使われる
    • HTML に追加されたコード
    • <meta content="authenticity_token" name="csrf-param">
    • <meta content="*****" name="csrf-token">
  • Rspec の let 関数は引数のシンボルと同名の変数にブロックの評価値を格納する

Ruby on Rails チュートリアル メモ #1

Ruby on Rails チュートリアルをする中でメモしたこと

http://railstutorial-ja.herokuapp.com/?version=4.0#top

第2章

  • scaffold コマンドの引数はリソース名の単数形(例:User)
  • bundle exec rake db:migrate 未実行のマイグレーションファイルを実行
    • rake db:migrateを実行
    • schema_migrationsテーブルを調べ、存在しなければ作成
    • db/migrateディレクトリ内のすべてのマイグレーションファイルを調べる
    • データベースの現在のバージョンと異なるバージョンがあった場合、データベースに適応
    • schema_migrationsテーブルの更新
  • 現在の Gemfile に対応するバージョンの Rake が実行されるように bundle exec を付けて rake を実行する(技術的な理由により rails コマンドだけは例外)
  • Rake = Ruby版make
  • rake -T 実行可能なタスクすべて表示
  • rake -T db データベースタスクを表示
  • Http リクエストメソッドと URL でアクションが決まる
  • REST においてリソース(Userなど)はRestHttp リクエストと DB の CRUD に対応している
Http request method DB(CRUD)
POST Create
GET Read
PATCH Update
DELETE Delete
  • 入力検査(validation)は model に書く
    • validates :content, length: { maximum: 140 }
  • モデル同士の1対多の関連付けの場合 has_many :モデル(複数), belongs :モデル(単数)をモデルに書く
  • モデルは ActiveRecord::Base を継承する
  • コントローラーは ApplicationController を継承する
  • ApplicationController は ActionController::Base を継承する

リポジトリ

https://github.com/kkabetani/sample_app_rails_4