merb-core 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
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