Ruby Gem Code Reading settings logic Part 1

background

After reading this article, I decided to deepen my knowledge of Ruby while reading the code. https://blog.freedom-man.com/try-rubygem-codereading

Target

settinglogic

Get ready to read the code

Install the target gem

$ bundle init

Stumble from the beginning ..

$ 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

It seems that the openssl link is wrong, so fix it by referring to this article 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

I was able to go with this.

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

Check the target file

file is only one file [settingslogic.rb] (https://github.com/binarylogic/settingslogic/blob/master/lib/settingslogic.rb)

Take a quick look at the contents of the code

class Settingslogic < Hash

It seems that it inherits the Hash class

Is it easy to insert the key and 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

This area seems to be an override of the Hash class

new(val, source)

You can write new like this. Here, initialize in the class is called (initialization), and it is added to the current instance with the specified key.

Or rather, you can override the behavior of the operator as a method.

  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

It seems to read yaml here. In this

 if self.class.namespace
        hash = hash[self.class.namespace] or return missing_key("Missing setting '#{self.class.namespace}' in #{hash_or_file}")
 end

I wonder if only the data in the specified namespace is used as hash here

Try using

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

For the time being, I was chased roughly. It's good to put it in rails and then try it, but I wanted to make it easier, so I output it a little.

Officially, there are dynamic binding etc. If you execute it directly, an error will occur. ..

Next, let's chase the code a little more while putting it in Rails.

Since there are not 200 lines, it is relatively easy to get along with.

Recommended Posts

Ruby Gem Code Reading settings logic Part 1
Ruby settings 1
Rails 5 Code Reading Part 1 ~ ActiveRecord new Method ~
Writing code Ruby
Ruby and Gem
Ruby Learning # 26 Reading Files
Thinking about logic Ruby
CRuby code reading (2): rb_newobj_of