TrestleAdmin/trestle

View on GitHub
lib/trestle/configurable.rb

Summary

Maintainability
A
25 mins
Test Coverage
module Trestle
  module Configurable
    extend ActiveSupport::Concern

    delegate :defaults, to: :class

    def configure(&block)
      yield self if block_given?
      self
    end

    def fetch(name)
      name = name.to_sym

      options.fetch(name) {
        if defaults.key?(name)
          value = defaults[name]

          # Avoid references to the same instance of a default value
          if value.respond_to?(:dup) && !value.is_a?(Proc)
            value = value.dup
          end

          assign(name, value)
        end
      }
    end

    def assign(name, value)
      options[name.to_sym] = value
    end

    def options
      @options ||= {}
    end

    def as_json(options=nil)
      @options.each_with_object({}) do |(k, v), h|
        h[k] = v.as_json(options)
      end
    end

    def inspect
      "#<#{self.class.name || "Anonymous(Trestle::Configurable)"}>"
    end

    module ClassMethods
      def defaults
        @defaults ||= {}
      end

      def option(name, default=nil, opts={})
        name = name.to_sym

        define_method("#{name}=") do |value|
          assign(name, value)
        end

        define_method(name) do |*args|
          value = fetch(name)

          if value.respond_to?(:call) && opts[:evaluate] != false
            value = value.call(*args)
          end

          value
        end

        defaults[name] = default
      end

      def deprecated_option(name, message=nil)
        define_method("#{name}=") do |value|
          Trestle.deprecator.warn(message)
        end

        define_method(name) do |*args|
          Trestle.deprecator.warn(message)
        end
      end
    end

    module Open
    protected
      def respond_to_missing(name, include_all=false)
        true
      end

      def method_missing(name, *args, &block)
        if name =~ /(.*)\=$/
          key, value = $1, args.first
          options[key.to_sym] = value
        else
          options[name.to_sym] ||= self.class.new
        end
      end
    end
  end
end