Skip to content

Instantly share code, notes, and snippets.

@dux
Last active November 13, 2017 23:10
Show Gist options
  • Save dux/8b36ff2c927225b4b7f5476867a3fe39 to your computer and use it in GitHub Desktop.
Save dux/8b36ff2c927225b4b7f5476867a3fe39 to your computer and use it in GitHub Desktop.
define class attributes the functional way
# frozen_string_literal: true
# ClassAttributes.define klass, :layout, 'default_value_optional'
# klass.layout -> get value
# klass.layout = value -> set value
# class A
# ClassAttributes.define self, :layout, 'default'
# class_attribute :layout, 'default'
# end
# class B < A
# layout 'l B'
# end
# class C < B
# end
# puts A.layout # default
# puts B.layout # l B
# puts C.layout # l B
module ClassAttributes
extend self
CA_DEFAULTS = {}
# defines class variable
def define klass, name, default=nil, &block
raise ArgumentError, 'name must be symbol' unless name.class == Symbol
default ||= block if block
::ClassAttributes::CA_DEFAULTS[name] = { 'Object'=>default }
klass.define_singleton_method('%s=' % name) { |*args| send name, *args}
klass.define_singleton_method(name) do |*args|
root = ::ClassAttributes::CA_DEFAULTS[name]
# set and return if argument defined
return root[to_s] = args[0] if args.length > 0
# find value and return
ancestors.map(&:to_s).each do |el|
value = root[el]
if value || el == 'Object'
value = instance_exec(&value) if value.is_a?(Proc)
return value
end
end
end
end
end
class Object
def class_attribute name, default=nil, &block
ClassAttributes.define self, name, default, &block
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment