merb 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/README +22 -4
  2. data/Rakefile +15 -3
  3. data/TODO +2 -3
  4. data/bin/merb +61 -36
  5. data/examples/sample_app/dist/app/controllers/files.rb +31 -0
  6. data/examples/sample_app/dist/app/controllers/posts.rb +26 -2
  7. data/examples/sample_app/dist/app/controllers/test.rb +7 -1
  8. data/examples/sample_app/dist/app/views/files/progress.jerb +3 -0
  9. data/examples/sample_app/dist/app/views/files/start.herb +62 -0
  10. data/examples/sample_app/dist/app/views/files/upload.herb +6 -0
  11. data/examples/sample_app/dist/app/views/layout/{application.rhtml → application.herb} +2 -3
  12. data/examples/sample_app/dist/app/views/layout/{foo.rhtml → foo.herb} +0 -0
  13. data/examples/sample_app/dist/app/views/posts/{_comments.rhtml → _comments.herb} +0 -0
  14. data/examples/sample_app/dist/app/views/posts/comment.jerb +1 -0
  15. data/examples/sample_app/dist/app/views/posts/{list.rhtml → list.herb} +0 -0
  16. data/examples/sample_app/dist/app/views/posts/{new.rhtml → new.herb} +0 -0
  17. data/examples/sample_app/dist/app/views/posts/{show.rhtml → show.herb} +0 -0
  18. data/examples/sample_app/dist/app/views/posts/xml_test.xerb +3 -0
  19. data/examples/sample_app/dist/app/views/test/{foo.rhtml → foo.herb} +0 -0
  20. data/examples/sample_app/dist/app/views/test/{hello.rhtml → hello.herb} +0 -0
  21. data/examples/sample_app/dist/app/views/test/json.jerb +1 -0
  22. data/examples/sample_app/dist/conf/merb.yml +11 -0
  23. data/examples/sample_app/dist/conf/merb_init.rb +1 -1
  24. data/examples/sample_app/dist/conf/mup.conf +11 -0
  25. data/examples/sample_app/dist/public/javascripts/mup.js +113 -0
  26. data/examples/sample_app/script/merb_stop +7 -3
  27. data/examples/sample_app/script/startdrb +8 -0
  28. data/lib/merb.rb +37 -2
  29. data/lib/merb/merb_class_extensions.rb +21 -22
  30. data/lib/merb/merb_controller.rb +101 -33
  31. data/lib/merb/merb_handler.rb +26 -25
  32. data/lib/merb/merb_router.rb +1 -1
  33. data/lib/merb/merb_utils.rb +35 -37
  34. data/lib/merb/mixins/basic_authentication_mixin.rb +39 -0
  35. data/lib/merb/mixins/controller_mixin.rb +119 -115
  36. data/lib/merb/mixins/javascript_mixin.rb +63 -0
  37. data/lib/merb/mixins/render_mixin.rb +85 -69
  38. data/lib/merb/mixins/responder_mixin.rb +38 -0
  39. data/lib/merb/session/merb_drb_server.rb +107 -0
  40. data/lib/merb/session/merb_drb_session.rb +71 -0
  41. data/lib/merb/session/merb_session.rb +1 -0
  42. data/lib/merb/vendor/paginator/README.txt +84 -0
  43. data/lib/merb/vendor/paginator/paginator.rb +121 -0
  44. data/lib/mutex_hotfix.rb +34 -0
  45. metadata +41 -63
  46. data/doc/rdoc/classes/ControllerMixin.html +0 -676
  47. data/doc/rdoc/classes/Hash.html +0 -148
  48. data/doc/rdoc/classes/Merb.html +0 -140
  49. data/doc/rdoc/classes/Merb/Controller.html +0 -338
  50. data/doc/rdoc/classes/Merb/RouteMatcher.html +0 -388
  51. data/doc/rdoc/classes/Merb/Server.html +0 -148
  52. data/doc/rdoc/classes/Merb/Session.html +0 -201
  53. data/doc/rdoc/classes/Merb/SessionMixin.html +0 -199
  54. data/doc/rdoc/classes/MerbControllerError.html +0 -111
  55. data/doc/rdoc/classes/MerbHandler.html +0 -430
  56. data/doc/rdoc/classes/MerbHash.html +0 -469
  57. data/doc/rdoc/classes/MerbHash/Mutex.html +0 -198
  58. data/doc/rdoc/classes/Noroutefound.html +0 -153
  59. data/doc/rdoc/classes/Object.html +0 -149
  60. data/doc/rdoc/classes/RenderMixin.html +0 -362
  61. data/doc/rdoc/classes/String.html +0 -212
  62. data/doc/rdoc/classes/Symbol.html +0 -179
  63. data/doc/rdoc/created.rid +0 -1
  64. data/doc/rdoc/files/LICENSE.html +0 -129
  65. data/doc/rdoc/files/README.html +0 -417
  66. data/doc/rdoc/files/TODO.html +0 -151
  67. data/doc/rdoc/files/lib/merb/merb_class_extensions_rb.html +0 -101
  68. data/doc/rdoc/files/lib/merb/merb_controller_rb.html +0 -101
  69. data/doc/rdoc/files/lib/merb/merb_handler_rb.html +0 -101
  70. data/doc/rdoc/files/lib/merb/merb_router_rb.html +0 -101
  71. data/doc/rdoc/files/lib/merb/merb_utils_rb.html +0 -108
  72. data/doc/rdoc/files/lib/merb/mixins/controller_mixin_rb.html +0 -101
  73. data/doc/rdoc/files/lib/merb/mixins/render_mixin_rb.html +0 -101
  74. data/doc/rdoc/files/lib/merb/session/merb_session_rb.html +0 -101
  75. data/doc/rdoc/files/lib/merb_rb.html +0 -140
  76. data/doc/rdoc/files/lib/merb_tasks_rb.html +0 -101
  77. data/doc/rdoc/fr_class_index.html +0 -43
  78. data/doc/rdoc/fr_file_index.html +0 -40
  79. data/doc/rdoc/fr_method_index.html +0 -104
  80. data/doc/rdoc/index.html +0 -24
  81. data/doc/rdoc/rdoc-style.css +0 -208
  82. data/examples/sample_app/dist/app/controllers/upload.rb +0 -29
  83. data/examples/sample_app/dist/app/views/posts/comment.merbjs +0 -1
  84. data/examples/sample_app/dist/app/views/upload/start.rhtml +0 -15
  85. data/examples/sample_app/dist/app/views/upload/upload.rhtml +0 -4
  86. data/examples/sample_app/dist/public/files/README +0 -35
  87. data/examples/sample_app/dist/public/files/setup.rb +0 -1346
  88. data/examples/sample_app/log/merb.log +0 -778
@@ -0,0 +1,63 @@
1
+ module Merb
2
+ module JavascriptMixin
3
+
4
+ # escape text for javascript.
5
+ def escape_js(javascript)
6
+ (javascript || '').gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
7
+ end
8
+
9
+ def link_to_function(name, function)
10
+ %{<a href="#" onclick="#{function}; return false;">#{name}</a>}
11
+ end
12
+
13
+ def js(data)
14
+ if data.respond_to? :to_json
15
+ data.to_json
16
+ else
17
+ data.inspect.to_json
18
+ end
19
+ end
20
+
21
+ def require_js(*scripts)
22
+ return nil if scripts.empty?
23
+ scripts.inject('') do |memo,script|
24
+ script = script.to_s
25
+ memo << %Q|<script src="/javascripts/#{script=~/\.js$/ ? script : script+'.js' }" type="text/javascript">//</script>\n|
26
+ end
27
+ end
28
+
29
+ def require_css(*scripts)
30
+ return nil if scripts.empty?
31
+ scripts.inject('') do |memo,script|
32
+ script = script.to_s
33
+ memo << %Q|<link href="/stylesheets/#{script=~/\.css$/ ? script : script+'.css' }" media="all" rel="Stylesheet" type="text/css"/>\n|
34
+ end
35
+ end
36
+
37
+ def js_hash(options)
38
+ '{' + options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}'
39
+ end
40
+
41
+ def insert_html(id, html, options = {})
42
+ position = options.fetch(:where, :before)
43
+ "new Insertion.#{position.to_s.camel_case}('#{id}', '#{escape_js html}');"
44
+ end
45
+
46
+ def replace_html(id, html, options = {})
47
+ "Element.update('#{id}', '#{escape_js html}');"
48
+ end
49
+
50
+ def hide(id)
51
+ "$('#{id}').style.display = 'none';"
52
+ end
53
+
54
+ def show(id)
55
+ "$('#{id}').style.display = 'block';"
56
+ end
57
+
58
+ def toggle(id)
59
+ "Element.toggle('#{id}');"
60
+ end
61
+
62
+ end
63
+ end
@@ -1,75 +1,91 @@
1
- module RenderMixin
1
+ module Merb
2
2
 
3
- # shortcut to a template path based on name.
4
- def template_dir(loc)
5
- File.expand_path(Merb::Server.config[:merb_root] + "/dist/app/views/#{loc}")
6
- end
7
-
8
- # returns the current method name. Used for
9
- # auto discovery of which template to render
10
- # based on the action name.
11
- def current_method_name(depth=0)
12
- caller[depth] =~ /`(.*)'$/; $1
13
- end
14
-
15
- # does a render with no layout. Also sets the
16
- # content type header to text/javascript and
17
- # escapes the template for javascript eval on
18
- # the client
19
- def render_js(template=current_method_name(1), b=binding)
20
- headers['Content-Type'] = "text/javascript"
21
- template = Erubis::Eruby.new(IO.read( template_dir(self.class.name.snake_case) + "/#{template}.merbjs" ))
22
- template.result(b)
23
- end
24
-
25
- # set the @layout. Use this right before a render to
26
- # set the name of the layout to use minus the .rhtml
27
- def layout(l)
28
- @layout = l
29
- end
3
+ module RenderMixin
30
4
 
31
- def render_nothing(status)
32
- @status = status
33
- return ''
34
- end
35
-
36
- # renders the action without wrapping it in a layout.
37
- def render_no_layout(template=current_method_name(1), b=binding)
38
- template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/#{template}.rhtml" ) )
39
- template.result(b)
40
- end
41
-
42
- def partial(template)
43
- template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/_#{template}.rhtml" ) )
44
- template.result(binding)
45
- end
46
-
47
- # renders a template based on the current action name
48
- # you can pass the name of a template if you want to
49
- # render a template with a different name then then
50
- # current action name. Wraps the rendered template in
51
- # the layout. Uses layout/application.rhtml unless
52
- # there is a layout named after the current controller
53
- # or @layout has been set to another value.
54
- def render(template=current_method_name(1), b=binding)
55
- MERB_LOGGER.info("Rendering template: #{template_dir(template)}")
56
- name = self.class.name.snake_case
57
- template = Erubis::Eruby.new( IO.read( template_dir(name) + "/#{template}.rhtml" ) )
58
- layout_content = template.result(b)
59
- return layout_content if (@layout.to_s == 'none')
60
- if ['application', name].include?(@layout.to_s)
61
- if File.exist?(template_dir("layout/#{name}.rhtml"))
62
- layout = name
5
+ # shortcut to a template path based on name.
6
+ def template_dir(loc)
7
+ File.expand_path(Merb::Server.config[:merb_root] + "/dist/app/views/#{loc}")
8
+ end
9
+
10
+ # returns the current method name. Used for
11
+ # auto discovery of which template to render
12
+ # based on the action name.
13
+ def current_method_name(depth=0)
14
+ caller[depth] =~ /`(.*)'$/; $1
15
+ end
16
+
17
+ def template_extension_for(ext)
18
+ Merb::Server.config[:template_ext][ext]
19
+ end
20
+
21
+ # does a render with no layout. Also sets the
22
+ # content type header to text/javascript
23
+ def render_js(template=current_method_name(1), b=binding)
24
+ headers['Content-Type'] = "text/javascript"
25
+ template = Erubis::Eruby.new(IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:js)}" ))
26
+ template.result(b)
27
+ end
28
+
29
+ # set the @layout. Use this right before a render to
30
+ # set the name of the layout to use minus the .rhtml
31
+ def layout(l)
32
+ @layout = l
33
+ end
34
+
35
+ # renders nothing but sets the status
36
+ def render_nothing(status=200)
37
+ @status = status
38
+ return "\n"
39
+ end
40
+
41
+ # renders the action without wrapping it in a layout.
42
+ def render_no_layout(template=current_method_name(1), b=binding)
43
+ template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:html)}" ) )
44
+ template.result(b)
45
+ end
46
+
47
+ def partial(template)
48
+ template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/_#{template}.#{template_extension_for(:html)}" ) )
49
+ template.result(binding)
50
+ end
51
+
52
+ def render_xml(template=current_method_name(1))
53
+ xml = Builder::XmlMarkup.new :indent => 2
54
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
55
+ eval IO.read( template_dir(self.class.name.snake_case) + "/#{template}.#{template_extension_for(:xml)}" )
56
+ @headers['Content-Type'] = 'application/xml'
57
+ @headers['Encoding'] = 'UTF-8'
58
+ xml.target!
59
+ end
60
+
61
+ # renders a template based on the current action name
62
+ # you can pass the name of a template if you want to
63
+ # render a template with a different name then then
64
+ # current action name. Wraps the rendered template in
65
+ # the layout. Uses layout/application.rhtml unless
66
+ # there is a layout named after the current controller
67
+ # or @layout has been set to another value.
68
+ def render(template=current_method_name(1), b=binding)
69
+ tmpl_ext = template_extension_for(:html)
70
+ MERB_LOGGER.info("Rendering template: #{template_dir(template)}..#{tmpl_ext}")
71
+ name = self.class.name.snake_case
72
+ template = Erubis::Eruby.new( IO.read( template_dir(name) + "/#{template}.#{tmpl_ext}" ) )
73
+ layout_content = template.result(b)
74
+ return layout_content if (@layout.to_s == 'none')
75
+ if ['application', name].include?(@layout.to_s)
76
+ if File.exist?(template_dir("layout/#{name}.#{tmpl_ext}"))
77
+ layout = name
78
+ else
79
+ layout = 'application'
80
+ end
63
81
  else
64
- layout = 'application'
82
+ layout = @layout.to_s
65
83
  end
66
- else
67
- layout = @layout.to_s
84
+ MERB_LOGGER.info("With Layout: #{template_dir('layout')}/#{layout}.#{tmpl_ext}")
85
+ @layout_content = layout_content
86
+ layout_tmpl = Erubis::Eruby.new( IO.read( "#{template_dir('layout')}/#{layout}.#{tmpl_ext}" ) )
87
+ layout_tmpl.result(b)
68
88
  end
69
- MERB_LOGGER.info("With Layout: #{template_dir('layout')}/#{layout}.rhtml")
70
- @layout_content = layout_content
71
- layout_tmpl = Erubis::Eruby.new( IO.read( template_dir('layout') + "/#{layout}.rhtml" ) )
72
- layout_tmpl.result(b)
73
- end
74
-
89
+
90
+ end
75
91
  end
@@ -0,0 +1,38 @@
1
+ module Merb
2
+ # Thanks to Chris Wanstrath
3
+ # use this in your controllers to switch output based on
4
+ # the HTTP_ACCEPT header. like so:
5
+ # respond_to do |type|
6
+ # type.js { render_js }
7
+ # type.html { render }
8
+ # type.xml { @foo.to_xml }
9
+ # type.yaml { @foo.to_yaml }
10
+ # end
11
+
12
+ module ResponderMixin
13
+ def respond_to
14
+ yield response = Response.new(@env['HTTP_ACCEPT'])
15
+ @headers['Content-Type'] = response.content_type
16
+ response.body
17
+ end
18
+
19
+ class Response
20
+ attr_reader :body, :content_type
21
+ def initialize(accept) @accept = accept end
22
+
23
+ TYPES = {
24
+ :yaml => %w[application/yaml text/yaml],
25
+ :text => %w[text/plain],
26
+ :html => %w[text/html */* application/html],
27
+ :xml => %w[application/xml]
28
+ }
29
+
30
+ def method_missing(method, *args)
31
+ if TYPES[method] && @accept =~ Regexp.union(*TYPES[method])
32
+ @content_type = TYPES[method].first
33
+ @body = yield if block_given?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,107 @@
1
+ require 'drb'
2
+ require 'thread'
3
+
4
+ module Merb
5
+
6
+ class DRbSession
7
+
8
+ include DRbUndumped
9
+
10
+ class << self
11
+
12
+ def setup(opts={})
13
+ @opts = opts
14
+ @sessions = Hash.new
15
+ @timestamps = Hash.new
16
+ @mutex = Mutex.new
17
+ @session_ttl = opts.fetch(:session_ttl, 15*60) # default 15 minutes
18
+ self
19
+ end
20
+
21
+ def create(opts={})
22
+ self[opts[:sess_id]] = opts[:data]
23
+ end
24
+
25
+ def [](key)
26
+ @mutex.synchronize {
27
+ @timestamps[key] = Time.now
28
+ @sessions[key]
29
+ }
30
+ end
31
+
32
+ def []=(key, val)
33
+ @mutex.synchronize {
34
+ @timestamps[key] = Time.now
35
+ @sessions[key] = val
36
+ }
37
+ end
38
+
39
+ def delete(key)
40
+ @mutex.synchronize {
41
+ @sessions.delete(key)
42
+ }
43
+ end
44
+
45
+ def reap_old_sessions
46
+ @timestamps.each do |key,stamp|
47
+ if stamp + @session_ttl < Time.now
48
+ delete(key)
49
+ end
50
+ end
51
+ GC.start
52
+ end
53
+
54
+ def sessions
55
+ @sessions
56
+ end
57
+
58
+ end # end singleton class
59
+
60
+ end # end DRbSession
61
+
62
+ # Keeps track of the status of all currently processing uploads
63
+ class UploadProgress
64
+ attr_accessor :debug
65
+ def initialize
66
+ @guard = Mutex.new
67
+ @counters = {}
68
+ end
69
+
70
+ def check(upid)
71
+ @counters[upid].last rescue nil
72
+ end
73
+
74
+ def last_checked(upid)
75
+ @counters[upid].first rescue nil
76
+ end
77
+
78
+ def update_checked_time(upid)
79
+ @guard.synchronize { @counters[upid][0] = Time.now }
80
+ end
81
+
82
+ def add(upid, size)
83
+ @guard.synchronize do
84
+ @counters[upid] = [Time.now, {:size => size, :received => 0}]
85
+ puts "#{upid}: Added" if @debug
86
+ end
87
+ end
88
+
89
+ def mark(upid, len)
90
+ return unless status = check(upid)
91
+ puts "#{upid}: Marking" if @debug
92
+ @guard.synchronize { status[:received] = status[:size] - len }
93
+ end
94
+
95
+ def finish(upid)
96
+ @guard.synchronize do
97
+ puts "#{upid}: Finished" if @debug
98
+ @counters.delete(upid)
99
+ end
100
+ end
101
+
102
+ def list
103
+ @counters.keys.sort
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,71 @@
1
+ module Merb
2
+
3
+ module SessionMixin
4
+
5
+ def setup_session
6
+ MERB_LOGGER.info("Setting up session")
7
+ @session = Merb::Session.persist(cookies)
8
+ @fingerprint_before = Marshal.dump(@session).hash
9
+ end
10
+
11
+ def finalize_session
12
+ MERB_LOGGER.info("Finalize session")
13
+ unless Marshal.dump(@session).hash == @fingerprint_before
14
+ @session.save
15
+ end
16
+ @headers['Set-Cookie'] = @cookies.map { |k,v| "#{k}=#{escape(v)}; path=/" if v != @k[k] } - [nil]
17
+ end
18
+
19
+ # accessor for @session. Please use session and
20
+ # never @session directly.
21
+ def session
22
+ @session
23
+ end
24
+
25
+ end
26
+
27
+ class Session
28
+
29
+ attr_reader :sess_id
30
+
31
+ def []=(k, v) # :nodoc:
32
+ (@data||={})[k] = v rescue nil
33
+ end
34
+ def [](k) # :nodoc:
35
+ @data[k] rescue nil
36
+ end
37
+
38
+ def initialize(sess_id,data={})
39
+ @data = data || {}
40
+ @sess_id = sess_id
41
+ end
42
+
43
+ def save
44
+ Merb::DRbSession[@sess_id] = @data
45
+ end
46
+
47
+ RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
48
+
49
+ # Generates a new session ID and creates a row for the new session in the database.
50
+ def self.generate(cookies)
51
+ rand_max = RAND_CHARS.size
52
+ sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] }
53
+ sess = Merb::DRbSession.create(:sess_id => sid, :data => {})
54
+ cookies[:sess_id] = sid
55
+ new(sid, sess)
56
+ end
57
+
58
+ # Gets the existing session based on the <tt>camping_sid</tt> available in cookies.
59
+ # If none is found, generates a new session.
60
+ def self.persist cookies
61
+ if cookies[:sess_id]
62
+ session = new(cookies[:sess_id], Merb::DRbSession[cookies[:sess_id]])
63
+ end
64
+ unless session
65
+ session = Merb::Session.generate(cookies)
66
+ end
67
+ session
68
+ end
69
+ end
70
+
71
+ end
@@ -1,3 +1,4 @@
1
+ require 'active_record'
1
2
  module Merb
2
3
  module SessionMixin
3
4