merb 0.3.4 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/README +206 -197
  2. data/Rakefile +12 -21
  3. data/bin/merb +1 -1
  4. data/examples/skeleton/Rakefile +6 -20
  5. data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
  6. data/examples/skeleton/dist/conf/database.yml +23 -0
  7. data/examples/skeleton/dist/conf/environments/development.rb +1 -0
  8. data/examples/skeleton/dist/conf/environments/production.rb +1 -0
  9. data/examples/skeleton/dist/conf/environments/test.rb +1 -0
  10. data/examples/skeleton/dist/conf/merb.yml +32 -28
  11. data/examples/skeleton/dist/conf/merb_init.rb +16 -13
  12. data/examples/skeleton/dist/conf/router.rb +9 -9
  13. data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
  14. data/lib/merb.rb +23 -18
  15. data/lib/merb/caching/fragment_cache.rb +3 -7
  16. data/lib/merb/caching/store/memcache.rb +20 -0
  17. data/lib/merb/core_ext/merb_array.rb +0 -0
  18. data/lib/merb/core_ext/merb_class.rb +44 -4
  19. data/lib/merb/core_ext/merb_enumerable.rb +43 -1
  20. data/lib/merb/core_ext/merb_hash.rb +200 -122
  21. data/lib/merb/core_ext/merb_kernel.rb +2 -0
  22. data/lib/merb/core_ext/merb_module.rb +41 -0
  23. data/lib/merb/core_ext/merb_numeric.rb +57 -5
  24. data/lib/merb/core_ext/merb_object.rb +172 -6
  25. data/lib/merb/generators/merb_app/merb_app.rb +15 -9
  26. data/lib/merb/merb_abstract_controller.rb +193 -0
  27. data/lib/merb/merb_constants.rb +26 -1
  28. data/lib/merb/merb_controller.rb +143 -234
  29. data/lib/merb/merb_dispatcher.rb +28 -20
  30. data/lib/merb/merb_drb_server.rb +2 -3
  31. data/lib/merb/merb_exceptions.rb +194 -49
  32. data/lib/merb/merb_handler.rb +34 -26
  33. data/lib/merb/merb_mail_controller.rb +200 -0
  34. data/lib/merb/merb_mailer.rb +33 -13
  35. data/lib/merb/merb_part_controller.rb +42 -0
  36. data/lib/merb/merb_plugins.rb +293 -0
  37. data/lib/merb/merb_request.rb +6 -4
  38. data/lib/merb/merb_router.rb +99 -65
  39. data/lib/merb/merb_server.rb +65 -21
  40. data/lib/merb/merb_upload_handler.rb +2 -1
  41. data/lib/merb/merb_view_context.rb +36 -15
  42. data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
  43. data/lib/merb/mixins/controller_mixin.rb +67 -28
  44. data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
  45. data/lib/merb/mixins/form_control_mixin.rb +280 -42
  46. data/lib/merb/mixins/render_mixin.rb +127 -45
  47. data/lib/merb/mixins/responder_mixin.rb +5 -7
  48. data/lib/merb/mixins/view_context_mixin.rb +260 -94
  49. data/lib/merb/session.rb +23 -0
  50. data/lib/merb/session/merb_ar_session.rb +28 -16
  51. data/lib/merb/session/merb_mem_cache_session.rb +108 -0
  52. data/lib/merb/session/merb_memory_session.rb +65 -20
  53. data/lib/merb/template/erubis.rb +22 -13
  54. data/lib/merb/template/haml.rb +5 -16
  55. data/lib/merb/template/markaby.rb +5 -3
  56. data/lib/merb/template/xml_builder.rb +17 -5
  57. data/lib/merb/test/merb_fake_request.rb +63 -0
  58. data/lib/merb/test/merb_multipart.rb +58 -0
  59. data/lib/tasks/db.rake +2 -0
  60. data/lib/tasks/merb.rake +20 -8
  61. metadata +24 -25
  62. data/examples/skeleton.tar +0 -0
@@ -0,0 +1,23 @@
1
+ module Merb
2
+ module SessionMixin
3
+
4
+ def rand_uuid
5
+ sid = "%04x%04x%04x%04x%04x%06x%06x" % [
6
+ rand(0x0010000),
7
+ rand(0x0010000),
8
+ rand(0x0010000),
9
+ rand(0x0010000),
10
+ rand(0x0010000),
11
+ rand(0x1000000),
12
+ rand(0x1000000),
13
+ ]
14
+ sid
15
+ end
16
+
17
+ def needs_new_cookie!
18
+ @_new_cookie = true
19
+ end
20
+
21
+ module_function :rand_uuid, :needs_new_cookie!
22
+ end
23
+ end
@@ -6,20 +6,17 @@ module Merb
6
6
 
7
7
  def setup_session
8
8
  MERB_LOGGER.info("Setting up session")
9
- before = @cookies[_session_id_key]
10
- @session, @cookies[_session_id_key] = Merb::Session.persist(@cookies[_session_id_key])
11
- @_fingerprint_before = Marshal.dump(@session).hash
12
- @_new_cookie = @cookies[_session_id_key] != before
9
+ before = @_cookies[_session_id_key]
10
+ @_session, @_cookies[_session_id_key] = Merb::Session.persist(@_cookies[_session_id_key])
11
+ @_fingerprint = Marshal.dump(@_session.data).hash
12
+ @_new_cookie = @_cookies[_session_id_key] != before
13
13
  end
14
14
 
15
15
  def finalize_session
16
16
  MERB_LOGGER.info("Finalize session")
17
- unless Marshal.dump(@session).hash == @_fingerprint_before
18
- @session.save
19
- end
20
- set_cookie(_session_id_key, @cookies[_session_id_key], Time.now+Merb::Const::WEEK*2) if @_new_cookie
21
- end
22
-
17
+ @_session.save if @_fingerprint != Marshal.dump(@_session.data).hash
18
+ set_cookie(_session_id_key, @_session.session_id, _session_expiry) if (@_new_cookie || @_session.needs_new_cookie)
19
+ end
23
20
  end
24
21
 
25
22
  class Session < ::ActiveRecord::Base
@@ -28,14 +25,12 @@ module Merb
28
25
  self.data_column_name = 'data'
29
26
  before_save :marshal_data!
30
27
  before_save :raise_on_session_data_overflow!
31
-
32
- RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
28
+ attr_accessor :needs_new_cookie
33
29
 
34
30
  class << self
35
31
  # Generates a new session ID and creates a row for the new session in the database.
36
32
  def generate
37
- rand_max = RAND_CHARS.size
38
- sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] }
33
+ sid = Merb::SessionMixin::rand_uuid
39
34
  create(:session_id => sid, :data => {})
40
35
  end
41
36
 
@@ -78,6 +73,24 @@ module Merb
78
73
  end
79
74
  end
80
75
 
76
+ # Regenerate the Session ID
77
+ def regenerate
78
+ new_id = Merb::SessionMixin::rand_uuid
79
+ update_attributes({:session_id => new_id})
80
+ self.needs_new_cookie=true
81
+ end
82
+
83
+ # Recreates the cookie with the default expiration time
84
+ # Useful during log in for pushing back the expiration date
85
+ def refresh_expiration
86
+ self.needs_new_cookie=true
87
+ end
88
+
89
+ # Lazy-delete of session data
90
+ def delete
91
+ self.data = {}
92
+ end
93
+
81
94
  def [](key)
82
95
  data[key]
83
96
  end
@@ -115,5 +128,4 @@ module Merb
115
128
  end
116
129
  end
117
130
  end
118
-
119
- end
131
+ end
@@ -0,0 +1,108 @@
1
+ require 'memcache_util'
2
+
3
+ module Merb
4
+
5
+ module SessionMixin
6
+
7
+ def setup_session
8
+ MERB_LOGGER.info("Setting up session")
9
+ before = @_cookies[_session_id_key]
10
+ @_session, @_cookies[_session_id_key] = Merb::MemCacheSession.persist(@_cookies[_session_id_key])
11
+ @_fingerprint = Marshal.dump(@_session.data).hash
12
+ @_new_cookie = cookies[_session_id_key] != before
13
+ end
14
+
15
+ def finalize_session
16
+ MERB_LOGGER.info("Finalize session")
17
+ ::Cache.put("session:#{@_session.session_id}", @_session) if @_fingerprint != Marshal.dump(@_session.data).hash
18
+ set_cookie(_session_id_key, @_session.session_id, _session_expiry) if (@_new_cookie || @_session.needs_new_cookie)
19
+ end
20
+
21
+ end
22
+
23
+ ##
24
+ # Sessions stored in memcached.
25
+ #
26
+ # Requires setup in your +merb_init.rb+:
27
+ #
28
+ # require 'memcache_util'
29
+ # CACHE = MemCache.new('127.0.0.1:11211', { :namespace => 'my_app' })
30
+ #
31
+ # And a setting in +merb.yml+:
32
+ #
33
+ # :mem_cache_session: true
34
+
35
+ class MemCacheSession
36
+
37
+ attr_accessor :session_id
38
+ attr_accessor :data
39
+ attr_accessor :needs_new_cookie
40
+
41
+ def initialize(session_id)
42
+ @session_id = session_id
43
+ @data = {}
44
+ end
45
+
46
+ class << self
47
+ # Generates a new session ID and creates a row for the new session in the database.
48
+ def generate
49
+ sid = Merb::SessionMixin::rand_uuid
50
+ new(sid)
51
+ end
52
+
53
+ # Gets the existing session based on the <tt>session_id</tt> available in cookies.
54
+ # If none is found, generates a new session.
55
+ def persist(session_id)
56
+ if session_id
57
+ session = ::Cache.get("session:#{session_id}")
58
+ if session.nil?
59
+ # Not in memcached, but assume that cookie exists
60
+ session = new(session_id)
61
+ end
62
+ else
63
+ # No cookie...make a new session_id
64
+ session = generate
65
+ end
66
+ [session, session.session_id]
67
+ end
68
+
69
+ # Don't try to reload in dev mode.
70
+ def reloadable? #:nodoc:
71
+ false
72
+ end
73
+
74
+ end
75
+
76
+ # Regenerate the Session ID
77
+ def regenerate
78
+ @session_id = Merb::SessionMixin::rand_uuid
79
+ self.needs_new_cookie=true
80
+ end
81
+
82
+ # Recreates the cookie with the default expiration time
83
+ # Useful during log in for pushing back the expiration date
84
+ def refresh_expiration
85
+ self.needs_new_cookie=true
86
+ end
87
+
88
+ # Lazy-delete of session data
89
+ def delete
90
+ @data = {}
91
+ end
92
+
93
+ def [](key)
94
+ @data[key]
95
+ end
96
+
97
+ def []=(key, val)
98
+ @data[key] = val
99
+ end
100
+
101
+ # Has the session been loaded yet?
102
+ def loaded?
103
+ !! @data
104
+ end
105
+
106
+ end
107
+
108
+ end
@@ -4,43 +4,88 @@ module Merb
4
4
 
5
5
  def setup_session
6
6
  MERB_LOGGER.info("Setting up session")
7
- before = @cookies[_session_id_key]
8
- @session , @cookies[_session_id_key] = Merb::MemorySession.persist(@cookies[_session_id_key])
9
- @_new_cookie = @cookies[_session_id_key] != before
7
+ before = @_cookies[_session_id_key]
8
+ @_session , @_cookies[_session_id_key] = Merb::MemorySession.persist(@_cookies[_session_id_key])
9
+ @_new_cookie = @_cookies[_session_id_key] != before
10
10
  end
11
11
 
12
12
  def finalize_session
13
13
  MERB_LOGGER.info("Finalize session")
14
- set_cookie(_session_id_key, @cookies[_session_id_key], Time.now+Merb::Const::WEEK*2) if @_new_cookie
14
+ set_cookie(_session_id_key, @_session.session_id, _session_expiry) if (@_new_cookie || @_session.needs_new_cookie)
15
15
  end
16
16
 
17
17
  end
18
18
 
19
19
  class MemorySession
20
-
21
- class << self
22
-
23
- RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
24
20
 
21
+ attr_accessor :session_id
22
+ attr_accessor :data
23
+ attr_accessor :needs_new_cookie
24
+
25
+ def initialize(session_id)
26
+ @session_id = session_id
27
+ @data = {}
28
+ end
29
+
30
+ class << self
25
31
  # Generates a new session ID and creates a row for the new session in the database.
26
32
  def generate
27
- rand_max = RAND_CHARS.size
28
- sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] }
29
- [create(:session_id => sid, :data => {}), sid]
33
+ sid = Merb::SessionMixin::rand_uuid
34
+ MemorySessionContainer[sid] = new(sid)
30
35
  end
31
-
36
+
32
37
  # Gets the existing session based on the <tt>session_id</tt> available in cookies.
33
38
  # If none is found, generates a new session.
34
39
  def persist(session_id)
35
40
  if session_id
36
- session = self[session_id]
37
- sid = session_id
41
+ session = session = MemorySessionContainer[session_id]
38
42
  end
39
43
  unless session
40
- session, sid = generate
44
+ session = generate
41
45
  end
42
- [session, sid]
46
+ [session, session.session_id]
43
47
  end
48
+
49
+ end
50
+
51
+ # Regenerate the Session ID
52
+ def regenerate
53
+ new_sid = Merb::SessionMixin::rand_uuid
54
+ old_sid = @session_id
55
+ MemorySessionContainer[new_sid] = MemorySessionContainer[old_sid]
56
+ @session_id = new_sid
57
+ MemorySessionContainer.delete(old_sid)
58
+ self.needs_new_cookie=true
59
+ end
60
+
61
+ # Recreates the cookie with the default expiration time
62
+ # Useful during log in for pushing back the expiration date
63
+ def refresh_expiration
64
+ self.needs_new_cookie=true
65
+ end
66
+
67
+ # Lazy-delete of session data
68
+ def delete
69
+ @data = {}
70
+ end
71
+
72
+ def [](key)
73
+ @data[key]
74
+ end
75
+
76
+ def []=(key, val)
77
+ @data[key] = val
78
+ end
79
+
80
+ # Has the session been loaded yet?
81
+ def loaded?
82
+ !! @data
83
+ end
84
+
85
+ end
86
+
87
+ class MemorySessionContainer
88
+ class << self
44
89
 
45
90
  def setup(opts={})
46
91
  @opts = opts
@@ -55,6 +100,7 @@ module Merb
55
100
  def create(opts={})
56
101
  self[opts[:session_id]] = opts[:data]
57
102
  end
103
+
58
104
 
59
105
  def [](key)
60
106
  @mutex.synchronize {
@@ -62,7 +108,7 @@ module Merb
62
108
  @sessions[key]
63
109
  }
64
110
  end
65
-
111
+
66
112
  def []=(key, val)
67
113
  @mutex.synchronize {
68
114
  @timestamps[key] = Time.now
@@ -98,9 +144,8 @@ module Merb
98
144
  def sessions
99
145
  @sessions
100
146
  end
101
-
147
+
102
148
  end # end singleton class
103
-
104
- end # end DRbSession
105
149
 
150
+ end # end MemorySessionContainer
106
151
  end
@@ -1,25 +1,33 @@
1
- module Merb
2
- module Template
3
-
1
+ module Merb # :nodoc:
2
+ module Template # :nodoc:
4
3
 
4
+ # Module to allow you to use Embedded Ruby templates through Erubis["http://www.kuwata-lab.com/erubis/"].
5
+ # Your template must end in .herb (HTML and ERb), .jerb (JavaScript ERb), .erb (Embedded Ruby), or .rhtml (Ruby HTML) for Merb to use it.
5
6
  module Erubis
6
- ::Merb::Controller.register_engine self, %w[ herb jerb erb rhtml ]
7
+ ::Merb::AbstractController.register_engine self, %w[ herb jerb erb rhtml html.erb js.erb ]
7
8
  class << self
8
9
 
9
10
  @@erbs = {}
10
11
  @@mtimes = {}
11
12
 
12
- def exempt_from_layout?
13
+ def exempt_from_layout? # :nodoc:
13
14
  false
14
15
  end
15
-
16
+
17
+ # Main method to compile the ERb template.
18
+ #
19
+ # In the case of ERb/Erubis, it first checks the template cache for precompiled
20
+ # templates. If no precompiled version is found, then it compiles the template
21
+ # by calling <tt>new_eruby_obj</tt>, which will compile the template and return an Erubis
22
+ # object holding the parsed template.
16
23
  def transform(options = {})
17
24
  opts, file, view_context = options.values_at(:opts, :file, :view_context)
18
25
  eruby = new_eruby_obj(file)
19
- view_context.class.send(:include, ::Merb::ErubisCaptureMixin)
26
+ view_context.extend(::Merb::ErubisCaptureMixin)
20
27
  eruby.evaluate(view_context)
21
28
  end
22
29
 
30
+ # Creates a new Erubis object to parse the template given in +path+.
23
31
  def new_eruby_obj(path)
24
32
  if @@erbs[path] && !cache_template?(path)
25
33
  return @@erbs[path]
@@ -37,12 +45,13 @@ module Merb
37
45
  end
38
46
  end
39
47
 
40
- def cache_template?(path)
41
- return false unless ::Merb::Server.config[:cache_templates]
42
- return true unless @@erbs[path]
43
- @@mtimes[path] < File.mtime(path) ||
44
- (File.symlink?(path) && (@@mtimes[path] < File.lstat(path).mtime))
45
- end
48
+ private
49
+ def cache_template?(path)
50
+ return false unless ::Merb::Server.config[:cache_templates]
51
+ return true unless @@erbs[path]
52
+ @@mtimes[path] < File.mtime(path) ||
53
+ (File.symlink?(path) && (@@mtimes[path] < File.lstat(path).mtime))
54
+ end
46
55
 
47
56
  end
48
57
  end
@@ -5,25 +5,13 @@ begin
5
5
  rescue LoadError
6
6
  puts "you must install the haml gem to use .haml templates"
7
7
  end
8
- module ActionView
9
- # stub, in case the requires for that active_* stuff of Haml doesn't fail
10
- # but we are not running Rails ;)
11
- class Base
12
- # stub
13
- def concat
14
- end
15
- # stub
16
- def form_tag
17
- end
18
- end
19
- end
20
8
 
21
9
 
22
10
  module Merb
23
11
  module Template
24
12
  module Haml
25
13
 
26
- ::Merb::Controller.register_engine self, %w[ haml ]
14
+ ::Merb::AbstractController.register_engine self, %w[ haml ]
27
15
 
28
16
  class << self
29
17
 
@@ -70,10 +58,11 @@ module Merb
70
58
  def build_locals(locals)
71
59
  locals_code = ""
72
60
  if locals
73
- locals_code = locals.keys.inject("") do |code, key|
74
- code << "- #{key} = @_merb_partial_locals[:#{key}]\n"
61
+ locals.keys.each do |key|
62
+ locals_code << "- #{key} = @_merb_partial_locals[:#{key}]\n"
75
63
  end
76
64
  end
65
+ locals_code
77
66
  end
78
67
 
79
68
  def cache_template?(path)
@@ -87,4 +76,4 @@ module Merb
87
76
 
88
77
  end
89
78
  end
90
- end
79
+ end
@@ -21,7 +21,7 @@ module Merb
21
21
 
22
22
  module Markaby
23
23
 
24
- ::Merb::Controller.register_engine self, %w[ mab ]
24
+ ::Merb::AbstractController.register_engine self, %w[ mab ]
25
25
 
26
26
  class << self
27
27
 
@@ -35,9 +35,11 @@ module Merb
35
35
  opts, file, view_context = options.values_at(:opts, :file, :view_context)
36
36
 
37
37
  if opts[:locals]
38
- locals = opts[:locals].keys.inject("") do |code, key|
39
- code << "#{key} = @_merb_partial_locals[:#{key}]\n"
38
+ locals = ""
39
+ opts[:locals].keys.each do |key|
40
+ locals << "#{key} = @_merb_partial_locals[:#{key}]\n"
40
41
  end
42
+ locals
41
43
  end
42
44
 
43
45
  mab = ::Markaby::Builder.new({}, view_context) {