SEAN_BLOG

プログラミング etc.

Ruby ActiveHash のソースコードリーディング

ActiveHash は、Ruby のHash をActiveRecord-like のモデルとして使用するためのbase class です。 ./lib 以下のコード量は、800行程度ですので決して多くはないと思います。 コードリーディングをするのは初めてですので、このぐらいの量がちょうど良いかなと思いました。

目的

  • ActiveRecord-like にHash を扱えるようにしている実装
  • ActiveYaml 読み取りの実装

基本情報

Version

active_hash v2.2.0(18/11/22)

コード量

$ cloc ./active_hash/lib/
      11 text files.
      11 unique files.
       0 files ignored.

github.com/AlDanial/cloc v 1.80  T=0.03 s (395.3 files/s, 36405.0 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Ruby                            11            190              5            818
-------------------------------------------------------------------------------
SUM:                            11            190              5            818
-------------------------------------------------------------------------------

階層構造

他の人のブログを読んでいるときに、階層構造見るたびに「描くの大変そうだなー。」と思っておりました。 皆さん恐らくtree コマンド使ってたのですね、、、勉強になりました。。。

$ tree ./lib/
./lib/
├── active_file
│   ├── base.rb
│   ├── hash_and_array_files.rb
│   └── multiple_files.rb
├── active_hash
│   ├── base.rb
│   └── version.rb
├── active_hash.rb
├── active_json
│   └── base.rb
├── active_yaml
│   ├── aliases.rb
│   └── base.rb
├── associations
│   └── associations.rb
└── enum
    └── enum.rb

    6 directories, 11 files

使い方

# active_hash 無し
# ./app/models/person.rb
class Person < ActiveRecord::Base
  COUNTRIES = ['US', 'Canada']
end

# in some view
<%= collection_select :person, :country_id, Person::COUNTRIES, :to_s, :to_s %>
# active_hash 有り
# ./app/models/country.rb
class Country < ActiveHash::Base
  self.data = [
    {id: 1, name: 'US'},
    {id: 2, name: 'Canada'}
  ]
end

# in some view
<%= collection_select :person, :country_id, Country.all, :id, :name %>

このように、Ruby のハッシュで書いたものをActiveRecord-like に利用することができます。

下記のようにすれば、クラスの外側でもデータを設定することもできます。

# ./app/models/country.rb
class Country < ActiveHash::Base
end

# ./config/initializers/data.rb
Rails.application.config.to_prepare do
  Country.data = [
      {id: 1, name: 'US'},
      {id: 2, name: 'Canada'}
  ]
end

データを呼び出したいときは、ActiveRecord を呼び出すときのように呼び出せます。

Country.all                    # => returns all Country objects
Country.count                  # => returns the length of the .data array
Country.first                  # => returns the first country object
Country.last                   # => returns the last country object
Country.find 1                 # => returns the first country object with that id
...

ちなみに、詳細は省きますがHash だけではなく、YAMLJSON 等の形式でもデータを格納できます。

詳細: README.md

実装内容

ActiveRecord-like にHash を扱えるようにしている実装

ActiveRecord-like なので、当然といえば当然なのですがDB 保存の処理は全く有りません。 その代わり、インスタンス変数にHash の内容を格納しているようです。 全くわかりませんが、本家のActiveRecord もDB 処理を除いては同じような実装なのでしょうかね??? また今度、ActiveRecord の方のコードリーディングにも挑戦してみたいと思います。

ActiveYaml 読み取りの実装

require 'yaml'

module ActiveYaml

  class Base < ActiveFile::Base
    extend ActiveFile::HashAndArrayFiles
    class << self
      def load_file
        if (data = raw_data).is_a?(Array)
          data
        else
          data.values
        end
      end

      def extension
        "yml"
      end

      private
      def load_path(path)
        YAML.load(ERB.new(File.read(path)).result)
      end
    end
  end
end

先日、YAML から読み取ったデータの処理を実装したばかりなので気になっていた箇所です。 ActiveYaml::Base でModule ActiveFile::HashAndArrayFilesextend する形でデータの読み取りを行なっています。 Module ActiveFile::HashAndArrayFilesYAMLJSON 共通の処理をまとめたという感じなのでしょうかね??? まだまだ設計的な話は全くわかりません。。。

気になった箇所・メソッド

まとめ

初めてのコードリーディングでどこから始めていいのか全くわかりませんでした。全体的な進め方に関しては、nipe0324さんのコードリーディングを参考にさせていただきました。少しずつ自分なりのコードリーディングのやり方を固めていきたいです。

ソースコードに関してだけでなく、cloc コマンドやtree コマンドに関しても知る機会になり、よかったと思います。

次回はクラス図の作成にも挑戦して見たいと思います。一つ一つできることを増やしてきたいと思います!