# encoding: utf-8 # # This file is part of the devdns gem. Copyright (C) 2012 and above Shogun . # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php. # module DevDNSd # This class encapsulate a rule for matching an hostname. class Rule # The pattern to match. Default: `/.+/`. attr_accessor :match # The type of request to match. Default: `:A`. # # @see .create attr_accessor :type # The IP or hostname to reply back to the client. Default: `127.0.0.1`. # # @see .create attr_accessor :reply # A list of options for the request. Default is an empty hash. attr_accessor :options # An optional block to compute the reply instead of using the `reply` parameter. # # @see .create attr_accessor :block # Creates a new rule. # # @param match [String|Regexp] The pattern to match. # @param reply [String] The IP or hostname to reply back to the client. # @param type [Symbol] The type of request to match. # @param options [Hash] A list of options for the request. # @param block [Proc] An optional block to compute the reply instead of using the `reply` parameter. # @see .create def initialize(match = /.+/, reply = "127.0.0.1", type = :A, options = {}, &block) reply ||= "127.0.0.1" type ||= :A @match = match @type = type @reply = block.blank? ? reply : nil @options = options @block = block raise(DevDNSd::Errors::InvalidRule.new("You must specify at least a rule and a host (also via a block). Optionally you can add a record type (default: A) and the options.")) if @reply.blank? && @block.nil? raise(DevDNSd::Errors::InvalidRule.new("You can only use hashs for options.")) if !@options.is_a?(::Hash) end # Returns the resource class(es) for the current rule. # # @return [Array|Class] The class(es) for the current rule. def resource_class classes = self.type.ensure_array.collect {|cls| self.class.symbol_to_resource_class(cls) }.compact.uniq classes.length == 1 ? classes.first : classes end # Checks if the rule is a regexp. # # @return [Boolean] `true` if the rule is a Regexp, `false` otherwise. def is_regexp? self.match.is_a?(::Regexp) end # Checks if the rule is a regexp. # # @return [Boolean] `true` if the rule has a block, `false` otherwise. def has_block? self.block.present? end # Matches a hostname to the rule. # # @param hostname [String] The hostname to match. # @return [MatchData|Boolean|Nil] Return `true` or MatchData (if the pattern is a regexp) if the rule matches, `false` or `nil` otherwise. def match_host(hostname) self.is_regexp? ? self.match.match(hostname) : (self.match == hostname) end # Creates a new rule. # # @param match [String|Regexp] The pattern to match. # @param reply_or_type [String|Symbol] The IP or hostname to reply back to the client (or the type of request to match, if a block is provided). # @param type [Symbol] The type of request to match. This is ignored if a block is provided. # @param options [Hash] A list of options for the request. # @param block [Proc] An optional block to compute the reply instead of using the `reply_or_type` parameter. In this case `reply_or_type` is used for the type of the request and `type` is ignored. def self.create(match, reply_or_type = nil, type = nil, options = {}, &block) raise(DevDNSd::Errors::InvalidRule.new("You must specify at least a rule and a host (also via a block). Optionally you can add a record type (default: A) and the options.")) if reply_or_type.blank? && block.nil? raise(DevDNSd::Errors::InvalidRule.new("You can only use hashs for options.")) if !options.is_a?(::Hash) rv = self.new(match) rv.options = options if block.present? then # reply_or_type acts like a type, type is ignored rv.type = reply_or_type || :A rv.reply = nil rv.block = block else # reply_or_type acts like a reply rv.reply = reply_or_type || "127.0.0.1" rv.type = type || :A end rv end # Converts a class to the correspondent symbol. # # @param klass [Class] The class to convert. # @return [Symbol] The symbol representation of the class. def self.resource_class_to_symbol(klass) klass.to_s.gsub(/(.+::)?(.+)/, "\\2").to_sym end # Converts a symbol to the correspondent DNS resource class. # # @param symbol [Symbol] The symbol to convert. # @return [Symbol] The class associated to the symbol. def self.symbol_to_resource_class(symbol) symbol = symbol.to_s.upcase begin "Resolv::DNS::Resource::IN::#{symbol}".constantize rescue ::NameError raise(DevDNSd::Errors::InvalidRule.new("Invalid resource class #{symbol}.")) end end end end