houston/houston-core

View on GitHub
app/concerns/project_adapter.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module ProjectAdapter


  def adapters
    @adapters ||= {}
  end


  def has_adapter(*adapter_namespaces)
    adapter_namespaces.each do |adapter_namespace|
      adapter_module = Houston::Adapters[adapter_namespace]
      raise ArgumentError, "#{adapter_module} should respond to `adapters`" unless adapter_module.respond_to?(:adapters)
      raise ArgumentError, "#{adapter_module} should respond to `adapter`" unless adapter_module.respond_to?(:adapter)
      raise ArgumentError, "#{adapter_namespace} has already been defined" if adapters[adapter_module.name]

      adapter = Adapter.new(self, adapter_module)
      adapters[adapter_module.name] = adapter
      adapter.define_methods!
    end
  end


  class Adapter

    def initialize(model, adapter_module)
      @model          = model
      @namespace      = adapter_module
      @name           = adapter_module.name
      @attribute_name = name.demodulize.underscore
    end

    attr_reader :model, :namespace, :name, :attribute_name

    def title
      name.demodulize.titleize
    end

    def validation_method
      :"#{attribute_name}_configuration_is_valid"
    end

    def before_update_method
      :"#{attribute_name}_before_update"
    end

    def adapter_method
      :"#{attribute_name}_adapter"
    end

    def params_method
      :"parameters_for_#{attribute_name}_adapter"
    end

    def concern_name
      :"#{name.demodulize}Concern"
    end

    def adapter_name_method
      :"#{attribute_name}_name"
    end

    def prop_name
      "adapter.#{attribute_name.to_s.camelize(:lower)}"
    end

    def define_methods!
      concern = ProjectAdapter.const_set(concern_name, Module.new)
      concern.extend ActiveSupport::Concern

      class_methods = concern.const_set(:ClassMethods, Module.new)
      class_methods.module_eval <<-RUBY, __FILE__ , __LINE__ + 1
        def with_#{attribute_name}(value=nil)
          if value
            where [ "COALESCE(projects.props->>'#{prop_name}', 'None') = ?", value ]
          else
            where "COALESCE(projects.props->>'#{prop_name}', 'None') != 'None'"
          end
        end
      RUBY

      concern.module_eval <<-RUBY, __FILE__ , __LINE__ + 1
        included do
          validate :#{validation_method}
          before_update :#{before_update_method}
        end

        def #{adapter_name_method}
          props["#{prop_name}"] || "None"
        end

        def has_#{attribute_name}?
          #{adapter_name_method} != "None"
        end

        def #{validation_method}
          #{adapter_method}.errors_with_parameters(self, *#{params_method}.values).each do |attribute, messages|
            errors.add(attribute, messages) if messages.any?
          end
        end

        def #{before_update_method}
          return true unless #{attribute_name}.respond_to?(:before_update)
          #{attribute_name}.before_update(self)
        end

        def #{attribute_name}
          @#{attribute_name} ||= #{adapter_method}
              .build(self, *#{params_method}.values)
              .extend(FeatureSupport)
        end

        def #{params_method}
          #{adapter_method}.parameters.each_with_object({}) do |parameter, hash|
            hash[parameter] = props[parameter.to_s]
          end
        end

        def #{adapter_method}
          #{namespace}.adapter(#{adapter_name_method})
        end
      RUBY

      model.send :include, concern
    end

  end


end