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,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