merb 0.3.4 → 0.3.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 (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) {