ようへいの日々精進XP

よかろうもん

2018 年 05 月 31 日 (木)

ジョギング

日課

  • (腕立て x 50 + 腹筋 x 50) x 3

AWS Summit

話題の EFS リリースよりも, 以下のブログ記事のサービスアップデートが気になった.

dev.classmethod.jp

夕飯

  • パスタを作る

今日のるびぃ ~ REx - Ruby Examination にチャレンジ (14) ~

REx - Ruby Examination の問題を自分なりにアレンジした上で 1 〜 3 問くらいずつ解いていく. 正直言ってかなり難しい. 尚, irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

定数

以下のコードを実行するとどうなるか.

m = Module.new

m.module_eval do
  EVAL_CONST = 100
end

puts "CONST is defined? #{m.const_defined?(:CONST)}"
puts "_CONST is defined? #{Object.const_defined?(:CONST)}"

CONST is defined? true CONST is defined? true

以下, irb にて確認.

irb(main):001:0> m = Module.new
=> #<Module:0x0055719c3d4f88>
irb(main):002:0> 
irb(main):003:0* m.module_eval do
irb(main):004:1*   CONST = 100
irb(main):005:1> end
=> 100
irb(main):006:0> 
irb(main):007:0* puts "CONST is defined? #{m.const_defined?(:CONST)}"
EVAL_CONST is defined? true
=> nil
irb(main):008:0> puts "CONST is defined? #{Object.const_defined?(:CONST)}"
EVAL_CONST is defined? true
=> nil

以下, 解説より抜粋.

  • 設問において, module_eval のブロックで定義した定数はこの問題ではトップレベルで定義したことになる
  • 定数 EVAL_CONST はトップレベルで定義していることになる為, Object クラスの定数あることであることを確認出来る
  • Module クラスのインスタンスには直接, 定数は定義されていないが継承関係を探索して参照することが可能
  • const_defined? メソッドは第二引数に継承関係を探索するか指定が可能である為, 継承関係を探索するかによって結果は異なる
irb(main):001:0> m = Module.new
=> #<Module:0x0055f166098fa0>
irb(main):002:0> m.module_eval do
irb(main):003:1*   CONST = 'foo'
irb(main):004:1> end
=> "foo"
irb(main):005:0> puts Object.const_defined?(:CONST)
true
=> nil
irb(main):006:0> puts m.const_defined?(:CONST)
true
=> nil
irb(main):007:0> puts m.const_defined?(:CONST, false)
false
=> nil

ちなみに, 設問において, module_eval で文字列でメソッドを定義した場合には以下のような挙動になる.

irb(main):001:0> m = Module.new
=> #<Module:0x0056220dff5138>
irb(main):002:0> m.module_eval %Q{
irb(main):003:0"   CONST = 'foo'
irb(main):004:0" } 
=> "foo"
irb(main):005:0> puts Object.const_defined?(:CONST)
false
=> nil
irb(main):006:0> puts m.const_defined?(:CONST)
true
=> nil
irb(main):007:0> puts m.const_defined?(:CONST, false)
true
=> nil

module_evalcalss_eval において, メソッドを文字列で渡すかブロックで渡すかによって定数とクラス変数のスコープは異なるので注意が必要となる. 上記の例だと, 定数は module m 内に定義されていることになる.

ところで, Module.new って出来るんだ...ということで, 以下, ドキュメントより抜粋.

  • Module.new
    • 名前の付いていないモジュールを新しく生成して返す
    • ブロックが与えられると生成したモジュールをブロックに渡し, モジュールのコンテキストでブロックを実行する
mod = Module.new
mod.module_eval {|m| ... }
mod

また, このメソッドで生成されたモジュールは, 最初に名前が必要になった時に名前が決定する

mod = Module.new
mod.module_eval {|m| ... }
mod

m = Module.new
p m               # => #<Module 0lx40198a54>
p m.name          # => nil   # まだ名前は未定
Foo = m
# m.name          # ここで m.name を呼べば m の名前は "Foo" に確定する
Bar = m
m.name            # "Foo" か "Bar" のどちらかに決まる

フムフム.