mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 17:50:22 +00:00
Building an army
This commit is contained in:
703
test/fixtures/ruby/formula.rb
vendored
Normal file
703
test/fixtures/ruby/formula.rb
vendored
Normal file
@@ -0,0 +1,703 @@
|
||||
require 'download_strategy'
|
||||
require 'dependencies'
|
||||
require 'formula_support'
|
||||
require 'hardware'
|
||||
require 'bottles'
|
||||
require 'extend/fileutils'
|
||||
require 'patches'
|
||||
require 'compilers'
|
||||
|
||||
# Derive and define at least @url, see Library/Formula for examples
|
||||
class Formula
|
||||
include FileUtils
|
||||
|
||||
attr_reader :name, :path, :url, :version, :homepage, :specs, :downloader
|
||||
attr_reader :standard, :unstable, :head
|
||||
attr_reader :bottle_version, :bottle_url, :bottle_sha1
|
||||
|
||||
# The build folder, usually in /tmp.
|
||||
# Will only be non-nil during the stage method.
|
||||
attr_reader :buildpath
|
||||
|
||||
# Homebrew determines the name
|
||||
def initialize name='__UNKNOWN__', path=nil
|
||||
set_instance_variable 'homepage'
|
||||
set_instance_variable 'url'
|
||||
set_instance_variable 'bottle_version'
|
||||
set_instance_variable 'bottle_url'
|
||||
set_instance_variable 'bottle_sha1'
|
||||
set_instance_variable 'head'
|
||||
set_instance_variable 'specs'
|
||||
set_instance_variable 'standard'
|
||||
set_instance_variable 'unstable'
|
||||
|
||||
if @head and (not @url or ARGV.build_head?)
|
||||
@url = @head
|
||||
@version = 'HEAD'
|
||||
@spec_to_use = @unstable
|
||||
else
|
||||
if @standard.nil?
|
||||
@spec_to_use = SoftwareSpecification.new(@url, @specs)
|
||||
else
|
||||
@spec_to_use = @standard
|
||||
end
|
||||
end
|
||||
|
||||
raise "No url provided for formula #{name}" if @url.nil?
|
||||
@name = name
|
||||
validate_variable :name
|
||||
|
||||
# If we got an explicit path, use that, else determine from the name
|
||||
@path = path.nil? ? self.class.path(name) : Pathname.new(path)
|
||||
|
||||
# Use a provided version, if any
|
||||
set_instance_variable 'version'
|
||||
# Otherwise detect the version from the URL
|
||||
@version ||= @spec_to_use.detect_version
|
||||
# Only validate if a version was set; GitHubGistFormula needs to get
|
||||
# the URL to determine the version
|
||||
validate_variable :version if @version
|
||||
|
||||
CHECKSUM_TYPES.each { |type| set_instance_variable type }
|
||||
|
||||
@downloader = download_strategy.new @spec_to_use.url, name, version, @spec_to_use.specs
|
||||
|
||||
@bottle_url ||= bottle_base_url + bottle_filename(self) if @bottle_sha1
|
||||
end
|
||||
|
||||
# if the dir is there, but it's empty we consider it not installed
|
||||
def installed?
|
||||
return installed_prefix.children.length > 0
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
|
||||
def explicitly_requested?
|
||||
# `ARGV.formulae` will throw an exception if it comes up with an empty list.
|
||||
# FIXME: `ARGV.formulae` shouldn't be throwing exceptions, see issue #8823
|
||||
return false if ARGV.named.empty?
|
||||
ARGV.formulae.include? self
|
||||
end
|
||||
|
||||
def linked_keg
|
||||
HOMEBREW_REPOSITORY/'Library/LinkedKegs'/@name
|
||||
end
|
||||
|
||||
def installed_prefix
|
||||
head_prefix = HOMEBREW_CELLAR+@name+'HEAD'
|
||||
if @version == 'HEAD' || head_prefix.directory?
|
||||
head_prefix
|
||||
else
|
||||
prefix
|
||||
end
|
||||
end
|
||||
|
||||
def prefix
|
||||
validate_variable :name
|
||||
validate_variable :version
|
||||
HOMEBREW_CELLAR+@name+@version
|
||||
end
|
||||
def rack; prefix.parent end
|
||||
|
||||
def bin; prefix+'bin' end
|
||||
def doc; prefix+'share/doc'+name end
|
||||
def include; prefix+'include' end
|
||||
def info; prefix+'share/info' end
|
||||
def lib; prefix+'lib' end
|
||||
def libexec; prefix+'libexec' end
|
||||
def man; prefix+'share/man' end
|
||||
def man1; man+'man1' end
|
||||
def man2; man+'man2' end
|
||||
def man3; man+'man3' end
|
||||
def man4; man+'man4' end
|
||||
def man5; man+'man5' end
|
||||
def man6; man+'man6' end
|
||||
def man7; man+'man7' end
|
||||
def man8; man+'man8' end
|
||||
def sbin; prefix+'sbin' end
|
||||
def share; prefix+'share' end
|
||||
|
||||
# configuration needs to be preserved past upgrades
|
||||
def etc; HOMEBREW_PREFIX+'etc' end
|
||||
# generally we don't want var stuff inside the keg
|
||||
def var; HOMEBREW_PREFIX+'var' end
|
||||
|
||||
# plist name, i.e. the name of the launchd service
|
||||
def plist_name; 'homebrew.mxcl.'+name end
|
||||
def plist_path; prefix+(plist_name+'.plist') end
|
||||
|
||||
# Use the @spec_to_use to detect the download strategy.
|
||||
# Can be overriden to force a custom download strategy
|
||||
def download_strategy
|
||||
@spec_to_use.download_strategy
|
||||
end
|
||||
|
||||
def cached_download
|
||||
@downloader.cached_location
|
||||
end
|
||||
|
||||
# tell the user about any caveats regarding this package, return a string
|
||||
def caveats; nil end
|
||||
|
||||
# any e.g. configure options for this package
|
||||
def options; [] end
|
||||
|
||||
# patches are automatically applied after extracting the tarball
|
||||
# return an array of strings, or if you need a patch level other than -p1
|
||||
# return a Hash eg.
|
||||
# {
|
||||
# :p0 => ['http://foo.com/patch1', 'http://foo.com/patch2'],
|
||||
# :p1 => 'http://bar.com/patch2',
|
||||
# :p2 => ['http://moo.com/patch5', 'http://moo.com/patch6']
|
||||
# }
|
||||
# The final option is to return DATA, then put a diff after __END__. You
|
||||
# can still return a Hash with DATA as the value for a patch level key.
|
||||
def patches; end
|
||||
|
||||
# rarely, you don't want your library symlinked into the main prefix
|
||||
# see gettext.rb for an example
|
||||
def keg_only?
|
||||
self.class.keg_only_reason || false
|
||||
end
|
||||
|
||||
def fails_with? cc
|
||||
return false if self.class.cc_failures.nil?
|
||||
cc = Compiler.new(cc) unless cc.is_a? Compiler
|
||||
return self.class.cc_failures.find do |failure|
|
||||
next unless failure.compiler == cc.name
|
||||
failure.build.zero? or failure.build >= cc.build
|
||||
end
|
||||
end
|
||||
|
||||
# sometimes the clean process breaks things
|
||||
# skip cleaning paths in a formula with a class method like this:
|
||||
# skip_clean [bin+"foo", lib+"bar"]
|
||||
# redefining skip_clean? now deprecated
|
||||
def skip_clean? path
|
||||
return true if self.class.skip_clean_all?
|
||||
to_check = path.relative_path_from(prefix).to_s
|
||||
self.class.skip_clean_paths.include? to_check
|
||||
end
|
||||
|
||||
# yields self with current working directory set to the uncompressed tarball
|
||||
def brew
|
||||
validate_variable :name
|
||||
validate_variable :version
|
||||
|
||||
stage do
|
||||
begin
|
||||
patch
|
||||
# we allow formulas to do anything they want to the Ruby process
|
||||
# so load any deps before this point! And exit asap afterwards
|
||||
yield self
|
||||
rescue Interrupt, RuntimeError, SystemCallError => e
|
||||
puts if Interrupt === e # don't print next to the ^C
|
||||
unless ARGV.debug?
|
||||
%w(config.log CMakeCache.txt).select{|f| File.exist? f}.each do |f|
|
||||
HOMEBREW_LOGS.install f
|
||||
puts "#{f} was copied to #{HOMEBREW_LOGS}"
|
||||
end
|
||||
raise
|
||||
end
|
||||
onoe e.inspect
|
||||
puts e.backtrace
|
||||
|
||||
ohai "Rescuing build..."
|
||||
if (e.was_running_configure? rescue false) and File.exist? 'config.log'
|
||||
puts "It looks like an autotools configure failed."
|
||||
puts "Gist 'config.log' and any error output when reporting an issue."
|
||||
puts
|
||||
end
|
||||
|
||||
puts "When you exit this shell Homebrew will attempt to finalise the installation."
|
||||
puts "If nothing is installed or the shell exits with a non-zero error code,"
|
||||
puts "Homebrew will abort. The installation prefix is:"
|
||||
puts prefix
|
||||
interactive_shell self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def == b
|
||||
name == b.name
|
||||
end
|
||||
def eql? b
|
||||
self == b and self.class.equal? b.class
|
||||
end
|
||||
def hash
|
||||
name.hash
|
||||
end
|
||||
def <=> b
|
||||
name <=> b.name
|
||||
end
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
# Standard parameters for CMake builds.
|
||||
# Using Build Type "None" tells cmake to use our CFLAGS,etc. settings.
|
||||
# Setting it to Release would ignore our flags.
|
||||
# Setting CMAKE_FIND_FRAMEWORK to "LAST" tells CMake to search for our
|
||||
# libraries before trying to utilize Frameworks, many of which will be from
|
||||
# 3rd party installs.
|
||||
# Note: there isn't a std_autotools variant because autotools is a lot
|
||||
# less consistent and the standard parameters are more memorable.
|
||||
def std_cmake_args
|
||||
%W[
|
||||
-DCMAKE_INSTALL_PREFIX=#{prefix}
|
||||
-DCMAKE_BUILD_TYPE=None
|
||||
-DCMAKE_FIND_FRAMEWORK=LAST
|
||||
-Wno-dev
|
||||
]
|
||||
end
|
||||
|
||||
def self.class_s name
|
||||
#remove invalid characters and then camelcase it
|
||||
name.capitalize.gsub(/[-_.\s]([a-zA-Z0-9])/) { $1.upcase } \
|
||||
.gsub('+', 'x')
|
||||
end
|
||||
|
||||
# an array of all Formula names
|
||||
def self.names
|
||||
Dir["#{HOMEBREW_REPOSITORY}/Library/Formula/*.rb"].map{ |f| File.basename f, '.rb' }.sort
|
||||
end
|
||||
|
||||
# an array of all Formula, instantiated
|
||||
def self.all
|
||||
map{ |f| f }
|
||||
end
|
||||
def self.map
|
||||
rv = []
|
||||
each{ |f| rv << yield(f) }
|
||||
rv
|
||||
end
|
||||
def self.each
|
||||
names.each do |n|
|
||||
begin
|
||||
yield Formula.factory(n)
|
||||
rescue
|
||||
# Don't let one broken formula break commands. But do complain.
|
||||
onoe "Formula #{n} will not import."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
name
|
||||
end
|
||||
|
||||
def self.aliases
|
||||
Dir["#{HOMEBREW_REPOSITORY}/Library/Aliases/*"].map{ |f| File.basename f }.sort
|
||||
end
|
||||
|
||||
def self.canonical_name name
|
||||
name = name.to_s if name.kind_of? Pathname
|
||||
|
||||
formula_with_that_name = HOMEBREW_REPOSITORY+"Library/Formula/#{name}.rb"
|
||||
possible_alias = HOMEBREW_REPOSITORY+"Library/Aliases/#{name}"
|
||||
possible_cached_formula = HOMEBREW_CACHE_FORMULA+"#{name}.rb"
|
||||
|
||||
if name.include? "/"
|
||||
if name =~ %r{(.+)/(.+)/(.+)}
|
||||
tapd = HOMEBREW_REPOSITORY/"Library/Taps"/"#$1-#$2".downcase
|
||||
tapd.find_formula do |relative_pathname|
|
||||
return "#{tapd}/#{relative_pathname}" if relative_pathname.stem.to_s == $3
|
||||
end if tapd.directory?
|
||||
end
|
||||
# Otherwise don't resolve paths or URLs
|
||||
name
|
||||
elsif formula_with_that_name.file? and formula_with_that_name.readable?
|
||||
name
|
||||
elsif possible_alias.file?
|
||||
possible_alias.realpath.basename('.rb').to_s
|
||||
elsif possible_cached_formula.file?
|
||||
possible_cached_formula.to_s
|
||||
else
|
||||
name
|
||||
end
|
||||
end
|
||||
|
||||
def self.factory name
|
||||
# If an instance of Formula is passed, just return it
|
||||
return name if name.kind_of? Formula
|
||||
|
||||
# Otherwise, convert to String in case a Pathname comes in
|
||||
name = name.to_s
|
||||
|
||||
# If a URL is passed, download to the cache and install
|
||||
if name =~ %r[(https?|ftp)://]
|
||||
url = name
|
||||
name = Pathname.new(name).basename
|
||||
target_file = HOMEBREW_CACHE_FORMULA+name
|
||||
name = name.basename(".rb").to_s
|
||||
|
||||
HOMEBREW_CACHE_FORMULA.mkpath
|
||||
FileUtils.rm target_file, :force => true
|
||||
curl url, '-o', target_file
|
||||
|
||||
require target_file
|
||||
install_type = :from_url
|
||||
else
|
||||
name = Formula.canonical_name(name)
|
||||
# If name was a path or mapped to a cached formula
|
||||
if name.include? "/"
|
||||
require name
|
||||
|
||||
# require allows filenames to drop the .rb extension, but everything else
|
||||
# in our codebase will require an exact and fullpath.
|
||||
name = "#{name}.rb" unless name =~ /\.rb$/
|
||||
|
||||
path = Pathname.new(name)
|
||||
name = path.stem
|
||||
install_type = :from_path
|
||||
target_file = path.to_s
|
||||
else
|
||||
# For names, map to the path and then require
|
||||
require Formula.path(name)
|
||||
install_type = :from_name
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
klass_name = self.class_s(name)
|
||||
klass = Object.const_get klass_name
|
||||
rescue NameError
|
||||
# TODO really this text should be encoded into the exception
|
||||
# and only shown if the UI deems it correct to show it
|
||||
onoe "class \"#{klass_name}\" expected but not found in #{name}.rb"
|
||||
puts "Double-check the name of the class in that formula."
|
||||
raise LoadError
|
||||
end
|
||||
|
||||
return klass.new(name) if install_type == :from_name
|
||||
return klass.new(name, target_file)
|
||||
rescue LoadError
|
||||
raise FormulaUnavailableError.new(name)
|
||||
end
|
||||
|
||||
def tap
|
||||
if path.realpath.to_s =~ %r{#{HOMEBREW_REPOSITORY}/Library/Taps/(\w+)-(\w+)}
|
||||
"#$1/#$2"
|
||||
else
|
||||
# remotely installed formula are not mxcl/master but this will do for now
|
||||
"mxcl/master"
|
||||
end
|
||||
end
|
||||
|
||||
def self.path name
|
||||
HOMEBREW_REPOSITORY+"Library/Formula/#{name.downcase}.rb"
|
||||
end
|
||||
|
||||
def mirrors; self.class.mirrors or []; end
|
||||
|
||||
def deps; self.class.dependencies.deps; end
|
||||
def external_deps; self.class.dependencies.external_deps; end
|
||||
|
||||
# deps are in an installable order
|
||||
# which means if a depends on b then b will be ordered before a in this list
|
||||
def recursive_deps
|
||||
Formula.expand_deps(self).flatten.uniq
|
||||
end
|
||||
|
||||
def self.expand_deps f
|
||||
f.deps.map do |dep|
|
||||
f_dep = Formula.factory dep.to_s
|
||||
expand_deps(f_dep) << f_dep
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Pretty titles the command and buffers stdout/stderr
|
||||
# Throws if there's an error
|
||||
def system cmd, *args
|
||||
# remove "boring" arguments so that the important ones are more likely to
|
||||
# be shown considering that we trim long ohai lines to the terminal width
|
||||
pretty_args = args.dup
|
||||
pretty_args.delete "--disable-dependency-tracking" if cmd == "./configure" and not ARGV.verbose?
|
||||
ohai "#{cmd} #{pretty_args*' '}".strip
|
||||
|
||||
removed_ENV_variables = case if args.empty? then cmd.split(' ').first else cmd end
|
||||
when "xcodebuild"
|
||||
ENV.remove_cc_etc
|
||||
end
|
||||
|
||||
if ARGV.verbose?
|
||||
safe_system cmd, *args
|
||||
else
|
||||
rd, wr = IO.pipe
|
||||
pid = fork do
|
||||
rd.close
|
||||
$stdout.reopen wr
|
||||
$stderr.reopen wr
|
||||
args.collect!{|arg| arg.to_s}
|
||||
exec(cmd, *args) rescue nil
|
||||
exit! 1 # never gets here unless exec threw or failed
|
||||
end
|
||||
wr.close
|
||||
out = ''
|
||||
out << rd.read until rd.eof?
|
||||
Process.wait
|
||||
unless $?.success?
|
||||
puts out
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
removed_ENV_variables.each do |key, value|
|
||||
ENV[key] = value # ENV.kind_of? Hash # => false
|
||||
end if removed_ENV_variables
|
||||
|
||||
rescue
|
||||
raise BuildError.new(self, cmd, args, $?)
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
# For brew-fetch and others.
|
||||
def fetch
|
||||
if install_bottle? self
|
||||
downloader = CurlBottleDownloadStrategy.new bottle_url, name, version, nil
|
||||
mirror_list = []
|
||||
else
|
||||
downloader = @downloader
|
||||
# Don't attempt mirrors if this install is not pointed at a "stable" URL.
|
||||
# This can happen when options like `--HEAD` are invoked.
|
||||
mirror_list = @spec_to_use == @standard ? mirrors : []
|
||||
end
|
||||
|
||||
# Ensure the cache exists
|
||||
HOMEBREW_CACHE.mkpath
|
||||
|
||||
begin
|
||||
fetched = downloader.fetch
|
||||
rescue CurlDownloadStrategyError => e
|
||||
raise e if mirror_list.empty?
|
||||
puts "Trying a mirror..."
|
||||
url, specs = mirror_list.shift.values_at :url, :specs
|
||||
downloader = download_strategy.new url, name, version, specs
|
||||
retry
|
||||
end
|
||||
|
||||
return fetched, downloader
|
||||
end
|
||||
|
||||
# Detect which type of checksum is being used, or nil if none
|
||||
def checksum_type
|
||||
CHECKSUM_TYPES.detect { |type| instance_variable_defined?("@#{type}") }
|
||||
end
|
||||
|
||||
# For FormulaInstaller.
|
||||
def verify_download_integrity fn, *args
|
||||
require 'digest'
|
||||
if args.length != 2
|
||||
type = checksum_type || :md5
|
||||
supplied = instance_variable_get("@#{type}")
|
||||
# Convert symbol to readable string
|
||||
type = type.to_s.upcase
|
||||
else
|
||||
supplied, type = args
|
||||
end
|
||||
|
||||
hasher = Digest.const_get(type)
|
||||
hash = fn.incremental_hash(hasher)
|
||||
|
||||
if supplied and not supplied.empty?
|
||||
message = <<-EOF
|
||||
#{type} mismatch
|
||||
Expected: #{supplied}
|
||||
Got: #{hash}
|
||||
Archive: #{fn}
|
||||
(To retry an incomplete download, remove the file above.)
|
||||
EOF
|
||||
raise message unless supplied.upcase == hash.upcase
|
||||
else
|
||||
opoo "Cannot verify package integrity"
|
||||
puts "The formula did not provide a download checksum"
|
||||
puts "For your reference the #{type} is: #{hash}"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CHECKSUM_TYPES=[:md5, :sha1, :sha256].freeze
|
||||
|
||||
def stage
|
||||
fetched, downloader = fetch
|
||||
verify_download_integrity fetched if fetched.kind_of? Pathname
|
||||
mktemp do
|
||||
downloader.stage
|
||||
# Set path after the downloader changes the working folder.
|
||||
@buildpath = Pathname.pwd
|
||||
yield
|
||||
@buildpath = nil
|
||||
end
|
||||
end
|
||||
|
||||
def patch
|
||||
patch_list = Patches.new(patches)
|
||||
return if patch_list.empty?
|
||||
|
||||
if patch_list.external_patches?
|
||||
ohai "Downloading patches"
|
||||
patch_list.download!
|
||||
end
|
||||
|
||||
ohai "Patching"
|
||||
patch_list.each do |p|
|
||||
case p.compression
|
||||
when :gzip then safe_system "/usr/bin/gunzip", p.compressed_filename
|
||||
when :bzip2 then safe_system "/usr/bin/bunzip2", p.compressed_filename
|
||||
end
|
||||
# -f means don't prompt the user if there are errors; just exit with non-zero status
|
||||
safe_system '/usr/bin/patch', '-f', *(p.patch_args)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_variable name
|
||||
v = instance_variable_get("@#{name}")
|
||||
raise "Invalid @#{name}" if v.to_s.empty? or v =~ /\s/
|
||||
end
|
||||
|
||||
def set_instance_variable(type)
|
||||
return if instance_variable_defined? "@#{type}"
|
||||
class_value = self.class.send(type)
|
||||
instance_variable_set("@#{type}", class_value) if class_value
|
||||
end
|
||||
|
||||
def self.method_added method
|
||||
raise 'You cannot override Formula.brew' if method == :brew
|
||||
end
|
||||
|
||||
class << self
|
||||
# The methods below define the formula DSL.
|
||||
attr_reader :standard, :unstable
|
||||
|
||||
def self.attr_rw(*attrs)
|
||||
attrs.each do |attr|
|
||||
class_eval %Q{
|
||||
def #{attr}(val=nil)
|
||||
val.nil? ? @#{attr} : @#{attr} = val
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
attr_rw :version, :homepage, :mirrors, :specs
|
||||
attr_rw :keg_only_reason, :skip_clean_all, :cc_failures
|
||||
attr_rw :bottle_version, :bottle_url, :bottle_sha1
|
||||
attr_rw(*CHECKSUM_TYPES)
|
||||
|
||||
def head val=nil, specs=nil
|
||||
return @head if val.nil?
|
||||
@unstable = SoftwareSpecification.new(val, specs)
|
||||
@head = val
|
||||
@specs = specs
|
||||
end
|
||||
|
||||
def url val=nil, specs=nil
|
||||
return @url if val.nil?
|
||||
@standard = SoftwareSpecification.new(val, specs)
|
||||
@url = val
|
||||
@specs = specs
|
||||
end
|
||||
|
||||
def stable &block
|
||||
raise "url and md5 must be specified in a block" unless block_given?
|
||||
instance_eval(&block) unless ARGV.build_devel? or ARGV.build_head?
|
||||
end
|
||||
|
||||
def devel &block
|
||||
raise "url and md5 must be specified in a block" unless block_given?
|
||||
if ARGV.build_devel?
|
||||
@mirrors = nil # clear out mirrors from the stable release
|
||||
instance_eval(&block)
|
||||
end
|
||||
end
|
||||
|
||||
def bottle url=nil, &block
|
||||
return unless block_given?
|
||||
|
||||
bottle_block = Class.new do
|
||||
def self.version version
|
||||
@version = version
|
||||
end
|
||||
|
||||
def self.url url
|
||||
@url = url
|
||||
end
|
||||
|
||||
def self.sha1 sha1
|
||||
case sha1
|
||||
when Hash
|
||||
key, value = sha1.shift
|
||||
@sha1 = key if value == MacOS.cat
|
||||
when String
|
||||
@sha1 = sha1 if MacOS.lion?
|
||||
end
|
||||
end
|
||||
|
||||
def self.data
|
||||
@version = 0 unless @version
|
||||
return @version, @url, @sha1 if @sha1 && @url
|
||||
return @version, nil, @sha1 if @sha1
|
||||
end
|
||||
end
|
||||
|
||||
bottle_block.instance_eval(&block)
|
||||
@bottle_version, @bottle_url, @bottle_sha1 = bottle_block.data
|
||||
end
|
||||
|
||||
def mirror val, specs=nil
|
||||
@mirrors ||= []
|
||||
@mirrors << {:url => val, :specs => specs}
|
||||
# Added the uniq after some inspection with Pry---seems `mirror` gets
|
||||
# called three times. The first two times only one copy of the input is
|
||||
# left in `@mirrors`. On the final call, two copies are present. This
|
||||
# happens with `@deps` as well. Odd.
|
||||
@mirrors.uniq!
|
||||
end
|
||||
|
||||
def dependencies
|
||||
@dependencies ||= DependencyCollector.new
|
||||
end
|
||||
|
||||
def depends_on dep
|
||||
dependencies.add(dep)
|
||||
end
|
||||
|
||||
def skip_clean paths
|
||||
if paths == :all
|
||||
@skip_clean_all = true
|
||||
return
|
||||
end
|
||||
@skip_clean_paths ||= []
|
||||
[paths].flatten.each do |p|
|
||||
@skip_clean_paths << p.to_s unless @skip_clean_paths.include? p.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def skip_clean_all?
|
||||
@skip_clean_all
|
||||
end
|
||||
|
||||
def skip_clean_paths
|
||||
@skip_clean_paths or []
|
||||
end
|
||||
|
||||
def keg_only reason, explanation=nil
|
||||
@keg_only_reason = KegOnlyReason.new(reason, explanation.to_s.chomp)
|
||||
end
|
||||
|
||||
def fails_with compiler, &block
|
||||
@cc_failures ||= CompilerFailures.new
|
||||
@cc_failures << if block_given?
|
||||
CompilerFailure.new(compiler, &block)
|
||||
else
|
||||
CompilerFailure.new(compiler)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'formula_specialties'
|
||||
323
test/fixtures/ruby/inflector.rb
vendored
Normal file
323
test/fixtures/ruby/inflector.rb
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
# encoding: utf-8
|
||||
|
||||
require 'active_support/inflector/inflections'
|
||||
|
||||
module ActiveSupport
|
||||
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
|
||||
# and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
|
||||
# in inflections.rb.
|
||||
#
|
||||
# The Rails core team has stated patches for the inflections library will not be accepted
|
||||
# in order to avoid breaking legacy applications which may be relying on errant inflections.
|
||||
# If you discover an incorrect inflection and require it for your application, you'll need
|
||||
# to correct it yourself (explained below).
|
||||
module Inflector
|
||||
extend self
|
||||
|
||||
# Returns the plural form of the word in the string.
|
||||
#
|
||||
# "post".pluralize # => "posts"
|
||||
# "octopus".pluralize # => "octopi"
|
||||
# "sheep".pluralize # => "sheep"
|
||||
# "words".pluralize # => "words"
|
||||
# "CamelOctopus".pluralize # => "CamelOctopi"
|
||||
def pluralize(word)
|
||||
apply_inflections(word, inflections.plurals)
|
||||
end
|
||||
|
||||
# The reverse of +pluralize+, returns the singular form of a word in a string.
|
||||
#
|
||||
# "posts".singularize # => "post"
|
||||
# "octopi".singularize # => "octopus"
|
||||
# "sheep".singularize # => "sheep"
|
||||
# "word".singularize # => "word"
|
||||
# "CamelOctopi".singularize # => "CamelOctopus"
|
||||
def singularize(word)
|
||||
apply_inflections(word, inflections.singulars)
|
||||
end
|
||||
|
||||
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
|
||||
# is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
|
||||
#
|
||||
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
|
||||
#
|
||||
# "active_model".camelize # => "ActiveModel"
|
||||
# "active_model".camelize(:lower) # => "activeModel"
|
||||
# "active_model/errors".camelize # => "ActiveModel::Errors"
|
||||
# "active_model/errors".camelize(:lower) # => "activeModel::Errors"
|
||||
#
|
||||
# As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
|
||||
# though there are cases where that does not hold:
|
||||
#
|
||||
# "SSLError".underscore.camelize # => "SslError"
|
||||
def camelize(term, uppercase_first_letter = true)
|
||||
string = term.to_s
|
||||
if uppercase_first_letter
|
||||
string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
|
||||
else
|
||||
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
|
||||
end
|
||||
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
|
||||
end
|
||||
|
||||
# Makes an underscored, lowercase form from the expression in the string.
|
||||
#
|
||||
# Changes '::' to '/' to convert namespaces to paths.
|
||||
#
|
||||
# "ActiveModel".underscore # => "active_model"
|
||||
# "ActiveModel::Errors".underscore # => "active_model/errors"
|
||||
#
|
||||
# As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
|
||||
# though there are cases where that does not hold:
|
||||
#
|
||||
# "SSLError".underscore.camelize # => "SslError"
|
||||
def underscore(camel_cased_word)
|
||||
word = camel_cased_word.to_s.dup
|
||||
word.gsub!('::', '/')
|
||||
word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
|
||||
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
||||
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
||||
word.tr!("-", "_")
|
||||
word.downcase!
|
||||
word
|
||||
end
|
||||
|
||||
# Capitalizes the first word and turns underscores into spaces and strips a
|
||||
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
|
||||
#
|
||||
# "employee_salary" # => "Employee salary"
|
||||
# "author_id" # => "Author"
|
||||
def humanize(lower_case_and_underscored_word)
|
||||
result = lower_case_and_underscored_word.to_s.dup
|
||||
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
|
||||
result.gsub!(/_id$/, "")
|
||||
result.tr!('_', ' ')
|
||||
result.gsub(/([a-z\d]*)/i) { |match|
|
||||
"#{inflections.acronyms[match] || match.downcase}"
|
||||
}.gsub(/^\w/) { $&.upcase }
|
||||
end
|
||||
|
||||
# Capitalizes all the words and replaces some characters in the string to create
|
||||
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
||||
# used in the Rails internals.
|
||||
#
|
||||
# +titleize+ is also aliased as +titlecase+.
|
||||
#
|
||||
# "man from the boondocks".titleize # => "Man From The Boondocks"
|
||||
# "x-men: the last stand".titleize # => "X Men: The Last Stand"
|
||||
# "TheManWithoutAPast".titleize # => "The Man Without A Past"
|
||||
# "raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark"
|
||||
def titleize(word)
|
||||
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
|
||||
end
|
||||
|
||||
# Create the name of a table like Rails does for models to table names. This method
|
||||
# uses the +pluralize+ method on the last word in the string.
|
||||
#
|
||||
# "RawScaledScorer".tableize # => "raw_scaled_scorers"
|
||||
# "egg_and_ham".tableize # => "egg_and_hams"
|
||||
# "fancyCategory".tableize # => "fancy_categories"
|
||||
def tableize(class_name)
|
||||
pluralize(underscore(class_name))
|
||||
end
|
||||
|
||||
# Create a class name from a plural table name like Rails does for table names to models.
|
||||
# Note that this returns a string and not a Class. (To convert to an actual class
|
||||
# follow +classify+ with +constantize+.)
|
||||
#
|
||||
# "egg_and_hams".classify # => "EggAndHam"
|
||||
# "posts".classify # => "Post"
|
||||
#
|
||||
# Singular names are not handled correctly:
|
||||
# "business".classify # => "Busines"
|
||||
def classify(table_name)
|
||||
# strip out any leading schema name
|
||||
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
|
||||
end
|
||||
|
||||
# Replaces underscores with dashes in the string.
|
||||
#
|
||||
# "puni_puni".dasherize # => "puni-puni"
|
||||
def dasherize(underscored_word)
|
||||
underscored_word.tr('_', '-')
|
||||
end
|
||||
|
||||
# Removes the module part from the expression in the string:
|
||||
#
|
||||
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
|
||||
# "Inflections".demodulize # => "Inflections"
|
||||
#
|
||||
# See also +deconstantize+.
|
||||
def demodulize(path)
|
||||
path = path.to_s
|
||||
if i = path.rindex('::')
|
||||
path[(i+2)..-1]
|
||||
else
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
# Removes the rightmost segment from the constant expression in the string:
|
||||
#
|
||||
# "Net::HTTP".deconstantize # => "Net"
|
||||
# "::Net::HTTP".deconstantize # => "::Net"
|
||||
# "String".deconstantize # => ""
|
||||
# "::String".deconstantize # => ""
|
||||
# "".deconstantize # => ""
|
||||
#
|
||||
# See also +demodulize+.
|
||||
def deconstantize(path)
|
||||
path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
|
||||
end
|
||||
|
||||
# Creates a foreign key name from a class name.
|
||||
# +separate_class_name_and_id_with_underscore+ sets whether
|
||||
# the method should put '_' between the name and 'id'.
|
||||
#
|
||||
# "Message".foreign_key # => "message_id"
|
||||
# "Message".foreign_key(false) # => "messageid"
|
||||
# "Admin::Post".foreign_key # => "post_id"
|
||||
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
|
||||
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
|
||||
end
|
||||
|
||||
# Tries to find a constant with the name specified in the argument string:
|
||||
#
|
||||
# "Module".constantize # => Module
|
||||
# "Test::Unit".constantize # => Test::Unit
|
||||
#
|
||||
# The name is assumed to be the one of a top-level constant, no matter whether
|
||||
# it starts with "::" or not. No lexical context is taken into account:
|
||||
#
|
||||
# C = 'outside'
|
||||
# module M
|
||||
# C = 'inside'
|
||||
# C # => 'inside'
|
||||
# "C".constantize # => 'outside', same as ::C
|
||||
# end
|
||||
#
|
||||
# NameError is raised when the name is not in CamelCase or the constant is
|
||||
# unknown.
|
||||
def constantize(camel_cased_word)
|
||||
names = camel_cased_word.split('::')
|
||||
names.shift if names.empty? || names.first.empty?
|
||||
|
||||
names.inject(Object) do |constant, name|
|
||||
if constant == Object
|
||||
constant.const_get(name)
|
||||
else
|
||||
candidate = constant.const_get(name)
|
||||
next candidate if constant.const_defined?(name, false)
|
||||
next candidate unless Object.const_defined?(name)
|
||||
|
||||
# Go down the ancestors to check it it's owned
|
||||
# directly before we reach Object or the end of ancestors.
|
||||
constant = constant.ancestors.inject do |const, ancestor|
|
||||
break const if ancestor == Object
|
||||
break ancestor if ancestor.const_defined?(name, false)
|
||||
const
|
||||
end
|
||||
|
||||
# owner is in Object, so raise
|
||||
constant.const_get(name, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Tries to find a constant with the name specified in the argument string:
|
||||
#
|
||||
# "Module".safe_constantize # => Module
|
||||
# "Test::Unit".safe_constantize # => Test::Unit
|
||||
#
|
||||
# The name is assumed to be the one of a top-level constant, no matter whether
|
||||
# it starts with "::" or not. No lexical context is taken into account:
|
||||
#
|
||||
# C = 'outside'
|
||||
# module M
|
||||
# C = 'inside'
|
||||
# C # => 'inside'
|
||||
# "C".safe_constantize # => 'outside', same as ::C
|
||||
# end
|
||||
#
|
||||
# nil is returned when the name is not in CamelCase or the constant (or part of it) is
|
||||
# unknown.
|
||||
#
|
||||
# "blargle".safe_constantize # => nil
|
||||
# "UnknownModule".safe_constantize # => nil
|
||||
# "UnknownModule::Foo::Bar".safe_constantize # => nil
|
||||
#
|
||||
def safe_constantize(camel_cased_word)
|
||||
begin
|
||||
constantize(camel_cased_word)
|
||||
rescue NameError => e
|
||||
raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
|
||||
e.name.to_s == camel_cased_word.to_s
|
||||
rescue ArgumentError => e
|
||||
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the suffix that should be added to a number to denote the position
|
||||
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
||||
#
|
||||
# ordinal(1) # => "st"
|
||||
# ordinal(2) # => "nd"
|
||||
# ordinal(1002) # => "nd"
|
||||
# ordinal(1003) # => "rd"
|
||||
# ordinal(-11) # => "th"
|
||||
# ordinal(-1021) # => "st"
|
||||
def ordinal(number)
|
||||
if (11..13).include?(number.to_i.abs % 100)
|
||||
"th"
|
||||
else
|
||||
case number.to_i.abs % 10
|
||||
when 1; "st"
|
||||
when 2; "nd"
|
||||
when 3; "rd"
|
||||
else "th"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Turns a number into an ordinal string used to denote the position in an
|
||||
# ordered sequence such as 1st, 2nd, 3rd, 4th.
|
||||
#
|
||||
# ordinalize(1) # => "1st"
|
||||
# ordinalize(2) # => "2nd"
|
||||
# ordinalize(1002) # => "1002nd"
|
||||
# ordinalize(1003) # => "1003rd"
|
||||
# ordinalize(-11) # => "-11th"
|
||||
# ordinalize(-1021) # => "-1021st"
|
||||
def ordinalize(number)
|
||||
"#{number}#{ordinal(number)}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Mount a regular expression that will match part by part of the constant.
|
||||
# For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
|
||||
def const_regexp(camel_cased_word) #:nodoc:
|
||||
parts = camel_cased_word.split("::")
|
||||
last = parts.pop
|
||||
|
||||
parts.reverse.inject(last) do |acc, part|
|
||||
part.empty? ? acc : "#{part}(::#{acc})?"
|
||||
end
|
||||
end
|
||||
|
||||
# Applies inflection rules for +singularize+ and +pluralize+.
|
||||
#
|
||||
# apply_inflections("post", inflections.plurals) # => "posts"
|
||||
# apply_inflections("posts", inflections.singulars) # => "post"
|
||||
def apply_inflections(word, rules)
|
||||
result = word.to_s.dup
|
||||
|
||||
if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
|
||||
result
|
||||
else
|
||||
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
138
test/fixtures/ruby/jekyll.rb
vendored
Normal file
138
test/fixtures/ruby/jekyll.rb
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
|
||||
|
||||
# Require all of the Ruby files in the given directory.
|
||||
#
|
||||
# path - The String relative path from here to the directory.
|
||||
#
|
||||
# Returns nothing.
|
||||
def require_all(path)
|
||||
glob = File.join(File.dirname(__FILE__), path, '*.rb')
|
||||
Dir[glob].each do |f|
|
||||
require f
|
||||
end
|
||||
end
|
||||
|
||||
# rubygems
|
||||
require 'rubygems'
|
||||
|
||||
# stdlib
|
||||
require 'fileutils'
|
||||
require 'time'
|
||||
require 'yaml'
|
||||
require 'English'
|
||||
|
||||
# 3rd party
|
||||
require 'liquid'
|
||||
require 'maruku'
|
||||
require 'albino'
|
||||
|
||||
# internal requires
|
||||
require 'jekyll/core_ext'
|
||||
require 'jekyll/site'
|
||||
require 'jekyll/convertible'
|
||||
require 'jekyll/layout'
|
||||
require 'jekyll/page'
|
||||
require 'jekyll/post'
|
||||
require 'jekyll/filters'
|
||||
require 'jekyll/static_file'
|
||||
require 'jekyll/errors'
|
||||
|
||||
# extensions
|
||||
require 'jekyll/plugin'
|
||||
require 'jekyll/converter'
|
||||
require 'jekyll/generator'
|
||||
require_all 'jekyll/converters'
|
||||
require_all 'jekyll/generators'
|
||||
require_all 'jekyll/tags'
|
||||
|
||||
module Jekyll
|
||||
VERSION = '0.11.2'
|
||||
|
||||
# Default options. Overriden by values in _config.yml or command-line opts.
|
||||
# (Strings rather symbols used for compatability with YAML).
|
||||
DEFAULTS = {
|
||||
'safe' => false,
|
||||
'auto' => false,
|
||||
'server' => false,
|
||||
'server_port' => 4000,
|
||||
|
||||
'source' => Dir.pwd,
|
||||
'destination' => File.join(Dir.pwd, '_site'),
|
||||
'plugins' => File.join(Dir.pwd, '_plugins'),
|
||||
|
||||
'future' => true,
|
||||
'lsi' => false,
|
||||
'pygments' => false,
|
||||
'markdown' => 'maruku',
|
||||
'permalink' => 'date',
|
||||
'include' => ['.htaccess'],
|
||||
'paginate_path' => 'page:num',
|
||||
|
||||
'markdown_ext' => 'markdown,mkd,mkdn,md',
|
||||
'textile_ext' => 'textile',
|
||||
|
||||
'maruku' => {
|
||||
'use_tex' => false,
|
||||
'use_divs' => false,
|
||||
'png_engine' => 'blahtex',
|
||||
'png_dir' => 'images/latex',
|
||||
'png_url' => '/images/latex'
|
||||
},
|
||||
'rdiscount' => {
|
||||
'extensions' => []
|
||||
},
|
||||
'redcarpet' => {
|
||||
'extensions' => []
|
||||
},
|
||||
'kramdown' => {
|
||||
'auto_ids' => true,
|
||||
'footnote_nr' => 1,
|
||||
'entity_output' => 'as_char',
|
||||
'toc_levels' => '1..6',
|
||||
'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
|
||||
'use_coderay' => false,
|
||||
|
||||
'coderay' => {
|
||||
'coderay_wrap' => 'div',
|
||||
'coderay_line_numbers' => 'inline',
|
||||
'coderay_line_number_start' => 1,
|
||||
'coderay_tab_width' => 4,
|
||||
'coderay_bold_every' => 10,
|
||||
'coderay_css' => 'style'
|
||||
}
|
||||
},
|
||||
'redcloth' => {
|
||||
'hard_breaks' => true
|
||||
}
|
||||
}
|
||||
|
||||
# Public: Generate a Jekyll configuration Hash by merging the default
|
||||
# options with anything in _config.yml, and adding the given options on top.
|
||||
#
|
||||
# override - A Hash of config directives that override any options in both
|
||||
# the defaults and the config file. See Jekyll::DEFAULTS for a
|
||||
# list of option names and their defaults.
|
||||
#
|
||||
# Returns the final configuration Hash.
|
||||
def self.configuration(override)
|
||||
# _config.yml may override default source location, but until
|
||||
# then, we need to know where to look for _config.yml
|
||||
source = override['source'] || Jekyll::DEFAULTS['source']
|
||||
|
||||
# Get configuration from <source>/_config.yml
|
||||
config_file = File.join(source, '_config.yml')
|
||||
begin
|
||||
config = YAML.load_file(config_file)
|
||||
raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash)
|
||||
$stdout.puts "Configuration from #{config_file}"
|
||||
rescue => err
|
||||
$stderr.puts "WARNING: Could not read configuration. " +
|
||||
"Using defaults (and options)."
|
||||
$stderr.puts "\t" + err.to_s
|
||||
config = {}
|
||||
end
|
||||
|
||||
# Merge DEFAULTS < _config.yml < override
|
||||
Jekyll::DEFAULTS.deep_merge(config).deep_merge(override)
|
||||
end
|
||||
end
|
||||
385
test/fixtures/ruby/resque.rb
vendored
Normal file
385
test/fixtures/ruby/resque.rb
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
require 'redis/namespace'
|
||||
|
||||
require 'resque/version'
|
||||
|
||||
require 'resque/errors'
|
||||
|
||||
require 'resque/failure'
|
||||
require 'resque/failure/base'
|
||||
|
||||
require 'resque/helpers'
|
||||
require 'resque/stat'
|
||||
require 'resque/job'
|
||||
require 'resque/worker'
|
||||
require 'resque/plugin'
|
||||
require 'resque/queue'
|
||||
require 'resque/multi_queue'
|
||||
require 'resque/coder'
|
||||
require 'resque/multi_json_coder'
|
||||
|
||||
module Resque
|
||||
include Helpers
|
||||
extend self
|
||||
|
||||
# Accepts:
|
||||
# 1. A 'hostname:port' String
|
||||
# 2. A 'hostname:port:db' String (to select the Redis db)
|
||||
# 3. A 'hostname:port/namespace' String (to set the Redis namespace)
|
||||
# 4. A Redis URL String 'redis://host:port'
|
||||
# 5. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
|
||||
# or `Redis::Namespace`.
|
||||
def redis=(server)
|
||||
case server
|
||||
when String
|
||||
if server =~ /redis\:\/\//
|
||||
redis = Redis.connect(:url => server, :thread_safe => true)
|
||||
else
|
||||
server, namespace = server.split('/', 2)
|
||||
host, port, db = server.split(':')
|
||||
redis = Redis.new(:host => host, :port => port,
|
||||
:thread_safe => true, :db => db)
|
||||
end
|
||||
namespace ||= :resque
|
||||
|
||||
@redis = Redis::Namespace.new(namespace, :redis => redis)
|
||||
when Redis::Namespace
|
||||
@redis = server
|
||||
else
|
||||
@redis = Redis::Namespace.new(:resque, :redis => server)
|
||||
end
|
||||
@queues = Hash.new { |h,name|
|
||||
h[name] = Resque::Queue.new(name, @redis, coder)
|
||||
}
|
||||
end
|
||||
|
||||
# Encapsulation of encode/decode. Overwrite this to use it across Resque.
|
||||
# This defaults to MultiJson for backwards compatibilty.
|
||||
def coder
|
||||
@coder ||= MultiJsonCoder.new
|
||||
end
|
||||
attr_writer :coder
|
||||
|
||||
# Returns the current Redis connection. If none has been created, will
|
||||
# create a new one.
|
||||
def redis
|
||||
return @redis if @redis
|
||||
self.redis = Redis.respond_to?(:connect) ? Redis.connect : "localhost:6379"
|
||||
self.redis
|
||||
end
|
||||
|
||||
def redis_id
|
||||
# support 1.x versions of redis-rb
|
||||
if redis.respond_to?(:server)
|
||||
redis.server
|
||||
elsif redis.respond_to?(:nodes) # distributed
|
||||
redis.nodes.map { |n| n.id }.join(', ')
|
||||
else
|
||||
redis.client.id
|
||||
end
|
||||
end
|
||||
|
||||
# The `before_first_fork` hook will be run in the **parent** process
|
||||
# only once, before forking to run the first job. Be careful- any
|
||||
# changes you make will be permanent for the lifespan of the
|
||||
# worker.
|
||||
#
|
||||
# Call with a block to set the hook.
|
||||
# Call with no arguments to return the hook.
|
||||
def before_first_fork(&block)
|
||||
block ? (@before_first_fork = block) : @before_first_fork
|
||||
end
|
||||
|
||||
# Set a proc that will be called in the parent process before the
|
||||
# worker forks for the first time.
|
||||
attr_writer :before_first_fork
|
||||
|
||||
# The `before_fork` hook will be run in the **parent** process
|
||||
# before every job, so be careful- any changes you make will be
|
||||
# permanent for the lifespan of the worker.
|
||||
#
|
||||
# Call with a block to set the hook.
|
||||
# Call with no arguments to return the hook.
|
||||
def before_fork(&block)
|
||||
block ? (@before_fork = block) : @before_fork
|
||||
end
|
||||
|
||||
# Set the before_fork proc.
|
||||
attr_writer :before_fork
|
||||
|
||||
# The `after_fork` hook will be run in the child process and is passed
|
||||
# the current job. Any changes you make, therefore, will only live as
|
||||
# long as the job currently being processed.
|
||||
#
|
||||
# Call with a block to set the hook.
|
||||
# Call with no arguments to return the hook.
|
||||
def after_fork(&block)
|
||||
block ? (@after_fork = block) : @after_fork
|
||||
end
|
||||
|
||||
# Set the after_fork proc.
|
||||
attr_writer :after_fork
|
||||
|
||||
def to_s
|
||||
"Resque Client connected to #{redis_id}"
|
||||
end
|
||||
|
||||
attr_accessor :inline
|
||||
|
||||
# If 'inline' is true Resque will call #perform method inline
|
||||
# without queuing it into Redis and without any Resque callbacks.
|
||||
# The 'inline' is false Resque jobs will be put in queue regularly.
|
||||
alias :inline? :inline
|
||||
|
||||
#
|
||||
# queue manipulation
|
||||
#
|
||||
|
||||
# Pushes a job onto a queue. Queue name should be a string and the
|
||||
# item should be any JSON-able Ruby object.
|
||||
#
|
||||
# Resque works generally expect the `item` to be a hash with the following
|
||||
# keys:
|
||||
#
|
||||
# class - The String name of the job to run.
|
||||
# args - An Array of arguments to pass the job. Usually passed
|
||||
# via `class.to_class.perform(*args)`.
|
||||
#
|
||||
# Example
|
||||
#
|
||||
# Resque.push('archive', :class => 'Archive', :args => [ 35, 'tar' ])
|
||||
#
|
||||
# Returns nothing
|
||||
def push(queue, item)
|
||||
queue(queue) << item
|
||||
end
|
||||
|
||||
# Pops a job off a queue. Queue name should be a string.
|
||||
#
|
||||
# Returns a Ruby object.
|
||||
def pop(queue)
|
||||
begin
|
||||
queue(queue).pop(true)
|
||||
rescue ThreadError
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an integer representing the size of a queue.
|
||||
# Queue name should be a string.
|
||||
def size(queue)
|
||||
queue(queue).size
|
||||
end
|
||||
|
||||
# Returns an array of items currently queued. Queue name should be
|
||||
# a string.
|
||||
#
|
||||
# start and count should be integer and can be used for pagination.
|
||||
# start is the item to begin, count is how many items to return.
|
||||
#
|
||||
# To get the 3rd page of a 30 item, paginatied list one would use:
|
||||
# Resque.peek('my_list', 59, 30)
|
||||
def peek(queue, start = 0, count = 1)
|
||||
queue(queue).slice start, count
|
||||
end
|
||||
|
||||
# Does the dirty work of fetching a range of items from a Redis list
|
||||
# and converting them into Ruby objects.
|
||||
def list_range(key, start = 0, count = 1)
|
||||
if count == 1
|
||||
decode redis.lindex(key, start)
|
||||
else
|
||||
Array(redis.lrange(key, start, start+count-1)).map do |item|
|
||||
decode item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an array of all known Resque queues as strings.
|
||||
def queues
|
||||
Array(redis.smembers(:queues))
|
||||
end
|
||||
|
||||
# Given a queue name, completely deletes the queue.
|
||||
def remove_queue(queue)
|
||||
queue(queue).destroy
|
||||
@queues.delete(queue.to_s)
|
||||
end
|
||||
|
||||
# Return the Resque::Queue object for a given name
|
||||
def queue(name)
|
||||
@queues[name.to_s]
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# job shortcuts
|
||||
#
|
||||
|
||||
# This method can be used to conveniently add a job to a queue.
|
||||
# It assumes the class you're passing it is a real Ruby class (not
|
||||
# a string or reference) which either:
|
||||
#
|
||||
# a) has a @queue ivar set
|
||||
# b) responds to `queue`
|
||||
#
|
||||
# If either of those conditions are met, it will use the value obtained
|
||||
# from performing one of the above operations to determine the queue.
|
||||
#
|
||||
# If no queue can be inferred this method will raise a `Resque::NoQueueError`
|
||||
#
|
||||
# Returns true if the job was queued, nil if the job was rejected by a
|
||||
# before_enqueue hook.
|
||||
#
|
||||
# This method is considered part of the `stable` API.
|
||||
def enqueue(klass, *args)
|
||||
enqueue_to(queue_from_class(klass), klass, *args)
|
||||
end
|
||||
|
||||
# Just like `enqueue` but allows you to specify the queue you want to
|
||||
# use. Runs hooks.
|
||||
#
|
||||
# `queue` should be the String name of the queue you're targeting.
|
||||
#
|
||||
# Returns true if the job was queued, nil if the job was rejected by a
|
||||
# before_enqueue hook.
|
||||
#
|
||||
# This method is considered part of the `stable` API.
|
||||
def enqueue_to(queue, klass, *args)
|
||||
# Perform before_enqueue hooks. Don't perform enqueue if any hook returns false
|
||||
before_hooks = Plugin.before_enqueue_hooks(klass).collect do |hook|
|
||||
klass.send(hook, *args)
|
||||
end
|
||||
return nil if before_hooks.any? { |result| result == false }
|
||||
|
||||
Job.create(queue, klass, *args)
|
||||
|
||||
Plugin.after_enqueue_hooks(klass).each do |hook|
|
||||
klass.send(hook, *args)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# This method can be used to conveniently remove a job from a queue.
|
||||
# It assumes the class you're passing it is a real Ruby class (not
|
||||
# a string or reference) which either:
|
||||
#
|
||||
# a) has a @queue ivar set
|
||||
# b) responds to `queue`
|
||||
#
|
||||
# If either of those conditions are met, it will use the value obtained
|
||||
# from performing one of the above operations to determine the queue.
|
||||
#
|
||||
# If no queue can be inferred this method will raise a `Resque::NoQueueError`
|
||||
#
|
||||
# If no args are given, this method will dequeue *all* jobs matching
|
||||
# the provided class. See `Resque::Job.destroy` for more
|
||||
# information.
|
||||
#
|
||||
# Returns the number of jobs destroyed.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# # Removes all jobs of class `UpdateNetworkGraph`
|
||||
# Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph)
|
||||
#
|
||||
# # Removes all jobs of class `UpdateNetworkGraph` with matching args.
|
||||
# Resque.dequeue(GitHub::Jobs::UpdateNetworkGraph, 'repo:135325')
|
||||
#
|
||||
# This method is considered part of the `stable` API.
|
||||
def dequeue(klass, *args)
|
||||
# Perform before_dequeue hooks. Don't perform dequeue if any hook returns false
|
||||
before_hooks = Plugin.before_dequeue_hooks(klass).collect do |hook|
|
||||
klass.send(hook, *args)
|
||||
end
|
||||
return if before_hooks.any? { |result| result == false }
|
||||
|
||||
Job.destroy(queue_from_class(klass), klass, *args)
|
||||
|
||||
Plugin.after_dequeue_hooks(klass).each do |hook|
|
||||
klass.send(hook, *args)
|
||||
end
|
||||
end
|
||||
|
||||
# Given a class, try to extrapolate an appropriate queue based on a
|
||||
# class instance variable or `queue` method.
|
||||
def queue_from_class(klass)
|
||||
klass.instance_variable_get(:@queue) ||
|
||||
(klass.respond_to?(:queue) and klass.queue)
|
||||
end
|
||||
|
||||
# This method will return a `Resque::Job` object or a non-true value
|
||||
# depending on whether a job can be obtained. You should pass it the
|
||||
# precise name of a queue: case matters.
|
||||
#
|
||||
# This method is considered part of the `stable` API.
|
||||
def reserve(queue)
|
||||
Job.reserve(queue)
|
||||
end
|
||||
|
||||
# Validates if the given klass could be a valid Resque job
|
||||
#
|
||||
# If no queue can be inferred this method will raise a `Resque::NoQueueError`
|
||||
#
|
||||
# If given klass is nil this method will raise a `Resque::NoClassError`
|
||||
def validate(klass, queue = nil)
|
||||
queue ||= queue_from_class(klass)
|
||||
|
||||
if !queue
|
||||
raise NoQueueError.new("Jobs must be placed onto a queue.")
|
||||
end
|
||||
|
||||
if klass.to_s.empty?
|
||||
raise NoClassError.new("Jobs must be given a class.")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# worker shortcuts
|
||||
#
|
||||
|
||||
# A shortcut to Worker.all
|
||||
def workers
|
||||
Worker.all
|
||||
end
|
||||
|
||||
# A shortcut to Worker.working
|
||||
def working
|
||||
Worker.working
|
||||
end
|
||||
|
||||
# A shortcut to unregister_worker
|
||||
# useful for command line tool
|
||||
def remove_worker(worker_id)
|
||||
worker = Resque::Worker.find(worker_id)
|
||||
worker.unregister_worker
|
||||
end
|
||||
|
||||
#
|
||||
# stats
|
||||
#
|
||||
|
||||
# Returns a hash, similar to redis-rb's #info, of interesting stats.
|
||||
def info
|
||||
return {
|
||||
:pending => queues.inject(0) { |m,k| m + size(k) },
|
||||
:processed => Stat[:processed],
|
||||
:queues => queues.size,
|
||||
:workers => workers.size.to_i,
|
||||
:working => working.size,
|
||||
:failed => Stat[:failed],
|
||||
:servers => [redis_id],
|
||||
:environment => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
||||
}
|
||||
end
|
||||
|
||||
# Returns an array of all known Resque keys in Redis. Redis' KEYS operation
|
||||
# is O(N) for the keyspace, so be careful - this can be slow for big databases.
|
||||
def keys
|
||||
redis.keys("*").map do |key|
|
||||
key.sub("#{redis.namespace}:", '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
1775
test/fixtures/ruby/sinatra.rb
vendored
Normal file
1775
test/fixtures/ruby/sinatra.rb
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user