ramaze 0.3.0 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/Rakefile +3 -2
  2. data/bin/ramaze +9 -3
  3. data/doc/AUTHORS +6 -2
  4. data/doc/CHANGELOG +272 -66
  5. data/doc/GPL +12 -13
  6. data/doc/README.html +729 -0
  7. data/doc/changes.txt +5757 -0
  8. data/doc/changes.xml +5759 -0
  9. data/doc/meta/announcement.txt +48 -39
  10. data/doc/tutorial/todolist.mkd +10 -12
  11. data/examples/blog/start.rb +1 -0
  12. data/examples/caching.rb +1 -0
  13. data/examples/element.rb +3 -0
  14. data/examples/hello.rb +1 -3
  15. data/examples/identity.rb +5 -8
  16. data/examples/layout.rb +1 -0
  17. data/examples/linking.rb +1 -0
  18. data/examples/memleak_detector.rb +1 -0
  19. data/examples/nitro_form.rb +1 -0
  20. data/examples/rammit/start.rb +1 -0
  21. data/examples/rapaste/Rakefile +7 -0
  22. data/examples/rapaste/{src/controller.rb → controller/paste.rb} +28 -3
  23. data/examples/rapaste/{src/model.rb → model/paste.rb} +0 -4
  24. data/examples/rapaste/public/css/display.css +17 -0
  25. data/examples/rapaste/spec/rapaste.rb +2 -2
  26. data/examples/rapaste/start.rb +9 -4
  27. data/examples/rapaste/{template → view}/copy.xhtml +0 -0
  28. data/examples/rapaste/{template → view}/index.xhtml +0 -0
  29. data/examples/rapaste/{template → view}/layout.xhtml +3 -0
  30. data/examples/rapaste/{template → view}/list.xhtml +4 -3
  31. data/examples/rapaste/view/search.xhtml +41 -0
  32. data/examples/rapaste/{template → view}/view.xhtml +0 -0
  33. data/examples/simple.rb +3 -3
  34. data/examples/templates/template/external.amrita +9 -9
  35. data/examples/templates/template/external.redcloth +19 -0
  36. data/examples/templates/template_amrita2.rb +40 -7
  37. data/examples/templates/template_erubis.rb +3 -3
  38. data/examples/templates/template_ezamar.rb +1 -3
  39. data/examples/templates/template_haml.rb +1 -3
  40. data/examples/templates/template_liquid.rb +3 -3
  41. data/examples/templates/template_markaby.rb +1 -3
  42. data/examples/templates/template_nagoro.rb +1 -3
  43. data/examples/templates/template_redcloth.rb +59 -0
  44. data/examples/templates/template_remarkably.rb +1 -3
  45. data/examples/templates/template_xslt.rb +1 -4
  46. data/examples/todolist/src/controller/main.rb +1 -1
  47. data/examples/todolist/start.rb +1 -2
  48. data/examples/upload/start.rb +19 -0
  49. data/examples/upload/view/index.xhtml +25 -0
  50. data/examples/whywiki/start.rb +1 -3
  51. data/examples/wikore/start.rb +3 -0
  52. data/examples/wiktacular/mkd/newpagename/current.mkd +1 -0
  53. data/examples/wiktacular/mkd/newpagename/current.mkd.bak +1 -0
  54. data/examples/wiktacular/start.rb +1 -0
  55. data/lib/proto/controller/main.rb +0 -3
  56. data/lib/proto/public/js/jquery.js +106 -93
  57. data/lib/proto/spec/main.rb +2 -5
  58. data/lib/proto/start.rb +0 -3
  59. data/lib/ramaze.rb +3 -2
  60. data/lib/ramaze/action.rb +6 -10
  61. data/lib/ramaze/adapter/lsws.rb +19 -0
  62. data/lib/ramaze/contrib/email.rb +84 -0
  63. data/lib/ramaze/contrib/email.rb-darcs-backup0 +81 -0
  64. data/lib/ramaze/contrib/gettext.rb +1 -0
  65. data/lib/ramaze/contrib/gettext/parser.rb +46 -0
  66. data/lib/ramaze/contrib/route.rb +3 -36
  67. data/lib/ramaze/controller.rb +4 -6
  68. data/lib/ramaze/controller/resolve.rb +28 -1
  69. data/lib/ramaze/dispatcher.rb +1 -1
  70. data/lib/ramaze/dispatcher/file.rb +17 -0
  71. data/lib/ramaze/global/globalstruct.rb +7 -4
  72. data/lib/ramaze/helper/auth.rb +1 -1
  73. data/lib/ramaze/helper/identity.rb +25 -15
  74. data/lib/ramaze/helper/link.rb +29 -8
  75. data/lib/ramaze/helper/maruku.rb +7 -0
  76. data/lib/ramaze/helper/partial.rb +25 -10
  77. data/lib/ramaze/route.rb +56 -0
  78. data/lib/ramaze/snippets/metaid.rb +17 -0
  79. data/lib/ramaze/spec/helper.rb +0 -2
  80. data/lib/ramaze/spec/helper/mock_http.rb +6 -0
  81. data/lib/ramaze/spec/helper/pretty_output.rb +5 -1
  82. data/lib/ramaze/store/default.rb +3 -1
  83. data/lib/ramaze/template.rb +1 -1
  84. data/lib/ramaze/template/amrita2.rb +21 -15
  85. data/lib/ramaze/template/bijou.rb +39 -0
  86. data/lib/ramaze/template/builder.rb +28 -0
  87. data/lib/ramaze/template/redcloth.rb +24 -0
  88. data/lib/ramaze/template/sass.rb +3 -1
  89. data/lib/ramaze/tool/create.rb +2 -1
  90. data/lib/ramaze/tool/localize.rb +6 -1
  91. data/lib/ramaze/trinity/request.rb +8 -0
  92. data/lib/ramaze/trinity/session.rb +8 -5
  93. data/lib/ramaze/version.rb +1 -1
  94. data/rake_tasks/gem.rake +0 -1
  95. data/rake_tasks/maintenance.rake +4 -1
  96. data/spec/contrib/route.rb +14 -63
  97. data/spec/examples/linking.rb +2 -2
  98. data/spec/examples/templates/template_redcloth.rb +28 -0
  99. data/spec/ramaze/controller/subclass.rb +21 -0
  100. data/spec/ramaze/dispatcher/file.rb +31 -0
  101. data/spec/ramaze/helper/link.rb +46 -0
  102. data/spec/ramaze/helper/partial.rb +22 -1
  103. data/spec/ramaze/helper/template/locals.xhtml +1 -0
  104. data/spec/ramaze/helper/template/recursive_locals.xhtml +7 -0
  105. data/spec/ramaze/params.rb +8 -0
  106. data/spec/ramaze/request.rb +14 -0
  107. data/spec/ramaze/route.rb +107 -0
  108. data/spec/ramaze/session.rb +4 -2
  109. data/spec/ramaze/template/amrita2.rb +21 -7
  110. data/spec/ramaze/template/amrita2/external.amrita +6 -0
  111. data/spec/ramaze/template/amrita2/sum.amrita +1 -1
  112. data/spec/ramaze/template/bijou.rb +25 -0
  113. data/spec/ramaze/template/builder.rb +55 -0
  114. data/spec/ramaze/template/builder/external.rxml +3 -0
  115. data/spec/ramaze/template/haml.rb +15 -0
  116. data/spec/ramaze/template/haml/locals.haml +1 -0
  117. data/spec/ramaze/template/redcloth.rb +38 -0
  118. data/spec/ramaze/template/redcloth/external.redcloth +1 -0
  119. metadata +472 -442
  120. data/examples/rapaste/rapaste.sqlite +0 -0
  121. data/spec/ramaze/template/amrita2/data.amrita +0 -6
  122. data/spec/ramaze/template/amrita2/index.amrita +0 -1
data/lib/ramaze.rb CHANGED
@@ -23,6 +23,7 @@ Thread.abort_on_exception = true
23
23
  require 'ramaze/version'
24
24
  require 'ramaze/snippets'
25
25
  require 'ramaze/inform'
26
+ require 'ramaze/route'
26
27
  require 'ramaze/global'
27
28
  require 'ramaze/cache'
28
29
  require 'ramaze/tool'
@@ -36,7 +37,6 @@ require 'ramaze/sourcereload'
36
37
  require 'ramaze/dispatcher'
37
38
  require 'ramaze/template/ezamar'
38
39
  require 'ramaze/contrib'
39
- require 'ramaze/controller/main'
40
40
 
41
41
  module Ramaze
42
42
 
@@ -52,7 +52,8 @@ module Ramaze
52
52
  # each class in trait[:essentials] by calling ::startup on them.
53
53
 
54
54
  def startup options = {}
55
- runner = options.delete(:runner) || caller[0].split(':').first
55
+ runner_from_caller = caller[0][/^(.*?):\d+/, 1]
56
+ runner = options.delete(:runner) || runner_from_caller
56
57
 
57
58
  if $0 == runner or options.delete(:force)
58
59
  Inform.info("Starting up Ramaze (Version #{VERSION})")
data/lib/ramaze/action.rb CHANGED
@@ -80,17 +80,13 @@ module Ramaze
80
80
  default = controller.trait.fetch(:engine, Template::Ezamar)
81
81
  return default unless template
82
82
 
83
- engines = Template::ENGINES
84
- return default if engines.empty?
85
-
86
- ext = File.extname(template).gsub(/^\./, '')
87
- ext_engines = engines.reject{|k,v| not v.include?(ext) }
88
-
89
- if ext_engines.has_key?(default)
90
- self[:engine] = default
91
- else
92
- self[:engine] = ext_engines.keys.sort.first
83
+ Template::ENGINES.sort_by{|v| v.join}.each do |(engine, exts)|
84
+ if template =~ /\.(#{Regexp.union(*exts)})$/
85
+ return self[:engine] = engine
86
+ end
93
87
  end
88
+
89
+ self[:engine] = default
94
90
  end
95
91
 
96
92
  # Returns an instance of controller, will be cached on first access.
@@ -0,0 +1,19 @@
1
+ require 'ramaze/adapter'
2
+
3
+ module Ramaze::Adapter
4
+
5
+ # Our Lsws adapter acts as wrapper for the Rack::Handler::LSWS.
6
+ class Lsws < Base
7
+ class << self
8
+
9
+ # start Lsws in a new thread, host and port parameter are only taken
10
+ # to make it compatible with other adapters but have no influence and
11
+ # can be omitted
12
+ def run_server host = nil, ports = nil
13
+ Thread.new do
14
+ Rack::Handler::LSWS.run(self)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,84 @@
1
+ # EmailHelper can be used as a simple way to send basic e-mails from your app.
2
+ #
3
+ # Usage:
4
+ #
5
+ # require 'ramaze/contrib/email'
6
+ #
7
+ # # Set the required traits:
8
+ # Ramaze::EmailHelper.trait[ :smtp_server ] = 'smtp.your-isp.com'
9
+ # Ramaze::EmailHelper.trait[ :smtp_helo_domain ] = "originating-server.com"
10
+ # Ramaze::EmailHelper.trait[ :smtp_username ] = 'username'
11
+ # Ramaze::EmailHelper.trait[ :smtp_password ] = 'password'
12
+ # Ramaze::EmailHelper.trait[ :sender_address ] = 'no-reply@your-domain.com'
13
+ #
14
+ # # Optionally, set some other traits:
15
+ # Ramaze::EmailHelper.trait[ :smtp_auth_type ] = :login
16
+ # Ramaze::EmailHelper.trait[ :bcc_addresses ] = [ 'admin@your-domain.com' ]
17
+ # Ramaze::EmailHelper.trait[ :sender_full ] = 'MailBot <no-reply@your-domain.com>'
18
+ # Ramaze::EmailHelper.trait[ :id_generator ] = lambda { "<#{Time.now.to_i}@your-domain.com>" }
19
+ # Ramaze::EmailHelper.trait[ :subject_prefix ] = "[SiteName]"
20
+ #
21
+ # To send an e-mail:
22
+ #
23
+ # Ramaze::EmailHelper.send(
24
+ # "foo@foobarmail.com",
25
+ # "Your fooness",
26
+ # "Hey, you are very fooey!"
27
+ # )
28
+
29
+ require 'net/smtp'
30
+
31
+ module Ramaze
32
+ class EmailHelper
33
+ # Required to be set
34
+ trait :smtp_server => 'smtp.your-isp.com'
35
+ trait :smtp_helo_domain => 'your.helo.domain.com'
36
+ trait :smtp_username => 'no-username-set'
37
+ trait :smtp_password => ''
38
+ trait :sender_address => 'no-reply@your-domain.com'
39
+
40
+ # Optionally set
41
+ trait :smtp_port => 25
42
+ trait :smtp_auth_type => :login
43
+ trait :bcc_addresses => []
44
+ trait :sender_full => nil
45
+ trait :id_generator => lambda { "<" + Time.now.to_i.to_s + "@" + trait[ :smtp_helo_domain ] + ">" }
46
+ trait :subject_prefix => ""
47
+
48
+ class << self
49
+ def send(recipient, subject, message)
50
+ {:recipient => recipient, :subject => subject, :message => message}.each do |k,v|
51
+ if v.nil? or v.empty?
52
+ raise(ArgumentError, "EmailHelper error: Missing or invalid #{k}: #{v.inspect}")
53
+ end
54
+ end
55
+ sender = trait[:sender_full] || "#{trait[:sender_address]} <#{trait[:sender_address]}>"
56
+ subject = [trait[:subject_prefix], subject].join(' ').strip
57
+ id = trait[:id_generator].call
58
+ email = %{From: #{sender}
59
+ To: <#{recipient}>
60
+ Date: #{Time.now.rfc2822}
61
+ Subject: #{subject}
62
+ Message-Id: #{id}
63
+
64
+ #{message}
65
+ }
66
+
67
+ send_smtp( email, recipient, subject )
68
+ end
69
+
70
+ def send_smtp( email, recipient, subject )
71
+ options = trait.values_at(:smtp_server, :smtp_port, :smtp_helo_domain,
72
+ :smtp_username, :smtp_password, :smtp_auth_type)
73
+
74
+ Net::SMTP.start( *options ) do |smtp|
75
+ smtp.send_message( email, trait[ :sender_address ], Array[ recipient, *trait[ :bcc_addresses ] ] )
76
+ Inform.info "E-mail sent to #{recipient} - '#{subject}'"
77
+ end
78
+ rescue => e
79
+ Inform.error "Failed to send e-mail to #{recipient}"
80
+ Inform.error e
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,81 @@
1
+ # EmailHelper can be used as a simple way to send basic e-mails from your app.
2
+ #
3
+ # Usage:
4
+ #
5
+ # require 'ramaze/contrib/email'
6
+ #
7
+ # # Set the required traits:
8
+ # EmailHelper.trait[ :smtp_server ] = 'smtp.your-isp.com'
9
+ # EmailHelper.trait[ :smtp_helo_domain ] = "originating-server.com"
10
+ # EmailHelper.trait[ :smtp_username ] = 'username'
11
+ # EmailHelper.trait[ :smtp_password ] = 'password'
12
+ # EmailHelper.trait[ :sender_address ] = 'no-reply@your-domain.com'
13
+ #
14
+ # # Optionally, set some other traits:
15
+ # EmailHelper.trait[ :smtp_auth_type ] = :login
16
+ # EmailHelper.trait[ :bcc_addresses ] = [ 'admin@your-domain.com' ]
17
+ # EmailHelper.trait[ :sender_full ] = 'MailBot <no-reply@your-domain.com>'
18
+ # EmailHelper.trait[ :id_generator ] = lambda { "<#{Time.now.to_i}@your-domain.com>" }
19
+ # EmailHelper.trait[ :subject_prefix ] = "[SiteName]"
20
+ #
21
+ # To send an e-mail:
22
+ #
23
+ # EmailHelper.send(
24
+ # "foo@foobarmail.com",
25
+ # "Your fooness",
26
+ # "Hey, you are very fooey!"
27
+ # )
28
+
29
+ require 'net/smtp'
30
+
31
+ module Ramaze
32
+ class EmailHelper
33
+ # Required to be set
34
+ trait :smtp_server => 'smtp.your-isp.com'
35
+ trait :smtp_helo_domain => 'your.helo.domain.com'
36
+ trait :smtp_username => 'no-username-set'
37
+ trait :smtp_password => ''
38
+ trait :sender_address => 'no-reply@your-domain.com'
39
+
40
+ # Optionally set
41
+ trait :smtp_port => 25
42
+ trait :smtp_auth_type => :login
43
+ trait :bcc_addresses => []
44
+ trait :sender_full => nil
45
+ trait :id_generator => lambda { "<" + Time.now.to_i.to_s + "@" + trait[ :smtp_helo_domain ] + ">" }
46
+ trait :subject_prefix => ""
47
+
48
+ class << self
49
+ def send(recipient, subject, message)
50
+ {:recipient => recipient, :subject => subject, :message => message}.each do |k,v|
51
+ raise(ArgumentError, "EmailHelper error: Missing or invalid #{k}: #{v.inspect}")
52
+ end
53
+ sender = trait[:sender_full] || "#{trait[:sender_address]} <#{trait[:sender_address]}>"
54
+ subject = [trait[:subject_prefix], subject].join(' ').strip
55
+ id = trait[:id_generator].call
56
+ email = %{From: #{sender}
57
+ To: <#{recipient}>
58
+ Date: #{Time.now.rfc2822}
59
+ Subject: #{subject}
60
+ Message-Id: #{id}
61
+
62
+ #{message}
63
+ }
64
+
65
+ send_smtp(email)
66
+ end
67
+
68
+ def send_smtp(email, recipient, subject, message)
69
+ options = trait.values_at(:smtp_server, :smtp_port, :smtp_helo_domain,
70
+ :smtp_username, :smtp_password, :smtp_auth_type)
71
+ Net::SMTP.start(*options) do |smtp|
72
+ smtp.send_message(email, sender_address, Array[recipient, *bcc_addresses])
73
+ Inform.info "E-mail sent to #{recipient} - '#{subject}'"
74
+ end
75
+ rescue => e
76
+ Inform.error "Failed to send e-mail to #{recipient}"
77
+ Inform.error e
78
+ end
79
+ end
80
+ end
81
+ end
@@ -89,6 +89,7 @@ class Ramaze::Tool::Gettext < Ramaze::Tool::Localize
89
89
  dictionary.each do |locale, dict|
90
90
  keys.concat dict.keys
91
91
  end
92
+ keys.delete ""
92
93
 
93
94
  data = ::GetText::RGetText.generate(keys.compact.uniq.sort.map {|x| [x] })
94
95
  file = (trait[:file] % trait[:default_language]) + '.pot'
@@ -0,0 +1,46 @@
1
+ # suppress load warning message
2
+ verbose = $VERBOSE
3
+ $VERBOSE = nil
4
+ require 'gettext/rgettext'
5
+ $VERBOSE = verbose
6
+
7
+ module Ramaze::Tool::Gettext::Parser
8
+ module_function
9
+
10
+ TARGETS = Ramaze::Template::ENGINES.values.inject([]) do |exts, list|
11
+ list += exts
12
+ end
13
+
14
+ def target?(file)
15
+ TARGETS.include?(File.extname(file)[1..-1])
16
+ end
17
+
18
+ def parse(file, ary)
19
+ regex = Ramaze::Tool::Gettext.trait[:regex]
20
+ body = File.read(file)
21
+ body.gsub!(regex) do
22
+ msg = $1
23
+ unless msg.to_s.empty?
24
+ line_number = body[0..(body.index(msg))].split("\n").size
25
+ add_message(ary, msg, file, line_number)
26
+ end
27
+ end
28
+ return ary
29
+ end
30
+
31
+ def add_message(ary, msg, file, line_number)
32
+ loc = message_location(file, line_number)
33
+ if value = ary.assoc(msg)
34
+ value << loc
35
+ else
36
+ ary << [msg, loc]
37
+ end
38
+ return ary
39
+ end
40
+
41
+ def message_location(file, line_number)
42
+ file + ":line" + line_number.to_s
43
+ end
44
+ end
45
+
46
+ GetText::RGetText.add_parser(Ramaze::Tool::Gettext::Parser)
@@ -1,55 +1,22 @@
1
1
  # Copyright (c) 2008 Michael Fellinger m.fellinger@gmail.com
2
2
  # All files in this distribution are subject to the terms of the Ruby license.
3
3
 
4
- # Usage:
5
- #
6
- # Ramaze.contrib :route
7
- # Ramaze::Contrib::Route[ %r!^/(\d+\.\d{2})$! ] = "/price/%.2f"
8
-
9
4
  module Ramaze
10
5
  module Contrib
11
6
  class Route
12
7
  class << self
13
8
  def startup
14
- trait :route => true
15
- trait :routes => Dictionary.new
16
- Ramaze::Controller::FILTER.put_before(:default, :routed)
9
+ warn "Ramaze::Contrib::Route is being deprecated, use Ramaze::Route instead"
17
10
  end
18
11
 
19
12
  def [](key)
20
- trait[:routes][key]
13
+ Ramaze::Route.trait[:routes][key]
21
14
  end
22
15
 
23
16
  def []=(key, value)
24
- trait[:routes][key] = value
17
+ Ramaze::Route.trait[:routes][key] = value
25
18
  end
26
19
  end
27
20
  end
28
21
  end
29
-
30
- class Controller
31
- class << self
32
- def routed(path)
33
- routes = Contrib::Route.trait[:routes]
34
- routes.each do |key, val|
35
- case
36
- when key.is_a?(Regexp)
37
- regex, pattern = key, val
38
- if md = path.match(regex)
39
- new_path = pattern % md.to_a[1..-1]
40
- return resolve(new_path, :routed)
41
- end
42
- when val.respond_to?(:call)
43
- if new_path = val.call(path, request)
44
- return resolve(new_path, :routed)
45
- end
46
- else
47
- Inform.error "Invalid route #{val}"
48
- end
49
- end
50
-
51
- nil
52
- end
53
- end
54
- end
55
22
  end
@@ -67,11 +67,9 @@ module Ramaze
67
67
  end
68
68
  end
69
69
 
70
- if Global.mapping.empty?
71
- Inform.warn("No Controllers mapped, will serve /public only.")
72
- else
73
- Inform.debug("mapped Controllers: #{Global.mapping.inspect}")
74
- end
70
+ require 'ramaze/controller/main' if Global.mapping.empty?
71
+
72
+ Inform.debug("mapped Controllers: #{Global.mapping.inspect}")
75
73
  end
76
74
 
77
75
  # checks paths for existance and logs a warning if it doesn't exist yet.
@@ -149,7 +147,7 @@ module Ramaze
149
147
  # Runs every given path through Controller::check_path
150
148
 
151
149
  def template_root path = nil
152
- if path
150
+ if path and path = path.to_s
153
151
  message = "#{self}.template_root is #{path} which does not exist"
154
152
  check_path(path, message)
155
153
  @template_root = path
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Ramaze
5
5
  class Controller
6
- FILTER = [ :cached, :default ] unless defined?(FILTER)
6
+ FILTER = [ :cached, :routed, :default ] unless defined?(FILTER)
7
7
 
8
8
  class << self
9
9
 
@@ -43,6 +43,33 @@ module Ramaze
43
43
  nil
44
44
  end
45
45
 
46
+ # Routing filter
47
+ # Loops over Route.trait[:routes] to find a matching route
48
+
49
+ def routed(path)
50
+ Route.trait[:routes].each do |key, val|
51
+ if key.is_a?(Regexp)
52
+ if md = path.match(key)
53
+ new_path = val % md.to_a[1..-1]
54
+ return resolve(new_path, :routed)
55
+ end
56
+
57
+ elsif val.respond_to?(:call)
58
+ if new_path = val.call(path, request)
59
+ return resolve(new_path, :routed)
60
+ end
61
+
62
+ elsif val.is_a?(String)
63
+ return resolve(val, :routed) if path == key
64
+
65
+ else
66
+ Inform.error "Invalid route #{key} => #{val}"
67
+ end
68
+ end
69
+
70
+ nil
71
+ end
72
+
46
73
  # Default element of FILTER.
47
74
  # The default handler that tries to find the best match for the given
48
75
  # path in terms of Controller/method/template and given arguments.
@@ -130,7 +130,7 @@ module Ramaze
130
130
 
131
131
  def set_cookie
132
132
  session.finalize
133
- hash = {:value => session.session_id, :path => '/'}
133
+ hash = {:value => session.session_id}.merge(Session::COOKIE)
134
134
  response.set_cookie(Session::SESSION_KEY, hash)
135
135
  end
136
136
 
@@ -1,6 +1,9 @@
1
1
  # Copyright (c) 2008 Michael Fellinger m.fellinger@gmail.com
2
2
  # All files in this distribution are subject to the terms of the Ruby license.
3
3
 
4
+ require "time"
5
+ require 'digest/md5'
6
+
4
7
  module Ramaze
5
8
  module Dispatcher
6
9
 
@@ -20,6 +23,9 @@ module Ramaze
20
23
 
21
24
  def process(path)
22
25
  return unless file = open_file(path)
26
+ if file == :NotModified
27
+ return response.build([], STATUS_CODE['Not Modified'])
28
+ end
23
29
  response.build(file, STATUS_CODE['OK'])
24
30
  end
25
31
 
@@ -31,6 +37,17 @@ module Ramaze
31
37
 
32
38
  if ::File.file?(file) or ::File.file?(file=file/'index')
33
39
  response['Content-Type'] = Tool::MIME.type_for(file) unless ::File.extname(file).empty?
40
+ mtime = ::File.mtime(file)
41
+ response['Last-Modified'] = mtime.httpdate
42
+ response['ETag']= Digest::MD5.hexdigest(file+mtime.to_s).inspect
43
+ if modified_since = request.env['HTTP_IF_MODIFIED_SINCE']
44
+ return :NotModified unless Time.parse(modified_since) < mtime
45
+ elsif match = request.env['HTTP_IF_NONE_MATCH']
46
+ # Should be a unique string enclosed in ""
47
+ # To avoiding more file reading we use mtime and filepath
48
+ # we could throw in inode and size for more uniqueness
49
+ return :NotModified if response['ETag']==match
50
+ end
34
51
  log(file)
35
52
  ::File.open(file, 'rb')
36
53
  end