merb 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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