Ruby Gem Code Reading settingslogic 編その１


## 背景
この記事を読んで、コードリーディングしながらRubyの知識深めようと思ったため
https://blog.freedom-man.com/try-rubygem-codereading

## 対象
[settinglogic](https://github.com/binarylogic/settingslogic)

## コードを読む準備をする 

### 対象のgem をインストールする

```
$ bundle init
```

さっそく最初からつまずく..


```bash
$ gem install settingslogic
ERROR:  Loading command: install (LoadError)
	dlopen(/Users/XXXX/.rbenv/versions/2.4.2/lib/ruby/2.4.0/x86_64-darwin16/openssl.bundle, 9): Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib
  Referenced from: /Users/XXXX/.rbenv/versions/2.4.2/lib/ruby/2.4.0/x86_64-darwin16/openssl.bundle
  Reason: image not found - /Users/XXXX/.rbenv/versions/2.4.2/lib/ruby/2.4.0/x86_64-darwin16/openssl.bundle
ERROR:  While executing gem ... (NoMethodError)
    undefined method `invoke_with_build_args' for nil:NilClass

```

opensslのリンクがおかしくなっているのが原因らしく、こちらの記事を参考に修正
https://qiita.com/Capotasto/items/16be7620c4eed42efccb
https://qiita.com/YoshiyukiKato/items/e4f67c588d2943c1253d

```
$ brew uninstall --force --ignore-dependencies openssl
$ brew install openssl
$ brew link openssl 
$ rbenv uninstall 2.4.2
$ rbenv install 2.4.2
```
これで行けた。

```
$ echo 'source "https://rubygems.org"' >> ./Gemfile
$ echo 'gem "settingslogic"' >> ./Gemfile
$ echo 'gem "pry"' >> ./Gemfile
$ bundle config set --local path 'vendor/bundle'
$ bundle install
```

## 対象のファイルを確認

fileは１ファイルだけ
[settingslogic.rb]
(https://github.com/binarylogic/settingslogic/blob/master/lib/settingslogic.rb)


## コードの中身をざっと見てみる

```ruby
class Settingslogic < Hash
```
Hashクラスを継承しているらしい

keyとvalueをかんたんに突っ込みやすくしてるのかな？


```ruby
    def [](key)
      instance.fetch(key.to_s, nil)
    end

    def []=(key, val)
      # Setting[:key][:key2] = 'value' for dynamic settings
      val = new(val, source) if val.is_a? Hash
      instance.store(key.to_s, val)
      instance.create_accessor_for(key, val)
    end
```

この辺はHashクラスのオーバーライドっぽい

`new(val, source)`

newってこんな書き方できるんだ。ここではクラス内のinitializeが呼ばれ(初期化),指定されたkeyで現状のインスタンスに追加されていますね。

というか演算子の挙動をメソッドとしてオーバーライドできるんですね。

```rb
  def initialize(hash_or_file = self.class.source, section = nil)
    #puts "new! #{hash_or_file}"
    case hash_or_file
    when nil
      raise Errno::ENOENT, "No file specified as Settingslogic source"
    when Hash
      self.replace hash_or_file
    else
      file_contents = open(hash_or_file).read
      hash = file_contents.empty? ? {} : YAML.load(ERB.new(file_contents).result).to_hash
      if self.class.namespace
        hash = hash[self.class.namespace] or return missing_key("Missing setting '#{self.class.namespace}' in #{hash_or_file}")
      end
      self.replace hash
    end
    @section = section || self.class.source  # so end of error says "in application.yml"
    create_accessors!
  end
```

ここでyamlを読み込むみたい。この中の

```rb
 if self.class.namespace
        hash = hash[self.class.namespace] or return missing_key("Missing setting '#{self.class.namespace}' in #{hash_or_file}")
 end
```
ここは指定したnamespaceのデータだけhashとして使うってことかな

## 使ってみる

```settings.yaml
defaults: &defaults
  cool:
    saweet: nested settings
  neat_setting: 24
  awesome_setting: <%= "Did you know 5 + 5 = #{5 + 5}?" %>

development:
  <<: *defaults
  neat_setting: 800

test:
  <<: *defaults

production:
  <<: *defaults
```

```setting.rb
require 'settingslogic'

class Settings < Settingslogic
  source File.expand_path("../settings.yml", __FILE__)
  namespace ENV['ENV']
end
```

```sample.rb
require File.expand_path("../settings.rb", __FILE__)

puts Settings.neat_setting
puts Settings.awesome_setting
puts Settings.cool.saweet

```

```
\w $ ENV=production ruby sample.rb 
24
Did you know 5 + 5 = 10?
nested settings

```

ひとまずざっくりは追いかけられた。
railsに入れてから試すのもいいが、ラクしたかったのでちょくで出力した。

公式見るとdynamic bindingなどがあるのだが、
直で実行するとエラーになってしまう。。

次はRailsに入れつつもう少しコード追いかけよう。

200行もないので割ととっつきやすい。