PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/examplefiles/example.rb

https://bitbucket.org/alex/pygments-main
Ruby | 1852 lines | 1275 code | 179 blank | 398 comment | 148 complexity | e27bc7f31e780e6280aa425d50751d7f MD5 | raw file
Possible License(s): BSD-2-Clause

Large files files are truncated, but you can click here to view the full file

  1. module CodeRay
  2. module Scanners
  3. class Ruby < Scanner
  4. RESERVED_WORDS = [
  5. 'and', 'def', 'end', 'in', 'or', 'unless', 'begin',
  6. 'defined?', 'ensure', 'module', 'redo', 'super', 'until',
  7. 'BEGIN', 'break', 'do', 'next', 'rescue', 'then',
  8. 'when', 'END', 'case', 'else', 'for', 'retry',
  9. 'while', 'alias', 'class', 'elsif', 'if', 'not', 'return',
  10. 'undef', 'yield',
  11. ]
  12. DEF_KEYWORDS = ['def']
  13. MODULE_KEYWORDS = ['class', 'module']
  14. DEF_NEW_STATE = WordList.new(:initial).
  15. add(DEF_KEYWORDS, :def_expected).
  16. add(MODULE_KEYWORDS, :module_expected)
  17. WORDS_ALLOWING_REGEXP = [
  18. 'and', 'or', 'not', 'while', 'until', 'unless', 'if', 'elsif', 'when'
  19. ]
  20. REGEXP_ALLOWED = WordList.new(false).
  21. add(WORDS_ALLOWING_REGEXP, :set)
  22. PREDEFINED_CONSTANTS = [
  23. 'nil', 'true', 'false', 'self',
  24. 'DATA', 'ARGV', 'ARGF', '__FILE__', '__LINE__',
  25. ]
  26. IDENT_KIND = WordList.new(:ident).
  27. add(RESERVED_WORDS, :reserved).
  28. add(PREDEFINED_CONSTANTS, :pre_constant)
  29. METHOD_NAME = / #{IDENT} [?!]? /xo
  30. METHOD_NAME_EX = /
  31. #{METHOD_NAME} # common methods: split, foo=, empty?, gsub!
  32. | \*\*? # multiplication and power
  33. | [-+~]@? # plus, minus
  34. | [\/%&|^`] # division, modulo or format strings, &and, |or, ^xor, `system`
  35. | \[\]=? # array getter and setter
  36. | <=?>? | >=? # comparison, rocket operator
  37. | << | >> # append or shift left, shift right
  38. | ===? # simple equality and case equality
  39. /ox
  40. GLOBAL_VARIABLE = / \$ (?: #{IDENT} | \d+ | [~&+`'=\/,;_.<>!@0$?*":F\\] | -[a-zA-Z_0-9] ) /ox
  41. DOUBLEQ = / " [^"\#\\]* (?: (?: \#\{.*?\} | \#(?:$")? | \\. ) [^"\#\\]* )* "? /ox
  42. SINGLEQ = / ' [^'\\]* (?: \\. [^'\\]* )* '? /ox
  43. STRING = / #{SINGLEQ} | #{DOUBLEQ} /ox
  44. SHELL = / ` [^`\#\\]* (?: (?: \#\{.*?\} | \#(?:$`)? | \\. ) [^`\#\\]* )* `? /ox
  45. REGEXP = / \/ [^\/\#\\]* (?: (?: \#\{.*?\} | \#(?:$\/)? | \\. ) [^\/\#\\]* )* \/? /ox
  46. DECIMAL = /\d+(?:_\d+)*/ # doesn't recognize 09 as octal error
  47. OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
  48. HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
  49. BINARY = /0b[01]+(?:_[01]+)*/
  50. EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
  51. FLOAT = / #{DECIMAL} (?: #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? ) /
  52. INTEGER = /#{OCTAL}|#{HEXADECIMAL}|#{BINARY}|#{DECIMAL}/
  53. def reset
  54. super
  55. @regexp_allowed = false
  56. end
  57. def next_token
  58. return if @scanner.eos?
  59. kind = :error
  60. if @scanner.scan(/\s+/) # in every state
  61. kind = :space
  62. @regexp_allowed = :set if @regexp_allowed or @scanner.matched.index(?\n) # delayed flag setting
  63. elsif @state == :def_expected
  64. if @scanner.scan(/ (?: (?:#{IDENT}(?:\.|::))* | (?:@@?|$)? #{IDENT}(?:\.|::) ) #{METHOD_NAME_EX} /ox)
  65. kind = :method
  66. @state = :initial
  67. else
  68. @scanner.getch
  69. end
  70. @state = :initial
  71. elsif @state == :module_expected
  72. if @scanner.scan(/<</)
  73. kind = :operator
  74. else
  75. if @scanner.scan(/ (?: #{IDENT} (?:\.|::))* #{IDENT} /ox)
  76. kind = :method
  77. else
  78. @scanner.getch
  79. end
  80. @state = :initial
  81. end
  82. elsif # state == :initial
  83. # IDENTIFIERS, KEYWORDS
  84. if @scanner.scan(GLOBAL_VARIABLE)
  85. kind = :global_variable
  86. elsif @scanner.scan(/ @@ #{IDENT} /ox)
  87. kind = :class_variable
  88. elsif @scanner.scan(/ @ #{IDENT} /ox)
  89. kind = :instance_variable
  90. elsif @scanner.scan(/ __END__\n ( (?!\#CODE\#) .* )? | \#[^\n]* | =begin(?=\s).*? \n=end(?=\s|\z)(?:[^\n]*)? /mx)
  91. kind = :comment
  92. elsif @scanner.scan(METHOD_NAME)
  93. if @last_token_dot
  94. kind = :ident
  95. else
  96. matched = @scanner.matched
  97. kind = IDENT_KIND[matched]
  98. if kind == :ident and matched =~ /^[A-Z]/
  99. kind = :constant
  100. elsif kind == :reserved
  101. @state = DEF_NEW_STATE[matched]
  102. @regexp_allowed = REGEXP_ALLOWED[matched]
  103. end
  104. end
  105. elsif @scanner.scan(STRING)
  106. kind = :string
  107. elsif @scanner.scan(SHELL)
  108. kind = :shell
  109. elsif @scanner.scan(/<<
  110. (?:
  111. ([a-zA-Z_0-9]+)
  112. (?: .*? ^\1$ | .* )
  113. |
  114. -([a-zA-Z_0-9]+)
  115. (?: .*? ^\s*\2$ | .* )
  116. |
  117. (["\'`]) (.+?) \3
  118. (?: .*? ^\4$ | .* )
  119. |
  120. - (["\'`]) (.+?) \5
  121. (?: .*? ^\s*\6$ | .* )
  122. )
  123. /mxo)
  124. kind = :string
  125. elsif @scanner.scan(/\//) and @regexp_allowed
  126. @scanner.unscan
  127. @scanner.scan(REGEXP)
  128. kind = :regexp
  129. /%(?:[Qqxrw](?:\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\\\\])(?:(?!\1)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\1)[^#\\\\])*)*\1?)|\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\s\\\\])(?:(?!\2)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\2)[^#\\\\])*)*\2?|\\\\[^#\\\\]*(?:(?:#\{.*?\}|#)[^#\\\\]*)*\\\\?)/
  130. elsif @scanner.scan(/:(?:#{GLOBAL_VARIABLE}|#{METHOD_NAME_EX}|#{STRING})/ox)
  131. kind = :symbol
  132. elsif @scanner.scan(/
  133. \? (?:
  134. [^\s\\]
  135. |
  136. \\ (?:M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-))? (?: \\ (?: . | [0-7]{3} | x[0-9A-Fa-f][0-9A-Fa-f] )
  137. )
  138. /mox)
  139. kind = :integer
  140. elsif @scanner.scan(/ [-+*\/%=<>;,|&!()\[\]{}~?] | \.\.?\.? | ::? /x)
  141. kind = :operator
  142. @regexp_allowed = :set if @scanner.matched[-1,1] =~ /[~=!<>|&^,\(\[+\-\/\*%]\z/
  143. elsif @scanner.scan(FLOAT)
  144. kind = :float
  145. elsif @scanner.scan(INTEGER)
  146. kind = :integer
  147. else
  148. @scanner.getch
  149. end
  150. end
  151. token = Token.new @scanner.matched, kind
  152. if kind == :regexp
  153. token.text << @scanner.scan(/[eimnosux]*/)
  154. end
  155. @regexp_allowed = (@regexp_allowed == :set) # delayed flag setting
  156. token
  157. end
  158. end
  159. register Ruby, 'ruby', 'rb'
  160. end
  161. end
  162. class Set
  163. include Enumerable
  164. # Creates a new set containing the given objects.
  165. def self.[](*ary)
  166. new(ary)
  167. end
  168. # Creates a new set containing the elements of the given enumerable
  169. # object.
  170. #
  171. # If a block is given, the elements of enum are preprocessed by the
  172. # given block.
  173. def initialize(enum = nil, &block) # :yields: o
  174. @hash ||= Hash.new
  175. enum.nil? and return
  176. if block
  177. enum.each { |o| add(block[o]) }
  178. else
  179. merge(enum)
  180. end
  181. end
  182. # Copy internal hash.
  183. def initialize_copy(orig)
  184. @hash = orig.instance_eval{@hash}.dup
  185. end
  186. # Returns the number of elements.
  187. def size
  188. @hash.size
  189. end
  190. alias length size
  191. # Returns true if the set contains no elements.
  192. def empty?
  193. @hash.empty?
  194. end
  195. # Removes all elements and returns self.
  196. def clear
  197. @hash.clear
  198. self
  199. end
  200. # Replaces the contents of the set with the contents of the given
  201. # enumerable object and returns self.
  202. def replace(enum)
  203. if enum.class == self.class
  204. @hash.replace(enum.instance_eval { @hash })
  205. else
  206. enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  207. clear
  208. enum.each { |o| add(o) }
  209. end
  210. self
  211. end
  212. # Converts the set to an array. The order of elements is uncertain.
  213. def to_a
  214. @hash.keys
  215. end
  216. def flatten_merge(set, seen = Set.new)
  217. set.each { |e|
  218. if e.is_a?(Set)
  219. if seen.include?(e_id = e.object_id)
  220. raise ArgumentError, "tried to flatten recursive Set"
  221. end
  222. seen.add(e_id)
  223. flatten_merge(e, seen)
  224. seen.delete(e_id)
  225. else
  226. add(e)
  227. end
  228. }
  229. self
  230. end
  231. protected :flatten_merge
  232. # Returns a new set that is a copy of the set, flattening each
  233. # containing set recursively.
  234. def flatten
  235. self.class.new.flatten_merge(self)
  236. end
  237. # Equivalent to Set#flatten, but replaces the receiver with the
  238. # result in place. Returns nil if no modifications were made.
  239. def flatten!
  240. if detect { |e| e.is_a?(Set) }
  241. replace(flatten())
  242. else
  243. nil
  244. end
  245. end
  246. # Returns true if the set contains the given object.
  247. def include?(o)
  248. @hash.include?(o)
  249. end
  250. alias member? include?
  251. # Returns true if the set is a superset of the given set.
  252. def superset?(set)
  253. set.is_a?(Set) or raise ArgumentError, "value must be a set"
  254. return false if size < set.size
  255. set.all? { |o| include?(o) }
  256. end
  257. # Returns true if the set is a proper superset of the given set.
  258. def proper_superset?(set)
  259. set.is_a?(Set) or raise ArgumentError, "value must be a set"
  260. return false if size <= set.size
  261. set.all? { |o| include?(o) }
  262. end
  263. # Returns true if the set is a subset of the given set.
  264. def subset?(set)
  265. set.is_a?(Set) or raise ArgumentError, "value must be a set"
  266. return false if set.size < size
  267. all? { |o| set.include?(o) }
  268. end
  269. # Returns true if the set is a proper subset of the given set.
  270. def proper_subset?(set)
  271. set.is_a?(Set) or raise ArgumentError, "value must be a set"
  272. return false if set.size <= size
  273. all? { |o| set.include?(o) }
  274. end
  275. # Calls the given block once for each element in the set, passing
  276. # the element as parameter.
  277. def each
  278. @hash.each_key { |o| yield(o) }
  279. self
  280. end
  281. # Adds the given object to the set and returns self. Use +merge+ to
  282. # add several elements at once.
  283. def add(o)
  284. @hash[o] = true
  285. self
  286. end
  287. alias << add
  288. # Adds the given object to the set and returns self. If the
  289. # object is already in the set, returns nil.
  290. def add?(o)
  291. if include?(o)
  292. nil
  293. else
  294. add(o)
  295. end
  296. end
  297. # Deletes the given object from the set and returns self. Use +subtract+ to
  298. # delete several items at once.
  299. def delete(o)
  300. @hash.delete(o)
  301. self
  302. end
  303. # Deletes the given object from the set and returns self. If the
  304. # object is not in the set, returns nil.
  305. def delete?(o)
  306. if include?(o)
  307. delete(o)
  308. else
  309. nil
  310. end
  311. end
  312. # Deletes every element of the set for which block evaluates to
  313. # true, and returns self.
  314. def delete_if
  315. @hash.delete_if { |o,| yield(o) }
  316. self
  317. end
  318. # Do collect() destructively.
  319. def collect!
  320. set = self.class.new
  321. each { |o| set << yield(o) }
  322. replace(set)
  323. end
  324. alias map! collect!
  325. # Equivalent to Set#delete_if, but returns nil if no changes were
  326. # made.
  327. def reject!
  328. n = size
  329. delete_if { |o| yield(o) }
  330. size == n ? nil : self
  331. end
  332. # Merges the elements of the given enumerable object to the set and
  333. # returns self.
  334. def merge(enum)
  335. if enum.is_a?(Set)
  336. @hash.update(enum.instance_eval { @hash })
  337. else
  338. enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  339. enum.each { |o| add(o) }
  340. end
  341. self
  342. end
  343. # Deletes every element that appears in the given enumerable object
  344. # and returns self.
  345. def subtract(enum)
  346. enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  347. enum.each { |o| delete(o) }
  348. self
  349. end
  350. # Returns a new set built by merging the set and the elements of the
  351. # given enumerable object.
  352. def |(enum)
  353. enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  354. dup.merge(enum)
  355. end
  356. alias + | ##
  357. alias union | ##
  358. # Returns a new set built by duplicating the set, removing every
  359. # element that appears in the given enumerable object.
  360. def -(enum)
  361. enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  362. dup.subtract(enum)
  363. end
  364. alias difference - ##
  365. # Returns a new array containing elements common to the set and the
  366. # given enumerable object.
  367. def &(enum)
  368. enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  369. n = self.class.new
  370. enum.each { |o| n.add(o) if include?(o) }
  371. n
  372. end
  373. alias intersection & ##
  374. # Returns a new array containing elements exclusive between the set
  375. # and the given enumerable object. (set ^ enum) is equivalent to
  376. # ((set | enum) - (set & enum)).
  377. def ^(enum)
  378. enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  379. n = dup
  380. enum.each { |o| if n.include?(o) then n.delete(o) else n.add(o) end }
  381. n
  382. end
  383. # Returns true if two sets are equal. The equality of each couple
  384. # of elements is defined according to Object#eql?.
  385. def ==(set)
  386. equal?(set) and return true
  387. set.is_a?(Set) && size == set.size or return false
  388. hash = @hash.dup
  389. set.all? { |o| hash.include?(o) }
  390. end
  391. def hash # :nodoc:
  392. @hash.hash
  393. end
  394. def eql?(o) # :nodoc:
  395. return false unless o.is_a?(Set)
  396. @hash.eql?(o.instance_eval{@hash})
  397. end
  398. # Classifies the set by the return value of the given block and
  399. # returns a hash of {value => set of elements} pairs. The block is
  400. # called once for each element of the set, passing the element as
  401. # parameter.
  402. #
  403. # e.g.:
  404. #
  405. # require 'set'
  406. # files = Set.new(Dir.glob("*.rb"))
  407. # hash = files.classify { |f| File.mtime(f).year }
  408. # p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>,
  409. # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
  410. # # 2002=>#<Set: {"f.rb"}>}
  411. def classify # :yields: o
  412. h = {}
  413. each { |i|
  414. x = yield(i)
  415. (h[x] ||= self.class.new).add(i)
  416. }
  417. h
  418. end
  419. # Divides the set into a set of subsets according to the commonality
  420. # defined by the given block.
  421. #
  422. # If the arity of the block is 2, elements o1 and o2 are in common
  423. # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are
  424. # in common if block.call(o1) == block.call(o2).
  425. #
  426. # e.g.:
  427. #
  428. # require 'set'
  429. # numbers = Set[1, 3, 4, 6, 9, 10, 11]
  430. # set = numbers.divide { |i,j| (i - j).abs == 1 }
  431. # p set # => #<Set: {#<Set: {1}>,
  432. # # #<Set: {11, 9, 10}>,
  433. # # #<Set: {3, 4}>,
  434. # # #<Set: {6}>}>
  435. def divide(&func)
  436. if func.arity == 2
  437. require 'tsort'
  438. class << dig = {} # :nodoc:
  439. include TSort
  440. alias tsort_each_node each_key
  441. def tsort_each_child(node, &block)
  442. fetch(node).each(&block)
  443. end
  444. end
  445. each { |u|
  446. dig[u] = a = []
  447. each{ |v| func.call(u, v) and a << v }
  448. }
  449. set = Set.new()
  450. dig.each_strongly_connected_component { |css|
  451. set.add(self.class.new(css))
  452. }
  453. set
  454. else
  455. Set.new(classify(&func).values)
  456. end
  457. end
  458. InspectKey = :__inspect_key__ # :nodoc:
  459. # Returns a string containing a human-readable representation of the
  460. # set. ("#<Set: {element1, element2, ...}>")
  461. def inspect
  462. ids = (Thread.current[InspectKey] ||= [])
  463. if ids.include?(object_id)
  464. return sprintf('#<%s: {...}>', self.class.name)
  465. end
  466. begin
  467. ids << object_id
  468. return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2])
  469. ensure
  470. ids.pop
  471. end
  472. end
  473. def pretty_print(pp) # :nodoc:
  474. pp.text sprintf('#<%s: {', self.class.name)
  475. pp.nest(1) {
  476. pp.seplist(self) { |o|
  477. pp.pp o
  478. }
  479. }
  480. pp.text "}>"
  481. end
  482. def pretty_print_cycle(pp) # :nodoc:
  483. pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...')
  484. end
  485. end
  486. # SortedSet implements a set which elements are sorted in order. See Set.
  487. class SortedSet < Set
  488. @@setup = false
  489. class << self
  490. def [](*ary) # :nodoc:
  491. new(ary)
  492. end
  493. def setup # :nodoc:
  494. @@setup and return
  495. begin
  496. require 'rbtree'
  497. module_eval %{
  498. def initialize(*args, &block)
  499. @hash = RBTree.new
  500. super
  501. end
  502. }
  503. rescue LoadError
  504. module_eval %{
  505. def initialize(*args, &block)
  506. @keys = nil
  507. super
  508. end
  509. def clear
  510. @keys = nil
  511. super
  512. end
  513. def replace(enum)
  514. @keys = nil
  515. super
  516. end
  517. def add(o)
  518. @keys = nil
  519. @hash[o] = true
  520. self
  521. end
  522. alias << add
  523. def delete(o)
  524. @keys = nil
  525. @hash.delete(o)
  526. self
  527. end
  528. def delete_if
  529. n = @hash.size
  530. @hash.delete_if { |o,| yield(o) }
  531. @keys = nil if @hash.size != n
  532. self
  533. end
  534. def merge(enum)
  535. @keys = nil
  536. super
  537. end
  538. def each
  539. to_a.each { |o| yield(o) }
  540. end
  541. def to_a
  542. (@keys = @hash.keys).sort! unless @keys
  543. @keys
  544. end
  545. }
  546. end
  547. @@setup = true
  548. end
  549. end
  550. def initialize(*args, &block) # :nodoc:
  551. SortedSet.setup
  552. initialize(*args, &block)
  553. end
  554. end
  555. module Enumerable
  556. # Makes a set from the enumerable object with given arguments.
  557. def to_set(klass = Set, *args, &block)
  558. klass.new(self, *args, &block)
  559. end
  560. end
  561. # =begin
  562. # == RestricedSet class
  563. # RestricedSet implements a set with restrictions defined by a given
  564. # block.
  565. #
  566. # === Super class
  567. # Set
  568. #
  569. # === Class Methods
  570. # --- RestricedSet::new(enum = nil) { |o| ... }
  571. # --- RestricedSet::new(enum = nil) { |rset, o| ... }
  572. # Creates a new restricted set containing the elements of the given
  573. # enumerable object. Restrictions are defined by the given block.
  574. #
  575. # If the block's arity is 2, it is called with the RestrictedSet
  576. # itself and an object to see if the object is allowed to be put in
  577. # the set.
  578. #
  579. # Otherwise, the block is called with an object to see if the object
  580. # is allowed to be put in the set.
  581. #
  582. # === Instance Methods
  583. # --- restriction_proc
  584. # Returns the restriction procedure of the set.
  585. #
  586. # =end
  587. #
  588. # class RestricedSet < Set
  589. # def initialize(*args, &block)
  590. # @proc = block or raise ArgumentError, "missing a block"
  591. #
  592. # if @proc.arity == 2
  593. # instance_eval %{
  594. # def add(o)
  595. # @hash[o] = true if @proc.call(self, o)
  596. # self
  597. # end
  598. # alias << add
  599. #
  600. # def add?(o)
  601. # if include?(o) || !@proc.call(self, o)
  602. # nil
  603. # else
  604. # @hash[o] = true
  605. # self
  606. # end
  607. # end
  608. #
  609. # def replace(enum)
  610. # enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  611. # clear
  612. # enum.each { |o| add(o) }
  613. #
  614. # self
  615. # end
  616. #
  617. # def merge(enum)
  618. # enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
  619. # enum.each { |o| add(o) }
  620. #
  621. # self
  622. # end
  623. # }
  624. # else
  625. # instance_eval %{
  626. # def add(o)
  627. # if @proc.call(o)
  628. # @hash[o] = true
  629. # end
  630. # self
  631. # end
  632. # alias << add
  633. #
  634. # def add?(o)
  635. # if include?(o) || !@proc.call(o)
  636. # nil
  637. # else
  638. # @hash[o] = true
  639. # self
  640. # end
  641. # end
  642. # }
  643. # end
  644. #
  645. # super(*args)
  646. # end
  647. #
  648. # def restriction_proc
  649. # @proc
  650. # end
  651. # end
  652. if $0 == __FILE__
  653. eval DATA.read, nil, $0, __LINE__+4
  654. end
  655. # = rweb - CGI Support Library
  656. #
  657. # Author:: Johannes Barre (mailto:rweb@igels.net)
  658. # Copyright:: Copyright (c) 2003, 04 by Johannes Barre
  659. # License:: GNU Lesser General Public License (COPYING, http://www.gnu.org/copyleft/lesser.html)
  660. # Version:: 0.1.0
  661. # CVS-ID:: $Id: example.rb 39 2005-11-05 03:33:55Z murphy $
  662. #
  663. # == What is Rweb?
  664. # Rweb is a replacement for the cgi class included in the ruby distribution.
  665. #
  666. # == How to use
  667. #
  668. # === Basics
  669. #
  670. # This class is made to be as easy as possible to use. An example:
  671. #
  672. # require "rweb"
  673. #
  674. # web = Rweb.new
  675. # web.out do
  676. # web.puts "Hello world!"
  677. # end
  678. #
  679. # The visitor will get a simple "Hello World!" in his browser. Please notice,
  680. # that won't set html-tags for you, so you should better do something like this:
  681. #
  682. # require "rweb"
  683. #
  684. # web = Rweb.new
  685. # web.out do
  686. # web.puts "<html><body>Hello world!</body></html>"
  687. # end
  688. #
  689. # === Set headers
  690. # Of course, it's also possible to tell the browser, that the content of this
  691. # page is plain text instead of html code:
  692. #
  693. # require "rweb"
  694. #
  695. # web = Rweb.new
  696. # web.out do
  697. # web.header("content-type: text/plain")
  698. # web.puts "Hello plain world!"
  699. # end
  700. #
  701. # Please remember, headers can't be set after the page content has been send.
  702. # You have to set all nessessary headers before the first puts oder print. It's
  703. # possible to cache the content until everything is complete. Doing it this
  704. # way, you can set headers everywhere.
  705. #
  706. # If you set a header twice, the second header will replace the first one. The
  707. # header name is not casesensitive, it will allways converted in to the
  708. # capitalised form suggested by the w3c (http://w3.org)
  709. #
  710. # === Set cookies
  711. # Setting cookies is quite easy:
  712. # include 'rweb'
  713. #
  714. # web = Rweb.new
  715. # Cookie.new("Visits", web.cookies['visits'].to_i +1)
  716. # web.out do
  717. # web.puts "Welcome back! You visited this page #{web.cookies['visits'].to_i +1} times"
  718. # end
  719. #
  720. # See the class Cookie for more details.
  721. #
  722. # === Get form and cookie values
  723. # There are four ways to submit data from the browser to the server and your
  724. # ruby script: via GET, POST, cookies and file upload. Rweb doesn't support
  725. # file upload by now.
  726. #
  727. # include 'rweb'
  728. #
  729. # web = Rweb.new
  730. # web.out do
  731. # web.print "action: #{web.get['action']} "
  732. # web.puts "The value of the cookie 'visits' is #{web.cookies['visits']}"
  733. # web.puts "The post parameter 'test['x']' is #{web.post['test']['x']}"
  734. # end
  735. RWEB_VERSION = "0.1.0"
  736. RWEB = "rweb/#{RWEB_VERSION}"
  737. #require 'rwebcookie' -> edit by bunny :-)
  738. class Rweb
  739. # All parameter submitted via the GET method are available in attribute
  740. # get. This is Hash, where every parameter is available as a key-value
  741. # pair.
  742. #
  743. # If your input tag has a name like this one, it's value will be available
  744. # as web.get["fieldname"]
  745. # <input name="fieldname">
  746. # You can submit values as a Hash
  747. # <input name="text['index']">
  748. # <input name="text['index2']">
  749. # will be available as
  750. # web.get["text"]["index"]
  751. # web.get["text"]["index2"]
  752. # Integers are also possible
  753. # <input name="int[2]">
  754. # <input name="int[3]['hi']>
  755. # will be available as
  756. # web.get["int"][2]
  757. # web.get["int"][3]["hi"]
  758. # If you specify no index, the lowest unused index will be used:
  759. # <input name="int[]"><!-- First Field -->
  760. # <input name="int[]"><!-- Second one -->
  761. # will be available as
  762. # web.get["int"][0] # First Field
  763. # web.get["int"][1] # Second one
  764. # Please notice, this doesn'd work like you might expect:
  765. # <input name="text[index]">
  766. # It will not be available as web.get["text"]["index"] but
  767. # web.get["text[index]"]
  768. attr_reader :get
  769. # All parameters submitted via POST are available in the attribute post. It
  770. # works like the get attribute.
  771. # <input name="text[0]">
  772. # will be available as
  773. # web.post["text"][0]
  774. attr_reader :post
  775. # All cookies submitted by the browser are available in cookies. This is a
  776. # Hash, where every cookie is a key-value pair.
  777. attr_reader :cookies
  778. # The name of the browser identification is submitted as USER_AGENT and
  779. # available in this attribute.
  780. attr_reader :user_agent
  781. # The IP address of the client.
  782. attr_reader :remote_addr
  783. # Creates a new Rweb object. This should only done once. You can set various
  784. # options via the settings hash.
  785. #
  786. # "cache" => true: Everything you script send to the client will be cached
  787. # until the end of the out block or until flush is called. This way, you
  788. # can modify headers and cookies even after printing something to the client.
  789. #
  790. # "safe" => level: Changes the $SAFE attribute. By default, $SAFE will be set
  791. # to 1. If $SAFE is already higher than this value, it won't be changed.
  792. #
  793. # "silend" => true: Normaly, Rweb adds automaticly a header like this
  794. # "X-Powered-By: Rweb/x.x.x (Ruby/y.y.y)". With the silend option you can
  795. # suppress this.
  796. def initialize (settings = {})
  797. # {{{
  798. @header = {}
  799. @cookies = {}
  800. @get = {}
  801. @post = {}
  802. # Internal attributes
  803. @status = nil
  804. @reasonPhrase = nil
  805. @setcookies = []
  806. @output_started = false;
  807. @output_allowed = false;
  808. @mod_ruby = false
  809. @env = ENV.to_hash
  810. if defined?(MOD_RUBY)
  811. @output_method = "mod_ruby"
  812. @mod_ruby = true
  813. elsif @env['SERVER_SOFTWARE'] =~ /^Microsoft-IIS/i
  814. @output_method = "nph"
  815. else
  816. @output_method = "ph"
  817. end
  818. unless settings.is_a?(Hash)
  819. raise TypeError, "settings must be a Hash"
  820. end
  821. @settings = settings
  822. unless @settings.has_key?("safe")
  823. @settings["safe"] = 1
  824. end
  825. if $SAFE < @settings["safe"]
  826. $SAFE = @settings["safe"]
  827. end
  828. unless @settings.has_key?("cache")
  829. @settings["cache"] = false
  830. end
  831. # mod_ruby sets no QUERY_STRING variable, if no GET-Parameters are given
  832. unless @env.has_key?("QUERY_STRING")
  833. @env["QUERY_STRING"] = ""
  834. end
  835. # Now we split the QUERY_STRING by the seperators & and ; or, if
  836. # specified, settings['get seperator']
  837. unless @settings.has_key?("get seperator")
  838. get_args = @env['QUERY_STRING'].split(/[&;]/)
  839. else
  840. get_args = @env['QUERY_STRING'].split(@settings['get seperator'])
  841. end
  842. get_args.each do | arg |
  843. arg_key, arg_val = arg.split(/=/, 2)
  844. arg_key = Rweb::unescape(arg_key)
  845. arg_val = Rweb::unescape(arg_val)
  846. # Parse names like name[0], name['text'] or name[]
  847. pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
  848. keys = []
  849. while match = pattern.match(arg_key)
  850. arg_key = match[1]
  851. keys = [match[2]] + keys
  852. end
  853. keys = [arg_key] + keys
  854. akt = @get
  855. last = nil
  856. lastkey = nil
  857. keys.each do |key|
  858. if key == ""
  859. # No key specified (like in "test[]"), so we use the
  860. # lowerst unused Integer as key
  861. key = 0
  862. while akt.has_key?(key)
  863. key += 1
  864. end
  865. elsif /^[0-9]*$/ =~ key
  866. # If the index is numerical convert it to an Integer
  867. key = key.to_i
  868. elsif key[0].chr == "'" || key[0].chr == '"'
  869. key = key[1, key.length() -2]
  870. end
  871. if !akt.has_key?(key) || !akt[key].class == Hash
  872. # create an empty Hash if there isn't already one
  873. akt[key] = {}
  874. end
  875. last = akt
  876. lastkey = key
  877. akt = akt[key]
  878. end
  879. last[lastkey] = arg_val
  880. end
  881. if @env['REQUEST_METHOD'] == "POST"
  882. if @env.has_key?("CONTENT_TYPE") && @env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" && @env.has_key?('CONTENT_LENGTH')
  883. unless @settings.has_key?("post seperator")
  884. post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(/[&;]/)
  885. else
  886. post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(@settings['post seperator'])
  887. end
  888. post_args.each do | arg |
  889. arg_key, arg_val = arg.split(/=/, 2)
  890. arg_key = Rweb::unescape(arg_key)
  891. arg_val = Rweb::unescape(arg_val)
  892. # Parse names like name[0], name['text'] or name[]
  893. pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
  894. keys = []
  895. while match = pattern.match(arg_key)
  896. arg_key = match[1]
  897. keys = [match[2]] + keys
  898. end
  899. keys = [arg_key] + keys
  900. akt = @post
  901. last = nil
  902. lastkey = nil
  903. keys.each do |key|
  904. if key == ""
  905. # No key specified (like in "test[]"), so we use
  906. # the lowerst unused Integer as key
  907. key = 0
  908. while akt.has_key?(key)
  909. key += 1
  910. end
  911. elsif /^[0-9]*$/ =~ key
  912. # If the index is numerical convert it to an Integer
  913. key = key.to_i
  914. elsif key[0].chr == "'" || key[0].chr == '"'
  915. key = key[1, key.length() -2]
  916. end
  917. if !akt.has_key?(key) || !akt[key].class == Hash
  918. # create an empty Hash if there isn't already one
  919. akt[key] = {}
  920. end
  921. last = akt
  922. lastkey = key
  923. akt = akt[key]
  924. end
  925. last[lastkey] = arg_val
  926. end
  927. else
  928. # Maybe we should print a warning here?
  929. $stderr.print("Unidentified form data recived and discarded.")
  930. end
  931. end
  932. if @env.has_key?("HTTP_COOKIE")
  933. cookie = @env['HTTP_COOKIE'].split(/; ?/)
  934. cookie.each do | c |
  935. cookie_key, cookie_val = c.split(/=/, 2)
  936. @cookies [Rweb::unescape(cookie_key)] = Rweb::unescape(cookie_val)
  937. end
  938. end
  939. if defined?(@env['HTTP_USER_AGENT'])
  940. @user_agent = @env['HTTP_USER_AGENT']
  941. else
  942. @user_agent = nil;
  943. end
  944. if defined?(@env['REMOTE_ADDR'])
  945. @remote_addr = @env['REMOTE_ADDR']
  946. else
  947. @remote_addr = nil
  948. end
  949. # }}}
  950. end
  951. # Prints a String to the client. If caching is enabled, the String will
  952. # buffered until the end of the out block ends.
  953. def print(str = "")
  954. # {{{
  955. unless @output_allowed
  956. raise "You just can write to output inside of a Rweb::out-block"
  957. end
  958. if @settings["cache"]
  959. @buffer += [str.to_s]
  960. else
  961. unless @output_started
  962. sendHeaders
  963. end
  964. $stdout.print(str)
  965. end
  966. nil
  967. # }}}
  968. end
  969. # Prints a String to the client and adds a line break at the end. Please
  970. # remember, that a line break is not visible in HTML, use the <br> HTML-Tag
  971. # for this. If caching is enabled, the String will buffered until the end
  972. # of the out block ends.
  973. def puts(str = "")
  974. # {{{
  975. self.print(str + "\n")
  976. # }}}
  977. end
  978. # Alias to print.
  979. def write(str = "")
  980. # {{{
  981. self.print(str)
  982. # }}}
  983. end
  984. # If caching is enabled, all cached data are send to the cliend and the
  985. # cache emptied.
  986. def flush
  987. # {{{
  988. unless @output_allowed
  989. raise "You can't use flush outside of a Rweb::out-block"
  990. end
  991. buffer = @buffer.join
  992. unless @output_started
  993. sendHeaders
  994. end
  995. $stdout.print(buffer)
  996. @buffer = []
  997. # }}}
  998. end
  999. # Sends one or more header to the client. All headers are cached just
  1000. # before body data are send to the client. If the same header are set
  1001. # twice, only the last value is send.
  1002. #
  1003. # Example:
  1004. # web.header("Last-Modified: Mon, 16 Feb 2004 20:15:41 GMT")
  1005. # web.header("Location: http://www.ruby-lang.org")
  1006. #
  1007. # You can specify more than one header at the time by doing something like
  1008. # this:
  1009. # web.header("Content-Type: text/plain\nContent-Length: 383")
  1010. # or
  1011. # web.header(["Content-Type: text/plain", "Content-Length: 383"])
  1012. def header(str)
  1013. # {{{
  1014. if @output_started
  1015. raise "HTTP-Headers are already send. You can't change them after output has started!"
  1016. end
  1017. unless @output_allowed
  1018. raise "You just can set headers inside of a Rweb::out-block"
  1019. end
  1020. if str.is_a?Array
  1021. str.each do | value |
  1022. self.header(value)
  1023. end
  1024. elsif str.split(/\n/).length > 1
  1025. str.split(/\n/).each do | value |
  1026. self.header(value)
  1027. end
  1028. elsif str.is_a? String
  1029. str.gsub!(/\r/, "")
  1030. if (str =~ /^HTTP\/1\.[01] [0-9]{3} ?.*$/) == 0
  1031. pattern = /^HTTP\/1.[01] ([0-9]{3}) ?(.*)$/
  1032. result = pattern.match(str)
  1033. self.setstatus(result[0], result[1])
  1034. elsif (str =~ /^status: [0-9]{3} ?.*$/i) == 0
  1035. pattern = /^status: ([0-9]{3}) ?(.*)$/i
  1036. result = pattern.match(str)
  1037. self.setstatus(result[0], result[1])
  1038. else
  1039. a = str.split(/: ?/, 2)
  1040. @header[a[0].downcase] = a[1]
  1041. end
  1042. end
  1043. # }}}
  1044. end
  1045. # Changes the status of this page. There are several codes like "200 OK",
  1046. # "302 Found", "404 Not Found" or "500 Internal Server Error". A list of
  1047. # all codes is available at
  1048. # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10
  1049. #
  1050. # You can just send the code number, the reason phrase will be added
  1051. # automaticly with the recommendations from the w3c if not specified. If
  1052. # you set the status twice or more, only the last status will be send.
  1053. # Examples:
  1054. # web.status("401 Unauthorized")
  1055. # web.status("410 Sad but true, this lonely page is gone :(")
  1056. # web.status(206)
  1057. # web.status("400")
  1058. #
  1059. # The default status is "200 OK". If a "Location" header is set, the
  1060. # default status is "302 Found".
  1061. def status(str)
  1062. # {{{
  1063. if @output_started
  1064. raise "HTTP-Headers are already send. You can't change them after output has started!"
  1065. end
  1066. unless @output_allowed
  1067. raise "You just can set headers inside of a Rweb::out-block"
  1068. end
  1069. if str.is_a?Integer
  1070. @status = str
  1071. elsif str.is_a?String
  1072. p1 = /^([0-9]{3}) ?(.*)$/
  1073. p2 = /^HTTP\/1\.[01] ([0-9]{3}) ?(.*)$/
  1074. p3 = /^status: ([0-9]{3}) ?(.*)$/i
  1075. if (a = p1.match(str)) == nil
  1076. if (a = p2.match(str)) == nil
  1077. if (a = p3.match(str)) == nil
  1078. raise ArgumentError, "Invalid argument", caller
  1079. end
  1080. end
  1081. end
  1082. @status = a[1].to_i
  1083. if a[2] != ""
  1084. @reasonPhrase = a[2]
  1085. else
  1086. @reasonPhrase = getReasonPhrase(@status)
  1087. end
  1088. else
  1089. raise ArgumentError, "Argument of setstatus must be integer or string", caller
  1090. end
  1091. # }}}
  1092. end
  1093. # Handles the output of your content and rescues all exceptions. Send all
  1094. # data in the block to this method. For example:
  1095. # web.out do
  1096. # web.header("Content-Type: text/plain")
  1097. # web.puts("Hello, plain world!")
  1098. # end
  1099. def out
  1100. # {{{
  1101. @output_allowed = true
  1102. @buffer = []; # We use an array as buffer, because it's more performant :)
  1103. begin
  1104. yield
  1105. rescue Exception => exception
  1106. $stderr.puts "Ruby exception rescued (#{exception.class}): #{exception.message}"
  1107. $stderr.puts exception.backtrace.join("\n")
  1108. unless @output_started
  1109. self.setstatus(500)
  1110. @header = {}
  1111. end
  1112. unless (@settings.has_key?("hide errors") and @settings["hide errors"] == true)
  1113. unless @output_started
  1114. self.header("Content-Type: text/html")
  1115. self.puts "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
  1116. self.puts "<html>"
  1117. self.puts "<head>"
  1118. self.puts "<title>500 Internal Server Error</title>"
  1119. self.puts "</head>"
  1120. self.puts "<body>"
  1121. end
  1122. if @header.has_key?("content-type") and (@header["content-type"] =~ /^text\/html/i) == 0
  1123. self.puts "<h1>Internal Server Error</h1>"
  1124. self.puts "<p>The server encountered an exception and was unable to complete your request.</p>"
  1125. self.puts "<p>The exception has provided the following information:</p>"
  1126. 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>"
  1127. self.puts
  1128. self.puts "#{exception.backtrace.join("\n")}</pre>"
  1129. self.puts "</body>"
  1130. self.puts "</html>"
  1131. else
  1132. self.puts "The server encountered an exception and was unable to complete your request"
  1133. self.puts "The exception has provided the following information:"
  1134. self.puts "#{exception.class}: #{exception.message}"
  1135. self.puts
  1136. self.puts exception.backtrace.join("\n")
  1137. end
  1138. end
  1139. end
  1140. if @settings["cache"]
  1141. buffer = @buffer.join
  1142. unless @output_started
  1143. unless @header.has_key?("content-length")
  1144. self.header("content-length: #{buffer.length}")
  1145. end
  1146. sendHeaders
  1147. end
  1148. $stdout.print(buffer)
  1149. elsif !@output_started
  1150. sendHeaders
  1151. end
  1152. @output_allowed = false;
  1153. # }}}
  1154. end
  1155. # Decodes URL encoded data, %20 for example stands for a space.
  1156. def Rweb.unescape(str)
  1157. # {{{
  1158. if defined? str and str.is_a? String
  1159. str.gsub!(/\+/, " ")
  1160. str.gsub(/%.{2}/) do | s |
  1161. s[1,2].hex.chr
  1162. end
  1163. end
  1164. # }}}
  1165. end
  1166. protected
  1167. def sendHeaders
  1168. # {{{
  1169. Cookie.disallow # no more cookies can be set or modified
  1170. if !(@settings.has_key?("silent") and @settings["silent"] == true) and !@header.has_key?("x-powered-by")
  1171. if @mod_ruby
  1172. header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION}, #{MOD_RUBY})");
  1173. else
  1174. header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION})");
  1175. end
  1176. end
  1177. if @output_method == "ph"
  1178. if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
  1179. header("content-type: text/html")
  1180. end
  1181. if @status != nil
  1182. $stdout.print "Status: #{@status} #{@reasonPhrase}\r\n"
  1183. end
  1184. @header.each do |key, value|
  1185. key = key *1 # "unfreeze" key :)
  1186. key[0] = key[0,1].upcase![0]
  1187. key = key.gsub(/-[a-z]/) do |char|
  1188. "-" + char[1,1].upcase
  1189. end
  1190. $stdout.print "#{key}: #{value}\r\n"
  1191. end
  1192. cookies = Cookie.getHttpHeader # Get all cookies as an HTTP Header
  1193. if cookies
  1194. $stdout.print cookies
  1195. end
  1196. $stdout.print "\r\n"
  1197. elsif @output_method == "nph"
  1198. elsif @output_method == "mod_ruby"
  1199. r = Apache.request
  1200. if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
  1201. header("text/html")
  1202. end
  1203. if @status != nil
  1204. r.status_line = "#{@status} #{@reasonPhrase}"
  1205. end
  1206. r.send_http_header
  1207. @header.each do |key, value|
  1208. key = key *1 # "unfreeze" key :)
  1209. key[0] = key[0,1].upcase![0]
  1210. key = key.gsub(/-[a-z]/) do |char|
  1211. "-" + char[1,1].upcase
  1212. end
  1213. puts "#{key}: #{value.class}"
  1214. #r.headers_out[key] = value
  1215. end
  1216. end
  1217. @output_started = true
  1218. # }}}
  1219. end
  1220. def getReasonPhrase (status)
  1221. # {{{
  1222. if status == 100
  1223. "Continue"
  1224. elsif status == 101
  1225. "Switching Protocols"
  1226. elsif status == 200
  1227. "OK"
  1228. elsif status == 201
  1229. "Created"
  1230. elsif status == 202
  1231. "Accepted"
  1232. elsif status == 203
  1233. "Non-Authoritative Information"
  1234. elsif status == 204
  1235. "No Content"
  1236. elsif status == 205
  1237. "Reset Content"
  1238. elsif status == 206
  1239. "Partial Content"
  1240. elsif status == 300
  1241. "Multiple Choices"
  1242. elsif status == 301
  1243. "Moved Permanently"
  1244. elsif status == 302
  1245. "Found"
  1246. elsif status == 303
  1247. "See Other"
  1248. elsif status == 304
  1249. "Not Modified"
  1250. elsif status == 305
  1251. "Use Proxy"
  1252. elsif status == 307
  1253. "Temporary Redirect"
  1254. elsif status == 400
  1255. "Bad Request"
  1256. elsif status == 401
  1257. "Unauthorized"
  1258. elsif status == 402
  1259. "Payment Required"
  1260. elsif status == 403
  1261. "Forbidden"
  1262. elsif status == 404
  1263. "Not Found"
  1264. elsif status == 405
  1265. "Method Not Allowed"
  1266. elsif status == 406
  1267. "Not Acceptable"
  1268. elsif status == 407
  1269. "Proxy Authentication Required"
  1270. elsif status == 408
  1271. "Request Time-out"
  1272. elsif status == 409
  1273. "Conflict"
  1274. elsif status == 410
  1275. "Gone"
  1276. elsif status == 411
  1277. "Length Required"
  1278. elsif status == 412
  1279. "Precondition Failed"
  1280. elsif status == 413
  1281. "Request Entity Too Large"
  1282. elsif status == 414
  1283. "Request-URI Too Large"
  1284. elsif status == 415
  1285. "Unsupported Media Type"
  1286. elsif status == 416
  1287. "Requested range not satisfiable"
  1288. elsif status == 417
  1289. "Expectation Failed"
  1290. elsif status == 500
  1291. "Internal Server Error"
  1292. elsif status == 501
  1293. "Not Implemented"
  1294. elsif status == 502
  1295. "Bad Gateway"
  1296. elsif status == 503
  1297. "Service Unavailable"
  1298. elsif status == 504
  1299. "Gateway Time-out"
  1300. elsif status == 505
  1301. "HTTP Version not supported"
  1302. else
  1303. raise "Unknown Statuscode. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 for more information."
  1304. end
  1305. # }}}
  1306. end
  1307. end
  1308. class Cookie
  1309. attr_reader :name, :value, :maxage, :path, :domain, :secure, :comment
  1310. # Sets a cookie. Please see below for details of the attributes.
  1311. def initialize (name, value = nil, maxage = nil, path = nil, domain = nil, secure = false)
  1312. # {{{
  1313. # HTTP headers (Cookies are a HTTP header) can only set, while no content
  1314. # is send. So an exception will be raised, when @@allowed is set to false
  1315. # and a new cookie has set.
  1316. unless defined?(@@allowed)
  1317. @@allowed = true
  1318. end
  1319. unless @@allowed
  1320. raise "You can't set cookies after the HTTP headers are send."
  1321. end
  1322. unless defined?(@@list)
  1323. @@list = []
  1324. end
  1325. @@list += [self]
  1326. unless defined?(@@type)
  1327. @@type = "netscape"
  1328. end
  1329. unless name.class == String
  1330. raise TypeError, "The name of a cookie must be a string", caller
  1331. end
  1332. if value.class.superclass == Integer || value.class == Float
  1333. value = value.to_s
  1334. elsif value.class != String && value != nil
  1335. raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
  1336. end
  1337. if maxage.class == Time
  1338. maxage = maxage - Time.now
  1339. elsif !maxage.class.superclass == Integer || !maxage == nil
  1340. raise TypeError, "The maxage date of a cookie must be an Integer or Time object or nil.", caller
  1341. end
  1342. unless path.class == String || path == nil
  1343. raise TypeError, "The path of a cookie must be nil or a string", caller
  1344. end
  1345. unless domain.class == String || domain == nil
  1346. raise TypeError, "The value of a cookie must be nil or a string", caller
  1347. end
  1348. unless secure == true || secure == false
  1349. raise TypeError, "The secure field of a cookie must be true or false", caller
  1350. end
  1351. @name, @value, @maxage, @path, @domain, @secure = name, value, maxage, path, domain, secure
  1352. @comment = nil
  1353. # }}}
  1354. end
  1355. # Modifies the value of this cookie. The information you want to store. If the
  1356. # value is nil, the cookie will be deleted by the client.
  1357. #
  1358. # This attribute can be a String, Integer or Float object or nil.
  1359. def value=(value)
  1360. # {{{
  1361. if value.class.superclass == Integer || value.class == Float
  1362. value = value.to_s
  1363. elsif value.class != String && value != nil
  1364. raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
  1365. end
  1366. @value = value
  1367. # }}}
  1368. end
  1369. # Modifies the maxage of this cookie. This attribute defines the lifetime of
  1370. # the cookie, in seconds. A value of 0 means the cookie should be discarded
  1371. # imediatly. If it set to nil, the cookie will be deleted when the browser
  1372. # will be closed.
  1373. #
  1374. # Attention: This is different from other implementations like PHP, where you
  1375. # gives the seconds since 1/1/1970 0:00:00 GMT.
  1376. #
  1377. # This attribute must be an Integer or Time object or nil.
  1378. def maxage=(maxage)
  1379. # {{{
  1380. if maxage.class == Time
  1381. maxage = maxage - Time.now
  1382. elsif maxage.class.superclass == Integer || !maxage == nil
  1383. raise TypeError, "The maxage of a cookie must be an Interger or Time object or nil.", caller
  1384. end
  1385. @maxage = maxage
  1386. # }}}
  1387. end
  1388. # Modifies the path value of this cookie. The client will send this cookie
  1389. # only, if the requested document is this directory or a subdirectory of it.
  1390. #
  1391. # The value of the attribute must be a String object or nil.
  1392. def path=(path)
  1393. # {{{
  1394. unless path.class == String || path == nil
  1395. raise TypeError, "The path of a cookie must be nil or a string", caller
  1396. end
  1397. @path = path
  1398. # }}}
  1399. end
  1400. # Modifies the domain value of this cookie. The client will send this cookie
  1401. # only if it's connected with this domain (or a subdomain, if the first
  1402. # character is a dot like in ".ruby-lang.org")
  1403. #
  1404. # The value of this attribute must be a String or nil.
  1405. def domain=(domain)
  1406. # {{{
  1407. unless domain.class == String || domain == nil
  1408. raise TypeError, "The domain of a cookie must be a String or nil.", caller
  1409. end
  1410. @domain = domain
  1411. # }}}
  1412. end
  1413. # Modifies the secure flag of this cookie. If it's true, the client will only
  1414. # send this cookie if it is secured connected with us.
  1415. #
  1416. # The value od this attribute has to be true or false.
  1417. def secure=(secure)
  1418. # {{{
  1419. unless secure == true || secure == false
  1420. raise TypeError, "The secure field of a cookie must be true or false", caller
  1421. end
  1422. @secure = secure
  1423. # }}}
  1424. end
  1425. # Modifies the comment value of this cookie. The comment won't be send, if
  1426. # type is "netscape".
  1427. def comment=(comment)
  1428. # {{{
  1429. unless comment.class == String || comment == nil
  1430. raise TypeError, "The comment of a cookie must be a string or nil", caller
  1431. end
  1432. @comment = comment
  1433. # }}}
  1434. end
  1435. # Changes the type of all cookies.
  1436. # Allowed values are RFC2109 and netscape (default).
  1437. def Cookie.type=(type)
  1438. # {{{
  1439. unless @@allowed
  1440. raise "The cookies are allready send, so you can't change the type anymore."
  1441. end
  1442. unless type.downcase == "rfc2109" && type.downcase == "netscape"
  1443. raise "The type of the cookies must be \"RFC2109\" or \"netscape\"."
  1444. end
  1445. @@type = type;
  1446. # }}}
  1447. end
  1448. # After sending this message, no cookies can be set or modified. Use it, when
  1449. # HTTP-Headers are send. Rweb does this for you.
  1450. def Cookie.disallow
  1451. # {{{
  1452. @@allowed = false
  1453. true
  1454. # }}}
  1455. end
  1456. # Returns a HTTP header (type String) with all cookies. Rweb does this for
  1457. # you.
  1458. def Cookie.getHttpHeader
  1459. # {{{
  1460. if defined?(@@list)
  1461. if @@type == "netscape"
  1462. str = ""
  1463. @@list.each do |cookie|
  1464. if cookie.value == nil
  1465. cookie.maxage = 0
  1466. cookie.value = ""
  1467. end
  1468. # TODO: Name and value should be escaped!
  1469. str += "Set-Cookie: #{cookie.name}=#{cookie.value}"
  1470. unless cookie.maxage == nil
  1471. expire = Time.now + cookie.maxage
  1472. expire.gmtime
  1473. str += "; Expire=#{expire.strftime("%a, %d-%b-%Y %H:%M:%S %Z")}"
  1474. end
  1475. unless cookie.domain == nil
  1476. str += "; Domain=#{cookie.domain}"
  1477. end
  1478. unless cookie.path == nil
  1479. str += "; Path=#{cookie.path}"
  1480. end
  1481. if cookie.secure
  1482. str += "; Secure"
  1483. end
  1484. str += "\r\n"
  1485. end
  1486. return str
  1487. else # type == "RFC2109"
  1488. str = "Set-Cookie: "
  1489. comma = false;
  1490. @@list.each do |cookie|
  1491. if cookie.value == nil
  1492. cookie.maxage = 0
  1493. cookie.value = ""
  1494. end
  1495. if comma
  1496. str += ","
  1497. end
  1498. comma = true
  1499. str += "#{cookie.name}=\"#{cookie.value}\""
  1500. unless cookie.maxage == nil
  1501. str += "; Max-Age=\"#{cookie.maxage}\""
  1502. end
  1503. unless cookie.domain == nil
  1504. str += "; Domain=\"#{cookie.domain}\""
  1505. end
  1506. unless cookie.path == nil
  1507. str += "; Path=\"#{cookie.path}\""
  1508. end
  1509. if cookie.secure
  1510. str += "; Secure"
  1511. end
  1512. unless cookie.comment == nil
  1513. str += "; Comment=\"#{cookie.comment}\""
  1514. end
  1515. str += "; Version=\"1\""
  1516. end
  1517. str
  1518. end
  1519. else
  1520. false
  1521. end
  1522. # }}}
  1523. end
  1524. end
  1525. require 'strscan'
  1526. module BBCode
  1527. DEBUG = true
  1528. use 'encoder', 'tags', 'tagstack', 'smileys'
  1529. =begin
  1530. The Parser class takes care of the encoding.
  1531. It scans the given BBCode (as plain text), finds tags
  1532. and smilies and also makes links of urls in text.
  1533. Normal text is send directly to the encoder.
  1534. If a tag was found, an instance of a Tag subclass is created
  1535. to handle the case.
  1536. The @tagstack manages tag nesting and ensures valid HTML.
  1537. =end
  1538. class Parser
  1539. class Attribute
  1540. # flatten and use only one empty_arg
  1541. def self.create attr
  1542. attr = flatten attr
  1543. return @@empty_attr if attr.empty?
  1544. new attr
  1545. end
  1546. private_class_method :new
  1547. # remove leading and trailing whitespace; concat lines
  1548. def self.flatten attr
  1549. attr.strip.gsub(/\n/, ' ')
  1550. # -> ^ and $ can only match at begin and end now
  1551. end
  1552. ATTRIBUTE_SCAN = /
  1553. (?!$) # don't match at end
  1554. \s*
  1555. ( # $1 = key
  1556. [^=\s\]"\\]*
  1557. (?:
  1558. (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? )
  1559. [^=\s\]"\\]*
  1560. )*
  1561. )
  1562. (?:
  1563. =
  1564. ( # $2 = v

Large files files are truncated, but you can click here to view the full file