rubyにはスコープゲートがある。
スコープゲートを越えて変数は参照できない。
スコープゲートには
class
module
def
があり、それぞれのブロックの中ではスコープが切り替わる。
1
2
3
4
5
6
7
8
9
10
| class Foo
str = "foo"
def print
str
end
end
foo = Foo.new
foo.print # => NameError: undefined local variable or method `str'が発生する
|
def printでスコープが変わるので、外側のstrが参照できない。
解決するには例えばフラットスコープ
1
2
3
4
5
6
7
8
9
10
| class Bar
str = "bar"
define_method :print do
str
end
end
bar = Bar.new
bar.print
|
スコープゲート(class/module/def)を使わなければいいという話。
今回はdefをdefine _methodに変更した
classはClass.new
moduleはModule.new
でブロック内部に中身を記述する。
一応クラスの場合の例。
1
2
3
4
5
6
7
8
| str = "bar"
Bar = Class.new do
define_method :print do
str
end
end
Bar.new.print
|
動的ディスパッチ
メソッド実行時にメソッドを動的に呼び出す。
日づけごとに天気を返すメソッドがあって、それを動的ディスパッチでメソッドを呼んでみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| class Weather
def weather_of_day(day)
send("weather_of_day_#{day}")
end
def weather_of_day_1
"快晴"
end
def weather_of_day_2
"多分雪"
end
def weather_of_day_3
"ほどよく寒い曇り空"
end
def weather_of_day_4
"寒いよ。雨"
end
def weather_of_day_5
"晴れ時々曇り"
end
def method_missing(method_id,*args)
return "#{$1}日は指定できません" if method_id =~ /^weather_of_day_(.*)/
super
end
end
Weather.new.weather_of_day(1) #=> "快晴"
Weather.new.weather_of_day(3) #=> "ほどよく寒い曇り空"
|
動的メソッド
動的にメソッドを作成する
instance variable getでインスタンス変数の値は簡単にとれますが、それがない体で。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| class Me
def initialize
@first_name = "gogo"
@last_name = "sakura"
end
def self.generate_name_accessor(prefix)
prefix = prefix.to_s
define_method "#{prefix}_name" do
prefix != "full" ? instance_eval("@#{prefix}_name") : [@first_name,@last_name].join("_")
end
end
generate_name_accessor :full
generate_name_accessor :first
generate_name_accessor :last
end
Me.new.first_name #=> "gogo"
Me.new.last_name #=> "saura"
Me.new.full_name #=> "gogo_sakura"
|
まとめ
メソッド名に一定のルールがあり、それを引数等に応じてメソッド名を決定し動的に呼ぶ場合
→動的ディスパッチ
重複している箇所がある場合にメソッドの内容が似通っているなら
→動的メソッド
辺りを使い分けてリファクタリングすると幸せになれるかも?
以下を入れれば良いっぽい
ruby-matchit - ’Matchit’ for Ruby. : vim
online
ページ下部のClick on the package to
download.からruby-matchit.vimをダウンロードして
~/.vim/plugin/
におきませう。
それだけで%で対応するdef endとか飛べちゃう。
vimで
:map
とすればキーマッピングの確認ができる
を変更したい場合は、.vimrcにこう書くと変更できる。
let mapleader = "任意のキー"
すでにあるメソッドに処理を追加したい場合。
すでにあるメソッドを踏襲した新しいメソッドを定義して、メソッドの呼び出し側を新しいメソッドに向けることができますが
現実的でないですね。
ではどうやってすり替えるか。
実現手段はいくつかあると思いますが、ここではアラウンドエイリアスを使ってみます。
今回はStringのto _sメソッドを、
文字列(文字列長)
と表示するよう変えてみる。
今日のコードではaliasを使いますが、二度呼ぶとエラーとなってしまうのでirbを都度起動してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| str = "あいう"
str.to_s #=> "あいう"
class String
alias :old_to_s :to_s
def to_s
old_to_s + "(" + length.to_s + ")"
end
end
str.to_s #=> "あいう(3)"
str.old_to_s #=> "あいう"
|
この前のrubyのオープンクラス -
なんとなく日々徒然と使ってStringクラスにコーディングしていきます。
構文は
alias 新しいメソッド名 古いメソッド名
です。
今回の例ではto sにold to _sという別名をつけます。
1
2
3
4
5
6
7
8
9
10
| str = "あいう"
str.to_s #=> "あいう"
class String
alias :old_to_s :to_s
end
str.to_s #=> "あいう"
str.old_to_s #=> "あいう"
|
それでto _sを書き換えちゃいます。
1
2
3
| def to_s
old_to_s + "(" + length.to_s + ")"
end
|
to sが呼ばれた場合は書き換えたメソッドが呼ばれ、old to sが呼ばれた場合は昔のメソッドが呼ばれる状態です。
書き換えられたto sは昔のto sをold to _sで呼び、lengthをそこにくっつけています。
これでメソッドのすり替えが完了です。
今日は肉の日。
肉関係ないけど.
機能追加中に、バグ報告がはいった場合。
今やってる修正を置いといて、最新を取得しなおしてバグフィックスしたい場合がある。
そんな場合
ってやると、今やっている作業内容(ステージングファイルもアンステージングファイルも)を一時退避できる。
戻した場合は
で、戻せる。
今保存しているstash一覧は
コンフリクトしたりして、stashがどうにもならなくなってstashを消し去りたい場合には
ってな感じでどうですか。
これ知るまでは
1
2
| git commit -m "一時退避"
git reset --soft HEAD^
|
ってやっていたマヌケは私です。
ああ、とりあえず肉食いたい。
昨日降りしきる雨の中灯油をもったまま玄関前でよろけた拍子に水鉢を踏み割ってしまい。
地面に放り出された冬眠中の金魚とメダカを救出して、45センチのグッピー水槽に移したら
ノロノロでかわせずにグッピー♀にいじめられて冬眠からさめたばかりの金魚が死んでしまってとても悲しいのです。
rubyのオープンクラス
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class Hoge
def aaa
p "aaa"
end
end
hoge = Hoge.new
hoge.methods.grep(/aaa/) # => [:aaa]
class Hoge
def bbb
p "bbb"
end
end
hoge.methods.grep(/aaa|bbb/) # => [:aaa, :bbb]
|
一度定義済みのクラスHogeに再度Hogeを定義している。
Hogeが定義されていると、二回目の呼び出しでは一回目の呼び出しのHogeが呼ばれ
そこにメソッドbbbが追加される。
当然String等の既存クラスにも対応。
1
2
3
4
5
6
7
8
9
| class String
def aaa
p "aaa"
end
end
str = "fuga"
str.methods.grep(/aaa/) # => [:aaa]
p str.aaa # => "aaa"
|
rubyで構造体を使いたい時
OpenStructが使える。
属性は動的に作ってくれる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| require 'ostruct'
user1 = OpenStruct.new
user2 = OpenStruct.new
user1.id = 1
user1.name = 'hoge'
user2.id = 2
user2.name = 'fuga'
p user1.id #=> 1
p user1.name #=> 'hoge'
p user2.id #=> 2
p user2.name #=> 'fuga'
#Arrayにラップしちゃえば、options_from_collection_for_selectにも渡せる。
require 'action_view'
require 'active_support/all'
include ActionView::Helpers::FormOptionsHelper
users = [user1,user2]
options_from_collection_for_select(users , "id" ,"name") #=> "<option value= "1 ">hoge</option> n<option value= "2 ">fuga</option>"
|
すばらしい