# encoding: utf-8 module Hexx # Contains methods to declare and inject module dependencies. # # Allows to provide DJ framework for the gem. # # @example # # Declare the dependencies for the gem root module # # # lib/my_gem.rb # module MyGem # extend Hexx::Dependencies # self.depends_on :get_item, :add_item, :delete_item # end # # # Inject dependencies (as a constant or its name) # # # config/my_gem.rb # MyGem.configure do |config| # config.get_item = AnotherGem::Services::Get # config.add_item = "AnotherGem::Services::Add" # end # # # Use dependency in the module code (models, services, etc.) # # When the dependency hasn't been set, fails with +NotImplementedError+ # # MyGem.get_item # => AnotherGem::Services::Get # MyGem.add_item # => AnotherGem::Services::Add # MyGem.delete_item # raises # module Dependencies # Yields a block and gives it self # # @example # module MyGem # extend Hexx::Dependencies # end # # MyGem.configure do |config| # c # => MyGem # end # # @yield The block. # @yieldparam [Dependencies] +self+. def configure yield(self) end # Declares dependency getter and setter. # # The getter constants, strings and symbols. The setter constantize the # assigned value. # # This allows setting dependency before definition of a corresponding # constant. # # @example # module MyGem # extend Hexx::Dependencies # self.depends_on :some_class # end # # MyGem.some_class = String # # => "String" # MyGem.some_class # => String # # MyGem.some_class = "String" # # => "String" # MyGem.some_class # => String # # MyGem.some_class = :String # # => "String" # MyGem.some_class # => String # # @param [String, Symbol] name The name for dependency. def depends_on(*names) names.each do |name| DependencyName.new(name).validate add_dependency_setter name add_dependency_getter name end end private # @api hide # Checks the name of the dependency. class DependencyName < Struct.new(:name) # @api hide # Runs validations and fails in case of any error. def validate check_type check_value end private def check_type return if [String, Symbol].include? name.class fail TypeError.new "#{ name.inspect } is neither a string nor symbol." end def check_value return unless name.to_s == "" fail ArgumentError.new "The dependency name should not be blank." end end # @api hide # Checks the value of the dependency. class Dependency < Struct.new(:name, :value) # @api hide # Runs validations and fails in case of any error. def validate check_type check_value end private def check_type return if value.is_a? String return if value.is_a? Symbol return if value.is_a? Module fail TypeError.new "#{ inspect } is not a string, symbol or module." end def check_value return unless value.to_s == "" fail ArgumentError.new "The dependency #{ name } should not be blank." end def inspect value.inspect end end def add_dependency_setter(name) instance_eval " def #{ name }=(value); Dependency.new(:#{ name }, value).validate; @#{ name }= value.to_s; end" end def add_dependency_getter(name) instance_eval " def #{ name }; value = @#{ name }; fail NotImplementedError unless value; value.constantize; end" end end end