aboutsummaryrefslogtreecommitdiff
path: root/samples/Ruby
diff options
context:
space:
mode:
authorJoshua Peek <josh@joshpeek.com>2012-07-23 15:52:49 -0500
committerJoshua Peek <josh@joshpeek.com>2012-07-23 15:52:49 -0500
commit7b6caa0f6c7603aabb5e6ed657e24aaddbeafc78 (patch)
treeec0522eae75bb04ae73d28ae3f599682a2bfdaeb /samples/Ruby
parent314f0e485227915931db361be14d308a210bedea (diff)
Rename samples subdirectories
Diffstat (limited to 'samples/Ruby')
-rw-r--r--samples/Ruby/filenames/Capfile3
-rw-r--r--samples/Ruby/filenames/Rakefile3
-rw-r--r--samples/Ruby/foo.rb2
-rw-r--r--samples/Ruby/formula.rb703
-rw-r--r--samples/Ruby/grit.rb2
-rw-r--r--samples/Ruby/inflector.rb323
-rw-r--r--samples/Ruby/jekyll.rb138
-rwxr-xr-xsamples/Ruby/macruby-script2
-rw-r--r--samples/Ruby/resque.rb385
-rwxr-xr-xsamples/Ruby/script.rake4
-rwxr-xr-xsamples/Ruby/script.rb2
-rw-r--r--samples/Ruby/script2.rb2
-rw-r--r--samples/Ruby/sinatra.rb1775
-rw-r--r--samples/Ruby/wrong_shebang.rb2
14 files changed, 3346 insertions, 0 deletions
diff --git a/samples/Ruby/filenames/Capfile b/samples/Ruby/filenames/Capfile
new file mode 100644
index 0000000..96291a7
--- /dev/null
+++ b/samples/Ruby/filenames/Capfile
@@ -0,0 +1,3 @@
+load 'deploy'
+Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
+load 'config/deploy'
diff --git a/samples/Ruby/filenames/Rakefile b/samples/Ruby/filenames/Rakefile
new file mode 100644
index 0000000..9d51ff7
--- /dev/null
+++ b/samples/Ruby/filenames/Rakefile
@@ -0,0 +1,3 @@
+task :default do
+ puts "Rake"
+end
diff --git a/samples/Ruby/foo.rb b/samples/Ruby/foo.rb
new file mode 100644
index 0000000..85026ed
--- /dev/null
+++ b/samples/Ruby/foo.rb
@@ -0,0 +1,2 @@
+module Foo
+end
diff --git a/samples/Ruby/formula.rb b/samples/Ruby/formula.rb
new file mode 100644
index 0000000..541ee65
--- /dev/null
+++ b/samples/Ruby/formula.rb
@@ -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'
diff --git a/samples/Ruby/grit.rb b/samples/Ruby/grit.rb
new file mode 100644
index 0000000..fc84753
--- /dev/null
+++ b/samples/Ruby/grit.rb
@@ -0,0 +1,2 @@
+module Grit
+end
diff --git a/samples/Ruby/inflector.rb b/samples/Ruby/inflector.rb
new file mode 100644
index 0000000..2acc6dd
--- /dev/null
+++ b/samples/Ruby/inflector.rb
@@ -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
diff --git a/samples/Ruby/jekyll.rb b/samples/Ruby/jekyll.rb
new file mode 100644
index 0000000..fd455fb
--- /dev/null
+++ b/samples/Ruby/jekyll.rb
@@ -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
diff --git a/samples/Ruby/macruby-script b/samples/Ruby/macruby-script
new file mode 100755
index 0000000..491be11
--- /dev/null
+++ b/samples/Ruby/macruby-script
@@ -0,0 +1,2 @@
+#!/usr/bin/env macruby
+puts "MacRuby"
diff --git a/samples/Ruby/resque.rb b/samples/Ruby/resque.rb
new file mode 100644
index 0000000..1e36524
--- /dev/null
+++ b/samples/Ruby/resque.rb
@@ -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
+
diff --git a/samples/Ruby/script.rake b/samples/Ruby/script.rake
new file mode 100755
index 0000000..96992aa
--- /dev/null
+++ b/samples/Ruby/script.rake
@@ -0,0 +1,4 @@
+#!/usr/bin/env rake
+task :default do
+ puts "Rake"
+end
diff --git a/samples/Ruby/script.rb b/samples/Ruby/script.rb
new file mode 100755
index 0000000..01a30f8
--- /dev/null
+++ b/samples/Ruby/script.rb
@@ -0,0 +1,2 @@
+#!/usr/bin/env ruby
+puts "Ruby"
diff --git a/samples/Ruby/script2.rb b/samples/Ruby/script2.rb
new file mode 100644
index 0000000..40aeb7c
--- /dev/null
+++ b/samples/Ruby/script2.rb
@@ -0,0 +1,2 @@
+#! /usr/bin/env ruby -w -Ilib:test
+echo "Ruby"
diff --git a/samples/Ruby/sinatra.rb b/samples/Ruby/sinatra.rb
new file mode 100644
index 0000000..36ec2a2
--- /dev/null
+++ b/samples/Ruby/sinatra.rb
@@ -0,0 +1,1775 @@
+# external dependencies
+require 'rack'
+require 'tilt'
+require "rack/protection"
+
+# stdlib dependencies
+require 'thread'
+require 'time'
+require 'uri'
+
+# other files we need
+require 'sinatra/showexceptions'
+require 'sinatra/version'
+
+module Sinatra
+ # The request object. See Rack::Request for more info:
+ # http://rack.rubyforge.org/doc/classes/Rack/Request.html
+ class Request < Rack::Request
+ # Returns an array of acceptable media types for the response
+ def accept
+ @env['sinatra.accept'] ||= begin
+ entries = @env['HTTP_ACCEPT'].to_s.split(',')
+ entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
+ end
+ end
+
+ def preferred_type(*types)
+ return accept.first if types.empty?
+ types.flatten!
+ accept.detect do |pattern|
+ type = types.detect { |t| File.fnmatch(pattern, t) }
+ return type if type
+ end
+ end
+
+ alias accept? preferred_type
+ alias secure? ssl?
+
+ def forwarded?
+ @env.include? "HTTP_X_FORWARDED_HOST"
+ end
+
+ def safe?
+ get? or head? or options? or trace?
+ end
+
+ def idempotent?
+ safe? or put? or delete?
+ end
+
+ private
+
+ def accept_entry(entry)
+ type, *options = entry.delete(' ').split(';')
+ quality = 0 # we sort smallest first
+ options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
+ [type, [quality, type.count('*'), 1 - options.size]]
+ end
+ end
+
+ # The response object. See Rack::Response and Rack::ResponseHelpers for
+ # more info:
+ # http://rack.rubyforge.org/doc/classes/Rack/Response.html
+ # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
+ class Response < Rack::Response
+ def body=(value)
+ value = value.body while Rack::Response === value
+ @body = String === value ? [value.to_str] : value
+ end
+
+ def each
+ block_given? ? super : enum_for(:each)
+ end
+
+ def finish
+ if status.to_i / 100 == 1
+ headers.delete "Content-Length"
+ headers.delete "Content-Type"
+ elsif Array === body and not [204, 304].include?(status.to_i)
+ # if some other code has already set Content-Length, don't muck with it
+ # currently, this would be the static file-handler
+ headers["Content-Length"] ||= body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
+ end
+
+ # Rack::Response#finish sometimes returns self as response body. We don't want that.
+ status, headers, result = super
+ result = body if result == self
+ [status, headers, result]
+ end
+ end
+
+ # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
+ # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
+ # This middleware will detect an extended body object and will make sure it reaches the
+ # handler directly. We do this here, so our middleware and middleware set up by the app will
+ # still be able to run.
+ class ExtendedRack < Struct.new(:app)
+ def call(env)
+ result, callback = app.call(env), env['async.callback']
+ return result unless callback and async?(*result)
+ after_response { callback.call result }
+ setup_close(env, *result)
+ throw :async
+ end
+
+ private
+
+ def setup_close(env, status, header, body)
+ return unless body.respond_to? :close and env.include? 'async.close'
+ env['async.close'].callback { body.close }
+ env['async.close'].errback { body.close }
+ end
+
+ def after_response(&block)
+ raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
+ EventMachine.next_tick(&block)
+ end
+
+ def async?(status, headers, body)
+ return true if status == -1
+ body.respond_to? :callback and body.respond_to? :errback
+ end
+ end
+
+ # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
+ # if another CommonLogger is already in the middleware chane.
+ class CommonLogger < Rack::CommonLogger
+ def call(env)
+ env['sinatra.commonlogger'] ? @app.call(env) : super
+ end
+
+ superclass.class_eval do
+ alias call_without_check call unless method_defined? :call_without_check
+ def call(env)
+ env['sinatra.commonlogger'] = true
+ call_without_check(env)
+ end
+ end
+ end
+
+ class NotFound < NameError #:nodoc:
+ def http_status; 404 end
+ end
+
+ # Methods available to routes, before/after filters, and views.
+ module Helpers
+ # Set or retrieve the response status code.
+ def status(value=nil)
+ response.status = value if value
+ response.status
+ end
+
+ # Set or retrieve the response body. When a block is given,
+ # evaluation is deferred until the body is read with #each.
+ def body(value=nil, &block)
+ if block_given?
+ def block.each; yield(call) end
+ response.body = block
+ elsif value
+ response.body = value
+ else
+ response.body
+ end
+ end
+
+ # Halt processing and redirect to the URI provided.
+ def redirect(uri, *args)
+ if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
+ status 303
+ else
+ status 302
+ end
+
+ # According to RFC 2616 section 14.30, "the field value consists of a
+ # single absolute URI"
+ response['Location'] = uri(uri, settings.absolute_redirects?, settings.prefixed_redirects?)
+ halt(*args)
+ end
+
+ # Generates the absolute URI for a given path in the app.
+ # Takes Rack routers and reverse proxies into account.
+ def uri(addr = nil, absolute = true, add_script_name = true)
+ return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
+ uri = [host = ""]
+ if absolute
+ host << "http#{'s' if request.secure?}://"
+ if request.forwarded? or request.port != (request.secure? ? 443 : 80)
+ host << request.host_with_port
+ else
+ host << request.host
+ end
+ end
+ uri << request.script_name.to_s if add_script_name
+ uri << (addr ? addr : request.path_info).to_s
+ File.join uri
+ end
+
+ alias url uri
+ alias to uri
+
+ # Halt processing and return the error status provided.
+ def error(code, body=nil)
+ code, body = 500, code.to_str if code.respond_to? :to_str
+ response.body = body unless body.nil?
+ halt code
+ end
+
+ # Halt processing and return a 404 Not Found.
+ def not_found(body=nil)
+ error 404, body
+ end
+
+ # Set multiple response headers with Hash.
+ def headers(hash=nil)
+ response.headers.merge! hash if hash
+ response.headers
+ end
+
+ # Access the underlying Rack session.
+ def session
+ request.session
+ end
+
+ # Access shared logger object.
+ def logger
+ request.logger
+ end
+
+ # Look up a media type by file extension in Rack's mime registry.
+ def mime_type(type)
+ Base.mime_type(type)
+ end
+
+ # Set the Content-Type of the response body given a media type or file
+ # extension.
+ def content_type(type = nil, params={})
+ return response['Content-Type'] unless type
+ default = params.delete :default
+ mime_type = mime_type(type) || default
+ fail "Unknown media type: %p" % type if mime_type.nil?
+ mime_type = mime_type.dup
+ unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
+ params[:charset] = params.delete('charset') || settings.default_encoding
+ end
+ params.delete :charset if mime_type.include? 'charset'
+ unless params.empty?
+ mime_type << (mime_type.include?(';') ? ', ' : ';')
+ mime_type << params.map { |kv| kv.join('=') }.join(', ')
+ end
+ response['Content-Type'] = mime_type
+ end
+
+ # Set the Content-Disposition to "attachment" with the specified filename,
+ # instructing the user agents to prompt to save.
+ def attachment(filename=nil)
+ response['Content-Disposition'] = 'attachment'
+ if filename
+ params = '; filename="%s"' % File.basename(filename)
+ response['Content-Disposition'] << params
+ ext = File.extname(filename)
+ content_type(ext) unless response['Content-Type'] or ext.empty?
+ end
+ end
+
+ # Use the contents of the file at +path+ as the response body.
+ def send_file(path, opts={})
+ if opts[:type] or not response['Content-Type']
+ content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
+ end
+
+ if opts[:disposition] == 'attachment' || opts[:filename]
+ attachment opts[:filename] || path
+ elsif opts[:disposition] == 'inline'
+ response['Content-Disposition'] = 'inline'
+ end
+
+ last_modified opts[:last_modified] if opts[:last_modified]
+
+ file = Rack::File.new nil
+ file.path = path
+ result = file.serving env
+ result[1].each { |k,v| headers[k] ||= v }
+ headers['Content-Length'] = result[1]['Content-Length']
+ halt opts[:status] || result[0], result[2]
+ rescue Errno::ENOENT
+ not_found
+ end
+
+ # Class of the response body in case you use #stream.
+ #
+ # Three things really matter: The front and back block (back being the
+ # blog generating content, front the one sending it to the client) and
+ # the scheduler, integrating with whatever concurrency feature the Rack
+ # handler is using.
+ #
+ # Scheduler has to respond to defer and schedule.
+ class Stream
+ def self.schedule(*) yield end
+ def self.defer(*) yield end
+
+ def initialize(scheduler = self.class, keep_open = false, &back)
+ @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
+ @callbacks, @closed = [], false
+ end
+
+ def close
+ return if @closed
+ @closed = true
+ @scheduler.schedule { @callbacks.each { |c| c.call }}
+ end
+
+ def each(&front)
+ @front = front
+ @scheduler.defer do
+ begin
+ @back.call(self)
+ rescue Exception => e
+ @scheduler.schedule { raise e }
+ end
+ close unless @keep_open
+ end
+ end
+
+ def <<(data)
+ @scheduler.schedule { @front.call(data.to_s) }
+ self
+ end
+
+ def callback(&block)
+ return yield if @closed
+ @callbacks << block
+ end
+
+ alias errback callback
+ end
+
+ # Allows to start sending data to the client even though later parts of
+ # the response body have not yet been generated.
+ #
+ # The close parameter specifies whether Stream#close should be called
+ # after the block has been executed. This is only relevant for evented
+ # servers like Thin or Rainbows.
+ def stream(keep_open = false)
+ scheduler = env['async.callback'] ? EventMachine : Stream
+ current = @params.dup
+ body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
+ end
+
+ # Specify response freshness policy for HTTP caches (Cache-Control header).
+ # Any number of non-value directives (:public, :private, :no_cache,
+ # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
+ # a Hash of value directives (:max_age, :min_stale, :s_max_age).
+ #
+ # cache_control :public, :must_revalidate, :max_age => 60
+ # => Cache-Control: public, must-revalidate, max-age=60
+ #
+ # See RFC 2616 / 14.9 for more on standard cache control directives:
+ # http://tools.ietf.org/html/rfc2616#section-14.9.1
+ def cache_control(*values)
+ if values.last.kind_of?(Hash)
+ hash = values.pop
+ hash.reject! { |k,v| v == false }
+ hash.reject! { |k,v| values << k if v == true }
+ else
+ hash = {}
+ end
+
+ values.map! { |value| value.to_s.tr('_','-') }
+ hash.each do |key, value|
+ key = key.to_s.tr('_', '-')
+ value = value.to_i if key == "max-age"
+ values << [key, value].join('=')
+ end
+
+ response['Cache-Control'] = values.join(', ') if values.any?
+ end
+
+ # Set the Expires header and Cache-Control/max-age directive. Amount
+ # can be an integer number of seconds in the future or a Time object
+ # indicating when the response should be considered "stale". The remaining
+ # "values" arguments are passed to the #cache_control helper:
+ #
+ # expires 500, :public, :must_revalidate
+ # => Cache-Control: public, must-revalidate, max-age=60
+ # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
+ #
+ def expires(amount, *values)
+ values << {} unless values.last.kind_of?(Hash)
+
+ if amount.is_a? Integer
+ time = Time.now + amount.to_i
+ max_age = amount
+ else
+ time = time_for amount
+ max_age = time - Time.now
+ end
+
+ values.last.merge!(:max_age => max_age)
+ cache_control(*values)
+
+ response['Expires'] = time.httpdate
+ end
+
+ # Set the last modified time of the resource (HTTP 'Last-Modified' header)
+ # and halt if conditional GET matches. The +time+ argument is a Time,
+ # DateTime, or other object that responds to +to_time+.
+ #
+ # When the current request includes an 'If-Modified-Since' header that is
+ # equal or later than the time specified, execution is immediately halted
+ # with a '304 Not Modified' response.
+ def last_modified(time)
+ return unless time
+ time = time_for time
+ response['Last-Modified'] = time.httpdate
+ return if env['HTTP_IF_NONE_MATCH']
+
+ if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
+ # compare based on seconds since epoch
+ since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
+ halt 304 if since >= time.to_i
+ end
+
+ if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
+ # compare based on seconds since epoch
+ since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
+ halt 412 if since < time.to_i
+ end
+ rescue ArgumentError
+ end
+
+ # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
+ # GET matches. The +value+ argument is an identifier that uniquely
+ # identifies the current version of the resource. The +kind+ argument
+ # indicates whether the etag should be used as a :strong (default) or :weak
+ # cache validator.
+ #
+ # When the current request includes an 'If-None-Match' header with a
+ # matching etag, execution is immediately halted. If the request method is
+ # GET or HEAD, a '304 Not Modified' response is sent.
+ def etag(value, options = {})
+ # Before touching this code, please double check RFC 2616 14.24 and 14.26.
+ options = {:kind => options} unless Hash === options
+ kind = options[:kind] || :strong
+ new_resource = options.fetch(:new_resource) { request.post? }
+
+ unless [:strong, :weak].include?(kind)
+ raise ArgumentError, ":strong or :weak expected"
+ end
+
+ value = '"%s"' % value
+ value = 'W/' + value if kind == :weak
+ response['ETag'] = value
+
+ if success? or status == 304
+ if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
+ halt(request.safe? ? 304 : 412)
+ end
+
+ if env['HTTP_IF_MATCH']
+ halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
+ end
+ end
+ end
+
+ # Sugar for redirect (example: redirect back)
+ def back
+ request.referer
+ end
+
+ # whether or not the status is set to 1xx
+ def informational?
+ status.between? 100, 199
+ end
+
+ # whether or not the status is set to 2xx
+ def success?
+ status.between? 200, 299
+ end
+
+ # whether or not the status is set to 3xx
+ def redirect?
+ status.between? 300, 399
+ end
+
+ # whether or not the status is set to 4xx
+ def client_error?
+ status.between? 400, 499
+ end
+
+ # whether or not the status is set to 5xx
+ def server_error?
+ status.between? 500, 599
+ end
+
+ # whether or not the status is set to 404
+ def not_found?
+ status == 404
+ end
+
+ # Generates a Time object from the given value.
+ # Used by #expires and #last_modified.
+ def time_for(value)
+ if value.respond_to? :to_time
+ value.to_time
+ elsif value.is_a? Time
+ value
+ elsif value.respond_to? :new_offset
+ # DateTime#to_time does the same on 1.9
+ d = value.new_offset 0
+ t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
+ t.getlocal
+ elsif value.respond_to? :mday
+ # Date#to_time does the same on 1.9
+ Time.local(value.year, value.mon, value.mday)
+ elsif value.is_a? Numeric
+ Time.at value
+ else
+ Time.parse value.to_s
+ end
+ rescue ArgumentError => boom
+ raise boom
+ rescue Exception
+ raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
+ end
+
+ private
+
+ # Helper method checking if a ETag value list includes the current ETag.
+ def etag_matches?(list, new_resource = request.post?)
+ return !new_resource if list == '*'
+ list.to_s.split(/\s*,\s*/).include? response['ETag']
+ end
+
+ def with_params(temp_params)
+ original, @params = @params, temp_params
+ yield
+ ensure
+ @params = original if original
+ end
+ end
+
+ private
+
+ # Template rendering methods. Each method takes the name of a template
+ # to render as a Symbol and returns a String with the rendered output,
+ # as well as an optional hash with additional options.
+ #
+ # `template` is either the name or path of the template as symbol
+ # (Use `:'subdir/myview'` for views in subdirectories), or a string
+ # that will be rendered.
+ #
+ # Possible options are:
+ # :content_type The content type to use, same arguments as content_type.
+ # :layout If set to false, no layout is rendered, otherwise
+ # the specified layout is used (Ignored for `sass` and `less`)
+ # :layout_engine Engine to use for rendering the layout.
+ # :locals A hash with local variables that should be available
+ # in the template
+ # :scope If set, template is evaluate with the binding of the given
+ # object rather than the application instance.
+ # :views Views directory to use.
+ module Templates
+ module ContentTyped
+ attr_accessor :content_type
+ end
+
+ def initialize
+ super
+ @default_layout = :layout
+ end
+
+ def erb(template, options={}, locals={})
+ render :erb, template, options, locals
+ end
+
+ def erubis(template, options={}, locals={})
+ warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
+ "If you have Erubis installed, it will be used automatically."
+ render :erubis, template, options, locals
+ end
+
+ def haml(template, options={}, locals={})
+ render :haml, template, options, locals
+ end
+
+ def sass(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :sass, template, options, locals
+ end
+
+ def scss(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :scss, template, options, locals
+ end
+
+ def less(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :less, template, options, locals
+ end
+
+ def builder(template=nil, options={}, locals={}, &block)
+ options[:default_content_type] = :xml
+ render_ruby(:builder, template, options, locals, &block)
+ end
+
+ def liquid(template, options={}, locals={})
+ render :liquid, template, options, locals
+ end
+
+ def markdown(template, options={}, locals={})
+ render :markdown, template, options, locals
+ end
+
+ def textile(template, options={}, locals={})
+ render :textile, template, options, locals
+ end
+
+ def rdoc(template, options={}, locals={})
+ render :rdoc, template, options, locals
+ end
+
+ def radius(template, options={}, locals={})
+ render :radius, template, options, locals
+ end
+
+ def markaby(template=nil, options={}, locals={}, &block)
+ render_ruby(:mab, template, options, locals, &block)
+ end
+
+ def coffee(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :js
+ render :coffee, template, options, locals
+ end
+
+ def nokogiri(template=nil, options={}, locals={}, &block)
+ options[:default_content_type] = :xml
+ render_ruby(:nokogiri, template, options, locals, &block)
+ end
+
+ def slim(template, options={}, locals={})
+ render :slim, template, options, locals
+ end
+
+ def creole(template, options={}, locals={})
+ render :creole, template, options, locals
+ end
+
+ def yajl(template, options={}, locals={})
+ options[:default_content_type] = :json
+ render :yajl, template, options, locals
+ end
+
+ # Calls the given block for every possible template file in views,
+ # named name.ext, where ext is registered on engine.
+ def find_template(views, name, engine)
+ yield ::File.join(views, "#{name}.#{@preferred_extension}")
+ Tilt.mappings.each do |ext, engines|
+ next unless ext != @preferred_extension and engines.include? engine
+ yield ::File.join(views, "#{name}.#{ext}")
+ end
+ end
+
+ private
+ # logic shared between builder and nokogiri
+ def render_ruby(engine, template, options={}, locals={}, &block)
+ options, template = template, nil if template.is_a?(Hash)
+ template = Proc.new { block } if template.nil?
+ render engine, template, options, locals
+ end
+
+ def render(engine, data, options={}, locals={}, &block)
+ # merge app-level options
+ options = settings.send(engine).merge(options) if settings.respond_to?(engine)
+ options[:outvar] ||= '@_out_buf'
+ options[:default_encoding] ||= settings.default_encoding
+
+ # extract generic options
+ locals = options.delete(:locals) || locals || {}
+ views = options.delete(:views) || settings.views || "./views"
+ layout = options.delete(:layout)
+ eat_errors = layout.nil?
+ layout = @default_layout if layout.nil? or layout == true
+ content_type = options.delete(:content_type) || options.delete(:default_content_type)
+ layout_engine = options.delete(:layout_engine) || engine
+ scope = options.delete(:scope) || self
+
+ # compile and render template
+ begin
+ layout_was = @default_layout
+ @default_layout = false
+ template = compile_template(engine, data, options, views)
+ output = template.render(scope, locals, &block)
+ ensure
+ @default_layout = layout_was
+ end
+
+ # render layout
+ if layout
+ options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope)
+ catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
+ end
+
+ output.extend(ContentTyped).content_type = content_type if content_type
+ output
+ end
+
+ def compile_template(engine, data, options, views)
+ eat_errors = options.delete :eat_errors
+ template_cache.fetch engine, data, options do
+ template = Tilt[engine]
+ raise "Template engine not found: #{engine}" if template.nil?
+
+ case data
+ when Symbol
+ body, path, line = settings.templates[data]
+ if body
+ body = body.call if body.respond_to?(:call)
+ template.new(path, line.to_i, options) { body }
+ else
+ found = false
+ @preferred_extension = engine.to_s
+ find_template(views, data, template) do |file|
+ path ||= file # keep the initial path rather than the last one
+ if found = File.exists?(file)
+ path = file
+ break
+ end
+ end
+ throw :layout_missing if eat_errors and not found
+ template.new(path, 1, options)
+ end
+ when Proc, String
+ body = data.is_a?(String) ? Proc.new { data } : data
+ path, line = settings.caller_locations.first
+ template.new(path, line.to_i, options, &body)
+ else
+ raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
+ end
+ end
+ end
+ end
+
+ # Base class for all Sinatra applications and middleware.
+ class Base
+ include Rack::Utils
+ include Helpers
+ include Templates
+
+ attr_accessor :app
+ attr_reader :template_cache
+
+ def initialize(app=nil)
+ super()
+ @app = app
+ @template_cache = Tilt::Cache.new
+ yield self if block_given?
+ end
+
+ # Rack call interface.
+ def call(env)
+ dup.call!(env)
+ end
+
+ attr_accessor :env, :request, :response, :params
+
+ def call!(env) # :nodoc:
+ @env = env
+ @request = Request.new(env)
+ @response = Response.new
+ @params = indifferent_params(@request.params)
+ template_cache.clear if settings.reload_templates
+ force_encoding(@params)
+
+ @response['Content-Type'] = nil
+ invoke { dispatch! }
+ invoke { error_block!(response.status) }
+
+ unless @response['Content-Type']
+ if Array === body and body[0].respond_to? :content_type
+ content_type body[0].content_type
+ else
+ content_type :html
+ end
+ end
+
+ @response.finish
+ end
+
+ # Access settings defined with Base.set.
+ def self.settings
+ self
+ end
+
+ # Access settings defined with Base.set.
+ def settings
+ self.class.settings
+ end
+
+ def options
+ warn "Sinatra::Base#options is deprecated and will be removed, " \
+ "use #settings instead."
+ settings
+ end
+
+ # Exit the current block, halts any further processing
+ # of the request, and returns the specified response.
+ def halt(*response)
+ response = response.first if response.length == 1
+ throw :halt, response
+ end
+
+ # Pass control to the next matching route.
+ # If there are no more matching routes, Sinatra will
+ # return a 404 response.
+ def pass(&block)
+ throw :pass, block
+ end
+
+ # Forward the request to the downstream app -- middleware only.
+ def forward
+ fail "downstream app not set" unless @app.respond_to? :call
+ status, headers, body = @app.call env
+ @response.status = status
+ @response.body = body
+ @response.headers.merge! headers
+ nil
+ end
+
+ private
+ # Run filters defined on the class and all superclasses.
+ def filter!(type, base = settings)
+ filter! type, base.superclass if base.superclass.respond_to?(:filters)
+ base.filters[type].each { |args| process_route(*args) }
+ end
+
+ # Run routes defined on the class and all superclasses.
+ def route!(base = settings, pass_block=nil)
+ if routes = base.routes[@request.request_method]
+ routes.each do |pattern, keys, conditions, block|
+ pass_block = process_route(pattern, keys, conditions) do |*args|
+ route_eval { block[*args] }
+ end
+ end
+ end
+
+ # Run routes defined in superclass.
+ if base.superclass.respond_to?(:routes)
+ return route!(base.superclass, pass_block)
+ end
+
+ route_eval(&pass_block) if pass_block
+ route_missing
+ end
+
+ # Run a route block and throw :halt with the result.
+ def route_eval
+ throw :halt, yield
+ end
+
+ # If the current request matches pattern and conditions, fill params
+ # with keys and call the given block.
+ # Revert params afterwards.
+ #
+ # Returns pass block.
+ def process_route(pattern, keys, conditions, block = nil, values = [])
+ route = @request.path_info
+ route = '/' if route.empty? and not settings.empty_path_info?
+ return unless match = pattern.match(route)
+ values += match.captures.to_a.map { |v| force_encoding URI.decode_www_form_component(v) if v }
+
+ if values.any?
+ original, @params = params, params.merge('splat' => [], 'captures' => values)
+ keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
+ end
+
+ catch(:pass) do
+ conditions.each { |c| throw :pass if c.bind(self).call == false }
+ block ? block[self, values] : yield(self, values)
+ end
+ ensure
+ @params = original if original
+ end
+
+ # No matching route was found or all routes passed. The default
+ # implementation is to forward the request downstream when running
+ # as middleware (@app is non-nil); when no downstream app is set, raise
+ # a NotFound exception. Subclasses can override this method to perform
+ # custom route miss logic.
+ def route_missing
+ if @app
+ forward
+ else
+ raise NotFound
+ end
+ end
+
+ # Attempt to serve static files from public directory. Throws :halt when
+ # a matching file is found, returns nil otherwise.
+ def static!
+ return if (public_dir = settings.public_folder).nil?
+ public_dir = File.expand_path(public_dir)
+
+ path = File.expand_path(public_dir + unescape(request.path_info))
+ return unless path.start_with?(public_dir) and File.file?(path)
+
+ env['sinatra.static_file'] = path
+ cache_control(*settings.static_cache_control) if settings.static_cache_control?
+ send_file path, :disposition => nil
+ end
+
+ # Enable string or symbol key access to the nested params hash.
+ def indifferent_params(object)
+ case object
+ when Hash
+ new_hash = indifferent_hash
+ object.each { |key, value| new_hash[key] = indifferent_params(value) }
+ new_hash
+ when Array
+ object.map { |item| indifferent_params(item) }
+ else
+ object
+ end
+ end
+
+ # Creates a Hash with indifferent access.
+ def indifferent_hash
+ Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
+ end
+
+ # Run the block with 'throw :halt' support and apply result to the response.
+ def invoke
+ res = catch(:halt) { yield }
+ res = [res] if Fixnum === res or String === res
+ if Array === res and Fixnum === res.first
+ status(res.shift)
+ body(res.pop)
+ headers(*res)
+ elsif res.respond_to? :each
+ body res
+ end
+ nil # avoid double setting the same response tuple twice
+ end
+
+ # Dispatch a request with error handling.
+ def dispatch!
+ invoke do
+ static! if settings.static? && (request.get? || request.head?)
+ filter! :before
+ route!
+ end
+ rescue ::Exception => boom
+ invoke { handle_exception!(boom) }
+ ensure
+ filter! :after unless env['sinatra.static_file']
+ end
+
+ # Error handling during requests.
+ def handle_exception!(boom)
+ @env['sinatra.error'] = boom
+
+ if boom.respond_to? :http_status
+ status(boom.http_status)
+ elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
+ status(boom.code)
+ else
+ status(500)
+ end
+
+ status(500) unless status.between? 400, 599
+
+ if server_error?
+ dump_errors! boom if settings.dump_errors?
+ raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
+ end
+
+ if not_found?
+ headers['X-Cascade'] = 'pass'
+ body '<h1>Not Found</h1>'
+ end
+
+ res = error_block!(boom.class, boom) || error_block!(status, boom)
+ return res if res or not server_error?
+ raise boom if settings.raise_errors? or settings.show_exceptions?
+ error_block! Exception, boom
+ end
+
+ # Find an custom error block for the key(s) specified.
+ def error_block!(key, *block_params)
+ base = settings
+ while base.respond_to?(:errors)
+ next base = base.superclass unless args_array = base.errors[key]
+ args_array.reverse_each do |args|
+ first = args == args_array.first
+ args += [block_params]
+ resp = process_route(*args)
+ return resp unless resp.nil? && !first
+ end
+ end
+ return false unless key.respond_to? :superclass and key.superclass < Exception
+ error_block!(key.superclass, *block_params)
+ end
+
+ def dump_errors!(boom)
+ msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
+ @env['rack.errors'].puts(msg)
+ end
+
+ class << self
+ attr_reader :routes, :filters, :templates, :errors
+
+ # Removes all routes, filters, middleware and extension hooks from the
+ # current class (not routes/filters/... defined by its superclass).
+ def reset!
+ @conditions = []
+ @routes = {}
+ @filters = {:before => [], :after => []}
+ @errors = {}
+ @middleware = []
+ @prototype = nil
+ @extensions = []
+
+ if superclass.respond_to?(:templates)
+ @templates = Hash.new { |hash,key| superclass.templates[key] }
+ else
+ @templates = {}
+ end
+ end
+
+ # Extension modules registered on this class and all superclasses.
+ def extensions
+ if superclass.respond_to?(:extensions)
+ (@extensions + superclass.extensions).uniq
+ else
+ @extensions
+ end
+ end
+
+ # Middleware used in this class and all superclasses.
+ def middleware
+ if superclass.respond_to?(:middleware)
+ superclass.middleware + @middleware
+ else
+ @middleware
+ end
+ end
+
+ # Sets an option to the given value. If the value is a proc,
+ # the proc will be called every time the option is accessed.
+ def set(option, value = (not_set = true), ignore_setter = false, &block)
+ raise ArgumentError if block and !not_set
+ value, not_set = block, false if block
+
+ if not_set
+ raise ArgumentError unless option.respond_to?(:each)
+ option.each { |k,v| set(k, v) }
+ return self
+ end
+
+ if respond_to?("#{option}=") and not ignore_setter
+ return __send__("#{option}=", value)
+ end
+
+ setter = proc { |val| set option, val, true }
+ getter = proc { value }
+
+ case value
+ when Proc
+ getter = value
+ when Symbol, Fixnum, FalseClass, TrueClass, NilClass
+ getter = value.inspect
+ when Hash
+ setter = proc do |val|
+ val = value.merge val if Hash === val
+ set option, val, true
+ end
+ end
+
+ define_singleton_method("#{option}=", setter) if setter
+ define_singleton_method(option, getter) if getter
+ define_singleton_method("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
+ self
+ end
+
+ # Same as calling `set :option, true` for each of the given options.
+ def enable(*opts)
+ opts.each { |key| set(key, true) }
+ end
+
+ # Same as calling `set :option, false` for each of the given options.
+ def disable(*opts)
+ opts.each { |key| set(key, false) }
+ end
+
+ # Define a custom error handler. Optionally takes either an Exception
+ # class, or an HTTP status code to specify which errors should be
+ # handled.
+ def error(*codes, &block)
+ args = compile! "ERROR", //, block
+ codes = codes.map { |c| Array(c) }.flatten
+ codes << Exception if codes.empty?
+ codes.each { |c| (@errors[c] ||= []) << args }
+ end
+
+ # Sugar for `error(404) { ... }`
+ def not_found(&block)
+ error 404, &block
+ end
+
+ # Define a named template. The block must return the template source.
+ def template(name, &block)
+ filename, line = caller_locations.first
+ templates[name] = [block, filename, line.to_i]
+ end
+
+ # Define the layout template. The block must return the template source.
+ def layout(name=:layout, &block)
+ template name, &block
+ end
+
+ # Load embeded templates from the file; uses the caller's __FILE__
+ # when no file is specified.
+ def inline_templates=(file=nil)
+ file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
+
+ begin
+ io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
+ app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
+ rescue Errno::ENOENT
+ app, data = nil
+ end
+
+ if data
+ if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
+ encoding = $2
+ else
+ encoding = settings.default_encoding
+ end
+ lines = app.count("\n") + 1
+ template = nil
+ force_encoding data, encoding
+ data.each_line do |line|
+ lines += 1
+ if line =~ /^@@\s*(.*\S)\s*$/
+ template = force_encoding('', encoding)
+ templates[$1.to_sym] = [template, file, lines]
+ elsif template
+ template << line
+ end
+ end
+ end
+ end
+
+ # Lookup or register a mime type in Rack's mime registry.
+ def mime_type(type, value=nil)
+ return type if type.nil? || type.to_s.include?('/')
+ type = ".#{type}" unless type.to_s[0] == ?.
+ return Rack::Mime.mime_type(type, nil) unless value
+ Rack::Mime::MIME_TYPES[type] = value
+ end
+
+ # provides all mime types matching type, including deprecated types:
+ # mime_types :html # => ['text/html']
+ # mime_types :js # => ['application/javascript', 'text/javascript']
+ def mime_types(type)
+ type = mime_type type
+ type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
+ end
+
+ # Define a before filter; runs before all requests within the same
+ # context as route handlers and may access/modify the request and
+ # response.
+ def before(path = nil, options = {}, &block)
+ add_filter(:before, path, options, &block)
+ end
+
+ # Define an after filter; runs after all requests within the same
+ # context as route handlers and may access/modify the request and
+ # response.
+ def after(path = nil, options = {}, &block)
+ add_filter(:after, path, options, &block)
+ end
+
+ # add a filter
+ def add_filter(type, path = nil, options = {}, &block)
+ path, options = //, path if path.respond_to?(:each_pair)
+ filters[type] << compile!(type, path || //, block, options)
+ end
+
+ # Add a route condition. The route is considered non-matching when the
+ # block returns false.
+ def condition(name = "#{caller.first[/`.*'/]} condition", &block)
+ @conditions << generate_method(name, &block)
+ end
+
+ def public=(value)
+ warn ":public is no longer used to avoid overloading Module#public, use :public_dir instead"
+ set(:public_folder, value)
+ end
+
+ def public_dir=(value)
+ self.public_folder = value
+ end
+
+ def public_dir
+ public_folder
+ end
+
+ private
+ # Dynamically defines a method on settings.
+ def define_singleton_method(name, content = Proc.new)
+ # replace with call to singleton_class once we're 1.9 only
+ (class << self; self; end).class_eval do
+ undef_method(name) if method_defined? name
+ String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
+ end
+ end
+
+ # Condition for matching host name. Parameter might be String or Regexp.
+ def host_name(pattern)
+ condition { pattern === request.host }
+ end
+
+ # Condition for matching user agent. Parameter should be Regexp.
+ # Will set params[:agent].
+ def user_agent(pattern)
+ condition do
+ if request.user_agent.to_s =~ pattern
+ @params[:agent] = $~[1..-1]
+ true
+ else
+ false
+ end
+ end
+ end
+ alias_method :agent, :user_agent
+
+ # Condition for matching mimetypes. Accepts file extensions.
+ def provides(*types)
+ types.map! { |t| mime_types(t) }
+ types.flatten!
+ condition do
+ if type = response['Content-Type']
+ types.include? type or types.include? type[/^[^;]+/]
+ elsif type = request.preferred_type(types)
+ content_type(type)
+ true
+ else
+ false
+ end
+ end
+ end
+
+ public
+ # Defining a `GET` handler also automatically defines
+ # a `HEAD` handler.
+ def get(path, opts={}, &block)
+ conditions = @conditions.dup
+ route('GET', path, opts, &block)
+
+ @conditions = conditions
+ route('HEAD', path, opts, &block)
+ end
+
+ def put(path, opts={}, &bk) route 'PUT', path, opts, &bk end
+ def post(path, opts={}, &bk) route 'POST', path, opts, &bk end
+ def delete(path, opts={}, &bk) route 'DELETE', path, opts, &bk end
+ def head(path, opts={}, &bk) route 'HEAD', path, opts, &bk end
+ def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
+ def patch(path, opts={}, &bk) route 'PATCH', path, opts, &bk end
+
+ private
+ def route(verb, path, options={}, &block)
+ # Because of self.options.host
+ host_name(options.delete(:host)) if options.key?(:host)
+ enable :empty_path_info if path == "" and empty_path_info.nil?
+ signature = compile!(verb, path, block, options)
+ (@routes[verb] ||= []) << signature
+ invoke_hook(:route_added, verb, path, block)
+ signature
+ end
+
+ def invoke_hook(name, *args)
+ extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
+ end
+
+ def generate_method(method_name, &block)
+ define_method(method_name, &block)
+ method = instance_method method_name
+ remove_method method_name
+ method
+ end
+
+ def compile!(verb, path, block, options = {})
+ options.each_pair { |option, args| send(option, *args) }
+ method_name = "#{verb} #{path}"
+ unbound_method = generate_method(method_name, &block)
+ pattern, keys = compile path
+ conditions, @conditions = @conditions, []
+
+ [ pattern, keys, conditions, block.arity != 0 ?
+ proc { |a,p| unbound_method.bind(a).call(*p) } :
+ proc { |a,p| unbound_method.bind(a).call } ]
+ end
+
+ def compile(path)
+ keys = []
+ if path.respond_to? :to_str
+ pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
+ pattern.gsub!(/((:\w+)|\*)/) do |match|
+ if match == "*"
+ keys << 'splat'
+ "(.*?)"
+ else
+ keys << $2[1..-1]
+ "([^/?#]+)"
+ end
+ end
+ [/^#{pattern}$/, keys]
+ elsif path.respond_to?(:keys) && path.respond_to?(:match)
+ [path, path.keys]
+ elsif path.respond_to?(:names) && path.respond_to?(:match)
+ [path, path.names]
+ elsif path.respond_to? :match
+ [path, keys]
+ else
+ raise TypeError, path
+ end
+ end
+
+ URI = ::URI.const_defined?(:Parser) ? ::URI::Parser.new : ::URI
+
+ def encoded(char)
+ enc = URI.escape(char)
+ enc = "(?:#{Regexp.escape enc}|#{URI.escape char, /./})" if enc == char
+ enc = "(?:#{enc}|#{encoded('+')})" if char == " "
+ enc
+ end
+
+ public
+ # Makes the methods defined in the block and in the Modules given
+ # in `extensions` available to the handlers and templates
+ def helpers(*extensions, &block)
+ class_eval(&block) if block_given?
+ include(*extensions) if extensions.any?
+ end
+
+ # Register an extension. Alternatively take a block from which an
+ # extension will be created and registered on the fly.
+ def register(*extensions, &block)
+ extensions << Module.new(&block) if block_given?
+ @extensions += extensions
+ extensions.each do |extension|
+ extend extension
+ extension.registered(self) if extension.respond_to?(:registered)
+ end
+ end
+
+ def development?; environment == :development end
+ def production?; environment == :production end
+ def test?; environment == :test end
+
+ # Set configuration options for Sinatra and/or the app.
+ # Allows scoping of settings for certain environments.
+ def configure(*envs, &block)
+ yield self if envs.empty? || envs.include?(environment.to_sym)
+ end
+
+ # Use the specified Rack middleware
+ def use(middleware, *args, &block)
+ @prototype = nil
+ @middleware << [middleware, args, block]
+ end
+
+ def quit!(server, handler_name)
+ # Use Thin's hard #stop! if available, otherwise just #stop.
+ server.respond_to?(:stop!) ? server.stop! : server.stop
+ $stderr.puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
+ end
+
+ # Run the Sinatra app as a self-hosted server using
+ # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
+ # with the constructed handler once we have taken the stage.
+ def run!(options={})
+ set options
+ handler = detect_rack_handler
+ handler_name = handler.name.gsub(/.*::/, '')
+ server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
+ handler.run self, server_settings.merge(:Port => port, :Host => bind) do |server|
+ unless handler_name =~ /cgi/i
+ $stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
+ "on #{port} for #{environment} with backup from #{handler_name}"
+ end
+ [:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
+ server.threaded = settings.threaded if server.respond_to? :threaded=
+ set :running, true
+ yield server if block_given?
+ end
+ rescue Errno::EADDRINUSE
+ $stderr.puts "== Someone is already performing on port #{port}!"
+ end
+
+ # The prototype instance used to process requests.
+ def prototype
+ @prototype ||= new
+ end
+
+ # Create a new instance without middleware in front of it.
+ alias new! new unless method_defined? :new!
+
+ # Create a new instance of the class fronted by its middleware
+ # pipeline. The object is guaranteed to respond to #call but may not be
+ # an instance of the class new was called on.
+ def new(*args, &bk)
+ instance = new!(*args, &bk)
+ Wrapper.new(build(instance).to_app, instance)
+ end
+
+ # Creates a Rack::Builder instance with all the middleware set up and
+ # the given +app+ as end point.
+ def build(app)
+ builder = Rack::Builder.new
+ setup_default_middleware builder
+ setup_middleware builder
+ builder.run app
+ builder
+ end
+
+ def call(env)
+ synchronize { prototype.call(env) }
+ end
+
+ private
+ def setup_default_middleware(builder)
+ builder.use ExtendedRack
+ builder.use ShowExceptions if show_exceptions?
+ builder.use Rack::MethodOverride if method_override?
+ builder.use Rack::Head
+ setup_logging builder
+ setup_sessions builder
+ setup_protection builder
+ end
+
+ def setup_middleware(builder)
+ middleware.each { |c,a,b| builder.use(c, *a, &b) }
+ end
+
+ def setup_logging(builder)
+ if logging?
+ setup_common_logger(builder)
+ setup_custom_logger(builder)
+ elsif logging == false
+ setup_null_logger(builder)
+ end
+ end
+
+ def setup_null_logger(builder)
+ builder.use Rack::NullLogger
+ end
+
+ def setup_common_logger(builder)
+ builder.use Sinatra::CommonLogger
+ end
+
+ def setup_custom_logger(builder)
+ if logging.respond_to? :to_int
+ builder.use Rack::Logger, logging
+ else
+ builder.use Rack::Logger
+ end
+ end
+
+ def setup_protection(builder)
+ return unless protection?
+ options = Hash === protection ? protection.dup : {}
+ options[:except] = Array options[:except]
+ options[:except] += [:session_hijacking, :remote_token] unless sessions?
+ options[:reaction] ||= :drop_session
+ builder.use Rack::Protection, options
+ end
+
+ def setup_sessions(builder)
+ return unless sessions?
+ options = {}
+ options[:secret] = session_secret if session_secret?
+ options.merge! sessions.to_hash if sessions.respond_to? :to_hash
+ builder.use Rack::Session::Cookie, options
+ end
+
+ def detect_rack_handler
+ servers = Array(server)
+ servers.each do |server_name|
+ begin
+ return Rack::Handler.get(server_name.to_s)
+ rescue LoadError, NameError
+ end
+ end
+ fail "Server handler (#{servers.join(',')}) not found."
+ end
+
+ def inherited(subclass)
+ subclass.reset!
+ subclass.set :app_file, caller_files.first unless subclass.app_file?
+ super
+ end
+
+ @@mutex = Mutex.new
+ def synchronize(&block)
+ if lock?
+ @@mutex.synchronize(&block)
+ else
+ yield
+ end
+ end
+
+ public
+ CALLERS_TO_IGNORE = [ # :nodoc:
+ /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
+ /lib\/tilt.*\.rb$/, # all tilt code
+ /^\(.*\)$/, # generated code
+ /rubygems\/custom_require\.rb$/, # rubygems require hacks
+ /active_support/, # active_support require hacks
+ /bundler(\/runtime)?\.rb/, # bundler require hacks
+ /<internal:/, # internal in ruby >= 1.9.2
+ /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
+ ]
+
+ # contrary to what the comment said previously, rubinius never supported this
+ if defined?(RUBY_IGNORE_CALLERS)
+ warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
+ CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
+ end
+
+ # Like Kernel#caller but excluding certain magic entries and without
+ # line / method information; the resulting array contains filenames only.
+ def caller_files
+ cleaned_caller(1).flatten
+ end
+
+ # Like caller_files, but containing Arrays rather than strings with the
+ # first element being the file, and the second being the line.
+ def caller_locations
+ cleaned_caller 2
+ end
+
+ private
+ # used for deprecation warnings
+ def warn(message)
+ super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
+ end
+
+ # Like Kernel#caller but excluding certain magic entries
+ def cleaned_caller(keep = 3)
+ caller(1).
+ map { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
+ reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
+ end
+ end
+
+ # Fixes encoding issues by
+ # * defaulting to UTF-8
+ # * casting params to Encoding.default_external
+ #
+ # The latter might not be necessary if Rack handles it one day.
+ # Keep an eye on Rack's LH #100.
+ def force_encoding(*args) settings.force_encoding(*args) end
+ if defined? Encoding
+ def self.force_encoding(data, encoding = default_encoding)
+ return if data == settings || data.is_a?(Tempfile)
+ if data.respond_to? :force_encoding
+ data.force_encoding(encoding).encode!
+ elsif data.respond_to? :each_value
+ data.each_value { |v| force_encoding(v, encoding) }
+ elsif data.respond_to? :each
+ data.each { |v| force_encoding(v, encoding) }
+ end
+ data
+ end
+ else
+ def self.force_encoding(data, *) data end
+ end
+
+ reset!
+
+ set :environment, (ENV['RACK_ENV'] || :development).to_sym
+ set :raise_errors, Proc.new { test? }
+ set :dump_errors, Proc.new { !test? }
+ set :show_exceptions, Proc.new { development? }
+ set :sessions, false
+ set :logging, false
+ set :protection, true
+ set :method_override, false
+ set :use_code, false
+ set :default_encoding, "utf-8"
+ set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
+ settings.add_charset << /^text\//
+
+ # explicitly generating a session secret eagerly to play nice with preforking
+ begin
+ require 'securerandom'
+ set :session_secret, SecureRandom.hex(64)
+ rescue LoadError, NotImplementedError
+ # SecureRandom raises a NotImplementedError if no random device is available
+ set :session_secret, "%064x" % Kernel.rand(2**256-1)
+ end
+
+ class << self
+ alias_method :methodoverride?, :method_override?
+ alias_method :methodoverride=, :method_override=
+ end
+
+ set :run, false # start server via at-exit hook?
+ set :running, false # is the built-in server running now?
+ set :server, %w[http webrick]
+ set :bind, '0.0.0.0'
+ set :port, 4567
+
+ ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
+
+ if ruby_engine == 'macruby'
+ server.unshift 'controll_tower'
+ else
+ server.unshift 'mongrel' if ruby_engine.nil?
+ server.unshift 'puma' if ruby_engine != 'rbx'
+ server.unshift 'thin' if ruby_engine != 'jruby'
+ server.unshift 'puma' if ruby_engine == 'rbx'
+ server.unshift 'trinidad' if ruby_engine =='jruby'
+ end
+
+ set :absolute_redirects, true
+ set :prefixed_redirects, false
+ set :empty_path_info, nil
+
+ set :app_file, nil
+ set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
+ set :views, Proc.new { root && File.join(root, 'views') }
+ set :reload_templates, Proc.new { development? }
+ set :lock, false
+ set :threaded, true
+
+ set :public_folder, Proc.new { root && File.join(root, 'public') }
+ set :static, Proc.new { public_folder && File.exist?(public_folder) }
+ set :static_cache_control, false
+
+ error ::Exception do
+ response.status = 500
+ content_type 'text/html'
+ '<h1>Internal Server Error</h1>'
+ end
+
+ configure :development do
+ get '/__sinatra__/:image.png' do
+ filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
+ content_type :png
+ send_file filename
+ end
+
+ error NotFound do
+ content_type 'text/html'
+
+ (<<-HTML).gsub(/^ {8}/, '')
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <style type="text/css">
+ body { text-align:center;font-family:helvetica,arial;font-size:22px;
+ color:#888;margin:20px}
+ #c {margin:0 auto;width:500px;text-align:left}
+ </style>
+ </head>
+ <body>
+ <h2>Sinatra doesn&rsquo;t know this ditty.</h2>
+ <img src='#{uri "/__sinatra__/404.png"}'>
+ <div id="c">
+ Try this:
+ <pre>#{request.request_method.downcase} '#{request.path_info}' do\n "Hello World"\nend</pre>
+ </div>
+ </body>
+ </html>
+ HTML
+ end
+ end
+ end
+
+ # Execution context for classic style (top-level) applications. All
+ # DSL methods executed on main are delegated to this class.
+ #
+ # The Application class should not be subclassed, unless you want to
+ # inherit all settings, routes, handlers, and error pages from the
+ # top-level. Subclassing Sinatra::Base is highly recommended for
+ # modular applications.
+ class Application < Base
+ set :logging, Proc.new { ! test? }
+ set :method_override, true
+ set :run, Proc.new { ! test? }
+ set :session_secret, Proc.new { super() unless development? }
+ set :app_file, nil
+
+ def self.register(*extensions, &block) #:nodoc:
+ added_methods = extensions.map {|m| m.public_instance_methods }.flatten
+ Delegator.delegate(*added_methods)
+ super(*extensions, &block)
+ end
+ end
+
+ # Sinatra delegation mixin. Mixing this module into an object causes all
+ # methods to be delegated to the Sinatra::Application class. Used primarily
+ # at the top-level.
+ module Delegator #:nodoc:
+ def self.delegate(*methods)
+ methods.each do |method_name|
+ define_method(method_name) do |*args, &block|
+ return super(*args, &block) if respond_to? method_name
+ Delegator.target.send(method_name, *args, &block)
+ end
+ private method_name
+ end
+ end
+
+ delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
+ :before, :after, :error, :not_found, :configure, :set, :mime_type,
+ :enable, :disable, :use, :development?, :test?, :production?,
+ :helpers, :settings
+
+ class << self
+ attr_accessor :target
+ end
+
+ self.target = Application
+ end
+
+ class Wrapper
+ def initialize(stack, instance)
+ @stack, @instance = stack, instance
+ end
+
+ def settings
+ @instance.settings
+ end
+
+ def helpers
+ @instance
+ end
+
+ def call(env)
+ @stack.call(env)
+ end
+
+ def inspect
+ "#<#{@instance.class} app_file=#{settings.app_file.inspect}>"
+ end
+ end
+
+ # Create a new Sinatra application. The block is evaluated in the new app's
+ # class scope.
+ def self.new(base=Base, options={}, &block)
+ base = Class.new(base)
+ base.class_eval(&block) if block_given?
+ base
+ end
+
+ # Extend the top-level DSL with the modules provided.
+ def self.register(*extensions, &block)
+ Delegator.target.register(*extensions, &block)
+ end
+
+ # Include the helper modules provided in Sinatra's request context.
+ def self.helpers(*extensions, &block)
+ Delegator.target.helpers(*extensions, &block)
+ end
+
+ # Use the middleware for classic applications.
+ def self.use(*args, &block)
+ Delegator.target.use(*args, &block)
+ end
+end
diff --git a/samples/Ruby/wrong_shebang.rb b/samples/Ruby/wrong_shebang.rb
new file mode 100644
index 0000000..22b4804
--- /dev/null
+++ b/samples/Ruby/wrong_shebang.rb
@@ -0,0 +1,2 @@
+#!/usr/bin/env python
+puts "Not Python"