gimeiを詠む

gimei?

github.com

gimei は、日本人の名前や、日本の住所をランダムに返すライブラリです。テストの時などに使います。似たようなライブラリにfakerがあります。fakerはとても優れたライブラリで、多言語対応もしていますが、ふりがな(フリガナ)は流石に対応していません。gimei はふりがな(及びフリガナ)に対応しています。

使い方は上記のドキュメントを見てもらうのが良いと思います。

ここでは、個人的に気になった点を深く掘り下げていきます。

yaml(静的データ)からRandomに配列を取得する

yamlファイルが以下のような形式である場合

first_name:
  male:
    - ['愛斗', 'あいと', 'アイト']
    - ['愛登', 'あいと', 'アイト']
    - ['愛翔', 'あいと', 'アイト']
    - ['藍斗', 'あいと', 'アイト']

yamlに配列を保存する場合は"-"を使います。 詳しい使い方はこちらを参照しました:YAML 基本記法まとめ

require 'yaml'

class Honmyo
  NAMES = YAML.load_file(File.expand_path(File.join('..', 'mydata', 'names.yml'), __FILE__))
end

names = Honmyo::NAMES
puts names.class                           #=> Hash
puts names["first_name"]["male"].class     #=> Array
puts names["first_name"]["male"].sample    #=> ["直志", "なおし", "ナオシ"]

クラスは定数という意味

gimeiの中のクラスの定義を見ていきます。

class Gimei::Name

  #-------------------省略------------------------
 
  class Last
 
  end
end

このLastから見たancestorsはこんな感じ。

[Honmyo::Name::Last, Object, PP::ObjectMixin, Kernel, BasicObject]

継承しているわけではなく、定数のスコープ演算子”::”で区切られているだけです。

【Ruby】定数について

定数のパスという表現をしましたが、実はクラスもモジュールも定数です。生成されたクラスオブジェクトが指定したクラス名の定数に代入されているわけです。クラス名を参照することは文法上は定数を参照しているだけという事が理解できれば、::演算子で表すパスも全てクラスやモジュールを含めてただの定数だということが分かります。

また、以下の2つは同様のものです。

class P
  class C
  end
end
class P::C
end

5-03【統一】1つのファイル内で、複数行の大きなクラスを別のクラス内で直接ネストしない

gimeiの例でいれば、Gimei::Nameは大きなファイルなので別ファイルに分離して、Gimei::Name::FirstGimei::Name::Lastはコードが少ないので、同一ファイルで定義しています。

同じメソッドをインスタンスメソッドにもクラスメソッドにも利用したい場合

インスタンスへの移譲

gimeiではrubyの標準モジュールであるFowardableが使われています。

インスタンスメソッドに対して使う場合。

ポイントは、initializeで作成した@innerをdef_delegators :@innerで定義してあげること。

class Outer
  extend Forwardable
  def_delegators :@inner, :hello

  def initialize
    @inner = Inner.new
  end

  class Inner
    extend Forwardable
    def_delegators :hello

    def hello
      puts "hello, world!!"
    end
  end
end

obj = Outer.new
obj.hello  #=> "hello, world!!"
#[7] pry(main)> Outer.instance_methods(false)
#=> [:hello]
#[8] pry(main)> Outer::Inner.instance_methods(false)
#=> [:hello]

クラスメソッドに使う場合

gimeiでは同じメソッドを、インスタンスメソッドでもクラスメソッドでも使えるようにしています。 おそらく、この理由でincludeや単純なextendではなく、Forwardableモジュールを利用したのだと思います。

以下のサンプルでは、Innerクラスの中身は全く変わっていないことに注目してください。

require 'forwardable'

class Outer
  class << self
    extend Forwardable
    def_delegators :inner, :hello

    def inner
      Inner.new
    end
  end

  class Inner
    extend Forwardable
    def_delegators :hello

    def hello
      puts "hello, world!!"
    end
  end
end

Outer.hello  #=> hello, world!!

Innerクラスのhelloメソッドはプライベートメソッドですが、Outerクラスではクラスメソッドになっています。

Outer.methods(false)
#=> [:inner, :hello]
Outer::Inner.methods(false)
#=> []
Outer::Inner.instance_methods(false)
#=> [:hello]

内部で使われているgem

romaji: https://github.com/makimoto/romaji