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