Skip to content

Instantly share code, notes, and snippets.

@adjam
Last active August 21, 2018 13:37
Show Gist options
  • Save adjam/501e29e1c1f0d3a87ad99a9ef4f74fb1 to your computer and use it in GitHub Desktop.
Save adjam/501e29e1c1f0d3a87ad99a9ef4f74fb1 to your computer and use it in GitHub Desktop.
'Configurable' module for Ruby, similar to https://github.com/markevans/configurable
#!/usr/bin/env ruby
# Makes a class globally configurable, e.g. in a Rails initializer.
# Usage:
# `class MyClass
# include Configurable
# create_config do
# # url attribute has a default value, which is the return value of the block
# url { 'http://www.example.com'}
# # name attribute is allowed but has no default value
# name
# end
#
# def do_something
# fetch(configuration.url + "?name=" + configuration.name)
# end
# end`
# Then, in your app's startup:
# `MyClass.configure do |c|
# c.name = 'my_name'
# end`
# and now, in your application:
# `MyClass.new.do_something` => `fetch('http://example.com?name=my_name')`
module Configurable
# Creates a configuration struct for a class based
# on the contents of the `configure` block.
class CFactory
attr_accessor :attributes
def initialize
@attributes = {}
end
def method_missing(name, *args, &block)
attributes[name] = {}
attributes[name][:dv] = block.call() unless block.nil?
end
def build(name)
attrs = attributes.keys.map(&:to_sym)
dvs = Hash[attributes.select { |k, v| v[:dv] }.map { |k, v| [k, v[:dv]] }]
Struct.new(name, *attrs) do
define_method(:initialize) do |options = {}|
members.each do |m|
send "#{m}=", options.fetch(m,dvs[m])
end
end
end
end
end
def self.included(base)
base.extend(ClassMethods)
base.instance_variable_set(:@config, {})
end
def configuration
# deep copy of configuration class
@configuration ||= Marshal.load(Marshal.dump(self.class.instance_variable_get(:@default_config)))
end
module ClassMethods
def configure
yield @default_config
end
def create_config(&block)
factory = CFactory.new
factory.instance_eval(&block)
conf = "#{self.name}Configuration"
struct = factory.build(conf)
const_set('Configuration', struct)
instance_variable_set(:@default_config, struct.new)
end
end
end
class Test
include Configurable
create_config do
url { 'http://www.example.com' }
name
end
end
#Test.configure do |c|
# c.url = 'ackthppt'
#end
t = Test.new
puts t.configuration
Test.configure do |c|
c.url = 'ackthppt'
end
t2 = Test.new
puts t2.configuration
puts "did we mess up?"
puts t.configuration == t2.configuration
@adjam
Copy link
Author

adjam commented Aug 21, 2018

One difference between the referenced gem and this is that it creates a custom 'Configuration' class for your class, which can be instantiated normally if you need multiples, e.g. Test::Configuration.new(url: 'https://www.example.com')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment