merb 0.0.6 → 0.0.7

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 (88) hide show
  1. data/README +22 -4
  2. data/Rakefile +15 -3
  3. data/TODO +2 -3
  4. data/bin/merb +61 -36
  5. data/examples/sample_app/dist/app/controllers/files.rb +31 -0
  6. data/examples/sample_app/dist/app/controllers/posts.rb +26 -2
  7. data/examples/sample_app/dist/app/controllers/test.rb +7 -1
  8. data/examples/sample_app/dist/app/views/files/progress.jerb +3 -0
  9. data/examples/sample_app/dist/app/views/files/start.herb +62 -0
  10. data/examples/sample_app/dist/app/views/files/upload.herb +6 -0
  11. data/examples/sample_app/dist/app/views/layout/{application.rhtml → application.herb} +2 -3
  12. data/examples/sample_app/dist/app/views/layout/{foo.rhtml → foo.herb} +0 -0
  13. data/examples/sample_app/dist/app/views/posts/{_comments.rhtml → _comments.herb} +0 -0
  14. data/examples/sample_app/dist/app/views/posts/comment.jerb +1 -0
  15. data/examples/sample_app/dist/app/views/posts/{list.rhtml → list.herb} +0 -0
  16. data/examples/sample_app/dist/app/views/posts/{new.rhtml → new.herb} +0 -0
  17. data/examples/sample_app/dist/app/views/posts/{show.rhtml → show.herb} +0 -0
  18. data/examples/sample_app/dist/app/views/posts/xml_test.xerb +3 -0
  19. data/examples/sample_app/dist/app/views/test/{foo.rhtml → foo.herb} +0 -0
  20. data/examples/sample_app/dist/app/views/test/{hello.rhtml → hello.herb} +0 -0
  21. data/examples/sample_app/dist/app/views/test/json.jerb +1 -0
  22. data/examples/sample_app/dist/conf/merb.yml +11 -0
  23. data/examples/sample_app/dist/conf/merb_init.rb +1 -1
  24. data/examples/sample_app/dist/conf/mup.conf +11 -0
  25. data/examples/sample_app/dist/public/javascripts/mup.js +113 -0
  26. data/examples/sample_app/script/merb_stop +7 -3
  27. data/examples/sample_app/script/startdrb +8 -0
  28. data/lib/merb.rb +37 -2
  29. data/lib/merb/merb_class_extensions.rb +21 -22
  30. data/lib/merb/merb_controller.rb +101 -33
  31. data/lib/merb/merb_handler.rb +26 -25
  32. data/lib/merb/merb_router.rb +1 -1
  33. data/lib/merb/merb_utils.rb +35 -37
  34. data/lib/merb/mixins/basic_authentication_mixin.rb +39 -0
  35. data/lib/merb/mixins/controller_mixin.rb +119 -115
  36. data/lib/merb/mixins/javascript_mixin.rb +63 -0
  37. data/lib/merb/mixins/render_mixin.rb +85 -69
  38. data/lib/merb/mixins/responder_mixin.rb +38 -0
  39. data/lib/merb/session/merb_drb_server.rb +107 -0
  40. data/lib/merb/session/merb_drb_session.rb +71 -0
  41. data/lib/merb/session/merb_session.rb +1 -0
  42. data/lib/merb/vendor/paginator/README.txt +84 -0
  43. data/lib/merb/vendor/paginator/paginator.rb +121 -0
  44. data/lib/mutex_hotfix.rb +34 -0
  45. metadata +41 -63
  46. data/doc/rdoc/classes/ControllerMixin.html +0 -676
  47. data/doc/rdoc/classes/Hash.html +0 -148
  48. data/doc/rdoc/classes/Merb.html +0 -140
  49. data/doc/rdoc/classes/Merb/Controller.html +0 -338
  50. data/doc/rdoc/classes/Merb/RouteMatcher.html +0 -388
  51. data/doc/rdoc/classes/Merb/Server.html +0 -148
  52. data/doc/rdoc/classes/Merb/Session.html +0 -201
  53. data/doc/rdoc/classes/Merb/SessionMixin.html +0 -199
  54. data/doc/rdoc/classes/MerbControllerError.html +0 -111
  55. data/doc/rdoc/classes/MerbHandler.html +0 -430
  56. data/doc/rdoc/classes/MerbHash.html +0 -469
  57. data/doc/rdoc/classes/MerbHash/Mutex.html +0 -198
  58. data/doc/rdoc/classes/Noroutefound.html +0 -153
  59. data/doc/rdoc/classes/Object.html +0 -149
  60. data/doc/rdoc/classes/RenderMixin.html +0 -362
  61. data/doc/rdoc/classes/String.html +0 -212
  62. data/doc/rdoc/classes/Symbol.html +0 -179
  63. data/doc/rdoc/created.rid +0 -1
  64. data/doc/rdoc/files/LICENSE.html +0 -129
  65. data/doc/rdoc/files/README.html +0 -417
  66. data/doc/rdoc/files/TODO.html +0 -151
  67. data/doc/rdoc/files/lib/merb/merb_class_extensions_rb.html +0 -101
  68. data/doc/rdoc/files/lib/merb/merb_controller_rb.html +0 -101
  69. data/doc/rdoc/files/lib/merb/merb_handler_rb.html +0 -101
  70. data/doc/rdoc/files/lib/merb/merb_router_rb.html +0 -101
  71. data/doc/rdoc/files/lib/merb/merb_utils_rb.html +0 -108
  72. data/doc/rdoc/files/lib/merb/mixins/controller_mixin_rb.html +0 -101
  73. data/doc/rdoc/files/lib/merb/mixins/render_mixin_rb.html +0 -101
  74. data/doc/rdoc/files/lib/merb/session/merb_session_rb.html +0 -101
  75. data/doc/rdoc/files/lib/merb_rb.html +0 -140
  76. data/doc/rdoc/files/lib/merb_tasks_rb.html +0 -101
  77. data/doc/rdoc/fr_class_index.html +0 -43
  78. data/doc/rdoc/fr_file_index.html +0 -40
  79. data/doc/rdoc/fr_method_index.html +0 -104
  80. data/doc/rdoc/index.html +0 -24
  81. data/doc/rdoc/rdoc-style.css +0 -208
  82. data/examples/sample_app/dist/app/controllers/upload.rb +0 -29
  83. data/examples/sample_app/dist/app/views/posts/comment.merbjs +0 -1
  84. data/examples/sample_app/dist/app/views/upload/start.rhtml +0 -15
  85. data/examples/sample_app/dist/app/views/upload/upload.rhtml +0 -4
  86. data/examples/sample_app/dist/public/files/README +0 -35
  87. data/examples/sample_app/dist/public/files/setup.rb +0 -1346
  88. data/examples/sample_app/log/merb.log +0 -778
@@ -0,0 +1,6 @@
1
+ <h2>File uploaded successfully</h2>
2
+
3
+ "UPLOADED: <%= params.inspect %><script type='text/javascript'>window.parent.UploadProgress.finish();</script>"
4
+
5
+
6
+
@@ -4,9 +4,8 @@
4
4
  <head>
5
5
  <title>Merb: Mongrel + Erb</title>
6
6
  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
7
- <link rel="stylesheet" href="/stylesheets/merb.css" type="text/css" />
8
- <script src="/javascripts/prototype.js" type="text/javascript"></script>
9
- <script src="/javascripts/effects.js" type="text/javascript"></script>
7
+ <%= require_css :merb %>
8
+ <%= require_js :prototype, :effects %>
10
9
  </head>
11
10
 
12
11
  <body>
@@ -0,0 +1 @@
1
+ $('comments').update(<%=js partial(:comments) %>);
@@ -0,0 +1,3 @@
1
+ xml.foo {
2
+ xml.bar "baz"
3
+ }
@@ -0,0 +1 @@
1
+ <%= @post.attributes.inspect.to_json %>
@@ -0,0 +1,11 @@
1
+ ---
2
+ :host: 0.0.0.0
3
+ :port: "4000"
4
+ :allow_reloading: true
5
+ :basic_auth:
6
+ :username: ezra
7
+ :password: test
8
+ :domain: localhost
9
+ #:daemonize: true
10
+ #:cluster: 3
11
+ #:session: true
@@ -9,7 +9,7 @@ ActiveRecord::Base.logger = MERB_LOGGER
9
9
  ActiveRecord::Base.establish_connection(
10
10
  :adapter => 'mysql',
11
11
  :username => 'root',
12
- :password => 'xxxxx',
12
+ :password => 'reversal',
13
13
  :database => 'merb'
14
14
  )
15
15
  ActiveRecord::Base.verification_timeout = 14400
@@ -0,0 +1,11 @@
1
+ uri "/",
2
+ :handler => plugin("/handlers/upload", :path_info => '/files/upload'),
3
+ :in_front => true,
4
+ :frequency => 1
5
+
6
+ #uri "/",
7
+ # :handler => plugin("/handlers/upload",
8
+ # :path_info => '/files/upload',
9
+ # :frequency => 1,
10
+ # :drb => 'druby://0.0.0.0:2999'),
11
+ # :in_front => true
@@ -0,0 +1,113 @@
1
+ var UploadProgress = {
2
+ uploading: null,
3
+ monitor: function(upid) {
4
+ if(!this.periodicExecuter) {
5
+ this.periodicExecuter = new PeriodicalExecuter(function() {
6
+ if(!UploadProgress.uploading) return;
7
+ new Ajax.Request('/files/progress?upload_id=' + upid);
8
+ }, 3);
9
+ }
10
+
11
+ this.uploading = true;
12
+ this.StatusBar.create();
13
+ },
14
+
15
+ update: function(total, current) {
16
+ if(!this.uploading) return;
17
+ var status = current / total;
18
+ var statusHTML = status.toPercentage();
19
+ $('results').innerHTML = statusHTML + "<br /><small>" + current.toHumanSize() + ' of ' + total.toHumanSize() + " uploaded.</small>";
20
+ this.StatusBar.update(status, statusHTML);
21
+ },
22
+
23
+ finish: function() {
24
+ this.uploading = false;
25
+ this.StatusBar.finish();
26
+ $('results').innerHTML = 'finished!';
27
+ },
28
+
29
+ cancel: function(msg) {
30
+ if(!this.uploading) return;
31
+ this.uploading = false;
32
+ if(this.StatusBar.statusText) this.StatusBar.statusText.innerHTML = msg || 'canceled';
33
+ },
34
+
35
+ StatusBar: {
36
+ statusBar: null,
37
+ statusText: null,
38
+ statusBarWidth: 500,
39
+
40
+ create: function() {
41
+ this.statusBar = this._createStatus('status-bar');
42
+ this.statusText = this._createStatus('status-text');
43
+ this.statusText.innerHTML = '0%';
44
+ this.statusBar.style.width = '0';
45
+ },
46
+
47
+ update: function(status, statusHTML) {
48
+ this.statusText.innerHTML = statusHTML;
49
+ this.statusBar.style.width = Math.floor(this.statusBarWidth * status);
50
+ },
51
+
52
+ finish: function() {
53
+ this.statusText.innerHTML = '100%';
54
+ this.statusBar.style.width = '100%';
55
+ },
56
+
57
+ _createStatus: function(id) {
58
+ el = $(id);
59
+ if(!el) {
60
+ el = document.createElement('span');
61
+ el.setAttribute('id', id);
62
+ $('progress-bar').appendChild(el);
63
+ }
64
+ return el;
65
+ }
66
+ },
67
+
68
+ FileField: {
69
+ add: function() {
70
+ new Insertion.Bottom('file-fields', '<p style="display:none"><input id="data" name="data" type="file" /> <a href="#" onclick="UploadProgress.FileField.remove(this);return false;">x</a></p>')
71
+ $$('#file-fields p').last().visualEffect('blind_down', {duration:0.3});
72
+ },
73
+
74
+ remove: function(anchor) {
75
+ anchor.parentNode.visualEffect('drop_out', {duration:0.25});
76
+ }
77
+ }
78
+ }
79
+
80
+ Number.prototype.bytes = function() { return this; };
81
+ Number.prototype.kilobytes = function() { return this * 1024; };
82
+ Number.prototype.megabytes = function() { return this * (1024).kilobytes(); };
83
+ Number.prototype.gigabytes = function() { return this * (1024).megabytes(); };
84
+ Number.prototype.terabytes = function() { return this * (1024).gigabytes(); };
85
+ Number.prototype.petabytes = function() { return this * (1024).terabytes(); };
86
+ Number.prototype.exabytes = function() { return this * (1024).petabytes(); };
87
+ ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte', 'petabyte', 'exabyte'].each(function(meth) {
88
+ Number.prototype[meth] = Number.prototype[meth+'s'];
89
+ });
90
+
91
+ Number.prototype.toPrecision = function() {
92
+ var precision = arguments[0] || 2;
93
+ var s = Math.round(this * Math.pow(10, precision)).toString();
94
+ var pos = s.length - precision;
95
+ var last = s.substr(pos, precision);
96
+ return s.substr(0, pos) + (last.match("^0{" + precision + "}$") ? '' : '.' + last);
97
+ }
98
+
99
+ // (1/10).toPercentage()
100
+ // # => '10%'
101
+ Number.prototype.toPercentage = function() {
102
+ return (this * 100).toPrecision() + '%';
103
+ }
104
+
105
+ Number.prototype.toHumanSize = function() {
106
+ if(this < (1).kilobyte()) return this + " Bytes";
107
+ if(this < (1).megabyte()) return (this / (1).kilobyte()).toPrecision() + ' KB';
108
+ if(this < (1).gigabytes()) return (this / (1).megabyte()).toPrecision() + ' MB';
109
+ if(this < (1).terabytes()) return (this / (1).gigabytes()).toPrecision() + ' GB';
110
+ if(this < (1).petabytes()) return (this / (1).terabytes()).toPrecision() + ' TB';
111
+ if(this < (1).exabytes()) return (this / (1).petabytes()).toPrecision() + ' PB';
112
+ return (this / (1).exabytes()).toPrecision() + ' EB';
113
+ }
@@ -1,9 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'fileutils'
3
- pids = IO.readlines(File.dirname(__FILE__)+"/../log/merb.pid").map{|p| p.to_i}
4
3
 
5
- pids.each do |pid|
4
+ pids=[]
5
+
6
+ port_or_star = ARGV[0] || '*'
7
+
8
+ Dir[File.dirname(__FILE__)+"/../log/merb.#{port_or_star}.pid"].each do |f|
9
+ pid = IO.read(f).chomp.to_i
6
10
  puts "killing PID: #{pid}"
7
11
  Process.kill(9, pid)
12
+ FileUtils.rm f
8
13
  end
9
- FileUtils.rm File.dirname(__FILE__)+"/../log/merb.pid"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'merb/session/merb_drb_server'
5
+ DRb.start_service("druby://localhost:2222", Merb::DRbSession.setup)
6
+ puts "DRb URI: #{DRb.uri}"
7
+ puts "Pid: #{Process.pid}"
8
+ DRb.thread.join
data/lib/merb.rb CHANGED
@@ -5,7 +5,7 @@ require 'erubis'
5
5
  require 'logger'
6
6
 
7
7
  module Merb
8
- VERSION='0.0.6' unless defined?VERSION
8
+ VERSION='0.0.7' unless defined?VERSION
9
9
  class Server
10
10
  def self.config
11
11
  @@merb_opts ||= {}
@@ -16,8 +16,43 @@ end
16
16
  class MerbControllerError < RuntimeError; end
17
17
 
18
18
  MERB_FRAMEWORK_ROOT = File.dirname(__FILE__)
19
+
20
+ begin
21
+ require 'thread'
22
+ require 'fastthread'
23
+ puts 'Using FastThread'
24
+ rescue LoadError
25
+ require 'mutex_hotfix'
26
+ end
27
+
28
+ begin
29
+ require 'fjson'
30
+ puts "using fjson"
31
+ rescue LoadError
32
+ require 'json'
33
+ end
34
+
19
35
  MERB_ROOT = Merb::Server.config[:merb_root] || Dir.pwd
20
36
  DIST_ROOT = Merb::Server.config[:dist_root] || Dir.pwd+'/dist'
21
- MERB_LOGGER = Logger.new("#{MERB_ROOT}/log/merb.log")
37
+
38
+ MERB_LOGGER = Logger.new("#{MERB_ROOT}/log/merb.log")
39
+ # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
40
+ MERB_LOGGER.level = case (Merb::Server.config[:log_level].downcase rescue '')
41
+ when 'debug'
42
+ Logger::DEBUG
43
+ when 'info'
44
+ Logger::INFO
45
+ when 'warn'
46
+ Logger::WARN
47
+ when 'error'
48
+ Logger::ERROR
49
+ when 'fatal'
50
+ Logger::FATAL
51
+ when 'unknown'
52
+ Logger::UNKNOWN
53
+ else
54
+ Logger::INFO
55
+ end
56
+
22
57
  lib = File.join(File.dirname(__FILE__), 'merb')
23
58
  Dir.entries(lib).sort.each {|fn| require File.join(lib, fn) if fn =~ /\.rb$/}
@@ -1,9 +1,9 @@
1
1
  class Class # :nodoc:
2
- def inheritable_reader(*syms)
2
+ def meta_reader(*syms)
3
3
  syms.each do |sym|
4
4
  class_eval <<-EOS
5
5
  def self.#{sym}
6
- read_inheritable_attribute(:#{sym})
6
+ read_meta_attribute(:#{sym})
7
7
  end
8
8
 
9
9
  def #{sym}
@@ -13,11 +13,11 @@ class Class # :nodoc:
13
13
  end
14
14
  end
15
15
 
16
- def inheritable_writer(*syms)
16
+ def meta_writer(*syms)
17
17
  syms.each do |sym|
18
18
  class_eval <<-EOS
19
19
  def self.#{sym}=(obj)
20
- write_inheritable_attribute(:#{sym}, obj)
20
+ write_meta_attribute(:#{sym}, obj)
21
21
  end
22
22
 
23
23
  def #{sym}=(obj)
@@ -27,39 +27,38 @@ class Class # :nodoc:
27
27
  end
28
28
  end
29
29
 
30
-
31
- def inheritable_accessor(*syms)
32
- inheritable_reader(*syms)
33
- inheritable_writer(*syms)
30
+ def meta_accessor(*syms)
31
+ meta_reader(*syms)
32
+ meta_writer(*syms)
34
33
  end
35
34
 
36
- def inheritable_attributes
37
- @inheritable_attributes ||= {}
35
+ def meta_attributes
36
+ @meta_attributes ||= {}
38
37
  end
39
38
 
40
- def write_inheritable_attribute(key, value)
41
- inheritable_attributes[key] = value
39
+ def write_meta_attribute(key, value)
40
+ meta_attributes[key] = value
42
41
  end
43
42
 
44
- def read_inheritable_attribute(key)
45
- inheritable_attributes[key]
43
+ def read_meta_attribute(key)
44
+ meta_attributes[key]
46
45
  end
47
46
 
48
- def reset_inheritable_attributes
49
- inheritable_attributes.clear
47
+ def reset_meta_attributes
48
+ meta_attributes.clear
50
49
  end
51
50
 
52
51
  private
53
- def inherited_with_inheritable_attributes(child)
54
- inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
52
+ def inherited_with_meta_attributes(child)
53
+ inherited_without_meta_attributes(child) if respond_to?(:inherited_without_meta_attributes)
55
54
 
56
- new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
55
+ new_meta_attributes = meta_attributes.inject({}) do |memo, (key, value)|
57
56
  memo.update(key => (value.dup rescue value))
58
57
  end
59
58
 
60
- child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
59
+ child.instance_variable_set('@meta_attributes', new_meta_attributes)
61
60
  end
62
61
 
63
- alias inherited_without_inheritable_attributes inherited
64
- alias inherited inherited_with_inheritable_attributes
62
+ alias inherited_without_meta_attributes inherited
63
+ alias inherited inherited_with_meta_attributes
65
64
  end
@@ -1,5 +1,7 @@
1
1
  require File.dirname(__FILE__)+'/mixins/controller_mixin'
2
2
  require File.dirname(__FILE__)+'/mixins/render_mixin'
3
+ require File.dirname(__FILE__)+'/mixins/javascript_mixin'
4
+ require File.dirname(__FILE__)+'/mixins/responder_mixin'
3
5
 
4
6
  module Merb
5
7
 
@@ -10,30 +12,36 @@ module Merb
10
12
  # to your controller via params. It also parses the ?query=string and
11
13
  # puts that into params as well.
12
14
  class Controller
13
- include ::ControllerMixin
14
- include ::RenderMixin
15
+ include Merb::ControllerMixin
16
+ include Merb::RenderMixin
17
+ include Merb::JavascriptMixin
18
+ include Merb::ResponderMixin
15
19
 
16
20
  if Merb::Server.config[:session]
17
- require File.dirname(__FILE__)+"/session/merb_session"
21
+ require "drb"
22
+ DRb.start_service('druby://localhost:0')
23
+ Merb.const_set :DRbSession, DRbObject.new(nil, "druby://#{Merb::Server.config[:host]}:#{Merb::Server.config[:session]}")
24
+ require File.dirname(__FILE__)+"/session/merb_drb_session"
18
25
  include ::Merb::SessionMixin
19
- puts "session mixed in"
26
+ puts "drb session mixed in"
20
27
  end
21
28
 
22
- attr_accessor :status
29
+ attr_accessor :status, :body
23
30
 
24
31
  # parses the http request into params, headers and cookies
25
32
  # that you can use in your controller classes. Also handles
26
33
  # file uploads by writing a tempfile and passing a reference
27
34
  # in params.
28
- def initialize(req, env, args, method=(env['REQUEST_METHOD']||"GET")) #:nodoc:
35
+ def initialize(req, env, args, method=(env['REQUEST_METHOD']||'GET'))
29
36
  env = MerbHash[env.to_hash]
30
- @layout = 'application'
31
- @status, @method, @env, @headers, @root = 200, method.downcase, env,
32
- {'Content-Type'=>'text/html'}, env['SCRIPT_NAME'].sub(/\/$/,'')
37
+ @layout = :application
38
+ @status, @method, @env, @headers, @root = 200, method.downcase.to_sym, env,
39
+ {'Content-Type' =>'text/html'}, env['SCRIPT_NAME'].sub(/\/$/,'')
33
40
  @k = query_parse(env['HTTP_COOKIE'], ';,')
34
41
  qs = query_parse(env['QUERY_STRING'])
42
+ #puts req.read; req.rewind
35
43
  @in = req
36
- if %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)|n.match(env['CONTENT_TYPE'])
44
+ if %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)|n =~ (env['CONTENT_TYPE'])
37
45
  b = /(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/
38
46
  until @in.eof?
39
47
  fh=MerbHash[]
@@ -65,21 +73,39 @@ module Merb
65
73
  qs[fn]=fh if fn
66
74
  fh[:tempfile].rewind if fh.is_a?MerbHash
67
75
  end
68
- elsif @method == "post"
69
- qs.merge!(query_parse(@in.read))
76
+ elsif @method == :post
77
+ if ['application/json', 'text/x-json'].include?(env['CONTENT_TYPE'])
78
+ MERB_LOGGER.info("JSON Request")
79
+ json = JSON.parse(@in.read || "") || {}
80
+ json = MerbHash.new(json) if json.is_a? Hash
81
+ qs.merge!(json)
82
+ else
83
+ qs.merge!(query_parse(@in.read))
84
+ end
70
85
  end
71
86
  @cookies, @params = @k.dup, qs.dup.merge(args)
87
+ @cookies.merge!(:sess_id => @params.delete(:sess_id)) if @params.has_key?:sess_id
72
88
  MERB_LOGGER.info("Params: #{params.inspect}")
73
89
  end
74
90
 
75
- def dispatch(action=nil)
91
+ def dispatch(action=:to_s)
92
+ start = Time.now
76
93
  setup_session if respond_to?:setup_session
77
- call_filters(before_filters)
78
- res = send(action || :to_s)
94
+ if catch(:halt) { call_filters(before_filters) }
95
+ @body = send(action)
96
+ else
97
+ @body = filters_halted
98
+ end
79
99
  finalize_session if respond_to?:finalize_session
80
- res
100
+ MERB_LOGGER.info("Time spent in #{action} action: #{Time.now - start} seconds")
81
101
  end
82
-
102
+
103
+ # override this method on your controller classes to specialize
104
+ # the output when the filter chain is halted.
105
+ def filters_halted
106
+ "<html><body><h1>Filter Chain Halted!</h1></body></html>"
107
+ end
108
+
83
109
  # accessor for @params. Please use params and
84
110
  # never @params directly.
85
111
  def params
@@ -98,28 +124,70 @@ module Merb
98
124
  @headers
99
125
  end
100
126
 
101
- inheritable_accessor :before_filters
127
+ # meta_accessor sets up a class instance variable that can
128
+ # be unique for each class but also inherits the meta attrs
129
+ # from its superclasses. Since @@class variables are almost
130
+ # global vars within an inheritance tree
131
+ meta_accessor :before_filters
102
132
 
103
133
  def call_filters(filter_set)
104
- (filter_set || []).each do |filter|
134
+ (filter_set || []).each do |(filter, rule)|
135
+ ok = false
136
+ if rule.has_key?(:include)
137
+ if rule[:include].include?(params[:action].intern)
138
+ ok = true
139
+ end
140
+ elsif rule.has_key?(:exclude)
141
+ if !rule[:exclude].include?(params[:action].intern)
142
+ ok = true
143
+ end
144
+ else
145
+ ok = true
146
+ end
105
147
  case filter
106
- when Symbol
107
- send(filter)
148
+ when Symbol, String
149
+ send(filter) if ok
108
150
  when Proc
109
- filter.call(self)
110
- else
111
- raise(
112
- MerbControllerError,
113
- 'filters needs to be either a Symbol or a Proc'
114
- )
115
- end
151
+ filter.call(self) if ok
152
+ end
153
+ end
154
+ end
155
+
156
+ # #before is a class method that allows you to specify before
157
+ # filters in your controllers. Filters can either before a symbol
158
+ # or string that corresponds to a method name or a proc object.
159
+ # if it is a method name that method will be called and if it
160
+ # is a proc it will be called with an argument of self. When
161
+ # you use a proc as a filter it needs to take one parameter.
162
+ def self.before(filter, opts={})
163
+ raise(ArgumentError,
164
+ "You can specify either :include or :exclude but
165
+ not both at the same time for the same filter."
166
+ ) if opts.has_key?(:include) && opts.has_key?(:exclude)
167
+
168
+ if opts[:include] && opts[:include].is_a?(Symbol)
169
+ opts[:include] = [opts[:include]]
170
+ end
171
+ if opts[:exclude] && opts[:exclude].is_a?(Symbol)
172
+ opts[:exclude] = [opts[:exclude]]
173
+ end
174
+
175
+ case filter
176
+ when Symbol, String, Proc
177
+ (self.before_filters ||= []) << [filter, opts]
178
+ else
179
+ raise(MerbControllerError,
180
+ 'filters need to be either a Symbol, String or a Proc'
181
+ )
116
182
  end
117
- end
118
-
119
- def self.before(filter)
120
- (self.before_filters ||= []) << filter
121
183
  end
122
184
 
185
+ if Merb::Server.config[:basic_auth]
186
+ require File.dirname(__FILE__)+"/mixins/basic_authentication_mixin"
187
+ include ::Merb::Authentication
188
+ puts "Basic Authentication mixed in"
189
+ end
190
+
123
191
  end
124
192
 
125
193
  end
@@ -129,7 +197,7 @@ class Noroutefound < Merb::Controller
129
197
  # match any defined routes.
130
198
  def method_missing
131
199
  @status = 404
132
- "<html><body><h1>No Matching Route</h1></body></html>"
200
+ "<html><body><h1>No Matching Route!</h1></body></html>"
133
201
  end
134
202
 
135
203
  end