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,211 @@
1
+ module Language
2
+
3
+ module English
4
+
5
+ # = English Nouns Number Inflection.
6
+ #
7
+ # This module provides english singular <-> plural noun inflections.
8
+ module Inflect
9
+
10
+ @singular_of = {}
11
+ @plural_of = {}
12
+
13
+ @singular_rules = []
14
+ @plural_rules = []
15
+
16
+ class << self
17
+ # Define a general exception.
18
+ def word(singular, plural=nil)
19
+ plural = singular unless plural
20
+ singular_word(singular, plural)
21
+ plural_word(singular, plural)
22
+ end
23
+
24
+ # Define a singularization exception.
25
+ def singular_word(singular, plural)
26
+ @singular_of[plural] = singular
27
+ end
28
+
29
+ # Define a pluralization exception.
30
+ def plural_word(singular, plural)
31
+ @plural_of[singular] = plural
32
+ end
33
+
34
+ # Define a general rule.
35
+ def rule(singular, plural)
36
+ singular_rule(singular, plural)
37
+ plural_rule(singular, plural)
38
+ end
39
+
40
+ # Define a singularization rule.
41
+ def singular_rule(singular, plural)
42
+ @singular_rules << [singular, plural]
43
+ end
44
+
45
+ # Define a plurualization rule.
46
+ def plural_rule(singular, plural)
47
+ @plural_rules << [singular, plural]
48
+ end
49
+
50
+ # Read prepared singularization rules.
51
+ def singularization_rules
52
+ return @singularization_rules if @singularization_rules
53
+ sorted = @singular_rules.sort_by{ |s, p| "#{p}".size }.reverse
54
+ @singularization_rules = sorted.collect do |s, p|
55
+ [ /#{p}$/, "#{s}" ]
56
+ end
57
+ end
58
+
59
+ # Read prepared pluralization rules.
60
+ def pluralization_rules
61
+ return @pluralization_rules if @pluralization_rules
62
+ sorted = @plural_rules.sort_by{ |s, p| "#{s}".size }.reverse
63
+ @pluralization_rules = sorted.collect do |s, p|
64
+ [ /#{s}$/, "#{p}" ]
65
+ end
66
+ end
67
+
68
+ #
69
+ def plural_of
70
+ @plural_of
71
+ end
72
+
73
+ #
74
+ def singular_of
75
+ @singular_of
76
+ end
77
+
78
+ # Convert an English word from plurel to singular.
79
+ #
80
+ # "boys".singular #=> boy
81
+ # "tomatoes".singular #=> tomato
82
+ #
83
+ def singular(word)
84
+ if result = singular_of[word]
85
+ return result.dup
86
+ end
87
+ result = word.dup
88
+ singularization_rules.each do |(match, replacement)|
89
+ break if result.gsub!(match, replacement)
90
+ end
91
+ return result
92
+ end
93
+
94
+ # Alias for #singular (a Railism).
95
+ #
96
+ alias_method(:singularize, :singular)
97
+
98
+ # Convert an English word from singular to plurel.
99
+ #
100
+ # "boy".plural #=> boys
101
+ # "tomato".plural #=> tomatoes
102
+ #
103
+ def plural(word)
104
+ if result = plural_of[word]
105
+ return result.dup
106
+ end
107
+ #return self.dup if /s$/ =~ self # ???
108
+ result = word.dup
109
+ pluralization_rules.each do |(match, replacement)|
110
+ break if result.gsub!(match, replacement)
111
+ end
112
+ return result
113
+ end
114
+
115
+ # Alias for #plural (a Railism).
116
+ alias_method(:pluralize, :plural)
117
+ end
118
+
119
+ # One argument means singular and plural are the same.
120
+
121
+ word 'equipment'
122
+ word 'information'
123
+ word 'money'
124
+ word 'species'
125
+ word 'series'
126
+ word 'fish'
127
+ word 'sheep'
128
+ word 'moose'
129
+ word 'hovercraft'
130
+
131
+ # Two arguments defines a singular and plural exception.
132
+
133
+ word 'Swiss' , 'Swiss'
134
+ word 'life' , 'lives'
135
+ word 'wife' , 'wives'
136
+ word 'goose' , 'geese'
137
+ word 'criterion' , 'criteria'
138
+ word 'alias' , 'aliases'
139
+ word 'status' , 'statuses'
140
+ word 'axis' , 'axes'
141
+ word 'crisis' , 'crises'
142
+ word 'testis' , 'testes'
143
+ word 'child' , 'children'
144
+ word 'person' , 'people'
145
+ word 'potato' , 'potatoes'
146
+ word 'tomato' , 'tomatoes'
147
+ word 'buffalo' , 'buffaloes'
148
+ word 'torpedo' , 'torpedoes'
149
+ word 'quiz' , 'quizes'
150
+ word 'matrix' , 'matrices'
151
+ word 'vertex' , 'vetices'
152
+ word 'index' , 'indices'
153
+ word 'ox' , 'oxen'
154
+ word 'mouse' , 'mice'
155
+ word 'louse' , 'lice'
156
+ word 'thesis' , 'theses'
157
+ word 'thief' , 'thieves'
158
+ word 'analysis' , 'analyses'
159
+
160
+ # One-way singularization exception (convert plural to singular).
161
+
162
+ singular_word 'cactus', 'cacti'
163
+
164
+ # General rules.
165
+
166
+ rule 'hive' , 'hives'
167
+ rule 'rf' , 'rves'
168
+ rule 'af' , 'aves'
169
+ rule 'ero' , 'eroes'
170
+ rule 'man' , 'men'
171
+ rule 'ch' , 'ches'
172
+ rule 'sh' , 'shes'
173
+ rule 'ss' , 'sses'
174
+ rule 'ta' , 'tum'
175
+ rule 'ia' , 'ium'
176
+ rule 'ra' , 'rum'
177
+ rule 'ay' , 'ays'
178
+ rule 'ey' , 'eys'
179
+ rule 'oy' , 'oys'
180
+ rule 'uy' , 'uys'
181
+ rule 'y' , 'ies'
182
+ rule 'x' , 'xes'
183
+ rule 'lf' , 'lves'
184
+ rule 'us' , 'uses'
185
+ rule '' , 's'
186
+
187
+ # One-way singular rules.
188
+
189
+ singular_rule 'of' , 'ofs' # proof
190
+ singular_rule 'o' , 'oes' # hero, heroes
191
+ singular_rule 'f' , 'ves'
192
+
193
+ # One-way plural rules.
194
+
195
+ plural_rule 'fe' , 'ves' # safe, wife
196
+ plural_rule 's' , 'ses'
197
+
198
+ end
199
+ end
200
+ end
201
+
202
+ class String
203
+ def singular
204
+ Language::English::Inflect.singular(self)
205
+ end
206
+ alias_method(:singularize, :singular)
207
+ def plural
208
+ Language::English::Inflect.plural(self)
209
+ end
210
+ alias_method(:pluralize, :plural)
211
+ end
@@ -0,0 +1,11 @@
1
+ module Merb
2
+ VERSION = '0.9.2' unless defined?(Merb::VERSION)
3
+
4
+ # Merb::RELEASE meanings:
5
+ # 'dev' : unreleased
6
+ # 'pre' : pre-release Gem candidates
7
+ # nil : released
8
+ # You should never check in to trunk with this changed. It should
9
+ # stay 'dev'. Change it to nil in release tags.
10
+ RELEASE = nil unless defined?(Merb::RELEASE)
11
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ MERB_BIN = File.dirname(__FILE__) + "/../../../bin/merb"
4
+
5
+ describe Merb::Config do
6
+ before do
7
+ ARGV.replace([])
8
+ Merb::Server.should_receive(:start).and_return(nil)
9
+ end
10
+
11
+ it "should load the runner adapter by default" do
12
+ Merb.start
13
+ Merb::Config[:adapter].should == "runner"
14
+ end
15
+
16
+ it "should load mongrel adapter when running `merb`" do
17
+ load(MERB_BIN)
18
+ Merb::Config[:adapter].should == "mongrel"
19
+ end
20
+
21
+ it "should override adapter when running `merb -a other`" do
22
+ ARGV.push *%w[-a other]
23
+ load(MERB_BIN)
24
+ Merb::Config[:adapter].should == "other"
25
+ end
26
+
27
+ it "should load irb adapter when running `merb -i`" do
28
+ ARGV << '-i'
29
+ load(MERB_BIN)
30
+ Merb::Config[:adapter].should == "irb"
31
+ end
32
+ end
@@ -0,0 +1,139 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Merb::Config do
4
+ before do
5
+ Merb::Config.setup
6
+ end
7
+
8
+ it "should be able to yield the configuration via #use" do
9
+ res = nil
10
+ Merb::Config.use {|c| res = c}
11
+ res.should == Merb::Config.defaults
12
+ end
13
+
14
+ it "should be able to get a configuration key" do
15
+ Merb::Config[:host].should == "0.0.0.0"
16
+ end
17
+
18
+ it "should be able to set a configuration key" do
19
+ Merb::Config[:bar] = "Hello"
20
+ Merb::Config[:bar].should == "Hello"
21
+ end
22
+
23
+ it "should be able to #delete a configuration key" do
24
+ Merb::Config[:bar] = "Hello"
25
+ Merb::Config[:bar].should == "Hello"
26
+ Merb::Config.delete(:bar)
27
+ Merb::Config[:bar].should == nil
28
+ end
29
+
30
+ it "should be able to #fetch a key that does exist" do
31
+ Merb::Config.fetch(:host, "192.168.2.1").should == "0.0.0.0"
32
+ end
33
+
34
+ it "should be able to #fetch a key that does exist" do
35
+ Merb::Config.fetch(:bar, "heylo").should == "heylo"
36
+ end
37
+
38
+ it "should be able to dump to YAML" do
39
+ Merb::Config.to_yaml.should == Merb::Config.instance_variable_get("@configuration").to_yaml
40
+ end
41
+
42
+ it "should support -u to set the user to run Merb as" do
43
+ Merb::Config.parse_args(["-u", "tester"])
44
+ Merb::Config[:user].should == "tester"
45
+ end
46
+
47
+ it "should support -G to set the group to run Merb as" do
48
+ Merb::Config.parse_args(["-G", "tester"])
49
+ Merb::Config[:group].should == "tester"
50
+ end
51
+
52
+ it "should support -f to set the filename to run Merb as" do
53
+ Merb::Config.parse_args(["-d"])
54
+ Merb::Config[:daemonize].should == true
55
+ end
56
+
57
+ it "should support -c to set the number of cluster nodes" do
58
+ Merb::Config.parse_args(["-c", "4"])
59
+ Merb::Config[:cluster].should == "4"
60
+ end
61
+
62
+ it "should support -p to set the port number" do
63
+ Merb::Config.parse_args(["-p", "6000"])
64
+ Merb::Config[:port].should == "6000"
65
+ end
66
+
67
+ it "should support -P to set the PIDfile" do
68
+ Merb::Config.parse_args(["-P", "pidfile"])
69
+ Merb::Config[:pid_file].should == "pidfile"
70
+ end
71
+
72
+ it "should support -h to set the hostname" do
73
+ Merb::Config.parse_args(["-h", "hostname"])
74
+ Merb::Config[:host].should == "hostname"
75
+ end
76
+
77
+ it "should support -i to specify loading IRB" do
78
+ Merb::Config.parse_args(["-i"])
79
+ Merb::Config[:adapter].should == "irb"
80
+ end
81
+
82
+ it "should support -l to specify the log level" do
83
+ Merb::Config.parse_args(["-l", "debug"])
84
+ Merb::Config[:log_level].should == :debug
85
+ end
86
+
87
+ it "should support -L to specify the location of the log file" do
88
+ Merb::Config.parse_args(["-L", "log_file"])
89
+ Merb::Config[:log_file].should == "log_file"
90
+ end
91
+
92
+ it "should support -r to specify a runner" do
93
+ Merb::Config.parse_args(["-r", "foo_runner"])
94
+ Merb::Config[:runner_code].should == "foo_runner"
95
+ Merb::Config[:adapter].should == "runner"
96
+ end
97
+
98
+ it "should support -K for a graceful kill" do
99
+ Merb::Server.should_receive(:kill).with("all", 1)
100
+ Merb::Config.parse_args(["-K", "all"])
101
+ end
102
+
103
+ it "should support -k for a hard kill" do
104
+ Merb::Server.should_receive(:kill).with("all", 9)
105
+ Merb::Config.parse_args(["-k", "all"])
106
+ end
107
+
108
+ it "should support -X off to turn off the mutex" do
109
+ Merb::Config.parse_args(["-X", "off"])
110
+ Merb::Config[:use_mutex].should == false
111
+ end
112
+
113
+ it "should support -X on to turn off the mutex" do
114
+ Merb::Config.parse_args(["-X", "on"])
115
+ Merb::Config[:use_mutex].should == true
116
+ end
117
+
118
+ it "should take Merb.disable into account" do
119
+ Merb::Config[:disabled_components].should == []
120
+ Merb::Config[:disabled_components] << :foo
121
+ Merb.disable(:bar)
122
+ Merb.disable(:buz, :fux)
123
+ Merb::Config[:disabled_components].should == [:foo, :bar, :buz, :fux]
124
+ Merb.disabled?(:foo).should == true
125
+ Merb.disabled?(:foo, :buz).should == true
126
+ end
127
+
128
+ it "should take Merb.testing? into account" do
129
+ $TESTING.should == true
130
+ Merb::Config[:testing].should be_nil
131
+ Merb.should be_testing
132
+ $TESTING = false
133
+ Merb.should_not be_testing
134
+ Merb::Config[:testing] = true
135
+ Merb.should be_testing
136
+ $TESTING = true; Merb::Config[:testing] = false # reset
137
+ end
138
+
139
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Merb::Config do
4
+ it "should apply environment from the command line option --environment" do
5
+ Merb.start %w( --environment performance_testing )
6
+ Merb.environment.should == "performance_testing"
7
+ end
8
+
9
+ it "should apply environment from the command line option -e" do
10
+ Merb.start %w( -e selenium )
11
+ Merb.environment.should == "selenium"
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "..", "..", "spec_helper")
@@ -0,0 +1,506 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require "date"
3
+ require 'bigdecimal'
4
+
5
+ describe Hash, "environmentize_keys!" do
6
+ it "should transform keys to uppercase text" do
7
+ { :test_1 => 'test', 'test_2' => 'test', 1 => 'test' }.environmentize_keys!.should ==
8
+ { 'TEST_1' => 'test', 'TEST_2' => 'test', '1' => 'test' }
9
+ end
10
+
11
+ it "should only transform one level of keys" do
12
+ { :test_1 => { :test2 => 'test'} }.environmentize_keys!.should ==
13
+ { 'TEST_1' => { :test2 => 'test'} }
14
+ end
15
+ end
16
+
17
+ describe Hash, "only" do
18
+ before do
19
+ @hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE' }
20
+ end
21
+
22
+ it "should return a hash with only the given key(s)" do
23
+ @hash.only(:one).should == { :one => 'ONE' }
24
+ @hash.only(:one, 3).should == { :one => 'ONE', 3 => 'THREE' }
25
+ end
26
+ end
27
+
28
+ describe Hash, "except" do
29
+ before do
30
+ @hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE' }
31
+ end
32
+
33
+ it "should return a hash without only the given key(s)" do
34
+ @hash.except(:one).should == { 'two' => 'TWO', 3 => 'THREE' }
35
+ @hash.except(:one, 3).should == { 'two' => 'TWO' }
36
+ end
37
+ end
38
+
39
+ describe Hash, "to_xml_attributes" do
40
+ before do
41
+ @hash = { :one => "ONE", "two" => "TWO" }
42
+ end
43
+
44
+ it "should turn the hash into xml attributes" do
45
+ attrs = @hash.to_xml_attributes
46
+ attrs.should match(/one="ONE"/m)
47
+ attrs.should match(/two="TWO"/m)
48
+ end
49
+ end
50
+
51
+ describe Hash, "from_xml" do
52
+ it "should transform a simple tag with content" do
53
+ xml = "<tag>This is the contents</tag>"
54
+ Hash.from_xml(xml).should == { 'tag' => 'This is the contents' }
55
+ end
56
+
57
+ it "should work with cdata tags" do
58
+ xml = <<-END
59
+ <tag>
60
+ <![CDATA[
61
+ text inside cdata
62
+ ]]>
63
+ </tag>
64
+ END
65
+ Hash.from_xml(xml)["tag"].strip.should == "text inside cdata"
66
+ end
67
+
68
+ it "should transform a simple tag with attributes" do
69
+ xml = "<tag attr1='1' attr2='2'></tag>"
70
+ hash = { 'tag' => { 'attr1' => '1', 'attr2' => '2' } }
71
+ Hash.from_xml(xml).should == hash
72
+ end
73
+
74
+ it "should transform repeating siblings into an array" do
75
+ xml =<<-XML
76
+ <opt>
77
+ <user login="grep" fullname="Gary R Epstein" />
78
+ <user login="stty" fullname="Simon T Tyson" />
79
+ </opt>
80
+ XML
81
+
82
+ Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Array)
83
+
84
+ hash = {
85
+ 'opt' => {
86
+ 'user' => [{
87
+ 'login' => 'grep',
88
+ 'fullname' => 'Gary R Epstein'
89
+ },{
90
+ 'login' => 'stty',
91
+ 'fullname' => 'Simon T Tyson'
92
+ }]
93
+ }
94
+ }
95
+
96
+ Hash.from_xml(xml).should == hash
97
+ end
98
+
99
+ it "should not transform non-repeating siblings into an array" do
100
+ xml =<<-XML
101
+ <opt>
102
+ <user login="grep" fullname="Gary R Epstein" />
103
+ </opt>
104
+ XML
105
+
106
+ Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Hash)
107
+
108
+ hash = {
109
+ 'opt' => {
110
+ 'user' => {
111
+ 'login' => 'grep',
112
+ 'fullname' => 'Gary R Epstein'
113
+ }
114
+ }
115
+ }
116
+
117
+ Hash.from_xml(xml).should == hash
118
+ end
119
+
120
+ it "should typecast an integer" do
121
+ xml = "<tag type='integer'>10</tag>"
122
+ Hash.from_xml(xml)['tag'].should == 10
123
+ end
124
+
125
+ it "should typecast a true boolean" do
126
+ xml = "<tag type='boolean'>true</tag>"
127
+ Hash.from_xml(xml)['tag'].should be_true
128
+ end
129
+
130
+ it "should typecast a false boolean" do
131
+ ["false"].each do |w|
132
+ Hash.from_xml("<tag type='boolean'>#{w}</tag>")['tag'].should be_false
133
+ end
134
+ end
135
+
136
+ it "should typecast a datetime" do
137
+ xml = "<tag type='datetime'>2007-12-31 10:32</tag>"
138
+ Hash.from_xml(xml)['tag'].should == Time.parse( '2007-12-31 10:32' ).utc
139
+ end
140
+
141
+ it "should typecast a date" do
142
+ xml = "<tag type='date'>2007-12-31</tag>"
143
+ Hash.from_xml(xml)['tag'].should == Date.parse('2007-12-31')
144
+ end
145
+
146
+ it "should unescape html entities" do
147
+ values = {
148
+ "<" => "&lt;",
149
+ ">" => "&gt;",
150
+ '"' => "&quot;",
151
+ "'" => "&apos;",
152
+ "&" => "&amp;"
153
+ }
154
+ values.each do |k,v|
155
+ xml = "<tag>Some content #{v}</tag>"
156
+ Hash.from_xml(xml)['tag'].should match(Regexp.new(k))
157
+ end
158
+ end
159
+
160
+ it "should undasherize keys as tags" do
161
+ xml = "<tag-1>Stuff</tag-1>"
162
+ Hash.from_xml(xml).keys.should include( 'tag_1' )
163
+ end
164
+
165
+ it "should undasherize keys as attributes" do
166
+ xml = "<tag1 attr-1='1'></tag1>"
167
+ Hash.from_xml(xml)['tag1'].keys.should include( 'attr_1')
168
+ end
169
+
170
+ it "should undasherize keys as tags and attributes" do
171
+ xml = "<tag-1 attr-1='1'></tag-1>"
172
+ Hash.from_xml(xml).keys.should include( 'tag_1' )
173
+ Hash.from_xml(xml)['tag_1'].keys.should include( 'attr_1')
174
+ end
175
+
176
+ it "should render nested content correctly" do
177
+ xml = "<root><tag1>Tag1 Content <em><strong>This is strong</strong></em></tag1></root>"
178
+ Hash.from_xml(xml)['root']['tag1'].should == "Tag1 Content <em><strong>This is strong</strong></em>"
179
+ end
180
+
181
+ it "should render nested content with split text nodes correctly" do
182
+ xml = "<root>Tag1 Content<em>Stuff</em> Hi There</root>"
183
+ Hash.from_xml(xml)['root'].should == "Tag1 Content<em>Stuff</em> Hi There"
184
+ end
185
+
186
+ it "should ignore attributes when a child is a text node" do
187
+ xml = "<root attr1='1'>Stuff</root>"
188
+ Hash.from_xml(xml).should == { "root" => "Stuff" }
189
+ end
190
+
191
+ it "should ignore attributes when any child is a text node" do
192
+ xml = "<root attr1='1'>Stuff <em>in italics</em></root>"
193
+ Hash.from_xml(xml).should == { "root" => "Stuff <em>in italics</em>" }
194
+ end
195
+
196
+ it "should correctly transform multiple children" do
197
+ xml = <<-XML
198
+ <user gender='m'>
199
+ <age type='integer'>35</age>
200
+ <name>Home Simpson</name>
201
+ <dob type='date'>1988-01-01</dob>
202
+ <joined-at type='datetime'>2000-04-28 23:01</joined-at>
203
+ <is-cool type='boolean'>true</is-cool>
204
+ </user>
205
+ XML
206
+
207
+ hash = {
208
+ "user" => {
209
+ "gender" => "m",
210
+ "age" => 35,
211
+ "name" => "Home Simpson",
212
+ "dob" => Date.parse('1988-01-01'),
213
+ "joined_at" => Time.parse("2000-04-28 23:01"),
214
+ "is_cool" => true
215
+ }
216
+ }
217
+
218
+ Hash.from_xml(xml).should == hash
219
+ end
220
+
221
+ it "should properly handle nil values (ActiveSupport Compatible)" do
222
+ topic_xml = <<-EOT
223
+ <topic>
224
+ <title></title>
225
+ <id type="integer"></id>
226
+ <approved type="boolean"></approved>
227
+ <written-on type="date"></written-on>
228
+ <viewed-at type="datetime"></viewed-at>
229
+ <content type="yaml"></content>
230
+ <parent-id></parent-id>
231
+ </topic>
232
+ EOT
233
+
234
+ expected_topic_hash = {
235
+ 'title' => nil,
236
+ 'id' => nil,
237
+ 'approved' => nil,
238
+ 'written_on' => nil,
239
+ 'viewed_at' => nil,
240
+ 'content' => nil,
241
+ 'parent_id' => nil
242
+ }
243
+ Hash.from_xml(topic_xml)["topic"].should == expected_topic_hash
244
+ end
245
+
246
+ it "should handle a single record from xml (ActiveSupport Compatible)" do
247
+ topic_xml = <<-EOT
248
+ <topic>
249
+ <title>The First Topic</title>
250
+ <author-name>David</author-name>
251
+ <id type="integer">1</id>
252
+ <approved type="boolean"> true </approved>
253
+ <replies-count type="integer">0</replies-count>
254
+ <replies-close-in type="integer">2592000000</replies-close-in>
255
+ <written-on type="date">2003-07-16</written-on>
256
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
257
+ <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
258
+ <author-email-address>david@loudthinking.com</author-email-address>
259
+ <parent-id></parent-id>
260
+ <ad-revenue type="decimal">1.5</ad-revenue>
261
+ <optimum-viewing-angle type="float">135</optimum-viewing-angle>
262
+ <resident type="symbol">yes</resident>
263
+ </topic>
264
+ EOT
265
+
266
+ expected_topic_hash = {
267
+ 'title' => "The First Topic",
268
+ 'author_name' => "David",
269
+ 'id' => 1,
270
+ 'approved' => true,
271
+ 'replies_count' => 0,
272
+ 'replies_close_in' => 2592000000,
273
+ 'written_on' => Date.new(2003, 7, 16),
274
+ 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
275
+ # Changed this line where the key is :message. The yaml specifies this as a symbol, and who am I to change what you specify
276
+ # The line in ActiveSupport is
277
+ # 'content' => { 'message' => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
278
+ 'content' => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
279
+ 'author_email_address' => "david@loudthinking.com",
280
+ 'parent_id' => nil,
281
+ 'ad_revenue' => BigDecimal("1.50"),
282
+ 'optimum_viewing_angle' => 135.0,
283
+ 'resident' => :yes
284
+ }
285
+
286
+ Hash.from_xml(topic_xml)["topic"].each do |k,v|
287
+ v.should == expected_topic_hash[k]
288
+ end
289
+ end
290
+
291
+ it "should handle multiple records (ActiveSupport Compatible)" do
292
+ topics_xml = <<-EOT
293
+ <topics type="array">
294
+ <topic>
295
+ <title>The First Topic</title>
296
+ <author-name>David</author-name>
297
+ <id type="integer">1</id>
298
+ <approved type="boolean">false</approved>
299
+ <replies-count type="integer">0</replies-count>
300
+ <replies-close-in type="integer">2592000000</replies-close-in>
301
+ <written-on type="date">2003-07-16</written-on>
302
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
303
+ <content>Have a nice day</content>
304
+ <author-email-address>david@loudthinking.com</author-email-address>
305
+ <parent-id nil="true"></parent-id>
306
+ </topic>
307
+ <topic>
308
+ <title>The Second Topic</title>
309
+ <author-name>Jason</author-name>
310
+ <id type="integer">1</id>
311
+ <approved type="boolean">false</approved>
312
+ <replies-count type="integer">0</replies-count>
313
+ <replies-close-in type="integer">2592000000</replies-close-in>
314
+ <written-on type="date">2003-07-16</written-on>
315
+ <viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
316
+ <content>Have a nice day</content>
317
+ <author-email-address>david@loudthinking.com</author-email-address>
318
+ <parent-id></parent-id>
319
+ </topic>
320
+ </topics>
321
+ EOT
322
+
323
+ expected_topic_hash = {
324
+ 'title' => "The First Topic",
325
+ 'author_name' => "David",
326
+ 'id' => 1,
327
+ 'approved' => false,
328
+ 'replies_count' => 0,
329
+ 'replies_close_in' => 2592000000,
330
+ 'written_on' => Date.new(2003, 7, 16),
331
+ 'viewed_at' => Time.utc(2003, 7, 16, 9, 28),
332
+ 'content' => "Have a nice day",
333
+ 'author_email_address' => "david@loudthinking.com",
334
+ 'parent_id' => nil
335
+ }
336
+ # puts Hash.from_xml(topics_xml)['topics'].first.inspect
337
+ Hash.from_xml(topics_xml)["topics"].first.each do |k,v|
338
+ v.should == expected_topic_hash[k]
339
+ end
340
+ end
341
+
342
+ it "should handle a single record from_xml with attributes other than type (ActiveSupport Compatible)" do
343
+ topic_xml = <<-EOT
344
+ <rsp stat="ok">
345
+ <photos page="1" pages="1" perpage="100" total="16">
346
+ <photo id="175756086" owner="55569174@N00" secret="0279bf37a1" server="76" title="Colored Pencil PhotoBooth Fun" ispublic="1" isfriend="0" isfamily="0"/>
347
+ </photos>
348
+ </rsp>
349
+ EOT
350
+
351
+ expected_topic_hash = {
352
+ 'id' => "175756086",
353
+ 'owner' => "55569174@N00",
354
+ 'secret' => "0279bf37a1",
355
+ 'server' => "76",
356
+ 'title' => "Colored Pencil PhotoBooth Fun",
357
+ 'ispublic' => "1",
358
+ 'isfriend' => "0",
359
+ 'isfamily' => "0",
360
+ }
361
+ Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"].each do |k,v|
362
+ v.should == expected_topic_hash[k]
363
+ end
364
+ end
365
+
366
+ it "should handle an emtpy array (ActiveSupport Compatible)" do
367
+ blog_xml = <<-XML
368
+ <blog>
369
+ <posts type="array"></posts>
370
+ </blog>
371
+ XML
372
+ expected_blog_hash = {"blog" => {"posts" => []}}
373
+ Hash.from_xml(blog_xml).should == expected_blog_hash
374
+ end
375
+
376
+ it "should handle empty array with whitespace from xml (ActiveSupport Compatible)" do
377
+ blog_xml = <<-XML
378
+ <blog>
379
+ <posts type="array">
380
+ </posts>
381
+ </blog>
382
+ XML
383
+ expected_blog_hash = {"blog" => {"posts" => []}}
384
+ Hash.from_xml(blog_xml).should == expected_blog_hash
385
+ end
386
+
387
+ it "should handle array with one entry from_xml (ActiveSupport Compatible)" do
388
+ blog_xml = <<-XML
389
+ <blog>
390
+ <posts type="array">
391
+ <post>a post</post>
392
+ </posts>
393
+ </blog>
394
+ XML
395
+ expected_blog_hash = {"blog" => {"posts" => ["a post"]}}
396
+ Hash.from_xml(blog_xml).should == expected_blog_hash
397
+ end
398
+
399
+ it "should handle array with multiple entries from xml (ActiveSupport Compatible)" do
400
+ blog_xml = <<-XML
401
+ <blog>
402
+ <posts type="array">
403
+ <post>a post</post>
404
+ <post>another post</post>
405
+ </posts>
406
+ </blog>
407
+ XML
408
+ expected_blog_hash = {"blog" => {"posts" => ["a post", "another post"]}}
409
+ Hash.from_xml(blog_xml).should == expected_blog_hash
410
+ end
411
+
412
+ it "should handle file types (ActiveSupport Compatible)" do
413
+ blog_xml = <<-XML
414
+ <blog>
415
+ <logo type="file" name="logo.png" content_type="image/png">
416
+ </logo>
417
+ </blog>
418
+ XML
419
+ hash = Hash.from_xml(blog_xml)
420
+ hash.should have_key('blog')
421
+ hash['blog'].should have_key('logo')
422
+
423
+ file = hash['blog']['logo']
424
+ file.original_filename.should == 'logo.png'
425
+ file.content_type.should == 'image/png'
426
+ end
427
+
428
+ it "should handle file from xml with defaults (ActiveSupport Compatible)" do
429
+ blog_xml = <<-XML
430
+ <blog>
431
+ <logo type="file">
432
+ </logo>
433
+ </blog>
434
+ XML
435
+ file = Hash.from_xml(blog_xml)['blog']['logo']
436
+ file.original_filename.should == 'untitled'
437
+ file.content_type.should == 'application/octet-stream'
438
+ end
439
+
440
+ it "should handle xsd like types from xml (ActiveSupport Compatible)" do
441
+ bacon_xml = <<-EOT
442
+ <bacon>
443
+ <weight type="double">0.5</weight>
444
+ <price type="decimal">12.50</price>
445
+ <chunky type="boolean"> 1 </chunky>
446
+ <expires-at type="dateTime">2007-12-25T12:34:56+0000</expires-at>
447
+ <notes type="string"></notes>
448
+ <illustration type="base64Binary">YmFiZS5wbmc=</illustration>
449
+ </bacon>
450
+ EOT
451
+
452
+ expected_bacon_hash = {
453
+ 'weight' => 0.5,
454
+ 'chunky' => true,
455
+ 'price' => BigDecimal("12.50"),
456
+ 'expires_at' => Time.utc(2007,12,25,12,34,56),
457
+ 'notes' => "",
458
+ 'illustration' => "babe.png"
459
+ }
460
+
461
+ Hash.from_xml(bacon_xml)["bacon"].should == expected_bacon_hash
462
+ end
463
+
464
+ it "should let type trickle through when unknown (ActiveSupport Compatible)" do
465
+ product_xml = <<-EOT
466
+ <product>
467
+ <weight type="double">0.5</weight>
468
+ <image type="ProductImage"><filename>image.gif</filename></image>
469
+
470
+ </product>
471
+ EOT
472
+
473
+ expected_product_hash = {
474
+ 'weight' => 0.5,
475
+ 'image' => {'type' => 'ProductImage', 'filename' => 'image.gif' },
476
+ }
477
+
478
+ Hash.from_xml(product_xml)["product"].should == expected_product_hash
479
+ end
480
+
481
+ it "should handle unescaping from xml (ActiveResource Compatible)" do
482
+ xml_string = '<person><bare-string>First &amp; Last Name</bare-string><pre-escaped-string>First &amp;amp; Last Name</pre-escaped-string></person>'
483
+ expected_hash = {
484
+ 'bare_string' => 'First & Last Name',
485
+ 'pre_escaped_string' => 'First &amp; Last Name'
486
+ }
487
+
488
+ Hash.from_xml(xml_string)['person'].should == expected_hash
489
+ end
490
+
491
+ end
492
+
493
+ describe Hash, 'to_params' do
494
+ before do
495
+ @hash = { :name => 'Bob', :address => { :street => '111 Ruby Ave.', :city => 'Ruby Central', :phones => ['111-111-1111', '222-222-2222'] } }
496
+ end
497
+
498
+ it 'should convert correctly into query parameters' do
499
+ @hash.to_params.split('&').sort.should ==
500
+ 'name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave.'.split('&').sort
501
+ end
502
+
503
+ it 'should not leave a trailing &' do
504
+ @hash.to_params.should_not match(/&$/)
505
+ end
506
+ end