merb 0.0.6 → 0.0.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 (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