summaryrefslogtreecommitdiff
path: root/tests/examplefiles/example.rb
diff options
context:
space:
mode:
Diffstat (limited to 'tests/examplefiles/example.rb')
-rw-r--r--tests/examplefiles/example.rb1852
1 files changed, 0 insertions, 1852 deletions
diff --git a/tests/examplefiles/example.rb b/tests/examplefiles/example.rb
deleted file mode 100644
index 93f8dc2b..00000000
--- a/tests/examplefiles/example.rb
+++ /dev/null
@@ -1,1852 +0,0 @@
-module CodeRay
- module Scanners
-
-class Ruby < Scanner
-
- RESERVED_WORDS = [
- 'and', 'def', 'end', 'in', 'or', 'unless', 'begin',
- 'defined?', 'ensure', 'module', 'redo', 'super', 'until',
- 'BEGIN', 'break', 'do', 'next', 'rescue', 'then',
- 'when', 'END', 'case', 'else', 'for', 'retry',
- 'while', 'alias', 'class', 'elsif', 'if', 'not', 'return',
- 'undef', 'yield',
- ]
-
- DEF_KEYWORDS = ['def']
- MODULE_KEYWORDS = ['class', 'module']
- DEF_NEW_STATE = WordList.new(:initial).
- add(DEF_KEYWORDS, :def_expected).
- add(MODULE_KEYWORDS, :module_expected)
-
- WORDS_ALLOWING_REGEXP = [
- 'and', 'or', 'not', 'while', 'until', 'unless', 'if', 'elsif', 'when'
- ]
- REGEXP_ALLOWED = WordList.new(false).
- add(WORDS_ALLOWING_REGEXP, :set)
-
- PREDEFINED_CONSTANTS = [
- 'nil', 'true', 'false', 'self',
- 'DATA', 'ARGV', 'ARGF', '__FILE__', '__LINE__',
- ]
-
- IDENT_KIND = WordList.new(:ident).
- add(RESERVED_WORDS, :reserved).
- add(PREDEFINED_CONSTANTS, :pre_constant)
-
- METHOD_NAME = / #{IDENT} [?!]? /xo
- METHOD_NAME_EX = /
- #{METHOD_NAME} # common methods: split, foo=, empty?, gsub!
- | \*\*? # multiplication and power
- | [-+~]@? # plus, minus
- | [\/%&|^`] # division, modulo or format strings, &and, |or, ^xor, `system`
- | \[\]=? # array getter and setter
- | <=?>? | >=? # comparison, rocket operator
- | << | >> # append or shift left, shift right
- | ===? # simple equality and case equality
- /ox
- GLOBAL_VARIABLE = / \$ (?: #{IDENT} | \d+ | [~&+`'=\/,;_.<>!@0$?*":F\\] | -[a-zA-Z_0-9] ) /ox
-
- DOUBLEQ = / " [^"\#\\]* (?: (?: \#\{.*?\} | \#(?:$")? | \\. ) [^"\#\\]* )* "? /ox
- SINGLEQ = / ' [^'\\]* (?: \\. [^'\\]* )* '? /ox
- STRING = / #{SINGLEQ} | #{DOUBLEQ} /ox
- SHELL = / ` [^`\#\\]* (?: (?: \#\{.*?\} | \#(?:$`)? | \\. ) [^`\#\\]* )* `? /ox
- REGEXP = / \/ [^\/\#\\]* (?: (?: \#\{.*?\} | \#(?:$\/)? | \\. ) [^\/\#\\]* )* \/? /ox
-
- DECIMAL = /\d+(?:_\d+)*/ # doesn't recognize 09 as octal error
- OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
- HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
- BINARY = /0b[01]+(?:_[01]+)*/
-
- EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
- FLOAT = / #{DECIMAL} (?: #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? ) /
- INTEGER = /#{OCTAL}|#{HEXADECIMAL}|#{BINARY}|#{DECIMAL}/
-
- def reset
- super
- @regexp_allowed = false
- end
-
- def next_token
- return if @scanner.eos?
-
- kind = :error
- if @scanner.scan(/\s+/) # in every state
- kind = :space
- @regexp_allowed = :set if @regexp_allowed or @scanner.matched.index(?\n) # delayed flag setting
-
- elsif @state == :def_expected
- if @scanner.scan(/ (?: (?:#{IDENT}(?:\.|::))* | (?:@@?|$)? #{IDENT}(?:\.|::) ) #{METHOD_NAME_EX} /ox)
- kind = :method
- @state = :initial
- else
- @scanner.getch
- end
- @state = :initial
-
- elsif @state == :module_expected
- if @scanner.scan(/<</)
- kind = :operator
- else
- if @scanner.scan(/ (?: #{IDENT} (?:\.|::))* #{IDENT} /ox)
- kind = :method
- else
- @scanner.getch
- end
- @state = :initial
- end
-
- elsif # state == :initial
- # IDENTIFIERS, KEYWORDS
- if @scanner.scan(GLOBAL_VARIABLE)
- kind = :global_variable
- elsif @scanner.scan(/ @@ #{IDENT} /ox)
- kind = :class_variable
- elsif @scanner.scan(/ @ #{IDENT} /ox)
- kind = :instance_variable
- elsif @scanner.scan(/ __END__\n ( (?!\#CODE\#) .* )? | \#[^\n]* | =begin(?=\s).*? \n=end(?=\s|\z)(?:[^\n]*)? /mx)
- kind = :comment
- elsif @scanner.scan(METHOD_NAME)
- if @last_token_dot
- kind = :ident
- else
- matched = @scanner.matched
- kind = IDENT_KIND[matched]
- if kind == :ident and matched =~ /^[A-Z]/
- kind = :constant
- elsif kind == :reserved
- @state = DEF_NEW_STATE[matched]
- @regexp_allowed = REGEXP_ALLOWED[matched]
- end
- end
-
- elsif @scanner.scan(STRING)
- kind = :string
- elsif @scanner.scan(SHELL)
- kind = :shell
- elsif @scanner.scan(/<<
- (?:
- ([a-zA-Z_0-9]+)
- (?: .*? ^\1$ | .* )
- |
- -([a-zA-Z_0-9]+)
- (?: .*? ^\s*\2$ | .* )
- |
- (["\'`]) (.+?) \3
- (?: .*? ^\4$ | .* )
- |
- - (["\'`]) (.+?) \5
- (?: .*? ^\s*\6$ | .* )
- )
- /mxo)
- kind = :string
- elsif @scanner.scan(/\//) and @regexp_allowed
- @scanner.unscan
- @scanner.scan(REGEXP)
- kind = :regexp
-/%(?:[Qqxrw](?:\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\\\\])(?:(?!\1)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\1)[^#\\\\])*)*\1?)|\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\s\\\\])(?:(?!\2)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\2)[^#\\\\])*)*\2?|\\\\[^#\\\\]*(?:(?:#\{.*?\}|#)[^#\\\\]*)*\\\\?)/
- elsif @scanner.scan(/:(?:#{GLOBAL_VARIABLE}|#{METHOD_NAME_EX}|#{STRING})/ox)
- kind = :symbol
- elsif @scanner.scan(/
- \? (?:
- [^\s\\]
- |
- \\ (?:M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-))? (?: \\ (?: . | [0-7]{3} | x[0-9A-Fa-f][0-9A-Fa-f] )
- )
- /mox)
- kind = :integer
-
- elsif @scanner.scan(/ [-+*\/%=<>;,|&!()\[\]{}~?] | \.\.?\.? | ::? /x)
- kind = :operator
- @regexp_allowed = :set if @scanner.matched[-1,1] =~ /[~=!<>|&^,\(\[+\-\/\*%]\z/
- elsif @scanner.scan(FLOAT)
- kind = :float
- elsif @scanner.scan(INTEGER)
- kind = :integer
- else
- @scanner.getch
- end
- end
-
- token = Token.new @scanner.matched, kind
-
- if kind == :regexp
- token.text << @scanner.scan(/[eimnosux]*/)
- end
-
- @regexp_allowed = (@regexp_allowed == :set) # delayed flag setting
-
- token
- end
-end
-
-register Ruby, 'ruby', 'rb'
-
- end
-end
-class Set
- include Enumerable
-
- # Creates a new set containing the given objects.
- def self.[](*ary)
- new(ary)
- end
-
- # Creates a new set containing the elements of the given enumerable
- # object.
- #
- # If a block is given, the elements of enum are preprocessed by the
- # given block.
- def initialize(enum = nil, &block) # :yields: o
- @hash ||= Hash.new
-
- enum.nil? and return
-
- if block
- enum.each { |o| add(block[o]) }
- else
- merge(enum)
- end
- end
-
- # Copy internal hash.
- def initialize_copy(orig)
- @hash = orig.instance_eval{@hash}.dup
- end
-
- # Returns the number of elements.
- def size
- @hash.size
- end
- alias length size
-
- # Returns true if the set contains no elements.
- def empty?
- @hash.empty?
- end
-
- # Removes all elements and returns self.
- def clear
- @hash.clear
- self
- end
-
- # Replaces the contents of the set with the contents of the given
- # enumerable object and returns self.
- def replace(enum)
- if enum.class == self.class
- @hash.replace(enum.instance_eval { @hash })
- else
- enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
- clear
- enum.each { |o| add(o) }
- end
-
- self
- end
-
- # Converts the set to an array. The order of elements is uncertain.
- def to_a
- @hash.keys
- end
-
- def flatten_merge(set, seen = Set.new)
- set.each { |e|
- if e.is_a?(Set)
- if seen.include?(e_id = e.object_id)
- raise ArgumentError, "tried to flatten recursive Set"
- end
-
- seen.add(e_id)
- flatten_merge(e, seen)
- seen.delete(e_id)
- else
- add(e)
- end
- }
-
- self
- end
- protected :flatten_merge
-
- # Returns a new set that is a copy of the set, flattening each
- # containing set recursively.
- def flatten
- self.class.new.flatten_merge(self)
- end
-
- # Equivalent to Set#flatten, but replaces the receiver with the
- # result in place. Returns nil if no modifications were made.
- def flatten!
- if detect { |e| e.is_a?(Set) }
- replace(flatten())
- else
- nil
- end
- end
-
- # Returns true if the set contains the given object.
- def include?(o)
- @hash.include?(o)
- end
- alias member? include?
-
- # Returns true if the set is a superset of the given set.
- def superset?(set)
- set.is_a?(Set) or raise ArgumentError, "value must be a set"
- return false if size < set.size
- set.all? { |o| include?(o) }
- end
-
- # Returns true if the set is a proper superset of the given set.
- def proper_superset?(set)
- set.is_a?(Set) or raise ArgumentError, "value must be a set"
- return false if size <= set.size
- set.all? { |o| include?(o) }
- end
-
- # Returns true if the set is a subset of the given set.
- def subset?(set)
- set.is_a?(Set) or raise ArgumentError, "value must be a set"
- return false if set.size < size
- all? { |o| set.include?(o) }
- end
-
- # Returns true if the set is a proper subset of the given set.
- def proper_subset?(set)
- set.is_a?(Set) or raise ArgumentError, "value must be a set"
- return false if set.size <= size
- all? { |o| set.include?(o) }
- end
-
- # Calls the given block once for each element in the set, passing
- # the element as parameter.
- def each
- @hash.each_key { |o| yield(o) }
- self
- end
-
- # Adds the given object to the set and returns self. Use +merge+ to
- # add several elements at once.
- def add(o)
- @hash[o] = true
- self
- end
- alias << add
-
- # Adds the given object to the set and returns self. If the
- # object is already in the set, returns nil.
- def add?(o)
- if include?(o)
- nil
- else
- add(o)
- end
- end
-
- # Deletes the given object from the set and returns self. Use +subtract+ to
- # delete several items at once.
- def delete(o)
- @hash.delete(o)
- self
- end
-
- # Deletes the given object from the set and returns self. If the
- # object is not in the set, returns nil.
- def delete?(o)
- if include?(o)
- delete(o)
- else
- nil
- end
- end
-
- # Deletes every element of the set for which block evaluates to
- # true, and returns self.
- def delete_if
- @hash.delete_if { |o,| yield(o) }
- self
- end
-
- # Do collect() destructively.
- def collect!
- set = self.class.new
- each { |o| set << yield(o) }
- replace(set)
- end
- alias map! collect!
-
- # Equivalent to Set#delete_if, but returns nil if no changes were
- # made.
- def reject!
- n = size
- delete_if { |o| yield(o) }
- size == n ? nil : self
- end
-
- # Merges the elements of the given enumerable object to the set and
- # returns self.
- def merge(enum)
- if enum.is_a?(Set)
- @hash.update(enum.instance_eval { @hash })
- else
- enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
- enum.each { |o| add(o) }
- end
-
- self
- end
-
- # Deletes every element that appears in the given enumerable object
- # and returns self.
- def subtract(enum)
- enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
- enum.each { |o| delete(o) }
- self
- end
-
- # Returns a new set built by merging the set and the elements of the
- # given enumerable object.
- def |(enum)
- enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
- dup.merge(enum)
- end
- alias + | ##
- alias union | ##
-
- # Returns a new set built by duplicating the set, removing every
- # element that appears in the given enumerable object.
- def -(enum)
- enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
- dup.subtract(enum)
- end
- alias difference - ##
-
- # Returns a new array containing elements common to the set and the
- # given enumerable object.
- def &(enum)
- enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
- n = self.class.new
- enum.each { |o| n.add(o) if include?(o) }
- n
- end
- alias intersection & ##
-
- # Returns a new array containing elements exclusive between the set
- # and the given enumerable object. (set ^ enum) is equivalent to
- # ((set | enum) - (set & enum)).
- def ^(enum)
- enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
- n = dup
- enum.each { |o| if n.include?(o) then n.delete(o) else n.add(o) end }
- n
- end
-
- # Returns true if two sets are equal. The equality of each couple
- # of elements is defined according to Object#eql?.
- def ==(set)
- equal?(set) and return true
-
- set.is_a?(Set) && size == set.size or return false
-
- hash = @hash.dup
- set.all? { |o| hash.include?(o) }
- end
-
- def hash # :nodoc:
- @hash.hash
- end
-
- def eql?(o) # :nodoc:
- return false unless o.is_a?(Set)
- @hash.eql?(o.instance_eval{@hash})
- end
-
- # Classifies the set by the return value of the given block and
- # returns a hash of {value => set of elements} pairs. The block is
- # called once for each element of the set, passing the element as
- # parameter.
- #
- # e.g.:
- #
- # require 'set'
- # files = Set.new(Dir.glob("*.rb"))
- # hash = files.classify { |f| File.mtime(f).year }
- # p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>,
- # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
- # # 2002=>#<Set: {"f.rb"}>}
- def classify # :yields: o
- h = {}
-
- each { |i|
- x = yield(i)
- (h[x] ||= self.class.new).add(i)
- }
-
- h
- end
-
- # Divides the set into a set of subsets according to the commonality
- # defined by the given block.
- #
- # If the arity of the block is 2, elements o1 and o2 are in common
- # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are
- # in common if block.call(o1) == block.call(o2).
- #
- # e.g.:
- #
- # require 'set'
- # numbers = Set[1, 3, 4, 6, 9, 10, 11]
- # set = numbers.divide { |i,j| (i - j).abs == 1 }
- # p set # => #<Set: {#<Set: {1}>,
- # # #<Set: {11, 9, 10}>,
- # # #<Set: {3, 4}>,
- # # #<Set: {6}>}>
- def divide(&func)
- if func.arity == 2
- require 'tsort'
-
- class << dig = {} # :nodoc:
- include TSort
-
- alias tsort_each_node each_key
- def tsort_each_child(node, &block)
- fetch(node).each(&block)
- end
- end
-
- each { |u|
- dig[u] = a = []
- each{ |v| func.call(u, v) and a << v }
- }
-
- set = Set.new()
- dig.each_strongly_connected_component { |css|
- set.add(self.class.new(css))
- }
- set
- else
- Set.new(classify(&func).values)
- end
- end
-
- InspectKey = :__inspect_key__ # :nodoc:
-
- # Returns a string containing a human-readable representation of the
- # set. ("#<Set: {element1, element2, ...}>")
- def inspect
- ids = (Thread.current[InspectKey] ||= [])
-
- if ids.include?(object_id)
- return sprintf('#<%s: {...}>', self.class.name)
- end
-
- begin
- ids << object_id
- return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2])
- ensure
- ids.pop
- end
- end
-
- def pretty_print(pp) # :nodoc:
- pp.text sprintf('#<%s: {', self.class.name)
- pp.nest(1) {
- pp.seplist(self) { |o|
- pp.pp o
- }
- }
- pp.text "}>"
- end
-
- def pretty_print_cycle(pp) # :nodoc:
- pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...')
- end
-end
-
-# SortedSet implements a set which elements are sorted in order. See Set.
-class SortedSet < Set
- @@setup = false
-
- class << self
- def [](*ary) # :nodoc:
- new(ary)
- end
-
- def setup # :nodoc:
- @@setup and return
-
- begin
- require 'rbtree'
-
- module_eval %{
- def initialize(*args, &block)
- @hash = RBTree.new
- super
- end
- }
- rescue LoadError
- module_eval %{
- def initialize(*args, &block)
- @keys = nil
- super
- end
-
- def clear
- @keys = nil
- super
- end
-
- def replace(enum)
- @keys = nil
- super
- end
-
- def add(o)
- @keys = nil
- @hash[o] = true
- self
- end
- alias << add
-
- def delete(o)
- @keys = nil
- @hash.delete(o)
- self
- end
-
- def delete_if
- n = @hash.size
- @hash.delete_if { |o,| yield(o) }
- @keys = nil if @hash.size != n
- self
- end
-
- def merge(enum)
- @keys = nil
- super
- end
-
- def each
- to_a.each { |o| yield(o) }
- end
-
- def to_a
- (@keys = @hash.keys).sort! unless @keys
- @keys
- end
- }
- end
-
- @@setup = true
- end
- end
-
- def initialize(*args, &block) # :nodoc:
- SortedSet.setup
- initialize(*args, &block)
- end
-end
-
-module Enumerable
- # Makes a set from the enumerable object with given arguments.
- def to_set(klass = Set, *args, &block)
- klass.new(self, *args, &block)
- end
-end
-
-# =begin
-# == RestricedSet class
-# RestricedSet implements a set with restrictions defined by a given
-# block.
-#
-# === Super class
-# Set
-#
-# === Class Methods
-# --- RestricedSet::new(enum = nil) { |o| ... }
-# --- RestricedSet::new(enum = nil) { |rset, o| ... }
-# Creates a new restricted set containing the elements of the given
-# enumerable object. Restrictions are defined by the given block.
-#
-# If the block's arity is 2, it is called with the RestrictedSet
-# itself and an object to see if the object is allowed to be put in
-# the set.
-#
-# Otherwise, the block is called with an object to see if the object
-# is allowed to be put in the set.
-#
-# === Instance Methods
-# --- restriction_proc
-# Returns the restriction procedure of the set.
-#
-# =end
-#
-# class RestricedSet < Set
-# def initialize(*args, &block)
-# @proc = block or raise ArgumentError, "missing a block"
-#
-# if @proc.arity == 2
-# instance_eval %{
-# def add(o)
-# @hash[o] = true if @proc.call(self, o)
-# self
-# end
-# alias << add
-#
-# def add?(o)
-# if include?(o) || !@proc.call(self, o)
-# nil
-# else
-# @hash[o] = true
-# self
-# end
-# end
-#
-# def replace(enum)
-# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
-# clear
-# enum.each { |o| add(o) }
-#
-# self
-# end
-#
-# def merge(enum)
-# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
-# enum.each { |o| add(o) }
-#
-# self
-# end
-# }
-# else
-# instance_eval %{
-# def add(o)
-# if @proc.call(o)
-# @hash[o] = true
-# end
-# self
-# end
-# alias << add
-#
-# def add?(o)
-# if include?(o) || !@proc.call(o)
-# nil
-# else
-# @hash[o] = true
-# self
-# end
-# end
-# }
-# end
-#
-# super(*args)
-# end
-#
-# def restriction_proc
-# @proc
-# end
-# end
-
-if $0 == __FILE__
- eval DATA.read, nil, $0, __LINE__+4
-end
-
-# = rweb - CGI Support Library
-#
-# Author:: Johannes Barre (mailto:rweb@igels.net)
-# Copyright:: Copyright (c) 2003, 04 by Johannes Barre
-# License:: GNU Lesser General Public License (COPYING, http://www.gnu.org/copyleft/lesser.html)
-# Version:: 0.1.0
-# CVS-ID:: $Id: example.rb 39 2005-11-05 03:33:55Z murphy $
-#
-# == What is Rweb?
-# Rweb is a replacement for the cgi class included in the ruby distribution.
-#
-# == How to use
-#
-# === Basics
-#
-# This class is made to be as easy as possible to use. An example:
-#
-# require "rweb"
-#
-# web = Rweb.new
-# web.out do
-# web.puts "Hello world!"
-# end
-#
-# The visitor will get a simple "Hello World!" in his browser. Please notice,
-# that won't set html-tags for you, so you should better do something like this:
-#
-# require "rweb"
-#
-# web = Rweb.new
-# web.out do
-# web.puts "<html><body>Hello world!</body></html>"
-# end
-#
-# === Set headers
-# Of course, it's also possible to tell the browser, that the content of this
-# page is plain text instead of html code:
-#
-# require "rweb"
-#
-# web = Rweb.new
-# web.out do
-# web.header("content-type: text/plain")
-# web.puts "Hello plain world!"
-# end
-#
-# Please remember, headers can't be set after the page content has been send.
-# You have to set all nessessary headers before the first puts oder print. It's
-# possible to cache the content until everything is complete. Doing it this
-# way, you can set headers everywhere.
-#
-# If you set a header twice, the second header will replace the first one. The
-# header name is not casesensitive, it will allways converted in to the
-# capitalised form suggested by the w3c (http://w3.org)
-#
-# === Set cookies
-# Setting cookies is quite easy:
-# include 'rweb'
-#
-# web = Rweb.new
-# Cookie.new("Visits", web.cookies['visits'].to_i +1)
-# web.out do
-# web.puts "Welcome back! You visited this page #{web.cookies['visits'].to_i +1} times"
-# end
-#
-# See the class Cookie for more details.
-#
-# === Get form and cookie values
-# There are four ways to submit data from the browser to the server and your
-# ruby script: via GET, POST, cookies and file upload. Rweb doesn't support
-# file upload by now.
-#
-# include 'rweb'
-#
-# web = Rweb.new
-# web.out do
-# web.print "action: #{web.get['action']} "
-# web.puts "The value of the cookie 'visits' is #{web.cookies['visits']}"
-# web.puts "The post parameter 'test['x']' is #{web.post['test']['x']}"
-# end
-
-RWEB_VERSION = "0.1.0"
-RWEB = "rweb/#{RWEB_VERSION}"
-
-#require 'rwebcookie' -> edit by bunny :-)
-
-class Rweb
- # All parameter submitted via the GET method are available in attribute
- # get. This is Hash, where every parameter is available as a key-value
- # pair.
- #
- # If your input tag has a name like this one, it's value will be available
- # as web.get["fieldname"]
- # <input name="fieldname">
- # You can submit values as a Hash
- # <input name="text['index']">
- # <input name="text['index2']">
- # will be available as
- # web.get["text"]["index"]
- # web.get["text"]["index2"]
- # Integers are also possible
- # <input name="int[2]">
- # <input name="int[3]['hi']>
- # will be available as
- # web.get["int"][2]
- # web.get["int"][3]["hi"]
- # If you specify no index, the lowest unused index will be used:
- # <input name="int[]"><!-- First Field -->
- # <input name="int[]"><!-- Second one -->
- # will be available as
- # web.get["int"][0] # First Field
- # web.get["int"][1] # Second one
- # Please notice, this doesn'd work like you might expect:
- # <input name="text[index]">
- # It will not be available as web.get["text"]["index"] but
- # web.get["text[index]"]
- attr_reader :get
-
- # All parameters submitted via POST are available in the attribute post. It
- # works like the get attribute.
- # <input name="text[0]">
- # will be available as
- # web.post["text"][0]
- attr_reader :post
-
- # All cookies submitted by the browser are available in cookies. This is a
- # Hash, where every cookie is a key-value pair.
- attr_reader :cookies
-
- # The name of the browser identification is submitted as USER_AGENT and
- # available in this attribute.
- attr_reader :user_agent
-
- # The IP address of the client.
- attr_reader :remote_addr
-
- # Creates a new Rweb object. This should only done once. You can set various
- # options via the settings hash.
- #
- # "cache" => true: Everything you script send to the client will be cached
- # until the end of the out block or until flush is called. This way, you
- # can modify headers and cookies even after printing something to the client.
- #
- # "safe" => level: Changes the $SAFE attribute. By default, $SAFE will be set
- # to 1. If $SAFE is already higher than this value, it won't be changed.
- #
- # "silend" => true: Normaly, Rweb adds automaticly a header like this
- # "X-Powered-By: Rweb/x.x.x (Ruby/y.y.y)". With the silend option you can
- # suppress this.
- def initialize (settings = {})
- # {{{
- @header = {}
- @cookies = {}
- @get = {}
- @post = {}
-
- # Internal attributes
- @status = nil
- @reasonPhrase = nil
- @setcookies = []
- @output_started = false;
- @output_allowed = false;
-
- @mod_ruby = false
- @env = ENV.to_hash
-
- if defined?(MOD_RUBY)
- @output_method = "mod_ruby"
- @mod_ruby = true
- elsif @env['SERVER_SOFTWARE'] =~ /^Microsoft-IIS/i
- @output_method = "nph"
- else
- @output_method = "ph"
- end
-
- unless settings.is_a?(Hash)
- raise TypeError, "settings must be a Hash"
- end
- @settings = settings
-
- unless @settings.has_key?("safe")
- @settings["safe"] = 1
- end
-
- if $SAFE < @settings["safe"]
- $SAFE = @settings["safe"]
- end
-
- unless @settings.has_key?("cache")
- @settings["cache"] = false
- end
-
- # mod_ruby sets no QUERY_STRING variable, if no GET-Parameters are given
- unless @env.has_key?("QUERY_STRING")
- @env["QUERY_STRING"] = ""
- end
-
- # Now we split the QUERY_STRING by the seperators & and ; or, if
- # specified, settings['get seperator']
- unless @settings.has_key?("get seperator")
- get_args = @env['QUERY_STRING'].split(/[&;]/)
- else
- get_args = @env['QUERY_STRING'].split(@settings['get seperator'])
- end
-
- get_args.each do | arg |
- arg_key, arg_val = arg.split(/=/, 2)
- arg_key = Rweb::unescape(arg_key)
- arg_val = Rweb::unescape(arg_val)
-
- # Parse names like name[0], name['text'] or name[]
- pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
- keys = []
- while match = pattern.match(arg_key)
- arg_key = match[1]
- keys = [match[2]] + keys
- end
- keys = [arg_key] + keys
-
- akt = @get
- last = nil
- lastkey = nil
- keys.each do |key|
- if key == ""
- # No key specified (like in "test[]"), so we use the
- # lowerst unused Integer as key
- key = 0
- while akt.has_key?(key)
- key += 1
- end
- elsif /^[0-9]*$/ =~ key
- # If the index is numerical convert it to an Integer
- key = key.to_i
- elsif key[0].chr == "'" || key[0].chr == '"'
- key = key[1, key.length() -2]
- end
- if !akt.has_key?(key) || !akt[key].class == Hash
- # create an empty Hash if there isn't already one
- akt[key] = {}
- end
- last = akt
- lastkey = key
- akt = akt[key]
- end
- last[lastkey] = arg_val
- end
-
- if @env['REQUEST_METHOD'] == "POST"
- if @env.has_key?("CONTENT_TYPE") && @env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" && @env.has_key?('CONTENT_LENGTH')
- unless @settings.has_key?("post seperator")
- post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(/[&;]/)
- else
- post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(@settings['post seperator'])
- end
- post_args.each do | arg |
- arg_key, arg_val = arg.split(/=/, 2)
- arg_key = Rweb::unescape(arg_key)
- arg_val = Rweb::unescape(arg_val)
-
- # Parse names like name[0], name['text'] or name[]
- pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
- keys = []
- while match = pattern.match(arg_key)
- arg_key = match[1]
- keys = [match[2]] + keys
- end
- keys = [arg_key] + keys
-
- akt = @post
- last = nil
- lastkey = nil
- keys.each do |key|
- if key == ""
- # No key specified (like in "test[]"), so we use
- # the lowerst unused Integer as key
- key = 0
- while akt.has_key?(key)
- key += 1
- end
- elsif /^[0-9]*$/ =~ key
- # If the index is numerical convert it to an Integer
- key = key.to_i
- elsif key[0].chr == "'" || key[0].chr == '"'
- key = key[1, key.length() -2]
- end
- if !akt.has_key?(key) || !akt[key].class == Hash
- # create an empty Hash if there isn't already one
- akt[key] = {}
- end
- last = akt
- lastkey = key
- akt = akt[key]
- end
- last[lastkey] = arg_val
- end
- else
- # Maybe we should print a warning here?
- $stderr.print("Unidentified form data recived and discarded.")
- end
- end
-
- if @env.has_key?("HTTP_COOKIE")
- cookie = @env['HTTP_COOKIE'].split(/; ?/)
- cookie.each do | c |
- cookie_key, cookie_val = c.split(/=/, 2)
-
- @cookies [Rweb::unescape(cookie_key)] = Rweb::unescape(cookie_val)
- end
- end
-
- if defined?(@env['HTTP_USER_AGENT'])
- @user_agent = @env['HTTP_USER_AGENT']
- else
- @user_agent = nil;
- end
-
- if defined?(@env['REMOTE_ADDR'])
- @remote_addr = @env['REMOTE_ADDR']
- else
- @remote_addr = nil
- end
- # }}}
- end
-
- # Prints a String to the client. If caching is enabled, the String will
- # buffered until the end of the out block ends.
- def print(str = "")
- # {{{
- unless @output_allowed
- raise "You just can write to output inside of a Rweb::out-block"
- end
-
- if @settings["cache"]
- @buffer += [str.to_s]
- else
- unless @output_started
- sendHeaders
- end
- $stdout.print(str)
- end
- nil
- # }}}
- end
-
- # Prints a String to the client and adds a line break at the end. Please
- # remember, that a line break is not visible in HTML, use the <br> HTML-Tag
- # for this. If caching is enabled, the String will buffered until the end
- # of the out block ends.
- def puts(str = "")
- # {{{
- self.print(str + "\n")
- # }}}
- end
-
- # Alias to print.
- def write(str = "")
- # {{{
- self.print(str)
- # }}}
- end
-
- # If caching is enabled, all cached data are send to the cliend and the
- # cache emptied.
- def flush
- # {{{
- unless @output_allowed
- raise "You can't use flush outside of a Rweb::out-block"
- end
- buffer = @buffer.join
-
- unless @output_started
- sendHeaders
- end
- $stdout.print(buffer)
-
- @buffer = []
- # }}}
- end
-
- # Sends one or more header to the client. All headers are cached just
- # before body data are send to the client. If the same header are set
- # twice, only the last value is send.
- #
- # Example:
- # web.header("Last-Modified: Mon, 16 Feb 2004 20:15:41 GMT")
- # web.header("Location: http://www.ruby-lang.org")
- #
- # You can specify more than one header at the time by doing something like
- # this:
- # web.header("Content-Type: text/plain\nContent-Length: 383")
- # or
- # web.header(["Content-Type: text/plain", "Content-Length: 383"])
- def header(str)
- # {{{
- if @output_started
- raise "HTTP-Headers are already send. You can't change them after output has started!"
- end
- unless @output_allowed
- raise "You just can set headers inside of a Rweb::out-block"
- end
- if str.is_a?Array
- str.each do | value |
- self.header(value)
- end
-
- elsif str.split(/\n/).length > 1
- str.split(/\n/).each do | value |
- self.header(value)
- end
-
- elsif str.is_a? String
- str.gsub!(/\r/, "")
-
- if (str =~ /^HTTP\/1\.[01] [0-9]{3} ?.*$/) == 0
- pattern = /^HTTP\/1.[01] ([0-9]{3}) ?(.*)$/
-
- result = pattern.match(str)
- self.setstatus(result[0], result[1])
- elsif (str =~ /^status: [0-9]{3} ?.*$/i) == 0
- pattern = /^status: ([0-9]{3}) ?(.*)$/i
-
- result = pattern.match(str)
- self.setstatus(result[0], result[1])
- else
- a = str.split(/: ?/, 2)
-
- @header[a[0].downcase] = a[1]
- end
- end
- # }}}
- end
-
- # Changes the status of this page. There are several codes like "200 OK",
- # "302 Found", "404 Not Found" or "500 Internal Server Error". A list of
- # all codes is available at
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10
- #
- # You can just send the code number, the reason phrase will be added
- # automaticly with the recommendations from the w3c if not specified. If
- # you set the status twice or more, only the last status will be send.
- # Examples:
- # web.status("401 Unauthorized")
- # web.status("410 Sad but true, this lonely page is gone :(")
- # web.status(206)
- # web.status("400")
- #
- # The default status is "200 OK". If a "Location" header is set, the
- # default status is "302 Found".
- def status(str)
- # {{{
- if @output_started
- raise "HTTP-Headers are already send. You can't change them after output has started!"
- end
- unless @output_allowed
- raise "You just can set headers inside of a Rweb::out-block"
- end
- if str.is_a?Integer
- @status = str
- elsif str.is_a?String
- p1 = /^([0-9]{3}) ?(.*)$/
- p2 = /^HTTP\/1\.[01] ([0-9]{3}) ?(.*)$/
- p3 = /^status: ([0-9]{3}) ?(.*)$/i
-
- if (a = p1.match(str)) == nil
- if (a = p2.match(str)) == nil
- if (a = p3.match(str)) == nil
- raise ArgumentError, "Invalid argument", caller
- end
- end
- end
- @status = a[1].to_i
- if a[2] != ""
- @reasonPhrase = a[2]
- else
- @reasonPhrase = getReasonPhrase(@status)
- end
- else
- raise ArgumentError, "Argument of setstatus must be integer or string", caller
- end
- # }}}
- end
-
- # Handles the output of your content and rescues all exceptions. Send all
- # data in the block to this method. For example:
- # web.out do
- # web.header("Content-Type: text/plain")
- # web.puts("Hello, plain world!")
- # end
- def out
- # {{{
- @output_allowed = true
- @buffer = []; # We use an array as buffer, because it's more performant :)
-
- begin
- yield
- rescue Exception => exception
- $stderr.puts "Ruby exception rescued (#{exception.class}): #{exception.message}"
- $stderr.puts exception.backtrace.join("\n")
-
- unless @output_started
- self.setstatus(500)
- @header = {}
- end
-
- unless (@settings.has_key?("hide errors") and @settings["hide errors"] == true)
- unless @output_started
- self.header("Content-Type: text/html")
- self.puts "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
- self.puts "<html>"
- self.puts "<head>"
- self.puts "<title>500 Internal Server Error</title>"
- self.puts "</head>"
- self.puts "<body>"
- end
- if @header.has_key?("content-type") and (@header["content-type"] =~ /^text\/html/i) == 0
- self.puts "<h1>Internal Server Error</h1>"
- self.puts "<p>The server encountered an exception and was unable to complete your request.</p>"
- self.puts "<p>The exception has provided the following information:</p>"
- self.puts "<pre style=\"background: #FFCCCC; border: black solid 2px; margin-left: 2cm; margin-right: 2cm; padding: 2mm;\"><b>#{exception.class}</b>: #{exception.message} <b>on</b>"
- self.puts
- self.puts "#{exception.backtrace.join("\n")}</pre>"
- self.puts "</body>"
- self.puts "</html>"
- else
- self.puts "The server encountered an exception and was unable to complete your request"
- self.puts "The exception has provided the following information:"
- self.puts "#{exception.class}: #{exception.message}"
- self.puts
- self.puts exception.backtrace.join("\n")
- end
- end
- end
-
- if @settings["cache"]
- buffer = @buffer.join
-
- unless @output_started
- unless @header.has_key?("content-length")
- self.header("content-length: #{buffer.length}")
- end
-
- sendHeaders
- end
- $stdout.print(buffer)
- elsif !@output_started
- sendHeaders
- end
- @output_allowed = false;
- # }}}
- end
-
- # Decodes URL encoded data, %20 for example stands for a space.
- def Rweb.unescape(str)
- # {{{
- if defined? str and str.is_a? String
- str.gsub!(/\+/, " ")
- str.gsub(/%.{2}/) do | s |
- s[1,2].hex.chr
- end
- end
- # }}}
- end
-
- protected
- def sendHeaders
- # {{{
-
- Cookie.disallow # no more cookies can be set or modified
- if !(@settings.has_key?("silent") and @settings["silent"] == true) and !@header.has_key?("x-powered-by")
- if @mod_ruby
- header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION}, #{MOD_RUBY})");
- else
- header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION})");
- end
- end
-
- if @output_method == "ph"
- if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
- header("content-type: text/html")
- end
-
- if @status != nil
- $stdout.print "Status: #{@status} #{@reasonPhrase}\r\n"
- end
-
- @header.each do |key, value|
- key = key *1 # "unfreeze" key :)
- key[0] = key[0,1].upcase![0]
-
- key = key.gsub(/-[a-z]/) do |char|
- "-" + char[1,1].upcase
- end
-
- $stdout.print "#{key}: #{value}\r\n"
- end
- cookies = Cookie.getHttpHeader # Get all cookies as an HTTP Header
- if cookies
- $stdout.print cookies
- end
-
- $stdout.print "\r\n"
-
- elsif @output_method == "nph"
- elsif @output_method == "mod_ruby"
- r = Apache.request
-
- if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
- header("text/html")
- end
-
- if @status != nil
- r.status_line = "#{@status} #{@reasonPhrase}"
- end
-
- r.send_http_header
- @header.each do |key, value|
- key = key *1 # "unfreeze" key :)
-
- key[0] = key[0,1].upcase![0]
- key = key.gsub(/-[a-z]/) do |char|
- "-" + char[1,1].upcase
- end
- puts "#{key}: #{value.class}"
- #r.headers_out[key] = value
- end
- end
- @output_started = true
- # }}}
- end
-
- def getReasonPhrase (status)
- # {{{
- if status == 100
- "Continue"
- elsif status == 101
- "Switching Protocols"
- elsif status == 200
- "OK"
- elsif status == 201
- "Created"
- elsif status == 202
- "Accepted"
- elsif status == 203
- "Non-Authoritative Information"
- elsif status == 204
- "No Content"
- elsif status == 205
- "Reset Content"
- elsif status == 206
- "Partial Content"
- elsif status == 300
- "Multiple Choices"
- elsif status == 301
- "Moved Permanently"
- elsif status == 302
- "Found"
- elsif status == 303
- "See Other"
- elsif status == 304
- "Not Modified"
- elsif status == 305
- "Use Proxy"
- elsif status == 307
- "Temporary Redirect"
- elsif status == 400
- "Bad Request"
- elsif status == 401
- "Unauthorized"
- elsif status == 402
- "Payment Required"
- elsif status == 403
- "Forbidden"
- elsif status == 404
- "Not Found"
- elsif status == 405
- "Method Not Allowed"
- elsif status == 406
- "Not Acceptable"
- elsif status == 407
- "Proxy Authentication Required"
- elsif status == 408
- "Request Time-out"
- elsif status == 409
- "Conflict"
- elsif status == 410
- "Gone"
- elsif status == 411
- "Length Required"
- elsif status == 412
- "Precondition Failed"
- elsif status == 413
- "Request Entity Too Large"
- elsif status == 414
- "Request-URI Too Large"
- elsif status == 415
- "Unsupported Media Type"
- elsif status == 416
- "Requested range not satisfiable"
- elsif status == 417
- "Expectation Failed"
- elsif status == 500
- "Internal Server Error"
- elsif status == 501
- "Not Implemented"
- elsif status == 502
- "Bad Gateway"
- elsif status == 503
- "Service Unavailable"
- elsif status == 504
- "Gateway Time-out"
- elsif status == 505
- "HTTP Version not supported"
- else
- raise "Unknown Statuscode. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 for more information."
- end
- # }}}
- end
-end
-
-class Cookie
- attr_reader :name, :value, :maxage, :path, :domain, :secure, :comment
-
- # Sets a cookie. Please see below for details of the attributes.
- def initialize (name, value = nil, maxage = nil, path = nil, domain = nil, secure = false)
- # {{{
- # HTTP headers (Cookies are a HTTP header) can only set, while no content
- # is send. So an exception will be raised, when @@allowed is set to false
- # and a new cookie has set.
- unless defined?(@@allowed)
- @@allowed = true
- end
- unless @@allowed
- raise "You can't set cookies after the HTTP headers are send."
- end
-
- unless defined?(@@list)
- @@list = []
- end
- @@list += [self]
-
- unless defined?(@@type)
- @@type = "netscape"
- end
-
- unless name.class == String
- raise TypeError, "The name of a cookie must be a string", caller
- end
- if value.class.superclass == Integer || value.class == Float
- value = value.to_s
- elsif value.class != String && value != nil
- raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
- end
- if maxage.class == Time
- maxage = maxage - Time.now
- elsif !maxage.class.superclass == Integer || !maxage == nil
- raise TypeError, "The maxage date of a cookie must be an Integer or Time object or nil.", caller
- end
- unless path.class == String || path == nil
- raise TypeError, "The path of a cookie must be nil or a string", caller
- end
- unless domain.class == String || domain == nil
- raise TypeError, "The value of a cookie must be nil or a string", caller
- end
- unless secure == true || secure == false
- raise TypeError, "The secure field of a cookie must be true or false", caller
- end
-
- @name, @value, @maxage, @path, @domain, @secure = name, value, maxage, path, domain, secure
- @comment = nil
- # }}}
- end
-
- # Modifies the value of this cookie. The information you want to store. If the
- # value is nil, the cookie will be deleted by the client.
- #
- # This attribute can be a String, Integer or Float object or nil.
- def value=(value)
- # {{{
- if value.class.superclass == Integer || value.class == Float
- value = value.to_s
- elsif value.class != String && value != nil
- raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
- end
- @value = value
- # }}}
- end
-
- # Modifies the maxage of this cookie. This attribute defines the lifetime of
- # the cookie, in seconds. A value of 0 means the cookie should be discarded
- # imediatly. If it set to nil, the cookie will be deleted when the browser
- # will be closed.
- #
- # Attention: This is different from other implementations like PHP, where you
- # gives the seconds since 1/1/1970 0:00:00 GMT.
- #
- # This attribute must be an Integer or Time object or nil.
- def maxage=(maxage)
- # {{{
- if maxage.class == Time
- maxage = maxage - Time.now
- elsif maxage.class.superclass == Integer || !maxage == nil
- raise TypeError, "The maxage of a cookie must be an Interger or Time object or nil.", caller
- end
- @maxage = maxage
- # }}}
- end
-
- # Modifies the path value of this cookie. The client will send this cookie
- # only, if the requested document is this directory or a subdirectory of it.
- #
- # The value of the attribute must be a String object or nil.
- def path=(path)
- # {{{
- unless path.class == String || path == nil
- raise TypeError, "The path of a cookie must be nil or a string", caller
- end
- @path = path
- # }}}
- end
-
- # Modifies the domain value of this cookie. The client will send this cookie
- # only if it's connected with this domain (or a subdomain, if the first
- # character is a dot like in ".ruby-lang.org")
- #
- # The value of this attribute must be a String or nil.
- def domain=(domain)
- # {{{
- unless domain.class == String || domain == nil
- raise TypeError, "The domain of a cookie must be a String or nil.", caller
- end
- @domain = domain
- # }}}
- end
-
- # Modifies the secure flag of this cookie. If it's true, the client will only
- # send this cookie if it is secured connected with us.
- #
- # The value od this attribute has to be true or false.
- def secure=(secure)
- # {{{
- unless secure == true || secure == false
- raise TypeError, "The secure field of a cookie must be true or false", caller
- end
- @secure = secure
- # }}}
- end
-
- # Modifies the comment value of this cookie. The comment won't be send, if
- # type is "netscape".
- def comment=(comment)
- # {{{
- unless comment.class == String || comment == nil
- raise TypeError, "The comment of a cookie must be a string or nil", caller
- end
- @comment = comment
- # }}}
- end
-
- # Changes the type of all cookies.
- # Allowed values are RFC2109 and netscape (default).
- def Cookie.type=(type)
- # {{{
- unless @@allowed
- raise "The cookies are allready send, so you can't change the type anymore."
- end
- unless type.downcase == "rfc2109" && type.downcase == "netscape"
- raise "The type of the cookies must be \"RFC2109\" or \"netscape\"."
- end
- @@type = type;
- # }}}
- end
-
- # After sending this message, no cookies can be set or modified. Use it, when
- # HTTP-Headers are send. Rweb does this for you.
- def Cookie.disallow
- # {{{
- @@allowed = false
- true
- # }}}
- end
-
- # Returns a HTTP header (type String) with all cookies. Rweb does this for
- # you.
- def Cookie.getHttpHeader
- # {{{
- if defined?(@@list)
- if @@type == "netscape"
- str = ""
- @@list.each do |cookie|
- if cookie.value == nil
- cookie.maxage = 0
- cookie.value = ""
- end
- # TODO: Name and value should be escaped!
- str += "Set-Cookie: #{cookie.name}=#{cookie.value}"
- unless cookie.maxage == nil
- expire = Time.now + cookie.maxage
- expire.gmtime
- str += "; Expire=#{expire.strftime("%a, %d-%b-%Y %H:%M:%S %Z")}"
- end
- unless cookie.domain == nil
- str += "; Domain=#{cookie.domain}"
- end
- unless cookie.path == nil
- str += "; Path=#{cookie.path}"
- end
- if cookie.secure
- str += "; Secure"
- end
- str += "\r\n"
- end
- return str
- else # type == "RFC2109"
- str = "Set-Cookie: "
- comma = false;
-
- @@list.each do |cookie|
- if cookie.value == nil
- cookie.maxage = 0
- cookie.value = ""
- end
- if comma
- str += ","
- end
- comma = true
-
- str += "#{cookie.name}=\"#{cookie.value}\""
- unless cookie.maxage == nil
- str += "; Max-Age=\"#{cookie.maxage}\""
- end
- unless cookie.domain == nil
- str += "; Domain=\"#{cookie.domain}\""
- end
- unless cookie.path == nil
- str += "; Path=\"#{cookie.path}\""
- end
- if cookie.secure
- str += "; Secure"
- end
- unless cookie.comment == nil
- str += "; Comment=\"#{cookie.comment}\""
- end
- str += "; Version=\"1\""
- end
- str
- end
- else
- false
- end
- # }}}
- end
-end
-
-require 'strscan'
-
-module BBCode
- DEBUG = true
-
- use 'encoder', 'tags', 'tagstack', 'smileys'
-
-=begin
- The Parser class takes care of the encoding.
- It scans the given BBCode (as plain text), finds tags
- and smilies and also makes links of urls in text.
-
- Normal text is send directly to the encoder.
-
- If a tag was found, an instance of a Tag subclass is created
- to handle the case.
-
- The @tagstack manages tag nesting and ensures valid HTML.
-=end
-
- class Parser
- class Attribute
- # flatten and use only one empty_arg
- def self.create attr
- attr = flatten attr
- return @@empty_attr if attr.empty?
- new attr
- end
-
- private_class_method :new
-
- # remove leading and trailing whitespace; concat lines
- def self.flatten attr
- attr.strip.gsub(/\n/, ' ')
- # -> ^ and $ can only match at begin and end now
- end
-
- ATTRIBUTE_SCAN = /
- (?!$) # don't match at end
- \s*
- ( # $1 = key
- [^=\s\]"\\]*
- (?:
- (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? )
- [^=\s\]"\\]*
- )*
- )
- (?:
- =
- ( # $2 = value
- [^\s\]"\\]*
- (?:
- (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? )
- [^\s\]"\\]*
- )*
- )?
- )?
- \s*
- /x
-
- def self.parse source
- source = source.dup
- # empty_tag: the tag looks like [... /]
- # slice!: this deletes the \s*/] at the end
- # \s+ because [url=http://rubybb.org/forum/] is NOT an empty tag.
- # In RubyBBCode, you can use [url=http://rubybb.org/forum/ /], and this has to be
- # interpreted correctly.
- empty_tag = source.sub!(/^:/, '=') or source.slice!(/\/$/)
- debug 'PARSE: ' + source.inspect + ' => ' + empty_tag.inspect
- #-> we have now an attr that's EITHER empty OR begins and ends with non-whitespace.
-
- attr = Hash.new
- attr[:flags] = []
- source.scan(ATTRIBUTE_SCAN) { |key, value|
- if not value
- attr[:flags] << unescape(key)
- else
- next if value.empty? and key.empty?
- attr[unescape(key)] = unescape(value)
- end
- }
- debug attr.inspect
-
- return empty_tag, attr
- end
-
- def self.unescape_char esc
- esc[1]
- end
-
- def self.unquote qt
- qt[1..-1].chomp('"').gsub(/\\./) { |esc| unescape_char esc }
- end
-
- def self.unescape str
- str.gsub(/ (\\.) | (" [^"\\]* (?:\\.[^"\\]*)* "?) /x) {
- if $1
- unescape_char $1
- else
- unquote $2
- end
- }
- end
-
- include Enumerable
- def each &block
- @args.each(&block)
- end
-
- attr_reader :source, :args, :value
-
- def initialize source
- @source = source
- debug 'Attribute#new(%p)' % source
- @empty_tag, @attr = Attribute.parse source
- @value = @attr[''].to_s
- end
-
- def empty?
- self == @@empty_attr
- end
-
- def empty_tag?
- @empty_tag
- end
-
- def [] *keys
- res = @attr[*keys]
- end
-
- def flags
- attr[:flags]
- end
-
- def to_s
- @attr
- end
-
- def inspect
- 'ATTR[' + @attr.inspect + (@empty_tag ? ' | empty tag' : '') + ']'
- end
- end
- class Attribute
- @@empty_attr = new ''
- end
- end
-