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.
- data/README +206 -197
- data/Rakefile +12 -21
- data/bin/merb +1 -1
- data/examples/skeleton/Rakefile +6 -20
- data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
- data/examples/skeleton/dist/conf/database.yml +23 -0
- data/examples/skeleton/dist/conf/environments/development.rb +1 -0
- data/examples/skeleton/dist/conf/environments/production.rb +1 -0
- data/examples/skeleton/dist/conf/environments/test.rb +1 -0
- data/examples/skeleton/dist/conf/merb.yml +32 -28
- data/examples/skeleton/dist/conf/merb_init.rb +16 -13
- data/examples/skeleton/dist/conf/router.rb +9 -9
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
- data/lib/merb.rb +23 -18
- data/lib/merb/caching/fragment_cache.rb +3 -7
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/core_ext/merb_array.rb +0 -0
- data/lib/merb/core_ext/merb_class.rb +44 -4
- data/lib/merb/core_ext/merb_enumerable.rb +43 -1
- data/lib/merb/core_ext/merb_hash.rb +200 -122
- data/lib/merb/core_ext/merb_kernel.rb +2 -0
- data/lib/merb/core_ext/merb_module.rb +41 -0
- data/lib/merb/core_ext/merb_numeric.rb +57 -5
- data/lib/merb/core_ext/merb_object.rb +172 -6
- data/lib/merb/generators/merb_app/merb_app.rb +15 -9
- data/lib/merb/merb_abstract_controller.rb +193 -0
- data/lib/merb/merb_constants.rb +26 -1
- data/lib/merb/merb_controller.rb +143 -234
- data/lib/merb/merb_dispatcher.rb +28 -20
- data/lib/merb/merb_drb_server.rb +2 -3
- data/lib/merb/merb_exceptions.rb +194 -49
- data/lib/merb/merb_handler.rb +34 -26
- data/lib/merb/merb_mail_controller.rb +200 -0
- data/lib/merb/merb_mailer.rb +33 -13
- data/lib/merb/merb_part_controller.rb +42 -0
- data/lib/merb/merb_plugins.rb +293 -0
- data/lib/merb/merb_request.rb +6 -4
- data/lib/merb/merb_router.rb +99 -65
- data/lib/merb/merb_server.rb +65 -21
- data/lib/merb/merb_upload_handler.rb +2 -1
- data/lib/merb/merb_view_context.rb +36 -15
- data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
- data/lib/merb/mixins/controller_mixin.rb +67 -28
- data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
- data/lib/merb/mixins/form_control_mixin.rb +280 -42
- data/lib/merb/mixins/render_mixin.rb +127 -45
- data/lib/merb/mixins/responder_mixin.rb +5 -7
- data/lib/merb/mixins/view_context_mixin.rb +260 -94
- data/lib/merb/session.rb +23 -0
- data/lib/merb/session/merb_ar_session.rb +28 -16
- data/lib/merb/session/merb_mem_cache_session.rb +108 -0
- data/lib/merb/session/merb_memory_session.rb +65 -20
- data/lib/merb/template/erubis.rb +22 -13
- data/lib/merb/template/haml.rb +5 -16
- data/lib/merb/template/markaby.rb +5 -3
- data/lib/merb/template/xml_builder.rb +17 -5
- data/lib/merb/test/merb_fake_request.rb +63 -0
- data/lib/merb/test/merb_multipart.rb +58 -0
- data/lib/tasks/db.rake +2 -0
- data/lib/tasks/merb.rake +20 -8
- metadata +24 -25
- data/examples/skeleton.tar +0 -0
data/lib/merb/session.rb
ADDED
@@ -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 = @
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@_new_cookie = @
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
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 = @
|
8
|
-
@
|
9
|
-
@_new_cookie = @
|
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, @
|
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
|
-
|
28
|
-
sid = (
|
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 =
|
37
|
-
sid = session_id
|
41
|
+
session = session = MemorySessionContainer[session_id]
|
38
42
|
end
|
39
43
|
unless session
|
40
|
-
session
|
44
|
+
session = generate
|
41
45
|
end
|
42
|
-
[session,
|
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
|
data/lib/merb/template/erubis.rb
CHANGED
@@ -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::
|
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.
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
data/lib/merb/template/haml.rb
CHANGED
@@ -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::
|
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
|
-
|
74
|
-
|
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::
|
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 =
|
39
|
-
|
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) {
|