<?xml version="1.0" encoding="UTF-8"?>
<wiki>
  <body>&lt;pre&gt;&lt;code&gt;
# As an example here is the FileResolver

class WarningShot::FileResolver
  include WarningShot::Resolver

  # What is the add_dependency garbage?  Where is require?
  # All core libraries and gems should be 'required' with add_dependency, this allows warningshot to 'pick' it dependencies.
  #  This makes warningshot minimalistic.  For example, if 'net/scp' was required for FileResolver (which it will in the future)
  #  you could still use warningshot without it, it would simply disable the use of scp_protocol resolution.  By default
  # if a dependency is missing warningshot will disable the whole Resolver (ie, fileutils and uri below).  This is because they are
  # critical in how the FileResolver works.  The additional dependencies are optional based on how warningshot is being used.
  add_dependency :core, 'fileutils'
  add_dependency :core, 'uri'
  add_dependency :core, 'net/http', :disable =&amp;gt; false, :unregister =&amp;gt; :http_protocol_resolver
  add_dependency :core, 'net/https', :disable =&amp;gt; false, :unregister =&amp;gt; :https_protocol_resolver

  # The default sequential order for the resolver to run in, this can be modified
  #   by the end user by FileResolver.order(1) or by specifying the order in
  #   the priority loader.
  #   WarningShot::Config.new({:pload =&amp;gt; [:file]})
  order  500

  # The branch of the dependency tree to use, in the near future multiple branches will be
  #    able to be specified
  branch :file

  # A description of what the resolver does
  description 'Validates presence of files'
  
  # A resource needs to be created to 'stuff' the YAML into.  Why?
  #   Because additional methods are added to track the status of tests and
  #   and resolution, so the dependency needs to be a mutable object.
  FileResource = Struct.new(:source,:target) do
    def exists?;File.exists?(File.expand_path(target.path));end;
    def remove;File.unlink(File.expand_path(target.path));end;
  end
    
  #  Resolver.typecast is what puts the YAML into the resource (above).
  #    typecast takes a class (optional) and a block multiple typecasts only need
  #    to be declared if there are multiple datatypes per dependency in the yaml file.
  #  FileResource can handle a String or a Hash.  The class is optional, so if there is only
  #    one type of data in your yaml, you can leave it off
  typecast String do |file|
    FileResource.new URI.parse(''), URI.parse(file)
  end
  
  #  Handling a hash from yaml
  typecast Hash do |file|
    file[:source].sub!(/file:\/\//i,'') unless file[:source].nil?
    FileResource.new URI.parse(file[:source] || ''), URI.parse(file[:target])
  end
  
  # Resolver#initialize can be defined.  It will be passed one arg and a variable list argument.
  #   Make sure to call super in the constructor
  #
  # there is also an attr_reader :config that will be available to the object
  #   and an attr_accessor :dependencies which stores the set of dependencies to be checked
  def initialize(config,*deps)
    super
  end
    
  # Multiple tests can be registered.  The first test to pass the :if or :unless block (if provided)
  #   will be executed, if none pass it, then a default test will be run (one without a conditional block).
  #   Examples of conditional blocks can be seen in register :resolution below
  #
  # The actual test block (after do) can take 0, 1, or 2 arguments.  The Resolver will pass in the correct number
  #   attributes.  If one is specified it will pass in the current dependency (at runtime).  If two are specified it will
  #   pass in the current dependency and the running configuration.
  #   
  #  @example
  #    #A test taking a file dependency and a configuration
  #    register(:test) do |file,config|
  #      if file.target.path =~ /database.yml/ &amp;amp;&amp;amp; config[:environment] == 'production'
  #        # ... do some specific test for production database config files
  #      end
  #    end
  #
  #    #A test with an :if conditional that takes the configuration...
  #    register(:test,:if =&amp;gt; lambda{|file,config| config[:environment] == 'development' }) do |file|
  #      # ... the :if statement only allows this to run in the 'development' environment
  #      # ... in this test I may only need file and not the config.
  #    end
  #
  register :test, {:name =&amp;gt; :file_check} do |file|
    if file_found = file.exists?
      logger.debug &quot; ~ [PASSED] file: #{file.target.path}&quot;
    else
      logger.warn &quot; ~ [FAILED] file: #{file.target.path}&quot;
    end

    file_found
  end
  
  # Register a resolution.  The :if condition checks to see that the protocol to resolve over
  #   is file://
  register(:resolution, { :name =&amp;gt; :file_protocol,
    :desc =&amp;gt; &quot;Resolves files from target sources&quot;,
    :if =&amp;gt; lambda { |file| 
      !!(file.source.scheme =~ /file/i || file.source.scheme == nil &amp;amp;&amp;amp; !file.source.path.empty?)
    }
  }) do |file|    
    begin
      FileUtils.cp File.expand_path(file.source.path), File.expand_path(file.target.path)
    rescue Exception =&amp;gt; ex
      logger.error &quot; ~ Could not restore file (#{file.target.path}) from #{file.source.path}&quot;
    end
    file.exists?
  end
  
  # Register a resolution.  The :if condition checks to see that the protocol to resolve over
  #   is http:// or https://
  register(:resolution, { :name =&amp;gt; :http_protocol,
    :desc =&amp;gt; &quot;Resolves files from HTTP sources&quot;,
    :if =&amp;gt; lambda { |file| !!(file.source.to_s =~ /http(s)?/i)}
  }) do |file|
    begin
      http = Net::HTTP.new(file.source.host,file.source.port)
      http.use_ssl = (file.source.scheme == 'https')
      file.source.path = '/' if file.source.path.empty?
      resp = http.get(file.source.path)

      File.open(file.target.path,&quot;w+&quot;){ |fs| fs.puts resp.body }  if resp.code == &quot;200&quot;
    rescue Exception =&amp;gt; ex  
      logger.error &quot; ~ Could not restore file (#{file.target.path}) from #{file.source.path}&quot;
    end
    file.exists?
  end

end
&lt;/code&gt;&lt;/pre&gt;</body>
  <created-at type="datetime">2009-11-23T11:06:26-08:00</created-at>
  <id type="integer">79686</id>
  <permalink>an-example-resolver</permalink>
  <repository-id type="integer">54782</repository-id>
  <title>An example resolver</title>
  <updated-at type="datetime">2008-11-21T18:01:32-08:00</updated-at>
  <user-id type="integer">11587</user-id>
</wiki>
