# File lib/automateit/template_manager/base.rb, line 86
  def _render_helper(*options, &block) # :yields: block_opts
    args, opts = args_and_opts(*options)
    source_filename = args[0] || opts[:file]
    target_filename = args[1] || opts[:to]
    source_text = opts[:text]
    opts[:backup] = true if opts[:backup].nil?

    raise ArgumentError.new("No source specified with :file or :text") if not source_filename and not source_text
    raise Errno::ENOENT.new(source_filename) if writing? and source_filename and not _exists?(source_filename)

    begin
      # source_filename, target_filename, opts={}
      opts[:check] ||= @default_check
      target_exists = target_filename && _exists?(target_filename)
      updates = []

      if target_filename
        unless opts[:force]
          case opts[:check]
          when :exists
            if target_exists
              log.debug(PNOTE+"Rendering for '#{target_filename}' skipped because it already exists")
              return false
            else
              log.info(PNOTE+"Rendering '#{target_filename}' because of it doesn't exist")
            end
          when :timestamp
            if target_exists
              updates = _newer(target_filename, \
                  *[source_filename, opts[:dependencies]].reject{|t| t.nil?}.flatten)
              if updates.empty?
                log.debug(PNOTE+"Rendering for '#{target_filename}' skipped because dependencies haven't been updated")
                return false
              end
            end
          end
        end
      end

      target_contents = target_exists ? _read(target_filename) : ""
      source_text ||= _read(source_filename)

      if source_text.blank? and preview?
        return true
      end

      binder = nil
      if opts[:locals]
        # Create a binding that the template can get variables from without
        # polluting the Driver's namespace.
        callback = lambda{
          code = ""
          for key in opts[:locals].keys
            code << "#{key} = opts[:locals][:#{key}]\n"
          end
          eval code
          binding
        }
        binder = callback.call
      end

      block_opts = {
        :binder => binder,
        :filename => source_filename,
        :text => source_text,
        :locals => opts[:locals],
        :opts => opts,
      }
      output = block.call(block_opts)

      if target_filename
        case opts[:check]
        when :compare
          if not target_exists
            log.info(PNOTE+"Rendering '#{target_filename}' because of it doesn't exist")
          elsif output == target_contents
            log.debug(PNOTE+"Rendering for '#{target_filename}' skipped because contents are the same")
            return false
          else
            log.info(PNOTE+"Rendering '#{target_filename}' because its contents changed")
          end
        when :timestamp
          log.info(PNOTE+"Rendering '#{target_filename}' because of updated: #{updates.join(' ')}")
        end
      end

      _backup(target_filename) if target_exists and opts[:backup]

      return(target_filename ? _write(target_filename, output) : output)
    ensure
      if opts[:mode] or opts[:user] or opts[:group]
        interpreter.chperm(target_filename, :mode => opts[:mode], :user => opts[:user], :group => opts[:group])
      end
    end
  end