Last active
August 21, 2018 13:37
-
-
Save adjam/501e29e1c1f0d3a87ad99a9ef4f74fb1 to your computer and use it in GitHub Desktop.
'Configurable' module for Ruby, similar to https://github.com/markevans/configurable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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')