merb 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. data/README +25 -26
  2. data/Rakefile +48 -36
  3. data/app_generators/merb/USAGE +5 -0
  4. data/app_generators/merb/merb_generator.rb +107 -0
  5. data/app_generators/merb/templates/Rakefile +99 -0
  6. data/{examples/skeleton/dist → app_generators/merb/templates}/app/controllers/application.rb +1 -1
  7. data/app_generators/merb/templates/app/controllers/exceptions.rb +13 -0
  8. data/{examples/skeleton/dist → app_generators/merb/templates}/app/helpers/global_helper.rb +0 -0
  9. data/{examples/skeleton/dist/app/mailers → app_generators/merb/templates/app/mailers/views}/layout/application.erb +0 -0
  10. data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +207 -0
  11. data/app_generators/merb/templates/app/views/exceptions/not_acceptable.html.erb +38 -0
  12. data/app_generators/merb/templates/app/views/exceptions/not_found.html.erb +40 -0
  13. data/app_generators/merb/templates/app/views/layout/application.html.erb +11 -0
  14. data/app_generators/merb/templates/config/boot.rb +11 -0
  15. data/app_generators/merb/templates/config/dependencies.rb +41 -0
  16. data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/development.rb +0 -0
  17. data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/production.rb +0 -0
  18. data/{examples/skeleton/dist/conf → app_generators/merb/templates/config}/environments/test.rb +0 -0
  19. data/app_generators/merb/templates/config/merb.yml +64 -0
  20. data/app_generators/merb/templates/config/merb_init.rb +16 -0
  21. data/app_generators/merb/templates/config/plugins.yml +1 -0
  22. data/app_generators/merb/templates/config/router.rb +32 -0
  23. data/{lib/merb/core_ext/merb_array.rb → app_generators/merb/templates/config/upload.conf} +0 -0
  24. data/app_generators/merb/templates/public/images/merb.jpg +0 -0
  25. data/app_generators/merb/templates/public/merb.fcgi +6 -0
  26. data/app_generators/merb/templates/public/stylesheets/master.css +119 -0
  27. data/app_generators/merb/templates/script/destroy +28 -0
  28. data/app_generators/merb/templates/script/generate +28 -0
  29. data/{examples/skeleton → app_generators/merb/templates}/script/stop_merb +0 -0
  30. data/app_generators/merb/templates/script/win_script.cmd +1 -0
  31. data/app_generators/merb/templates/spec/spec.opts +6 -0
  32. data/app_generators/merb/templates/spec/spec_helper.rb +10 -0
  33. data/app_generators/merb/templates/test/test_helper.rb +13 -0
  34. data/app_generators/merb_plugin/USAGE +5 -0
  35. data/app_generators/merb_plugin/merb_plugin_generator.rb +64 -0
  36. data/app_generators/merb_plugin/templates/LICENSE +20 -0
  37. data/app_generators/merb_plugin/templates/README +4 -0
  38. data/app_generators/merb_plugin/templates/Rakefile +35 -0
  39. data/app_generators/merb_plugin/templates/TODO +5 -0
  40. data/app_generators/merb_plugin/templates/merbtasks.rb +6 -0
  41. data/app_generators/merb_plugin/templates/sampleplugin.rb +10 -0
  42. data/app_generators/merb_plugin/templates/sampleplugin_spec.rb +7 -0
  43. data/app_generators/merb_plugin/templates/spec_helper.rb +2 -0
  44. data/bin/merb +1 -1
  45. data/lib/autotest/discover.rb +3 -0
  46. data/lib/autotest/merb_rspec.rb +79 -0
  47. data/lib/merb.rb +72 -93
  48. data/lib/merb/{merb_abstract_controller.rb → abstract_controller.rb} +28 -5
  49. data/lib/merb/caching/action_cache.rb +65 -29
  50. data/lib/merb/caching/fragment_cache.rb +9 -4
  51. data/lib/merb/caching/store/file_cache.rb +22 -14
  52. data/lib/merb/caching/store/memory_cache.rb +26 -8
  53. data/lib/merb/{merb_constants.rb → constants.rb} +9 -7
  54. data/lib/merb/controller.rb +178 -0
  55. data/lib/merb/core_ext.rb +13 -11
  56. data/lib/merb/core_ext/array.rb +0 -0
  57. data/lib/merb/core_ext/{merb_class.rb → class.rb} +0 -0
  58. data/lib/merb/core_ext/{merb_enumerable.rb → enumerable.rb} +0 -0
  59. data/lib/merb/core_ext/get_args.rb +52 -0
  60. data/lib/merb/core_ext/{merb_hash.rb → hash.rb} +40 -11
  61. data/lib/merb/core_ext/{merb_inflections.rb → inflections.rb} +0 -0
  62. data/lib/merb/core_ext/{merb_inflector.rb → inflector.rb} +1 -1
  63. data/lib/merb/core_ext/{merb_kernel.rb → kernel.rb} +56 -3
  64. data/lib/merb/core_ext/mash.rb +88 -0
  65. data/lib/merb/core_ext/{merb_module.rb → module.rb} +0 -0
  66. data/lib/merb/core_ext/{merb_numeric.rb → numeric.rb} +0 -0
  67. data/lib/merb/core_ext/{merb_object.rb → object.rb} +10 -47
  68. data/lib/merb/core_ext/string.rb +56 -0
  69. data/lib/merb/core_ext/{merb_symbol.rb → symbol.rb} +0 -0
  70. data/lib/merb/dispatcher.rb +109 -0
  71. data/lib/merb/{merb_drb_server.rb → drb_server.rb} +0 -0
  72. data/lib/merb/erubis_ext.rb +10 -0
  73. data/lib/merb/exceptions.rb +173 -0
  74. data/lib/merb/generators/merb_app/merb_app.rb +5 -25
  75. data/lib/merb/generators/merb_generator_helpers.rb +317 -0
  76. data/lib/merb/generators/merb_plugin.rb +19 -0
  77. data/lib/merb/logger.rb +65 -0
  78. data/lib/merb/{merb_mail_controller.rb → mail_controller.rb} +102 -49
  79. data/lib/merb/{merb_mailer.rb → mailer.rb} +31 -27
  80. data/lib/merb/mixins/{basic_authentication_mixin.rb → basic_authentication.rb} +3 -3
  81. data/lib/merb/mixins/{controller_mixin.rb → controller.rb} +131 -112
  82. data/lib/merb/mixins/{erubis_capture_mixin.rb → erubis_capture.rb} +12 -21
  83. data/lib/merb/mixins/{form_control_mixin.rb → form_control.rb} +6 -12
  84. data/lib/merb/mixins/render.rb +401 -0
  85. data/lib/merb/mixins/responder.rb +378 -0
  86. data/lib/merb/mixins/{view_context_mixin.rb → view_context.rb} +65 -10
  87. data/lib/merb/mixins/web_controller.rb +29 -0
  88. data/lib/merb/{merb_handler.rb → mongrel_handler.rb} +59 -38
  89. data/lib/merb/part_controller.rb +19 -0
  90. data/lib/merb/plugins.rb +16 -0
  91. data/lib/merb/rack_adapter.rb +37 -0
  92. data/lib/merb/request.rb +421 -0
  93. data/lib/merb/router.rb +576 -0
  94. data/lib/merb/{merb_server.rb → server.rb} +275 -71
  95. data/lib/merb/session.rb +10 -10
  96. data/lib/merb/session/cookie_store.rb +125 -0
  97. data/lib/merb/session/{merb_mem_cache_session.rb → mem_cache_session.rb} +22 -9
  98. data/lib/merb/session/{merb_memory_session.rb → memory_session.rb} +15 -11
  99. data/lib/merb/template.rb +35 -8
  100. data/lib/merb/template/erubis.rb +16 -10
  101. data/lib/merb/template/haml.rb +33 -20
  102. data/lib/merb/template/markaby.rb +16 -14
  103. data/lib/merb/template/xml_builder.rb +8 -4
  104. data/lib/merb/test/{merb_fake_request.rb → fake_request.rb} +11 -5
  105. data/lib/merb/test/helper.rb +31 -0
  106. data/lib/merb/test/hpricot.rb +136 -0
  107. data/lib/merb/test/{merb_multipart.rb → multipart.rb} +1 -1
  108. data/lib/merb/test/rspec.rb +93 -0
  109. data/lib/merb/{merb_upload_handler.rb → upload_handler.rb} +5 -6
  110. data/lib/merb/{merb_upload_progress.rb → upload_progress.rb} +1 -1
  111. data/lib/merb/{merb_view_context.rb → view_context.rb} +27 -42
  112. data/lib/{merb_tasks.rb → tasks.rb} +0 -0
  113. data/lib/tasks/merb.rake +21 -11
  114. data/merb_default_generators/model/USAGE +0 -0
  115. data/merb_default_generators/model/model_generator.rb +16 -0
  116. data/merb_default_generators/model/templates/new_model_template.erb +5 -0
  117. data/merb_default_generators/resource_controller/USAGE +0 -0
  118. data/merb_default_generators/resource_controller/resource_controller_generator.rb +26 -0
  119. data/merb_default_generators/resource_controller/templates/controller.rb +30 -0
  120. data/merb_default_generators/resource_controller/templates/edit.html.erb +1 -0
  121. data/merb_default_generators/resource_controller/templates/helper.rb +5 -0
  122. data/merb_default_generators/resource_controller/templates/index.html.erb +1 -0
  123. data/merb_default_generators/resource_controller/templates/new.html.erb +1 -0
  124. data/merb_default_generators/resource_controller/templates/show.html.erb +1 -0
  125. data/merb_generators/controller/USAGE +5 -0
  126. data/merb_generators/controller/controller_generator.rb +16 -0
  127. data/merb_generators/controller/templates/controller.rb +8 -0
  128. data/merb_generators/controller/templates/helper.rb +5 -0
  129. data/merb_generators/controller/templates/index.html.erb +3 -0
  130. data/merb_generators/resource/USAGE +0 -0
  131. data/merb_generators/resource/resource_generator.rb +60 -0
  132. data/rspec_generators/merb_controller_test/merb_controller_test_generator.rb +67 -0
  133. data/rspec_generators/merb_controller_test/templates/controller_spec.rb +8 -0
  134. data/rspec_generators/merb_controller_test/templates/edit_spec.rb +12 -0
  135. data/rspec_generators/merb_controller_test/templates/helper_spec.rb +5 -0
  136. data/rspec_generators/merb_controller_test/templates/index_spec.rb +12 -0
  137. data/rspec_generators/merb_controller_test/templates/new_spec.rb +12 -0
  138. data/rspec_generators/merb_controller_test/templates/show_spec.rb +5 -0
  139. data/rspec_generators/merb_model_test/merb_model_test_generator.rb +26 -0
  140. data/rspec_generators/merb_model_test/templates/model_spec_template.erb +7 -0
  141. data/script/destroy +14 -0
  142. data/script/generate +14 -0
  143. data/test_unit_generators/merb_controller_test/merb_controller_test_generator.rb +53 -0
  144. data/test_unit_generators/merb_controller_test/templates/functional_test.rb +17 -0
  145. data/test_unit_generators/merb_controller_test/templates/helper_test.rb +9 -0
  146. data/test_unit_generators/merb_model_test/merb_model_test_generator.rb +29 -0
  147. data/test_unit_generators/merb_model_test/templates/model_test_unit_template.erb +9 -0
  148. metadata +172 -94
  149. data/examples/README_EXAMPLES +0 -10
  150. data/examples/skeleton/Rakefile +0 -68
  151. data/examples/skeleton/dist/app/views/layout/application.herb +0 -12
  152. data/examples/skeleton/dist/conf/database.yml +0 -23
  153. data/examples/skeleton/dist/conf/merb.yml +0 -57
  154. data/examples/skeleton/dist/conf/merb_init.rb +0 -24
  155. data/examples/skeleton/dist/conf/router.rb +0 -22
  156. data/examples/skeleton/dist/conf/upload.conf +0 -5
  157. data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +0 -14
  158. data/examples/skeleton/script/new_migration +0 -21
  159. data/lib/merb/core_ext/merb_string.rb +0 -18
  160. data/lib/merb/merb_controller.rb +0 -206
  161. data/lib/merb/merb_dispatcher.rb +0 -87
  162. data/lib/merb/merb_exceptions.rb +0 -319
  163. data/lib/merb/merb_part_controller.rb +0 -42
  164. data/lib/merb/merb_plugins.rb +0 -293
  165. data/lib/merb/merb_request.rb +0 -165
  166. data/lib/merb/merb_router.rb +0 -309
  167. data/lib/merb/merb_yaml_store.rb +0 -31
  168. data/lib/merb/mixins/render_mixin.rb +0 -283
  169. data/lib/merb/mixins/responder_mixin.rb +0 -159
  170. data/lib/merb/session/merb_ar_session.rb +0 -131
  171. data/lib/merb/vendor/paginator/README.txt +0 -84
  172. data/lib/merb/vendor/paginator/paginator.rb +0 -124
  173. data/lib/tasks/db.rake +0 -55
@@ -1,7 +1,7 @@
1
- require 'hpricot'
1
+ #require 'hpricot'
2
2
 
3
3
  class Hash
4
-
4
+
5
5
  class << self
6
6
  # Converts valid XML into a Ruby Hash structure.
7
7
  # <tt>xml</tt>:: A string representation of valid XML
@@ -68,6 +68,14 @@ class Hash
68
68
  end
69
69
  end
70
70
 
71
+ # convert this hash into a Mash for string or symbol key access
72
+ def to_mash
73
+ hash = Mash.new(self)
74
+ hash.default = self.default
75
+ hash
76
+ end
77
+
78
+ # convert this hash to a query string param
71
79
  def to_params
72
80
  result = ''
73
81
  stack = []
@@ -104,7 +112,34 @@ class Hash
104
112
  end
105
113
  alias except block
106
114
 
107
-
115
+ # Converts the hash into xml attributes
116
+ # { :one => "ONE", "two"=>"TWO" }.to_xml_attributes
117
+ # #=> 'one="ONE" two="TWO"'
118
+ def to_xml_attributes
119
+ map do |k,v|
120
+ "#{k.to_s.camelize.sub(/^(.{1,1})/){|m| m.downcase}}=\"#{v}\""
121
+ end.join(" ")
122
+ end
123
+
124
+ alias_method :to_html_attributes, :to_xml_attributes
125
+
126
+ # Adds the given class symbol or string to the hash in the
127
+ # :class key. This will add a html class if there are already any existing
128
+ # or create the key and add this as the first class
129
+ #
130
+ # Example
131
+ # @hash[:class] #=> nil
132
+ # @hash.add_html_class!(:selected) #=> @hash[:class] == "selected"
133
+ #
134
+ # @hash.add_html_class!("class1 class2") #=> @hash[:class] == "selected class1 class2"
135
+ def add_html_class!(html_class)
136
+ if self[:class]
137
+ self[:class] = "#{self[:class]} #{html_class}"
138
+ else
139
+ self[:class] = html_class.to_s
140
+ end
141
+ end
142
+
108
143
  # Destructively convert all keys to symbols recursively.
109
144
  def symbolize_keys!
110
145
  keys.each do |key|
@@ -221,14 +256,8 @@ class REXMLUtilityNode # :nodoc:
221
256
  end
222
257
 
223
258
  def to_html
224
- "<#{name}#{attributes_to_xml}>#{inner_html}</#{name}>"
259
+ "<#{name}#{attributes.to_xml_attributes}>#{inner_html}</#{name}>"
225
260
  end
226
-
227
- def attributes_to_xml
228
- attributes.keys.map do |key|
229
- "#{key}='#{attributes[key]}'"
230
- end.join
231
- end
232
261
  end
233
262
 
234
263
  class ToHashParser # :nodoc:
@@ -256,4 +285,4 @@ class ToHashParser # :nodoc:
256
285
  end
257
286
  stack.pop.to_hash
258
287
  end
259
- end
288
+ end
@@ -272,4 +272,4 @@ module Inflector
272
272
  end
273
273
  end
274
274
 
275
- require File.dirname(__FILE__) + '/merb_inflections'
275
+ require File.dirname(__FILE__) + '/inflections'
@@ -1,9 +1,9 @@
1
1
  module Kernel
2
2
  # Example:
3
- # aquire 'foo/bar/*'
3
+ # acquire 'foo/bar/*'
4
4
  # requires all files inside foo/bar - recursive
5
5
  # can take multiple parameters
6
- def aquire(*files)
6
+ def acquire(*files)
7
7
  files.each do |file|
8
8
  require file if %w(rb so).any?{|f| File.file?("#{file}.#{f}")}
9
9
  $:.each do |path|
@@ -22,6 +22,38 @@ module Kernel
22
22
  puts message if message
23
23
  end
24
24
 
25
+ def dependencies(*args)
26
+ args.each do |arg|
27
+ case arg
28
+ when String : dependency(arg)
29
+ when Hash : arg.each { |r,v| dependency(r, v) }
30
+ end
31
+ end
32
+ end
33
+
34
+ def dependency(gem, *ver)
35
+ Gem.activate(gem, true, *ver)
36
+ end
37
+
38
+ def use_orm(orm)
39
+ raise "Don't call use_orm more than once" unless
40
+ Merb::GENERATOR_SCOPE.delete(:merb_default)
41
+ orm = orm.to_sym
42
+ orm_plugin = orm.to_s.match(/^merb_/) ? orm.to_s : "merb_#{orm}"
43
+ Merb::GENERATOR_SCOPE.unshift(orm) unless
44
+ Merb::GENERATOR_SCOPE.include?(orm)
45
+ Kernel.dependency(orm_plugin)
46
+ end
47
+
48
+ def use_test(test_framework)
49
+ test_framework = test_framework.to_sym
50
+ raise "use_test only supports :rspec and :test_unit currently" unless
51
+ [:rspec, :test_unit].include?(test_framework)
52
+ Merb::GENERATOR_SCOPE.delete(:rspec)
53
+ Merb::GENERATOR_SCOPE.delete(:test_unit)
54
+ Merb::GENERATOR_SCOPE.push(test_framework)
55
+ end
56
+
25
57
  # Gives you back the file, line and method of the caller number i
26
58
  # Example:
27
59
  # __caller_info__(1) # -> ['/usr/lib/ruby/1.8/irb/workspace.rb', '52', 'irb_binding']
@@ -53,7 +85,7 @@ module Kernel
53
85
  # [ 124, " @suspend_next = false", false ]
54
86
  # ]
55
87
 
56
- def __caller_lines__ file, line, size = 4
88
+ def __caller_lines__(file, line, size = 4)
57
89
  return [['Template Error!', "problem while rendering", false]] if file =~ /\(erubis\)/
58
90
  lines = File.readlines(file)
59
91
  current = line.to_i - 1
@@ -75,4 +107,25 @@ module Kernel
75
107
 
76
108
  area
77
109
  end
110
+
111
+ def __profile__(name, min=1)
112
+ require 'ruby-prof' unless defined?(RubyProf)
113
+ return_result = ''
114
+ result = RubyProf.profile do
115
+ 100.times{return_result = yield}
116
+ end
117
+ printer = RubyProf::GraphHtmlPrinter.new(result)
118
+ path = File.join(MERB_ROOT, 'log', "#{name}.html")
119
+ File.open(path, 'w') do |file|
120
+ printer.print(file, {:min_percent => min,
121
+ :print_file => true})
122
+ end
123
+ return_result
124
+ end
125
+
126
+ # Extracts an options hash if it is the last item in the args array
127
+ def extract_options_from_args!(args)
128
+ args.pop if args.last.is_a?( Hash )
129
+ end
130
+
78
131
  end
@@ -0,0 +1,88 @@
1
+
2
+ # This class has dubious semantics and we only have it so that
3
+ # people can write params[:key] instead of params['key']
4
+
5
+ class Mash < Hash
6
+ def initialize(constructor = {})
7
+ if constructor.is_a?(Hash)
8
+ super()
9
+ update(constructor)
10
+ else
11
+ super(constructor)
12
+ end
13
+ end
14
+
15
+ def default(key = nil)
16
+ if key.is_a?(Symbol) && include?(key = key.to_s)
17
+ self[key]
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
24
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
25
+
26
+ def []=(key, value)
27
+ regular_writer(convert_key(key), convert_value(value))
28
+ end
29
+
30
+ def update(other_hash)
31
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
32
+ self
33
+ end
34
+
35
+ alias_method :merge!, :update
36
+
37
+ def key?(key)
38
+ super(convert_key(key))
39
+ end
40
+
41
+ alias_method :include?, :key?
42
+ alias_method :has_key?, :key?
43
+ alias_method :member?, :key?
44
+
45
+ def fetch(key, *extras)
46
+ super(convert_key(key), *extras)
47
+ end
48
+
49
+ def values_at(*indices)
50
+ indices.collect {|key| self[convert_key(key)]}
51
+ end
52
+
53
+ def dup
54
+ Mash.new(self)
55
+ end
56
+
57
+ def merge(hash)
58
+ self.dup.update(hash)
59
+ end
60
+
61
+ def delete(key)
62
+ super(convert_key(key))
63
+ end
64
+
65
+ def stringify_keys!; self end
66
+ def symbolize_keys!; self end
67
+
68
+ # Convert to a Hash with String keys.
69
+ def to_hash
70
+ Hash.new(default).merge(self)
71
+ end
72
+
73
+ protected
74
+ def convert_key(key)
75
+ key.kind_of?(Symbol) ? key.to_s : key
76
+ end
77
+
78
+ def convert_value(value)
79
+ case value
80
+ when Hash
81
+ value.to_mash
82
+ when Array
83
+ value.collect { |e| e.is_a?(Hash) ? e.to_mash : e }
84
+ else
85
+ value
86
+ end
87
+ end
88
+ end
@@ -151,12 +151,18 @@ class Object
151
151
 
152
152
  # Returns true if:
153
153
  # * it's an empty array
154
+ # * it's an empty string
154
155
  # * !self evaluates to true
155
156
  #
156
- # [].blank? #=> true
157
- # nil.blank? #=> true
158
- # false.blank? #=> true
159
- # [nil].blank? #=> false
157
+ # [].blank? #=> true
158
+ # [1].blank? #=> false
159
+ # [nil].blank? #=> false
160
+ # nil.blank? #=> true
161
+ # true.blank? #=> false
162
+ # false.blank? #=> true
163
+ # "".blank? #=> true
164
+ # " ".blank? #=> true
165
+ # " hey ho ".blank? #=> false
160
166
  def blank?
161
167
  if respond_to?(:empty?) && respond_to?(:strip)
162
168
  empty? or strip.empty?
@@ -173,48 +179,5 @@ class Object
173
179
  list.each {|x| obj = obj.const_get(x) }
174
180
  obj
175
181
  end
176
-
177
- # An elegant way to refactor out common options
178
- #
179
- # with_options :order => 'created_at', :class_name => 'Comment' do |post|
180
- # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all
181
- # post.has_many :unapproved_comments, :conditions => ['approved = ?', false]
182
- # post.has_many :all_comments
183
- # end
184
- #
185
- # Can also be used with an explicit reciever:
186
- #
187
- # map.with_options :controller => "people" do |people|
188
- # people.connect "/people", :action => "index"
189
- # people.connect "/people/:id", :action => "show"
190
- # end
191
- def with_options(options)
192
- yield Merb::OptionMerger.new(self, options)
193
- end
194
- end
195
182
 
196
- module Merb
197
- class OptionMerger #:nodoc:
198
- instance_methods.each do |method|
199
- undef_method(method) if method !~ /^(__|instance_eval|class)/
200
- end
201
-
202
- def initialize(context, options)
203
- @context, @options = context, options
204
- end
205
-
206
- private
207
- def method_missing(method, *arguments, &block)
208
- merge_argument_options! arguments
209
- @context.send(method, *arguments, &block)
210
- end
211
-
212
- def merge_argument_options!(arguments)
213
- arguments << if arguments.last.respond_to? :to_hash
214
- @options.merge(arguments.pop)
215
- else
216
- @options.dup
217
- end
218
- end
219
- end
220
183
  end
@@ -0,0 +1,56 @@
1
+ require 'strscan'
2
+
3
+ class String
4
+ class InvalidPathConversion < Exception; end
5
+
6
+ def escape_regexp
7
+ Regexp.escape self
8
+ end
9
+
10
+ # "FooBar".snake_case #=> "foo_bar"
11
+ def snake_case
12
+ gsub(/\B[A-Z]/, '_\&').downcase
13
+ end
14
+
15
+ # "foo_bar".camel_case #=> "FooBar"
16
+ def camel_case
17
+ split('_').map{|e| e.capitalize}.join
18
+ end
19
+
20
+ # About 50% faster than string.split('/').map{ |s| s.camel_case }.join('::')
21
+ def to_const_string
22
+ new_string = ""
23
+ input = StringScanner.new(self.downcase)
24
+ until input.eos?
25
+ if input.scan(/([a-z][a-zA-Z\d]*)(_|$|\/)/)
26
+ new_string << input[1].capitalize
27
+ new_string << "::" if input[2] == '/'
28
+ else
29
+ raise InvalidPathConversion, self
30
+ end
31
+ end
32
+ new_string
33
+ end
34
+
35
+ # Concatenates a path
36
+ def /(o)
37
+ File.join(self, o.to_s)
38
+ end
39
+
40
+ # Takes lines of text, removes any indentation, and
41
+ # adds +indentation+ number of spaces to each line
42
+ def indent(indentation)
43
+ lines = to_a
44
+ initial_indentation = lines.first.scan(/^(\s+)/).flatten.first
45
+ lines.map do |line|
46
+ if initial_indentation.nil?
47
+ " " * indentation + line
48
+ elsif line.index(initial_indentation) == 0
49
+ " " * indentation + line[initial_indentation.size..-1]
50
+ else
51
+ " " * indentation + line
52
+ end
53
+ end.join
54
+ end
55
+
56
+ end
@@ -0,0 +1,109 @@
1
+ module Merb
2
+ class Dispatcher
3
+
4
+ DEFAULT_ERROR_TEMPLATE = Erubis::MEruby.new((File.read(
5
+ File.join(MERB_ROOT, 'app/views/exceptions/internal_server_error.html.erb')) rescue "Internal Server Error!"))
6
+
7
+ class << self
8
+
9
+ def use_mutex=(val)
10
+ @@use_mutex = val
11
+ end
12
+
13
+ @@mutex = Mutex.new
14
+ @@use_mutex = ::Merb::Server.config[:use_mutex]
15
+ # This is where we grab the incoming request REQUEST_URI and use that in
16
+ # the merb RouteMatcher to determine which controller and method to run.
17
+ # Returns a 2 element tuple of: [controller, action]
18
+ #
19
+ # ControllerExceptions are rescued here and redispatched.
20
+ # Exceptions still return [controller, action]
21
+ def handle(http_request, response)
22
+ start = Time.now
23
+ request = Merb::Request.new(http_request)
24
+ MERB_LOGGER.info("Params: #{request.params.inspect}")
25
+ MERB_LOGGER.info("Cookies: #{request.cookies.inspect}")
26
+ # user friendly error messages
27
+ if request.route_params.empty?
28
+ raise ControllerExceptions::NotFound, "No routes match the request"
29
+ elsif request.controller_name.nil?
30
+ raise ControllerExceptions::NotFound, "Route matched, but route did not specify a controller"
31
+ end
32
+ MERB_LOGGER.debug("Routed to: #{request.route_params.inspect}")
33
+ # set controller class and the action to call
34
+ klass = request.controller_class
35
+ dispatch_action(klass, request.action, request, response)
36
+ rescue => exception
37
+ MERB_LOGGER.error(Merb.exception(exception))
38
+ exception = controller_exception(exception)
39
+ dispatch_exception(request, response, exception)
40
+ end
41
+
42
+ private
43
+
44
+ # setup the controller and call the chosen action
45
+ def dispatch_action(klass, action, request, response, status=200)
46
+ # build controller
47
+ controller = klass.build(request, response, status)
48
+ # complete setup benchmarking
49
+ #controller._benchmarks[:setup_time] = Time.now - start
50
+ if @@use_mutex
51
+ @@mutex.synchronize { controller.dispatch(action) }
52
+ else
53
+ controller.dispatch(action)
54
+ end
55
+ [controller, action]
56
+ end
57
+
58
+ # Re-route the current request to the Exception controller
59
+ # if it is available, and try to render the exception nicely
60
+ # if it is not available then just render a simple text error
61
+ def dispatch_exception(request, response, exception)
62
+ klass = Exceptions rescue Controller
63
+ request.params[:original_params] = request.params.dup rescue {}
64
+ request.params[:original_session] = request.session.dup rescue {}
65
+ request.params[:original_cookies] = request.cookies.dup rescue {}
66
+ request.params[:exception] = exception
67
+ request.params[:action] = exception.name
68
+ dispatch_action(klass, exception.name, request, response, exception.class::STATUS)
69
+ rescue => dispatch_issue
70
+ dispatch_issue = controller_exception(dispatch_issue)
71
+ # when no action/template exist for an exception, or an
72
+ # exception occurs on an InternalServerError the message is
73
+ # rendered as simple text.
74
+ # ControllerExceptions raised from exception actions are
75
+ # dispatched back into the Exceptions controller
76
+ if dispatch_issue.is_a?(ControllerExceptions::NotFound)
77
+ dispatch_default_exception(klass, request, response, exception)
78
+ elsif dispatch_issue.is_a?(ControllerExceptions::InternalServerError)
79
+ dispatch_default_exception(klass, request, response, dispatch_issue)
80
+ else
81
+ exception = dispatch_issue
82
+ retry
83
+ end
84
+ end
85
+
86
+ # if no custom actions are available to render an exception
87
+ # then the errors will end up here for processing
88
+ def dispatch_default_exception(klass, request, response, e)
89
+ controller = klass.build(request, response, e.class::STATUS)
90
+ if e.is_a? ControllerExceptions::Redirection
91
+ controller.headers.merge!('Location' => e.message)
92
+ controller.instance_variable_set("@_body", %{ }) #fix
93
+ else
94
+ @exception = e # for ERB
95
+ controller.instance_variable_set("@_body", DEFAULT_ERROR_TEMPLATE.result(binding))
96
+ end
97
+ [controller, e.name]
98
+ end
99
+
100
+ # Wraps any non-ControllerException errors in an
101
+ # InternalServerError ready for displaying over HTTP
102
+ def controller_exception(e)
103
+ e.kind_of?(ControllerExceptions::Base) ?
104
+ e : ControllerExceptions::InternalServerError.new(e)
105
+ end
106
+
107
+ end # end class << self
108
+ end
109
+ end