1/27(水)にNTT-ATさんの会議室にてkawasaki.rb #032を開催しました。

togetterのまとめはこちら

パーフェクトRuby読書会

「5-1-4 繰り返し処理」まで終えました。

4-3-2 オブジェクトの変更を禁止する

Object#freezeを呼び出すと、レシーバへの破壊的な操作を禁止することができます。

1
2
3
4
s = "hoge"
s.freeze # 変更を禁止する
s.frozen? # => true
s << "2" # can't modify frozen String (RuntimeError)

以下のように、Hashの変更も禁止されます。

1
2
DEFAULT_CONST = {foo: 123}.freeze
DEFAULT_CONST[:foo] = 456

ただし、以下のような再代入は禁止されません。

1
2
3
i = 1
i.freeze # 変更を禁止する
i += 2 # 再代入なので例外は発生しない

4-3-3 オブジェクトをコピーする

オブジェクトをコピーするにはObject#dupやObject#cloneを用います。どちらのメソッドも汚染状態を含めてコピーされます。

1
2
3
4
5
6
7
8
9
10
11
original = Object.new
p original.object_id # => 70295490884320
original.freeze

dup = original.dup
p dup.object_id # => 70295490884200
p dup.frozen? # => false

clone = original.clone
p clone.object_id # => 70138977021500
p clone.frozen? # => true # cloneではfreezeの情報もコピーされる

Arrayでの挙動も調べてみました

1
2
3
4
5
6
7
8
value = 'foo'
array = [value]
array_dup   = array.dup
array_clone = array.clone

p value.object_id # => 70265087245460
p array_dup[0].object_id # => 70265087245460
p array_clone[0].object_id # => 70265087245460

dupした配列に破壊的な変更を加えてみます。

1
2
3
array_dup[0] << "2"
p array_dup[0] # => "foo2"
p array[0] # => "foo2"

このように、オリジナルのarrayにも破壊的な変更が加えられます。 これを避けるには、 Marshal.load/dump を使用してコピーすればよいのではないか?という話題になりました。

1
2
3
4
5
array_dump = Marshal.load(Marshal.dump(array))
p array_dump[0].object_id # => 70265086490020
array_dump[0] << "0"
p array_dump[0] # => "foo20"
p array[0] # => "foo2"

この場合、コピー先の破壊的変更はオリジナルに影響を与えません。(ただしパフォーマンスは度外視)

4-3-4 汚染されたオブジェクト

Rubyには、セーフレベルという危険な操作を防ぐ機能があります。

※セーフレベル2以降は2.3.0より廃止されました。くわしくはこちらをご確認ください。

Rubyでは、外部から入力されたものは汚染されたオブジェクトとして扱われます。オブジェクトが汚染されているかどうかはObject#tainted?で確認できます。

1
2
3
4
5
6
7
p $SAFE # => 0
p ENV['HOME'].tainted? # => true

o = Object.new
p o.tainted? # => false
o.taint # オブジェクトを汚染させる
p o.tainted? # => true

一度上げたセーフレベルを下げることはできません。

1
2
3
4
$SAFE = 1
$SAFE = 0 # => SecurityError: tried to downgrade safe level from 1 to 0

rcfile = ENV["HOME"] + '/test_file'

セーフレベル1では、汚染されたオブジェクトを使ってファイルを削除することを禁止できます。 ※なおpryで試した場合、pryの履歴を残す挙動によりファイル操作以外でも例外が発生することが確認されました。

1
2
3
4
$SAFE = 1

rcfile = ENV["HOME"] + '/test_file'
File.unlink rcfile # unlink: Insecure operation - unlink (SecurityError)

5章

5-1 Numeric

全ての数値クラスはNumericクラスから派生します。

1
2
3
4
5
6
p Integer.ancestors # => [Integer, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
p Float.ancestors # => [Float, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
p Rational.ancestors # => [Rational, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
p Complex.ancestors # => [Complex, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
p Fixnum.ancestors # => [Fixnum, Integer, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
p Bignum.ancestors # => [Bignum, Integer, Numeric, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]

数値クラスには、いくつかの述語メソッドが定義されています。 その中の zero?nonzero? はいつ使えば良いのか?という話になり、以下のように使うことでスマートに記述することができる、という話になりました。

1
2
3
4
ary = [1, 0, 3]
p ary.select(&:zero?) # => [0]
p ary.reject(&:zero?) # => [1, 3]
p ary.select(&:nonzero?) # => [1, 3]

5-1-1 算術演算

除算はInt同士だと結果もIntになる、という話をしました。

1
2
3
4
5
6
p 10 / 3 # => 3
p 10.0 /3 # => 3.3333333333333335

num = 10
p num / 3 # => 3
p num.to_f / 3 # => 3.3333333333333335

5-1-2 比較演算

同値であればIntとFloatを比較してもtrueとなる、という話をしました。

1
123 == 123.0 # => true

5-1-3 丸め操作

round, ceil, floor, truncate についての挙動は以下のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
p 1.4.round # => 1
p 1.5.round # => 2
p -1.5.round # => -2
p 1.4.ceil # => 2
p 1.5.ceil # => 2
p -1.5.ceil # => -1
p -1.4.ceil # => -1
p 1.5.floor # => 1
p 1.4.floor # => 1
p -1.4.floor # => -2
p 1.5.truncate # => 1
p -1.5.truncate # => -1
p 1.6.truncate # => 1
p 1.7.truncate # => 1
p 1.9.truncate # => 1
p -0.9.truncate # => 0
p -1.9.truncate # => -1

5-1-4 繰り返し処理

繰り返しのためのメソッドとしてNumeric#stepがあります。

1
2
3
4
5
6
3.step 5 do |num|
  puts num
end
# 3
# 4
# 5

第二引数に増加していく数値を指定できます。

1
2
3
1.2.step 2.0, 0.2 do |num|
  puts num
end

他にもよく使うメソッドとして upto があります。

1
2
3
3.upto(5) do |num|
  puts num
end

今回のiruby notebookは以下のとおりです。

別タブで開く

セッション

今日はLTの発表はなく、RubyおよびRailsについてのトークをしました。

RailsのEnum使ってる?

Rails4で追加されたEnumですが、プロダクションでどれだけ使っているか?という質問がありました。

会場ではあまり意識的に使っているようではありませんでしたが、同一クラス内で重複するキーが使えないなど、ハマりどころはありそうです。

参考: ActiveRecord::Enum の注意点

また、同じハマりどころとしてdefault_scopeがあげられました。

参考: Railsのdefault_scopeは悪だ!(default_scope is evil) ということらしい

管理画面どうやって作ってる?

Railsでサービスを作る際、管理画面をどのように作っているかという質問がありました。

回答としてRailsAdminを使うか、Gemに依存したくなければScaffoldで作った方が良い、ということがあげられました。

また、RailsのMountable Engineを使う方法もあげられました。

次回予告

次回は2016/2/24(水)開催予定です。ご参加お待ちしております。

寄稿者について

松久保 敬人 (@Peranikov)

RailsからiOSアプリ開発を経て今はDDDとScalaの人。
お酒を投稿するサイトPuhaar!を運営しています。

Published: January 27 2016

blog comments powered by Disqus