AlchemyCMS/alchemy_cms

View on GitHub
lib/alchemy/modules.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

module Alchemy
  module Modules
    mattr_accessor :alchemy_modules

    @@alchemy_modules = YAML.load_file(File.expand_path("../../config/alchemy/modules.yml", __dir__))

    class << self
      def included(base)
        base.send :helper_method, :alchemy_modules, :module_definition_for
      end

      # Register a Alchemy module.
      #
      # A module is a Hash that must have at least a name and a navigation key
      # that has a controller and action name.
      #
      # == Example:
      #
      #     name: 'module',
      #     navigation: {
      #       controller: 'admin/controller_name',
      #       action: 'index'
      #     }
      #
      def register_module(module_definition)
        definition_hash = module_definition.deep_stringify_keys

        ### Validate controller(s) existence
        if definition_hash["navigation"].is_a?(Hash)
          defined_controllers = [definition_hash["navigation"]["controller"]]

          if definition_hash["navigation"]["sub_navigation"].is_a?(Array)
            defined_controllers.concat(definition_hash["navigation"]["sub_navigation"].map { |x| x["controller"] })
          end

          validate_controllers_existence(defined_controllers, definition_hash)
        end

        @@alchemy_modules |= [definition_hash]
      end

      private

      def validate_controllers_existence(controllers, definition_hash)
        controllers.each do |controller_val|
          next if controller_val.blank?

          controller_name = "#{controller_val.camelize}Controller"

          begin
            controller_name.constantize
          rescue NameError
            raise "Error in AlchemyCMS module definition: '#{definition_hash["name"]}'. Could not find the matching controller class #{controller_name.sub(/^::/, "")} for the specified controller: '#{controller_val}'"
          end
        end
      end
    end

    # Get the module definition for given module name
    #
    # You can also pass a hash of an module definition.
    # It then tries to find the module defintion from controller name and action name
    #
    def module_definition_for(name_or_params)
      case name_or_params
      when String
        alchemy_modules.detect { |m| m["name"] == name_or_params }
      when Hash
        name_or_params.stringify_keys!
        alchemy_modules.detect do |alchemy_module|
          module_navi = alchemy_module.fetch("navigation", {})
          definition_from_mainnavi(module_navi, name_or_params) ||
            definition_from_subnavi(module_navi, name_or_params) ||
            definition_from_nested(module_navi, name_or_params)
        end
      else
        raise ArgumentError, "Could not find module definition for #{name_or_params}"
      end
    end

    private

    def definition_from_mainnavi(module_navi, params)
      controller_matches?(module_navi, params) && action_matches?(module_navi, params)
    end

    def definition_from_subnavi(module_navi, params)
      subnavi = module_navi["sub_navigation"]
      return if subnavi.nil?

      subnavi.any? do |navi|
        controller_matches?(navi, params) && action_matches?(navi, params)
      end
    end

    def definition_from_nested(module_navi, params)
      nested = module_navi["nested"]
      return if nested.nil?

      nested.any? do |navi|
        controller_matches?(navi, params) && action_matches?(navi, params)
      end
    end

    def controller_matches?(navi, params)
      remove_slash(navi["controller"]) == remove_slash(params["controller"])
    end

    def action_matches?(navi, params)
      navi["action"] == params["action"]
    end

    def remove_slash(str)
      str.gsub(/^\//, "")
    end
  end
end