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
@@ -0,0 +1,19 @@
1
+ require 'fileutils'
2
+ require 'find'
3
+
4
+ module Merb
5
+
6
+ class PluginGenerator
7
+ def self.run(path)
8
+ require 'rubygems'
9
+ require 'rubigen'
10
+
11
+ require 'rubigen/scripts/generate'
12
+ RubiGen::Base.use_application_sources!
13
+ RubiGen::Scripts::Generate.new.run([path], :generator => 'merb_plugin', :backtrace => true)
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
@@ -0,0 +1,65 @@
1
+ module Merb
2
+
3
+ class Logger
4
+ module Severity
5
+ DEBUG = 0
6
+ INFO = 1
7
+ WARN = 2
8
+ ERROR = 3
9
+ FATAL = 4
10
+ UNKNOWN = 5
11
+ end
12
+ include Severity
13
+
14
+ attr_accessor :level
15
+ attr_reader :buffer
16
+
17
+ def initialize(log, level = DEBUG)
18
+ @level = level
19
+ @buffer = []
20
+ if log.respond_to?(:write)
21
+ @log = log
22
+ elsif File.exist?(log)
23
+ @log = open(log, (File::WRONLY | File::APPEND))
24
+ @log.sync = true
25
+ else
26
+ @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
27
+ @log.sync = true
28
+ @log.write("# Logfile created on %s\n" % [Time.now.to_s])
29
+ end
30
+ end
31
+
32
+ def flush
33
+ @log.write @buffer.slice!(0..-1).to_s unless @buffer.size == 0
34
+ end
35
+
36
+ def close
37
+ flush
38
+ @log.close if @log.respond_to?(:close)
39
+ @log = nil
40
+ end
41
+
42
+ def add(severity, message = nil, progname = nil, &block)
43
+ return if @level > severity
44
+ message = (message || (block && block.call) || progname).to_s
45
+ # If a newline is necessary then create a new message ending with a newline.
46
+ # Ensures that the original message is not mutated.
47
+ message = "#{message}\n" unless message[-1] == ?\n
48
+ @buffer << message
49
+ message
50
+ end
51
+
52
+ Severity.constants.each do |severity|
53
+ class_eval <<-EOT
54
+ def #{severity.downcase}(message = nil, progname = nil, &block)
55
+ add(#{severity}, message, progname, &block)
56
+ end
57
+
58
+ def #{severity.downcase}?
59
+ #{severity} >= @level
60
+ end
61
+ EOT
62
+ end
63
+
64
+ end
65
+ end
@@ -1,9 +1,72 @@
1
- require File.dirname(__FILE__)+'/merb_mailer'
2
1
 
3
2
  module Merb
3
+
4
+ # Sending mail from a controller involves three steps:
5
+ #
6
+ # * Set mail settings in merb_init.rb (Not shown here...see the Mailer docs).
7
+ # * Create a MailController subclass with actions and templates.
8
+ # * Call the MailController from another Controller via the send_mail method.
9
+ #
10
+ # First, create a file in app/mailers that subclasses Merb::MailController.
11
+ # The actions in this controller will do nothing but render mail.
12
+ #
13
+ # # app/mailers/article_mailer.rb
14
+ # class ArticleMailer < Merb::MailController
15
+ #
16
+ # def notify
17
+ # @user = params[:user]
18
+ # render_mail
19
+ # end
20
+ #
21
+ # end
22
+ #
23
+ # You also can access the params hash for values passed with the
24
+ # Controller.send_mail method. See also the documentation for
25
+ # render_mail to see all the ways it can be called.
26
+ #
27
+ # Create a template in a subdirectory of app/mailers/views that corresponds
28
+ # to the controller and action name. Put plain text and ERB tags here:
29
+ #
30
+ # # app/mailers/views/article_mailer/notify.text.erb
31
+ # Hey, <%= @user.name %>,
32
+ #
33
+ # We're running a sale on dog bones!
34
+ #
35
+ # Finally, call the Controller.send_mail method from a standard
36
+ # Merb controller.
37
+ #
38
+ # class Articles < Application
39
+ #
40
+ # def index
41
+ # @user = User.find_by_name('louie')
42
+ #
43
+ # send_mail(ArticleMailer, :notify, {
44
+ # :from => "me@example.com",
45
+ # :to => "louie@example.com",
46
+ # :subject => "Sale on Dog Bones!"
47
+ # }, { :user => @user })
48
+ # render
49
+ # end
50
+ #
51
+ # end
52
+ #
53
+ # Note: If you don't pass a fourth argument to Controller.send_mail,
54
+ # the controller's params will be sent to the MailController subclass
55
+ # as params. However, you can explicitly send a hash of objects that
56
+ # will populate the params hash instead. In either case, you must
57
+ # set instance variables in the MailController's actions if you
58
+ # want to use them in the MailController's views.
59
+ #
60
+ # The MailController class is very powerful. You can:
61
+ #
62
+ # * Send multipart email with a single call to render_mail.
63
+ # * Attach files.
64
+ # * Render layouts and other templates.
65
+ # * Use any template engine supported by Merb.
66
+
4
67
  class MailController < AbstractController
5
68
 
6
- self._template_root = File.expand_path(DIST_ROOT / "app/mailers/views")
69
+ self._template_root = File.expand_path(self._template_root / "../app/mailers/views")
7
70
  class_inheritable_accessor :_mailer_klass
8
71
  self._mailer_klass = Merb::Mailer
9
72
 
@@ -18,6 +81,7 @@ module Merb
18
81
  @params = params
19
82
  @base_controller = controller
20
83
  @session = (controller && controller.session) || {}
84
+ super
21
85
  end
22
86
 
23
87
  def filters_halted
@@ -32,13 +96,13 @@ module Merb
32
96
  # default Merb render method.
33
97
  #
34
98
  # First of all, you'll need to store email files in your
35
- # dist/app/mailers/views directory. They should be under a directory that
99
+ # app/mailers/views directory. They should be under a directory that
36
100
  # matches the name of your mailer (e.g. TestMailer's views would be stored
37
101
  # under test_mailer).
38
102
  #
39
103
  # The files themselves should be named action_name.mime_type.extension. For
40
- # example, a herb template that should be the HTML part of the email, and
41
- # rendered from the "foo" action would be named foo.html.herb.
104
+ # example, an erb template that should be the HTML part of the email, and
105
+ # rendered from the "foo" action would be named foo.html.erb.
42
106
  #
43
107
  # The only mime-types currently supported are "html" and "text", which
44
108
  # correspond to text/html and text/plain respectively. All template systems
@@ -91,37 +155,41 @@ module Merb
91
155
  #
92
156
  # render_mail :html => :foo, :text => "BAR"
93
157
  def render_mail(options = @method)
94
- # If the options are not a hash, normalize to an action hash
158
+ @_missing_templates = false # used to make sure that at least one template was found
159
+ # # If the options are not a hash, normalize to an action hash
95
160
  options = {:action => {:html => options, :text => options}} if !options.is_a?(Hash)
96
- # Normalize :html => :sym and :text => :sym to an action hash
97
- options[:action] ||= {}
98
- options[:action][:html] = options.delete(:html) if options[:html].is_a?(Symbol)
99
- options[:action][:text] = options.delete(:text) if options[:text].is_a?(Symbol)
100
- options.delete(:action) if options[:action].empty?
101
-
102
- # set type to action or template, if the hash includes :action or :template
103
- type = options.keys.find {|x| x.to_s =~ /(action|template)/}
104
- # normalize :action/:template => ... to :action/template => {:html => ..., :text => ...}
105
- options[type] = {:html => options[type], :text => options[type]} if type && !options[type].is_a?(Hash)
106
- for mime_type in [:html, :text]
107
- # if action or template, get the template to render. If not, get the string to render. If none
108
- # are available, use the default params[:action]
109
- renderer = (type && options[type][mime_type] ? options[type][mime_type] : options[mime_type]).to_s
110
- # action or template
111
- if type && options[type][mime_type]
112
- # build the template to render
113
- mime_renderer = renderer + ".#{mime_type}" if !(renderer =~ /.#{mime_type}$/)
114
- if find_template(type => mime_renderer)
115
- @mail.send((mime_type == :html ? "rawhtml=" : "text="),
116
- render(type => mime_renderer, :layout => mimed_layout(mime_type, options[:layout])))
117
- elsif find_template(type => renderer) && mime_type == :text
118
- @mail.text = render(type => renderer, :layout => options[:layout])
161
+
162
+ # Take care of the options
163
+ use_options = {}
164
+ opts = options.dup
165
+ actions = opts.delete(:action) if opts[:action].is_a?(Hash)
166
+ templates = opts.delete(:template) if opts[:template].is_a?(Hash)
167
+
168
+ # Prepare the options hash for each format
169
+ # We need to delete anything relating to the other format here
170
+ # before we try to render the template.
171
+ [:html, :text].each do |fmt|
172
+ opts_hash = use_options[fmt] = {}
173
+ opts_hash[fmt] = opts.delete(fmt)
174
+ opts_hash[fmt] ||= actions[fmt] if actions && actions[fmt]
175
+ opts_hash[:tempalte] = templates[fmt] if templates && templates[fmt]
176
+ end
177
+
178
+ # Send the result to the mailer
179
+ { :html => "rawhtml=", :text => "text="}.each do |fmt,meth|
180
+ begin
181
+ value = render use_options[fmt].merge!(opts).merge!(:clean_context => true, :format => fmt)
182
+ @mail.send(meth,value) unless value.nil? || value.empty?
183
+ rescue => e
184
+ # An error should be logged if no template is found instead of an error raised
185
+ if @_missing_templates
186
+ MERB_LOGGER.error(e)
187
+ else
188
+ @_missing_templates = true
119
189
  end
120
- # string to render
121
- else
122
- @mail.send((mime_type == :html ? "rawhtml=" : "text="), renderer)
123
190
  end
124
191
  end
192
+ @mail
125
193
  end
126
194
 
127
195
  # Attaches a file or multiple files to an email. You call this from a
@@ -163,6 +231,8 @@ module Merb
163
231
  @mailer = self.class._mailer_klass.new(mail_params)
164
232
  @mail = @mailer.mail
165
233
  @method = method
234
+
235
+ # dipatch and render use params[:action], so set it
166
236
  params[:action] = method
167
237
  body = dispatch method
168
238
  if !@mail.html.blank? || !@mail.text.blank?
@@ -179,22 +249,5 @@ module Merb
179
249
  new({}).dispatch_and_deliver method, mail_params
180
250
  end
181
251
 
182
- private
183
- def mimed_layout(mime_type, layout = nil)
184
- if layout && layout != :application
185
- layout_choice = find_template(:layout => layout + ".#{mime_type}") ||
186
- find_template(:layout => layout)
187
- elsif name = find_template(:layout => self.class.name.snake_case + ".#{mime_type}")
188
- layout_choice = name
189
- elsif name = find_template(:layout => self.class.name.snake_case)
190
- layout_choice = name
191
- elsif name = find_template(:layout => "application.#{mime_type}")
192
- layout_choice = name
193
- else
194
- layout_choice = find_template(:layout => "application")
195
- end
196
- layout_choice = layout_choice.split("/").last.split(".")[0...-1].join(".")
197
- end
198
-
199
252
  end
200
253
  end
@@ -11,6 +11,32 @@ class MailFactory
11
11
  end
12
12
 
13
13
  module Merb
14
+
15
+ # You'll need a simple config like this in merb_init.rb if you want
16
+ # to actually send mail:
17
+ #
18
+ # Merb::Mailer.config = {
19
+ # :host=>'smtp.yourserver.com',
20
+ # :port=>'25',
21
+ # :user=>'user',
22
+ # :pass=>'pass',
23
+ # :auth=>:plain # :plain, :login, or :cram_md5, default :plain
24
+ # }
25
+ #
26
+ # or
27
+ #
28
+ # Merb::Mailer.config = {:sendmail_path => '/somewhere/odd')
29
+ # Merb::Mailer.delivery_method = :sendmail
30
+ #
31
+ # You could send mail manually like this (but it's better to use
32
+ # a MailController instead).
33
+ #
34
+ # m = Merb::Mailer.new :to => 'foo@bar.com',
35
+ # :from => 'bar@foo.com',
36
+ # :subject => 'Welcome to whatever!',
37
+ # :body => partial(:sometemplate)
38
+ # m.deliver!
39
+
14
40
  class Mailer
15
41
 
16
42
  class_inheritable_accessor :config, :delivery_method, :deliveries
@@ -18,7 +44,7 @@ module Merb
18
44
  self.deliveries = []
19
45
 
20
46
  def sendmail
21
- sendmail = IO.popen("sendmail #{@mail.to}", 'w+')
47
+ sendmail = IO.popen("#{config[:sendmail_path]} #{@mail.to}", 'w+')
22
48
  sendmail.puts @mail.to_s
23
49
  sendmail.close
24
50
  end
@@ -50,34 +76,12 @@ module Merb
50
76
  end
51
77
 
52
78
  def initialize(o={})
53
- self.config = :sendmail if config.nil?
79
+ self.config = {:sendmail_path => '/usr/sbin/sendmail'} if config.nil?
54
80
  o[:rawhtml] = o.delete(:html)
55
- @mail = returning MailFactory.new() do |m|
56
- o.each { |k,v| m.send "#{k}=", v }
57
- end
81
+ m = MailFactory.new()
82
+ o.each { |k,v| m.send "#{k}=", v }
83
+ @mail = m
58
84
  end
59
85
 
60
86
  end
61
87
  end
62
-
63
- =begin
64
-
65
-
66
- Merb::Mailer.config = {
67
- :host=>'smtp.yourserver.com',
68
- :port=>'25',
69
- :user=>'user',
70
- :pass=>'pass',
71
- :auth=>:plain # :plain, :login, or :cram_md5, default :plain
72
- }
73
-
74
- Merb::Mailer.delivery_method = :sendmail
75
-
76
- m = Merb::Mailer.new :to => 'foo@bar.com',
77
- :from => 'bar@foo.com',
78
- :subject => 'Welcome to whatever!',
79
- :body => partial(:sometemplate)
80
- m.deliver!
81
-
82
-
83
- =end
@@ -1,6 +1,6 @@
1
1
  module Merb
2
2
 
3
- module Authentication
3
+ module AuthenticationMixin
4
4
  require 'base64'
5
5
 
6
6
  def credentials
@@ -13,7 +13,7 @@ module Merb
13
13
 
14
14
  def authenticated?
15
15
  username, password = *credentials
16
- username == Merb::Server.basic_auth[:username] and password == Merb::Server.basic_auth[:password]
16
+ username == Merb::Server.config[:basic_auth][:username] and password == Merb::Server.config[:basic_auth][:password]
17
17
  end
18
18
 
19
19
  def basic_authentication
@@ -26,7 +26,7 @@ module Merb
26
26
  set_status(401)
27
27
  headers['Content-type'] = 'text/plain'
28
28
  headers['Status'] = 'Unauthorized'
29
- headers['WWW-Authenticate'] = "Basic realm=\"#{Merb::Server.basic_auth[:domain]}\""
29
+ headers['WWW-Authenticate'] = "Basic realm=\"#{Merb::Server.config[:basic_auth][:domain]}\""
30
30
  return 'Unauthorized'
31
31
  end
32
32
 
@@ -4,6 +4,15 @@ module Merb
4
4
  # Returns a URL according to the defined route. Accepts the path and
5
5
  # an options hash. The path specifies the route requested. The options
6
6
  # hash fills in the dynamic parts of the route.
7
+ #
8
+ # Merb routes can often be one-way; if they use a regex to define
9
+ # the route, then knowing the controller & action won't be enough
10
+ # to reverse-generate the route. However, if you use the default
11
+ # /controller/action/id?query route, +default_route+ can generate
12
+ # it for you.
13
+ #
14
+ # For easy reverse-routes that use a Regex, be sure to also add
15
+ # a name to the route, so +url+ can find it.
7
16
  #
8
17
  # Nested resources such as:
9
18
  # r.resources :blogposts do |post|
@@ -39,123 +48,129 @@ module Merb
39
48
  # url(:edit_comment, @comment) # => /blogposts/1/comments/1/edit
40
49
  # url(:custom_new_comment, :blogpost => @post)
41
50
  #
42
- def url(path, o={})
43
- ::Merb::Router.generate(path,o)
44
- end
45
-
46
- protected
47
- NAME_REGEX = /Content-Disposition:.* name="?([^\";]*)"?/ni.freeze
48
- CONTENT_TYPE_REGEX = /Content-Type: (.*)\r\n/ni.freeze
49
- FILENAME_REGEX = /Content-Disposition:.* filename="?([^\";]*)"?/ni.freeze
50
- CRLF = "\r\n".freeze
51
- EOL = CRLF
52
- def parse_multipart(request,boundary,env)
53
- boundary = "--#{boundary}"
54
- paramhsh = {}
55
- buf = ""
56
- content_length = env['CONTENT_LENGTH'].to_i
57
- input = request
58
- input.binmode if defined? input.binmode
59
- boundary_size = boundary.size + EOL.size
60
- bufsize = 16384
61
- content_length -= boundary_size
62
- status = input.read(boundary_size)
63
- raise EOFError, "bad content body" unless status == boundary + EOL
64
- rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
65
- loop {
66
- head = nil
67
- body = ''
68
- filename = content_type = name = nil
69
- read_size = 0
70
- until head && buf =~ rx
71
- i = buf.index("\r\n\r\n")
72
- if( i == nil && read_size == 0 && content_length == 0 )
73
- content_length = -1
74
- break
75
- end
76
- if !head && i
77
- head = buf.slice!(0, i+2) # First \r\n
78
- buf.slice!(0, 2) # Second \r\n
79
- filename = head[FILENAME_REGEX, 1]
80
- content_type = head[CONTENT_TYPE_REGEX, 1]
81
- name = head[NAME_REGEX, 1]
82
-
83
- if filename && !filename.empty?
84
- body = Tempfile.new(:Merb)
85
- body.binmode if defined? body.binmode
86
- end
87
- next
88
- end
89
-
90
-
91
- # Save the read body part.
92
- if head && (boundary_size+4 < buf.size)
93
- body << buf.slice!(0, buf.size - (boundary_size+4))
94
- end
51
+ # url(:page => 2) # => /posts/show/1?page=2
52
+ # url(:new_post, :page => 3) # => /posts/new?page=3
53
+ # url('/go/here', :page => 3) # => /go/here?page=3
54
+ #
55
+ # url(:controller => "welcome") # => /welcome
56
+ # url(:controller => "welcome", :action => "greet")
57
+ # # => /welcome/greet
58
+ #
59
+ def url(route_name = nil, new_params = {})
60
+ if route_name.is_a?(Hash)
61
+ new_params = route_name
62
+ route_name = nil
63
+ end
95
64
 
96
- read_size = bufsize < content_length ? bufsize : content_length
97
- if( read_size > 0 )
98
- c = input.read(read_size)
99
- raise EOFError, "bad content body" if c.nil? || c.empty?
100
- buf << c
101
- content_length -= c.size
102
- end
65
+ url = if new_params.respond_to?(:keys) &&
66
+ !(new_params.keys & [:controller, :action, :id]).empty?
67
+ url_from_default_route(new_params)
68
+ elsif route_name.nil? && !route.regexp?
69
+ url_from_route(route, new_params)
70
+ elsif route_name.nil?
71
+ request.path + (new_params.empty? ? "" : "?" + params_to_query_string(new_params))
72
+ elsif route_name.is_a?(Symbol)
73
+ url_from_route(route_name, new_params)
74
+ elsif route_name.is_a?(String)
75
+ route_name + (new_params.empty? ? "" : "?" + params_to_query_string(new_params))
76
+ else
77
+ raise "URL not generated: #{route_name.inspect}, #{new_params.inspect}"
103
78
  end
79
+ url = MerbHandler.path_prefix + url if MerbHandler.path_prefix
80
+ url
81
+ end
104
82
 
105
- # Save the rest.
106
- if i = buf.index(rx)
107
- body << buf.slice!(0, i)
108
- buf.slice!(0, boundary_size+2)
109
-
110
- content_length = -1 if $1 == "--"
111
- end
112
-
113
- if filename && !filename.empty?
114
- body.rewind
115
- data = {
116
- :filename => File.basename(filename),
117
- :content_type => content_type,
118
- :tempfile => body,
119
- :size => File.size(body)
120
- }
121
- else
122
- data = body
83
+ def url_from_route(symbol, new_params = {})
84
+ route = symbol.is_a?(Symbol) ? Merb::Router.named_routes[symbol] : symbol
85
+ raise "URL could not be constructed. Route symbol not found: #{symbol.inspect}" unless route
86
+ path = route.generate(new_params, params)
87
+ keys = route.symbol_segments
88
+ if new_params.is_a? Hash
89
+ if ext = format_extension(new_params)
90
+ new_params.delete(:format)
91
+ path += "." + ext
123
92
  end
124
- paramhsh = normalize_params(paramhsh,name,data)
125
- break if buf.empty? || content_length == -1
126
- }
127
- paramhsh
93
+ extras = new_params.reject{ |k, v| keys.include?(k) }
94
+ path += "?" + params_to_query_string(extras) unless extras.empty?
95
+ end
96
+ path
128
97
  end
129
98
 
130
- def normalize_params(parms, key, val)
131
- case key
132
- when /(.+)\[(.+)\]\[\]$/
133
- parms[$1] ||= {}
134
- parms[$1] = normalize_params(parms[$1], "#{$2}[]", val)
135
- when /(.+)\[(.+)\]$/
136
- parms[$1] ||= {}
137
- parms[$1] = normalize_params(parms[$1], $2, val)
138
- when /(.+)\[\]$/
139
- (parms[$1] ||= []) << val
99
+ # this is pretty ugly, but it works. TODO: make this cleaner
100
+ def url_from_default_route(new_params)
101
+ query_params = new_params.reject do |k,v|
102
+ [:controller, :action, :id, :format].include?(k)
103
+ end
104
+ controller = new_params[:controller] || params[:controller]
105
+ controller = params[:controller] if controller == :current
106
+ url = "/#{controller}"
107
+ if new_params[:action] || new_params[:id] ||
108
+ new_params[:format] || !query_params.empty?
109
+ action = new_params[:action] || params[:action]
110
+ url += "/#{action}"
111
+ end
112
+ if new_params[:id]
113
+ url += "/#{new_params[:id]}"
114
+ end
115
+ if format = new_params[:format]
116
+ format = params[:format] if format == :current
117
+ url += ".#{format}"
118
+ end
119
+ unless query_params.empty?
120
+ url += "?" + params_to_query_string(query_params)
121
+ end
122
+ url
123
+ end
124
+
125
+ protected
126
+
127
+ # Creates query string from params, supporting nested arrays and hashes.
128
+ # ==== Example
129
+ # params_to_query_string(:user => {:filter => {:name => "quux*"}, :order => ["name"]})
130
+ # # => user[filter][name]=quux%2A&user[order][]=name
131
+ def params_to_query_string(value, prefix = nil)
132
+ case value
133
+ when Array
134
+ value.map { |v|
135
+ params_to_query_string(v, "#{prefix}[]")
136
+ } * "&"
137
+ when Hash
138
+ value.map { |k, v|
139
+ params_to_query_string(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
140
+ } * "&"
140
141
  else
141
- parms[key] = val if val
142
+ "#{prefix}=#{escape(value)}"
142
143
  end
143
- parms
144
144
  end
145
145
 
146
-
147
- # parses a query string or the payload of a POST
148
- # request into the params hash. So for example:
149
- # /foo?bar=nik&post[title]=heya&post[body]=whatever
150
- # parses into:
151
- # {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever'}}
152
- def query_parse(qs, d = '&;')
153
- m = proc {|_,o,n|o.update(n,&m)rescue([*o]<<n)}
154
- (qs||'').split(/[#{d}] */n).inject(Hash[]) { |h,p|
155
- k, v=unescape(p).split('=',2)
156
- h.update(k.split(/[\]\[]+/).reverse.
157
- inject(v) { |x,i| Hash[i,x] },&m)
158
- }
146
+ # +format_extension+ dictates when named route urls generated by #url
147
+ # will have a file-extension. It will return false or the format
148
+ # extension to append.
149
+ #
150
+ # url(:post, :id => post, :format => 'xml')
151
+ #
152
+ # generates:
153
+ #
154
+ # /posts/34.xml
155
+ #
156
+ # by default NON-HTML urls will be given an extension. it is posible
157
+ # to override this behaviour by setting +:use_format_in_urls+ in your
158
+ # server config (merb.yml) to either true/false
159
+ #
160
+ # +true+ would result in ALL urls (even html) being given extensions
161
+ # this is often desirable when you have many formats and dont
162
+ # wish to treat .html any differently from
163
+ # +false+ would result in NO urls being given extensions, and format
164
+ # gets treated just like any other param. leave it unset for
165
+ # the default behavior
166
+ #
167
+ def format_extension(new_params={})
168
+ use_format = Merb::Server.config[:use_format_in_urls]
169
+ if use_format.nil?
170
+ prms = params.merge(new_params)
171
+ use_format = prms[:format] != 'html' && prms[:format]
172
+ end
173
+ use_format
159
174
  end
160
175
 
161
176
  # render using chunked encoding
@@ -231,10 +246,8 @@ module Merb
231
246
  # stream_file( { :filename => file_name,
232
247
  # :type => content_type,
233
248
  # :content_length => content_length }) do
234
- # response.send_status(opts[:content_length])
235
- # response.send_header
236
249
  # AWS::S3::S3Object.stream(user.folder_name + "-" + user_file.unique_id, bucket_name) do |chunk|
237
- # @response.write chunk
250
+ # response.write chunk
238
251
  # end
239
252
  # end
240
253
  def stream_file(opts={}, &stream)
@@ -247,6 +260,8 @@ module Merb
247
260
  'Content-Transfer-Encoding' => 'binary',
248
261
  'CONTENT-LENGTH' => opts[:content_length]
249
262
  )
263
+ response.send_status(opts[:content_length])
264
+ response.send_header
250
265
  stream
251
266
  end
252
267
 
@@ -261,8 +276,12 @@ module Merb
261
276
 
262
277
  # Sets a cookie to be included in the response.
263
278
  def set_cookie(name, value, expires)
264
- (headers['Set-Cookie'] ||='') <<
265
- (Merb::Const::SET_COOKIE % [name.to_s, escape(value.to_s), expires.rfc2822])
279
+ (headers['Set-Cookie'] ||='') << (Merb::Const::SET_COOKIE % [
280
+ name.to_s,
281
+ escape(value.to_s),
282
+ # Cookie expiration time must be GMT. See RFC 2109
283
+ expires.gmtime.strftime(Merb::Const::COOKIE_EXPIRATION_FORMAT)
284
+ ])
266
285
  end
267
286
 
268
287
  # Marks a cookie as deleted. The cookie is given an expires stamp in