背景
この記事を読んで、コードリーディングしながらRubyの知識深めようと思ったため
https://blog.freedom-man.com/try-rubygem-codereading
対象
コードを読む準備をする
対象のgem をインストールする
$ bundle init
さっそく最初からつまずく..
$ 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は1ファイルだけ
[settingslogic.rb]
(https://github.com/binarylogic/settingslogic/blob/master/lib/settingslogic.rb)
コードの中身をざっと見てみる
class Settingslogic < Hash
Hashクラスを継承しているらしい
keyとvalueをかんたんに突っ込みやすくしてるのかな?
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で現状のインスタンスに追加されていますね。
というか演算子の挙動をメソッドとしてオーバーライドできるんですね。
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を読み込むみたい。この中の
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として使うってことかな
使ってみる
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
require 'settingslogic'
class Settings < Settingslogic
source File.expand_path("../settings.yml", __FILE__)
namespace ENV['ENV']
end
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行もないので割ととっつきやすい。