ramaze 0.3.0 → 0.3.5

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 (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