Skip to content

Instantly share code, notes, and snippets.

@backpackerhh
Last active May 4, 2024 22:12
Show Gist options
  • Save backpackerhh/899f3464ee9c692b59da210b33551878 to your computer and use it in GitHub Desktop.
Save backpackerhh/899f3464ee9c692b59da210b33551878 to your computer and use it in GitHub Desktop.
Configuration holder
class Config
def initialize(options = {})
@config = options.each_with_object({}) do |(key, value), memo|
memo[key.to_sym] = assign_value(value)
end
rescue NoMethodError
raise ArgumentError, "Invalid value name"
end
def method_missing(method_name, *args, &block)
if method_name.to_s.end_with?("=")
value = args.length == 1 ? args.first : args
@config[method_name.to_s[0..-2].to_sym] = assign_value(value)
else
@config[method_name.to_sym]
end
end
def respond_to_missing?(method_name, include_private = false)
@config.key?(method_name.to_sym) || super
end
private
def assign_value(value)
value.is_a?(Hash) ? Config.new(value) : value
end
end

Configuration holder solution for a new startup based on microservices

Introduction

You've been hired as a senior Ruby developer in the new hottest startup that is building a really big thing. Obviously, they have selected microservices architecture which will require holding and accessing tons of configuration values. This is where you step in as you've been assigned a task to show your exceptional skills in Ruby metaprogramming to develop a flexible class capable of storing absolutely any config value.

Problem statement

Implement the Config class that takes a hash in a constructor:

conf = Config.new(foo: {bar: "x", "baz" => "y"}, "some" => "other")

and which then allows to read the values as follows:

conf.foo.bar
=> "x"
conf.foo.baz
=> "y"
conf.some
=> "other"
conf.foo.class
=> Config
conf.unknown
=> nil

The Config class also allows to override the values and assign new values:

conf.foo = 13
=> 13
conf.foo
=> 13
conf.foo = {bar: 18, "baz" => [30, 31]}
=> {:bar=>18, "baz"=>[30, 31]}
conf.foo.bar
=> 18
conf.foo.other = ["42", "44"]
=> ["42", "44"]
conf.foo.other
=> ["42", "44"]

In case of invalid hash key usage, we should raise an ArgumentError:

conf.foo = {10 => 11}
ArgumentError (Invalid value name: {})

Implementation

The internal implementation of the Config class is totally up to you as long as it behaves as in the code samples above. However, you may not use any external libraries (your CTO is a security nerd) and your tech lead forbids using the OpenStruct class for the reasons only known to him.

We have provided some really basic test coverage for the Config class, so you can test some happy paths. Please feel free to add your own tests.

Good luck!

conf = Config.new(foo: {bar: "x", "baz" => "y"}, "some" => "other")
conf.foo.bar
conf.foo.baz
conf.some
conf.foo.class
conf.unknown
conf.foo = 13
conf.foo
conf.foo = {bar: 18, "baz" => [30, 31]}
conf.foo.bar
conf.foo.other = ["42", "44"]
conf.foo.other
conf.foo = {10 => 11}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment