merb-core 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. data/LICENSE +20 -0
  2. data/README +21 -0
  3. data/Rakefile +285 -0
  4. data/TODO +0 -0
  5. data/bin/merb +8 -0
  6. data/bin/merb-specs +5 -0
  7. data/docs/bootloading.dox +57 -0
  8. data/docs/documentation_standards +40 -0
  9. data/docs/new_render_api +51 -0
  10. data/lib/merb-core.rb +304 -0
  11. data/lib/merb-core/autoload.rb +29 -0
  12. data/lib/merb-core/bootloader.rb +601 -0
  13. data/lib/merb-core/config.rb +284 -0
  14. data/lib/merb-core/constants.rb +43 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +531 -0
  16. data/lib/merb-core/controller/exceptions.rb +257 -0
  17. data/lib/merb-core/controller/merb_controller.rb +214 -0
  18. data/lib/merb-core/controller/mime.rb +88 -0
  19. data/lib/merb-core/controller/mixins/controller.rb +262 -0
  20. data/lib/merb-core/controller/mixins/render.rb +324 -0
  21. data/lib/merb-core/controller/mixins/responder.rb +464 -0
  22. data/lib/merb-core/controller/template.rb +205 -0
  23. data/lib/merb-core/core_ext.rb +12 -0
  24. data/lib/merb-core/core_ext/class.rb +192 -0
  25. data/lib/merb-core/core_ext/hash.rb +422 -0
  26. data/lib/merb-core/core_ext/kernel.rb +304 -0
  27. data/lib/merb-core/core_ext/mash.rb +154 -0
  28. data/lib/merb-core/core_ext/object.rb +136 -0
  29. data/lib/merb-core/core_ext/object_space.rb +14 -0
  30. data/lib/merb-core/core_ext/rubygems.rb +28 -0
  31. data/lib/merb-core/core_ext/set.rb +41 -0
  32. data/lib/merb-core/core_ext/string.rb +69 -0
  33. data/lib/merb-core/dispatch/cookies.rb +92 -0
  34. data/lib/merb-core/dispatch/dispatcher.rb +233 -0
  35. data/lib/merb-core/dispatch/exceptions.html.erb +297 -0
  36. data/lib/merb-core/dispatch/request.rb +560 -0
  37. data/lib/merb-core/dispatch/router.rb +141 -0
  38. data/lib/merb-core/dispatch/router/behavior.rb +777 -0
  39. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  40. data/lib/merb-core/dispatch/router/route.rb +212 -0
  41. data/lib/merb-core/dispatch/session.rb +28 -0
  42. data/lib/merb-core/dispatch/session/cookie.rb +166 -0
  43. data/lib/merb-core/dispatch/session/memcached.rb +161 -0
  44. data/lib/merb-core/dispatch/session/memory.rb +234 -0
  45. data/lib/merb-core/gem_ext/erubis.rb +19 -0
  46. data/lib/merb-core/logger.rb +230 -0
  47. data/lib/merb-core/plugins.rb +25 -0
  48. data/lib/merb-core/rack.rb +15 -0
  49. data/lib/merb-core/rack/adapter.rb +42 -0
  50. data/lib/merb-core/rack/adapter/ebb.rb +22 -0
  51. data/lib/merb-core/rack/adapter/evented_mongrel.rb +24 -0
  52. data/lib/merb-core/rack/adapter/fcgi.rb +16 -0
  53. data/lib/merb-core/rack/adapter/irb.rb +108 -0
  54. data/lib/merb-core/rack/adapter/mongrel.rb +25 -0
  55. data/lib/merb-core/rack/adapter/runner.rb +27 -0
  56. data/lib/merb-core/rack/adapter/thin.rb +27 -0
  57. data/lib/merb-core/rack/adapter/webrick.rb +35 -0
  58. data/lib/merb-core/rack/application.rb +77 -0
  59. data/lib/merb-core/rack/handler/mongrel.rb +97 -0
  60. data/lib/merb-core/server.rb +184 -0
  61. data/lib/merb-core/test.rb +10 -0
  62. data/lib/merb-core/test/helpers.rb +9 -0
  63. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  64. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  65. data/lib/merb-core/test/helpers/request_helper.rb +257 -0
  66. data/lib/merb-core/test/helpers/route_helper.rb +33 -0
  67. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  68. data/lib/merb-core/test/matchers.rb +9 -0
  69. data/lib/merb-core/test/matchers/controller_matchers.rb +269 -0
  70. data/lib/merb-core/test/matchers/route_matchers.rb +136 -0
  71. data/lib/merb-core/test/matchers/view_matchers.rb +293 -0
  72. data/lib/merb-core/test/run_specs.rb +38 -0
  73. data/lib/merb-core/test/tasks/spectasks.rb +39 -0
  74. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  75. data/lib/merb-core/test/test_ext/object.rb +14 -0
  76. data/lib/merb-core/vendor/facets.rb +2 -0
  77. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  78. data/lib/merb-core/vendor/facets/inflect.rb +211 -0
  79. data/lib/merb-core/version.rb +11 -0
  80. data/spec/private/config/adapter_spec.rb +32 -0
  81. data/spec/private/config/config_spec.rb +139 -0
  82. data/spec/private/config/environment_spec.rb +13 -0
  83. data/spec/private/config/spec_helper.rb +1 -0
  84. data/spec/private/core_ext/hash_spec.rb +506 -0
  85. data/spec/private/core_ext/kernel_spec.rb +46 -0
  86. data/spec/private/core_ext/object_spec.rb +39 -0
  87. data/spec/private/core_ext/set_spec.rb +26 -0
  88. data/spec/private/core_ext/string_spec.rb +9 -0
  89. data/spec/private/dispatch/cookies_spec.rb +107 -0
  90. data/spec/private/dispatch/dispatch_spec.rb +26 -0
  91. data/spec/private/dispatch/fixture/app/controllers/application.rb +4 -0
  92. data/spec/private/dispatch/fixture/app/controllers/exceptions.rb +27 -0
  93. data/spec/private/dispatch/fixture/app/controllers/foo.rb +21 -0
  94. data/spec/private/dispatch/fixture/app/helpers/global_helpers.rb +8 -0
  95. data/spec/private/dispatch/fixture/app/views/exeptions/client_error.html.erb +37 -0
  96. data/spec/private/dispatch/fixture/app/views/exeptions/internal_server_error.html.erb +216 -0
  97. data/spec/private/dispatch/fixture/app/views/exeptions/not_acceptable.html.erb +38 -0
  98. data/spec/private/dispatch/fixture/app/views/exeptions/not_found.html.erb +40 -0
  99. data/spec/private/dispatch/fixture/app/views/foo/bar.html.erb +0 -0
  100. data/spec/private/dispatch/fixture/app/views/layout/application.html.erb +11 -0
  101. data/spec/private/dispatch/fixture/config/environments/development.rb +6 -0
  102. data/spec/private/dispatch/fixture/config/environments/production.rb +5 -0
  103. data/spec/private/dispatch/fixture/config/environments/test.rb +6 -0
  104. data/spec/private/dispatch/fixture/config/init.rb +45 -0
  105. data/spec/private/dispatch/fixture/config/rack.rb +1 -0
  106. data/spec/private/dispatch/fixture/config/router.rb +35 -0
  107. data/spec/private/dispatch/fixture/log/development.log +1 -0
  108. data/spec/private/dispatch/fixture/log/merb.4000.pid +1 -0
  109. data/spec/private/dispatch/fixture/log/merb_test.log +2040 -0
  110. data/spec/private/dispatch/fixture/log/production.log +1 -0
  111. data/spec/private/dispatch/fixture/merb.4000.pid +1 -0
  112. data/spec/private/dispatch/fixture/public/images/merb.jpg +0 -0
  113. data/spec/private/dispatch/fixture/public/merb.fcgi +4 -0
  114. data/spec/private/dispatch/fixture/public/stylesheets/master.css +119 -0
  115. data/spec/private/dispatch/route_params_spec.rb +24 -0
  116. data/spec/private/dispatch/spec_helper.rb +1 -0
  117. data/spec/private/plugins/plugin_spec.rb +81 -0
  118. data/spec/private/rack/application_spec.rb +43 -0
  119. data/spec/public/DEFINITIONS +11 -0
  120. data/spec/public/abstract_controller/controllers/alt_views/layout/application.erb +1 -0
  121. data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_string_controller_layout.erb +1 -0
  122. data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_template_controller_layout.erb +1 -0
  123. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/index.erb +1 -0
  124. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/show.erb +1 -0
  125. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
  126. data/spec/public/abstract_controller/controllers/alt_views/partial/basic_partial_with_multiple_roots/_partial.erb +1 -0
  127. data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_and_custom_location/index.erb +1 -0
  128. data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_inherited/index.erb +1 -0
  129. data/spec/public/abstract_controller/controllers/display.rb +54 -0
  130. data/spec/public/abstract_controller/controllers/filters.rb +167 -0
  131. data/spec/public/abstract_controller/controllers/helpers.rb +31 -0
  132. data/spec/public/abstract_controller/controllers/partial.rb +106 -0
  133. data/spec/public/abstract_controller/controllers/render.rb +86 -0
  134. data/spec/public/abstract_controller/controllers/views/helpers/capture/index.erb +1 -0
  135. data/spec/public/abstract_controller/controllers/views/helpers/concat/index.erb +1 -0
  136. data/spec/public/abstract_controller/controllers/views/layout/alt.erb +1 -0
  137. data/spec/public/abstract_controller/controllers/views/layout/custom.erb +1 -0
  138. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object/index.erb +1 -0
  139. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object_with_action/new.erb +1 -0
  140. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template/index.erb +1 -0
  141. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_app_layout/index.erb +0 -0
  142. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_custom_layout/index.erb +1 -0
  143. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
  144. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/show.erb +1 -0
  145. data/spec/public/abstract_controller/controllers/views/partial/another_directory/_partial.erb +1 -0
  146. data/spec/public/abstract_controller/controllers/views/partial/basic_partial/_partial.erb +1 -0
  147. data/spec/public/abstract_controller/controllers/views/partial/basic_partial/index.erb +1 -0
  148. data/spec/public/abstract_controller/controllers/views/partial/basic_partial_with_multiple_roots/index.erb +1 -0
  149. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_first.erb +1 -0
  150. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_second.erb +1 -0
  151. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/index.erb +1 -0
  152. data/spec/public/abstract_controller/controllers/views/partial/partial_in_another_directory/index.erb +1 -0
  153. data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/_collection.erb +1 -0
  154. data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/index.erb +1 -0
  155. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/_collection.erb +1 -0
  156. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/index.erb +1 -0
  157. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/_collection.erb +1 -0
  158. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/index.erb +1 -0
  159. data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/_variables.erb +1 -0
  160. data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/index.erb +1 -0
  161. data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/_both.erb +1 -0
  162. data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/index.erb +1 -0
  163. data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/_with_partial.erb +1 -0
  164. data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/index.erb +1 -0
  165. data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/_with_partial.erb +1 -0
  166. data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/index.erb +1 -0
  167. data/spec/public/abstract_controller/controllers/views/partial/with_partial/_with_partial.erb +1 -0
  168. data/spec/public/abstract_controller/controllers/views/partial/with_partial/index.erb +1 -0
  169. data/spec/public/abstract_controller/controllers/views/test_display/foo.html.erb +1 -0
  170. data/spec/public/abstract_controller/controllers/views/test_render/foo.html.erb +0 -0
  171. data/spec/public/abstract_controller/controllers/views/wonderful/index.erb +1 -0
  172. data/spec/public/abstract_controller/display_spec.rb +33 -0
  173. data/spec/public/abstract_controller/filter_spec.rb +80 -0
  174. data/spec/public/abstract_controller/helper_spec.rb +13 -0
  175. data/spec/public/abstract_controller/partial_spec.rb +53 -0
  176. data/spec/public/abstract_controller/render_spec.rb +70 -0
  177. data/spec/public/abstract_controller/spec_helper.rb +27 -0
  178. data/spec/public/boot_loader/boot_loader_spec.rb +33 -0
  179. data/spec/public/boot_loader/spec_helper.rb +1 -0
  180. data/spec/public/controller/base_spec.rb +31 -0
  181. data/spec/public/controller/controllers/base.rb +41 -0
  182. data/spec/public/controller/controllers/display.rb +40 -0
  183. data/spec/public/controller/controllers/responder.rb +67 -0
  184. data/spec/public/controller/controllers/url.rb +7 -0
  185. data/spec/public/controller/controllers/views/layout/custom.html.erb +1 -0
  186. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -0
  187. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.xml.erb +1 -0
  188. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template/index.html.erb +1 -0
  189. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/html_default/index.html.erb +1 -0
  190. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/layout/custom.html.erb +1 -0
  191. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.html.erb +1 -0
  192. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.xml.erb +1 -0
  193. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.html.erb +1 -0
  194. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.js.erb +1 -0
  195. data/spec/public/controller/display_spec.rb +34 -0
  196. data/spec/public/controller/log/merb.4000.pid +1 -0
  197. data/spec/public/controller/responder_spec.rb +95 -0
  198. data/spec/public/controller/spec_helper.rb +9 -0
  199. data/spec/public/controller/url_spec.rb +152 -0
  200. data/spec/public/directory_structure/directory/app/controllers/application.rb +3 -0
  201. data/spec/public/directory_structure/directory/app/controllers/base.rb +13 -0
  202. data/spec/public/directory_structure/directory/app/controllers/custom.rb +19 -0
  203. data/spec/public/directory_structure/directory/app/views/base/template.html.erb +1 -0
  204. data/spec/public/directory_structure/directory/app/views/wonderful/template.erb +1 -0
  205. data/spec/public/directory_structure/directory/config/router.rb +3 -0
  206. data/spec/public/directory_structure/directory/log/merb.4000.pid +1 -0
  207. data/spec/public/directory_structure/directory/log/merb_test.log +265 -0
  208. data/spec/public/directory_structure/directory/merb.4000.pid +1 -0
  209. data/spec/public/directory_structure/directory_spec.rb +44 -0
  210. data/spec/public/logger/logger_spec.rb +175 -0
  211. data/spec/public/logger/spec_helper.rb +1 -0
  212. data/spec/public/reloading/directory/app/controllers/application.rb +3 -0
  213. data/spec/public/reloading/directory/app/controllers/reload.rb +6 -0
  214. data/spec/public/reloading/directory/config/init.rb +2 -0
  215. data/spec/public/reloading/directory/log/merb.4000.pid +1 -0
  216. data/spec/public/reloading/directory/log/merb_test.log +59 -0
  217. data/spec/public/reloading/directory/merb.4000.pid +1 -0
  218. data/spec/public/reloading/reload_spec.rb +80 -0
  219. data/spec/public/request/multipart_spec.rb +15 -0
  220. data/spec/public/request/request_spec.rb +207 -0
  221. data/spec/public/router/default_spec.rb +21 -0
  222. data/spec/public/router/deferred_spec.rb +22 -0
  223. data/spec/public/router/namespace_spec.rb +113 -0
  224. data/spec/public/router/nested_resources_spec.rb +34 -0
  225. data/spec/public/router/resource_spec.rb +45 -0
  226. data/spec/public/router/resources_spec.rb +57 -0
  227. data/spec/public/router/spec_helper.rb +72 -0
  228. data/spec/public/router/special_spec.rb +44 -0
  229. data/spec/public/router/string_spec.rb +61 -0
  230. data/spec/public/template/template_spec.rb +92 -0
  231. data/spec/public/template/templates/error.html.erb +2 -0
  232. data/spec/public/template/templates/template.html.erb +1 -0
  233. data/spec/public/template/templates/template.html.myt +1 -0
  234. data/spec/public/test/controller_matchers_spec.rb +378 -0
  235. data/spec/public/test/controllers/controller_assertion_mock.rb +7 -0
  236. data/spec/public/test/controllers/dispatch_controller.rb +11 -0
  237. data/spec/public/test/controllers/spec_helper_controller.rb +30 -0
  238. data/spec/public/test/multipart_request_helper_spec.rb +159 -0
  239. data/spec/public/test/multipart_upload_text_file.txt +1 -0
  240. data/spec/public/test/request_helper_spec.rb +153 -0
  241. data/spec/public/test/route_helper_spec.rb +54 -0
  242. data/spec/public/test/route_matchers_spec.rb +133 -0
  243. data/spec/public/test/view_helper_spec.rb +96 -0
  244. data/spec/public/test/view_matchers_spec.rb +107 -0
  245. data/spec/spec_helper.rb +71 -0
  246. metadata +488 -0
@@ -0,0 +1,205 @@
1
+ module Merb::InlineTemplates
2
+ end
3
+
4
+ module Merb::Template
5
+
6
+ EXTENSIONS = {} unless defined?(EXTENSIONS)
7
+ METHOD_LIST = {} unless defined?(METHOD_LIST)
8
+ MTIMES = {} unless defined?(MTIMES)
9
+
10
+ class << self
11
+ # Get the template's method name from a full path. This replaces
12
+ # non-alphanumeric characters with __ and "." with "_"
13
+ #
14
+ # Collisions are potentially possible with something like:
15
+ # ~foo.bar and __foo.bar or !foo.bar.
16
+ #
17
+ # ==== Parameters
18
+ # path<String>:: A full path to convert to a valid Ruby method name
19
+ #
20
+ # ==== Returns
21
+ # String:: The template name.
22
+ #
23
+ #---
24
+ # We might want to replace this with something that varies the
25
+ # character replaced based on the non-alphanumeric character
26
+ # to avoid edge-case collisions.
27
+ def template_name(path)
28
+ path = File.expand_path(path)
29
+ path.gsub(/[^\.a-zA-Z0-9]/, "__").gsub(/\./, "_")
30
+ end
31
+
32
+ # Get the name of the template method for a particular path.
33
+ #
34
+ # ==== Parameters
35
+ # path<String>:: A full path to find a template method for.
36
+ # template_stack<Array>:: The template stack. Not used.
37
+ #
38
+ # ==== Returns
39
+ # DOC
40
+ #---
41
+ # @semipublic
42
+ def template_for(path, template_stack = [])
43
+ path = File.expand_path(path)
44
+
45
+ ret =
46
+ if Merb::Config[:reload_templates]
47
+ file = Dir["#{path}.{#{Merb::Template::EXTENSIONS.keys.join(',')}}"].first
48
+ METHOD_LIST[path] = file ? inline_template(file) : nil
49
+ else
50
+ METHOD_LIST[path] ||= begin
51
+ file = Dir["#{path}.{#{Merb::Template::EXTENSIONS.keys.join(',')}}"].first
52
+ file ? inline_template(file) : nil
53
+ end
54
+ end
55
+
56
+ ret
57
+ end
58
+
59
+ # Takes a template at a particular path and inlines it into a module and
60
+ # adds it to the METHOD_LIST table to speed lookup later.
61
+ #
62
+ # ==== Parameters
63
+ # path<String>::
64
+ # The full path of the template (minus the templating specifier) to
65
+ # inline.
66
+ # mod<Module>::
67
+ # The module to put the compiled method into. Defaults to
68
+ # Merb::InlineTemplates
69
+ #
70
+ # ==== Note
71
+ # Even though this method supports inlining into any module, the method
72
+ # must be available to instances of AbstractController that will use it.
73
+ #---
74
+ # @public
75
+ def inline_template(path, mod = Merb::InlineTemplates)
76
+ path = File.expand_path(path)
77
+ METHOD_LIST[path.gsub(/\.[^\.]*$/, "")] =
78
+ engine_for(path).compile_template(path, template_name(path), mod)
79
+ end
80
+
81
+ # Finds the engine for a particular path.
82
+ #
83
+ # ==== Parameters
84
+ # path<String>:: The path of the file to find an engine for.
85
+ #
86
+ # ==== Returns
87
+ # Class:: The engine.
88
+ #---
89
+ # @semipublic
90
+ def engine_for(path)
91
+ path = File.expand_path(path)
92
+ EXTENSIONS[path.match(/\.([^\.]*)$/)[1]]
93
+ end
94
+
95
+ # Registers the extensions that will trigger a particular templating
96
+ # engine.
97
+ #
98
+ # ==== Parameters
99
+ # engine<Class>:: The class of the engine that is being registered
100
+ # extensions<Array[String]>::
101
+ # The list of extensions that will be registered with this templating
102
+ # language
103
+ #
104
+ # ==== Raises
105
+ # ArgumentError:: engine does not have a compile_template method.
106
+ #
107
+ # ==== Example
108
+ # Merb::Template.register_extensions(Merb::Template::Erubis, ["erb"])
109
+ #---
110
+ # @public
111
+ def register_extensions(engine, extensions)
112
+ raise ArgumentError, "The class you are registering does not have a compile_template method" unless
113
+ engine.respond_to?(:compile_template)
114
+ extensions.each{|ext| EXTENSIONS[ext] = engine }
115
+ Merb::AbstractController.class_eval <<-HERE
116
+ include #{engine}::Mixin
117
+ HERE
118
+ end
119
+ end
120
+
121
+ require 'erubis'
122
+
123
+ class Erubis
124
+ # ==== Parameters
125
+ # path<String>:: A full path to the template.
126
+ # name<String>:: The name of the method that will be created.
127
+ # mod<Module>:: The module that the compiled method will be placed into.
128
+ def self.compile_template(path, name, mod)
129
+ template = ::Erubis::Eruby.new(File.read(path))
130
+ template.def_method(mod, name, path)
131
+ name
132
+ end
133
+
134
+ module Mixin
135
+
136
+ # Provides direct acccess to the buffer for this view context
137
+ #
138
+ # ==== Parameters
139
+ # the_binding<Binding>:: The binding to pass to the buffer.
140
+ #
141
+ # ==== Returns
142
+ # DOC
143
+ def _erb_buffer( the_binding )
144
+ @_buffer = eval( "_buf", the_binding, __FILE__, __LINE__)
145
+ end
146
+
147
+ # ==== Parameters
148
+ # *args:: Arguments to pass to the block.
149
+ # &block:: The template block to call.
150
+ #
151
+ # ==== Returns
152
+ # String:: The output of the block.
153
+ #
154
+ # ==== Examples
155
+ # Capture being used in a .html.erb page:
156
+ #
157
+ # <% @foo = capture do %>
158
+ # <p>Some Foo content!</p>
159
+ # <% end %>
160
+ def capture_erb(*args, &block)
161
+ # get the buffer from the block's binding
162
+ buffer = _erb_buffer( block.binding ) rescue nil
163
+
164
+ # If there is no buffer, just call the block and get the contents
165
+ if buffer.nil?
166
+ block.call(*args)
167
+ # If there is a buffer, execute the block, then extract its contents
168
+ else
169
+ pos = buffer.length
170
+ block.call(*args)
171
+
172
+ # extract the block
173
+ data = buffer[pos..-1]
174
+
175
+ # replace it in the original with empty string
176
+ buffer[pos..-1] = ''
177
+
178
+ data
179
+ end
180
+ end
181
+
182
+ # DOC
183
+ def concat_erb(string, binding)
184
+ _erb_buffer(binding) << string
185
+ end
186
+
187
+ end
188
+
189
+ Merb::Template.register_extensions(self, %w[erb])
190
+ end
191
+
192
+ end
193
+
194
+ module Erubis
195
+ module RubyEvaluator
196
+
197
+ # DOC
198
+ def def_method(object, method_name, filename=nil)
199
+ m = object.is_a?(Module) ? :module_eval : :instance_eval
200
+ setup = "@_engine = 'erb'"
201
+ object.__send__(m, "def #{method_name}(locals={}); #{setup}; #{@src}; end", filename || @filename || '(erubis)')
202
+ end
203
+
204
+ end
205
+ end
@@ -0,0 +1,12 @@
1
+ corelib = File.join(File.dirname(__FILE__), "core_ext")
2
+
3
+ require "#{corelib}/string"
4
+ require corelib/:class
5
+ require corelib/:hash
6
+ require corelib/:kernel
7
+ require corelib/:mash
8
+ require corelib/:object
9
+ require corelib/:object_space
10
+ require corelib/:rubygems
11
+ require corelib/:set
12
+
@@ -0,0 +1,192 @@
1
+ # Allows attributes to be shared within an inheritance hierarchy, but where
2
+ # each descendant gets a copy of their parents' attributes, instead of just a
3
+ # pointer to the same. This means that the child can add elements to, for
4
+ # example, an array without those additions being shared with either their
5
+ # parent, siblings, or children, which is unlike the regular class-level
6
+ # attributes that are shared across the entire hierarchy.
7
+ class Class # :nodoc:
8
+
9
+ def cattr_reader(*syms)
10
+ syms.flatten.each do |sym|
11
+ next if sym.is_a?(Hash)
12
+ class_eval(<<-EOS, __FILE__, __LINE__)
13
+ unless defined? @@#{sym}
14
+ @@#{sym} = nil
15
+ end
16
+
17
+ def self.#{sym}
18
+ @@#{sym}
19
+ end
20
+
21
+ def #{sym}
22
+ @@#{sym}
23
+ end
24
+ EOS
25
+ end
26
+ end
27
+
28
+ def cattr_writer(*syms)
29
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
30
+ syms.flatten.each do |sym|
31
+ class_eval(<<-EOS, __FILE__, __LINE__)
32
+ unless defined? @@#{sym}
33
+ @@#{sym} = nil
34
+ end
35
+
36
+ def self.#{sym}=(obj)
37
+ @@#{sym} = obj
38
+ end
39
+
40
+ #{"
41
+
42
+ def #{sym}=(obj)
43
+ @@#{sym} = obj
44
+ end
45
+ " unless options[:instance_writer] == false }
46
+ EOS
47
+ end
48
+ end
49
+
50
+ def cattr_accessor(*syms)
51
+ cattr_reader(*syms)
52
+ cattr_writer(*syms)
53
+ end
54
+
55
+ def class_inheritable_reader(*syms)
56
+ syms.each do |sym|
57
+ next if sym.is_a?(Hash)
58
+ class_eval <<-EOS, __FILE__, __LINE__
59
+
60
+ def self.#{sym}
61
+ read_inheritable_attribute(:#{sym})
62
+ end
63
+
64
+ def #{sym}
65
+ self.class.#{sym}
66
+ end
67
+ EOS
68
+ end
69
+ end
70
+
71
+ def class_inheritable_writer(*syms)
72
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
73
+ syms.each do |sym|
74
+ class_eval <<-EOS, __FILE__, __LINE__
75
+
76
+ def self.#{sym}=(obj)
77
+ write_inheritable_attribute(:#{sym}, obj)
78
+ end
79
+
80
+ #{"
81
+
82
+ def #{sym}=(obj)
83
+ self.class.#{sym} = obj
84
+ end
85
+ " unless options[:instance_writer] == false }
86
+ EOS
87
+ end
88
+ end
89
+
90
+ def class_inheritable_array_writer(*syms)
91
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
92
+ syms.each do |sym|
93
+ class_eval <<-EOS, __FILE__, __LINE__
94
+
95
+ def self.#{sym}=(obj)
96
+ write_inheritable_array(:#{sym}, obj)
97
+ end
98
+
99
+ #{"
100
+
101
+ def #{sym}=(obj)
102
+ self.class.#{sym} = obj
103
+ end
104
+ " unless options[:instance_writer] == false }
105
+ EOS
106
+ end
107
+ end
108
+
109
+ def class_inheritable_hash_writer(*syms)
110
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
111
+ syms.each do |sym|
112
+ class_eval <<-EOS, __FILE__, __LINE__
113
+
114
+ def self.#{sym}=(obj)
115
+ write_inheritable_hash(:#{sym}, obj)
116
+ end
117
+
118
+ #{"
119
+
120
+ def #{sym}=(obj)
121
+ self.class.#{sym} = obj
122
+ end
123
+ " unless options[:instance_writer] == false }
124
+ EOS
125
+ end
126
+ end
127
+
128
+ def class_inheritable_accessor(*syms)
129
+ class_inheritable_reader(*syms)
130
+ class_inheritable_writer(*syms)
131
+ end
132
+
133
+ def class_inheritable_array(*syms)
134
+ class_inheritable_reader(*syms)
135
+ class_inheritable_array_writer(*syms)
136
+ end
137
+
138
+ def class_inheritable_hash(*syms)
139
+ class_inheritable_reader(*syms)
140
+ class_inheritable_hash_writer(*syms)
141
+ end
142
+
143
+ def inheritable_attributes
144
+ @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
145
+ end
146
+
147
+ def write_inheritable_attribute(key, value)
148
+ if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
149
+ @inheritable_attributes = {}
150
+ end
151
+ inheritable_attributes[key] = value
152
+ end
153
+
154
+ def write_inheritable_array(key, elements)
155
+ write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
156
+ write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
157
+ end
158
+
159
+ def write_inheritable_hash(key, hash)
160
+ write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
161
+ write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
162
+ end
163
+
164
+ def read_inheritable_attribute(key)
165
+ inheritable_attributes[key]
166
+ end
167
+
168
+ def reset_inheritable_attributes
169
+ @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
170
+ end
171
+
172
+ private
173
+ # Prevent this constant from being created multiple times
174
+ EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
175
+
176
+ def inherited_with_inheritable_attributes(child)
177
+ inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
178
+
179
+ if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
180
+ new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
181
+ else
182
+ new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
183
+ memo.update(key => (value.dup rescue value))
184
+ end
185
+ end
186
+
187
+ child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
188
+ end
189
+
190
+ alias inherited_without_inheritable_attributes inherited
191
+ alias inherited inherited_with_inheritable_attributes
192
+ end
@@ -0,0 +1,422 @@
1
+ require 'base64'
2
+
3
+ class Hash
4
+ class << self
5
+ # Converts valid XML into a Ruby Hash structure.
6
+ #
7
+ # ==== Paramters
8
+ # xml<String>:: A string representation of valid XML.
9
+ #
10
+ # ==== Notes
11
+ # * Mixed content is treated as text and any tags in it are left unparsed
12
+ # * Any attributes other than type on a node containing a text node will be
13
+ # discarded
14
+ #
15
+ # ===== Typecasting
16
+ # Typecasting is performed on elements that have a +type+ attribute:
17
+ # integer::
18
+ # boolean:: Anything other than "true" evaluates to false.
19
+ # datetime::
20
+ # Returns a Time object. See Time documentation for valid Time strings.
21
+ # date::
22
+ # Returns a Date object. See Date documentation for valid Date strings.
23
+ #
24
+ # Keys are automatically converted to +snake_case+
25
+ #
26
+ # ==== Examples
27
+ #
28
+ # ===== Standard
29
+ # <user gender='m'>
30
+ # <age type='integer'>35</age>
31
+ # <name>Home Simpson</name>
32
+ # <dob type='date'>1988-01-01</dob>
33
+ # <joined-at type='datetime'>2000-04-28 23:01</joined-at>
34
+ # <is-cool type='boolean'>true</is-cool>
35
+ # </user>
36
+ #
37
+ # evaluates to
38
+ #
39
+ # { "user" => {
40
+ # "gender" => "m",
41
+ # "age" => 35,
42
+ # "name" => "Home Simpson",
43
+ # "dob" => DateObject( 1998-01-01 ),
44
+ # "joined_at" => TimeObject( 2000-04-28 23:01),
45
+ # "is_cool" => true
46
+ # }
47
+ # }
48
+ #
49
+ # ===== Mixed Content
50
+ # <story>
51
+ # A Quick <em>brown</em> Fox
52
+ # </story>
53
+ #
54
+ # evaluates to
55
+ #
56
+ # { "story" => "A Quick <em>brown</em> Fox" }
57
+ #
58
+ # ====== Attributes other than type on a node containing text
59
+ # <story is-good='false'>
60
+ # A Quick <em>brown</em> Fox
61
+ # </story>
62
+ #
63
+ # evaluates to
64
+ #
65
+ # { "story" => "A Quick <em>brown</em> Fox" }
66
+ #
67
+ # <bicep unit='inches' type='integer'>60</bicep>
68
+ #
69
+ # evaluates with a typecast to an integer. But unit attribute is ignored.
70
+ #
71
+ # { "bicep" => 60 }
72
+ def from_xml( xml )
73
+ ToHashParser.from_xml(xml)
74
+ end
75
+ end
76
+
77
+ # ==== Returns
78
+ # Mash:: This hash as a Mash for string or symbol key access.
79
+ def to_mash
80
+ hash = Mash.new(self)
81
+ hash.default = default
82
+ hash
83
+ end
84
+
85
+ # ==== Returns
86
+ # String:: This hash as a query string
87
+ #
88
+ # ==== Examples
89
+ # { :name => "Bob",
90
+ # :address => {
91
+ # :street => '111 Ruby Ave.',
92
+ # :city => 'Ruby Central',
93
+ # :phones => ['111-111-1111', '222-222-2222']
94
+ # }
95
+ # }.to_params
96
+ # #=> "name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave."
97
+ def to_params
98
+ params = ''
99
+ stack = []
100
+
101
+ each do |k, v|
102
+ if v.is_a?(Hash)
103
+ stack << [k,v]
104
+ else
105
+ params << "#{k}=#{v}&"
106
+ end
107
+ end
108
+
109
+ stack.each do |parent, hash|
110
+ hash.each do |k, v|
111
+ if v.is_a?(Hash)
112
+ stack << ["#{parent}[#{k}]", v]
113
+ else
114
+ params << "#{parent}[#{k}]=#{v}&"
115
+ end
116
+ end
117
+ end
118
+
119
+ params.chop! # trailing &
120
+ params
121
+ end
122
+
123
+ # ==== Parameters
124
+ # *allowed:: The hash keys to include.
125
+ #
126
+ # ==== Returns
127
+ # Hash:: A new hash with only the selected keys.
128
+ #
129
+ # ==== Examples
130
+ # { :one => 1, :two => 2, :three => 3 }.only(:one)
131
+ # #=> { :one => 1 }
132
+ def only(*allowed)
133
+ reject { |k,v| !allowed.include?(k) }
134
+ end
135
+
136
+ # ==== Parameters
137
+ # *rejected:: The hash keys to exclude.
138
+ #
139
+ # ==== Returns
140
+ # Hash:: A new hash without the selected keys.
141
+ #
142
+ # ==== Examples
143
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
144
+ # #=> { :two => 2, :three => 3 }
145
+ def except(*rejected)
146
+ reject { |k,v| rejected.include?(k) }
147
+ end
148
+
149
+ # ==== Returns
150
+ # String:: The hash as attributes for an XML tag.
151
+ #
152
+ # ==== Examples
153
+ # { :one => 1, "two"=>"TWO" }.to_xml_attributes
154
+ # #=> 'one="1" two="TWO"'
155
+ def to_xml_attributes
156
+ map do |k,v|
157
+ %{#{k.to_s.camel_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
158
+ end.join(' ')
159
+ end
160
+
161
+ alias_method :to_html_attributes, :to_xml_attributes
162
+
163
+ # ==== Parameters
164
+ # html_class<~to_s>::
165
+ # The HTML class to add to the :class key. The html_class will be
166
+ # concatenated to any existing classes.
167
+ #
168
+ # ==== Examples
169
+ # hash[:class] #=> nil
170
+ # hash.add_html_class!(:selected)
171
+ # hash[:class] #=> "selected"
172
+ # hash.add_html_class!("class1 class2")
173
+ # hash[:class] #=> "selected class1 class2"
174
+ def add_html_class!(html_class)
175
+ if self[:class]
176
+ self[:class] = "#{self[:class]} #{html_class}"
177
+ else
178
+ self[:class] = html_class.to_s
179
+ end
180
+ end
181
+
182
+ # Converts all keys into string values. This is used during reloading to
183
+ # prevent problems when classes are no longer declared.
184
+ #
185
+ # === Examples
186
+ # hash = { One => 1, Two => 2 }.proctect_keys!
187
+ # hash # => { "One" => 1, "Two" => 2 }
188
+ def protect_keys!
189
+ keys.each {|key| self[key.to_s] = delete(key) }
190
+ end
191
+
192
+ # Attempts to convert all string keys into Class keys. We run this after
193
+ # reloading to convert protected hashes back into usable hashes.
194
+ #
195
+ # === Examples
196
+ # # Provided that classes One and Two are declared in this scope:
197
+ # hash = { "One" => 1, "Two" => 2 }.unproctect_keys!
198
+ # hash # => { One => 1, Two => 2 }
199
+ def unprotect_keys!
200
+ keys.each do |key|
201
+ (self[Object.full_const_get(key)] = delete(key)) rescue nil
202
+ end
203
+ end
204
+
205
+ # Destructively and non-recursively convert each key to an uppercase string,
206
+ # deleting nil values along the way.
207
+ #
208
+ # ==== Returns
209
+ # Hash:: The newly environmentized hash.
210
+ #
211
+ # ==== Examples
212
+ # { :name => "Bob", :contact => { :email => "bob@bob.com" } }.environmentize_keys!
213
+ # #=> { "NAME" => "Bob", "CONTACT" => { :email => "bob@bob.com" } }
214
+ def environmentize_keys!
215
+ keys.each do |key|
216
+ val = delete(key)
217
+ next if val.nil?
218
+ self[key.to_s.upcase] = val
219
+ end
220
+ self
221
+ end
222
+ end
223
+
224
+ require 'rexml/parsers/streamparser'
225
+ require 'rexml/parsers/baseparser'
226
+ require 'rexml/light/node'
227
+
228
+ # This is a slighly modified version of the XMLUtilityNode from
229
+ # http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
230
+ # It's mainly just adding vowels, as I ht cd wth n vwls :)
231
+ # This represents the hard part of the work, all I did was change the
232
+ # underlying parser.
233
+ class REXMLUtilityNode # :nodoc:
234
+ attr_accessor :name, :attributes, :children, :type
235
+ cattr_accessor :typecasts, :available_typecasts
236
+
237
+ self.typecasts = {}
238
+ self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i}
239
+ self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")}
240
+ self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
241
+ self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)}
242
+ self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
243
+ self.typecasts["decimal"] = lambda{|v| BigDecimal(v)}
244
+ self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f}
245
+ self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f}
246
+ self.typecasts["symbol"] = lambda{|v| v.to_sym}
247
+ self.typecasts["string"] = lambda{|v| v.to_s}
248
+ self.typecasts["yaml"] = lambda{|v| v.nil? ? nil : YAML.load(v)}
249
+ self.typecasts["base64Binary"] = lambda{|v| Base64.decode64(v)}
250
+
251
+ self.available_typecasts = self.typecasts.keys
252
+
253
+ def initialize(name, attributes = {})
254
+ @name = name.tr("-", "_")
255
+ # leave the type alone if we don't know what it is
256
+ @type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
257
+
258
+ @nil_element = attributes.delete("nil") == "true"
259
+ @attributes = undasherize_keys(attributes)
260
+ @children = []
261
+ @text = false
262
+ end
263
+
264
+ def add_node(node)
265
+ @text = true if node.is_a? String
266
+ @children << node
267
+ end
268
+
269
+ def to_hash
270
+ if @type == "file"
271
+ f = StringIO.new(::Base64.decode64(@children.first || ""))
272
+ class << f
273
+ attr_accessor :original_filename, :content_type
274
+ end
275
+ f.original_filename = attributes['name'] || 'untitled'
276
+ f.content_type = attributes['content_type'] || 'application/octet-stream'
277
+ return {name => f}
278
+ end
279
+
280
+ if @text
281
+ return { name => typecast_value( translate_xml_entities( inner_html ) ) }
282
+ else
283
+ #change repeating groups into an array
284
+ groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s }
285
+
286
+ out = nil
287
+ if @type == "array"
288
+ out = []
289
+ groups.each do |k, v|
290
+ if v.size == 1
291
+ out << v.first.to_hash.entries.first.last
292
+ else
293
+ out << v.map{|e| e.to_hash[k]}
294
+ end
295
+ end
296
+ out = out.flatten
297
+
298
+ else # If Hash
299
+ out = {}
300
+ groups.each do |k,v|
301
+ if v.size == 1
302
+ out.merge!(v.first)
303
+ else
304
+ out.merge!( k => v.map{|e| e.to_hash[k]})
305
+ end
306
+ end
307
+ out.merge! attributes unless attributes.empty?
308
+ out = out.empty? ? nil : out
309
+ end
310
+
311
+ if @type && out.nil?
312
+ { name => typecast_value(out) }
313
+ else
314
+ { name => out }
315
+ end
316
+ end
317
+ end
318
+
319
+ # Typecasts a value based upon its type. For instance, if
320
+ # +node+ has #type == "integer",
321
+ # {{[node.typecast_value("12") #=> 12]}}
322
+ #
323
+ # ==== Parameters
324
+ # value<String>:: The value that is being typecast.
325
+ #
326
+ # ==== :type options
327
+ # "integer"::
328
+ # converts +value+ to an integer with #to_i
329
+ # "boolean"::
330
+ # checks whether +value+, after removing spaces, is the literal
331
+ # "true"
332
+ # "datetime"::
333
+ # Parses +value+ using Time.parse, and returns a UTC Time
334
+ # "date"::
335
+ # Parses +value+ using Date.parse
336
+ #
337
+ # ==== Returns
338
+ # Integer, true, false, Time, Date, Object::
339
+ # The result of typecasting +value+.
340
+ #
341
+ # ==== Note
342
+ # If +self+ does not have a "type" key, or if it's not one of the
343
+ # options specified above, the raw +value+ will be returned.
344
+ def typecast_value(value)
345
+ return value unless @type
346
+ proc = self.class.typecasts[@type]
347
+ proc.nil? ? value : proc.call(value)
348
+ end
349
+
350
+ # Convert basic XML entities into their literal values.
351
+ #
352
+ # ==== Parameters
353
+ # value<~gsub>::
354
+ # An XML fragment.
355
+ #
356
+ # ==== Returns
357
+ # ~gsub::
358
+ # The XML fragment after converting entities.
359
+ def translate_xml_entities(value)
360
+ value.gsub(/&lt;/, "<").
361
+ gsub(/&gt;/, ">").
362
+ gsub(/&quot;/, '"').
363
+ gsub(/&apos;/, "'").
364
+ gsub(/&amp;/, "&")
365
+ end
366
+
367
+ # Take keys of the form foo-bar and convert them to foo_bar
368
+ def undasherize_keys(params)
369
+ params.keys.each do |key, value|
370
+ params[key.tr("-", "_")] = params.delete(key)
371
+ end
372
+ params
373
+ end
374
+
375
+ # Get the inner_html of the REXML node.
376
+ def inner_html
377
+ @children.join
378
+ end
379
+
380
+ # Converts the node into a readable HTML node.
381
+ #
382
+ # ==== Returns
383
+ # String:: The HTML node in text form.
384
+ def to_html
385
+ attributes.merge!(:type => @type ) if @type
386
+ "<#{name}#{attributes.to_xml_attributes}>#{@nil_element ? '' : inner_html}</#{name}>"
387
+ end
388
+
389
+ # ==== Alias
390
+ # #to_html
391
+ def to_s
392
+ to_html
393
+ end
394
+ end
395
+
396
+ class ToHashParser # :nodoc:
397
+
398
+ def self.from_xml(xml)
399
+ stack = []
400
+ parser = REXML::Parsers::BaseParser.new(xml)
401
+
402
+ while true
403
+ event = parser.pull
404
+ case event[0]
405
+ when :end_document
406
+ break
407
+ when :end_doctype, :start_doctype
408
+ # do nothing
409
+ when :start_element
410
+ stack.push REXMLUtilityNode.new(event[1], event[2])
411
+ when :end_element
412
+ if stack.size > 1
413
+ temp = stack.pop
414
+ stack.last.add_node(temp)
415
+ end
416
+ when :text, :cdata
417
+ stack.last.add_node(event[1]) unless event[1].strip.length == 0
418
+ end
419
+ end
420
+ stack.pop.to_hash
421
+ end
422
+ end