merb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Ezra Zygmuntowicz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,135 @@
1
+ <html><body>
2
+ <pre>
3
+ Copyright (c) 2006 Ezra Zygmuntowicz
4
+
5
+ Merb. Mongrel+Erb
6
+
7
+ Little bitty lightweight ruby app server. FOr when you really need performance
8
+ for simple dynamic pages.
9
+
10
+ **Sample APP included**
11
+
12
+ Unpack the gem and grab the sample app
13
+
14
+ $ gem unpack merb
15
+
16
+ cd merb*
17
+ cd examples/smaple_app
18
+
19
+ $ merb start -t
20
+
21
+ then the sample app will be running on port 4000 in the foreground.
22
+ you can go to a few urls:
23
+
24
+ http://localhost:4000/upload/start
25
+
26
+ http://localhost:4000/foo/123/baz/12234345
27
+
28
+
29
+ **FEATURES** 1. Mongrel handler built in that parses incoming requests
30
+ including multipart uploads and post as well as ?query=strings. Puts the
31
+ params into @params and the cookies into @cookies when it instantiates your
32
+ controller class.
33
+
34
+ 2. New RouteMatcher and route compiler. Reads your route definition and compiles
35
+ a method on the fly that will match the request path against each route and do the
36
+ right thing. So the following routes:
37
+
38
+ Merb::RouteMatcher.prepare do |r|
39
+ r.add '/foo/:bar/baz/:id', :class => 'Test', :method => 'foo'
40
+ r.add '/hey/:there/you/:guys', :class => 'Test', :method => 'hello'
41
+ r.add '/:class/:method/:id', {}
42
+ r.add '/:class/:method', {}
43
+ r.add '.*', {:class => 'NoRouteFound', :method => 'noroute'}
44
+ end
45
+
46
+ Will be compiled and defined as a method with this lambda as the body:
47
+
48
+ lambda{|path|
49
+ if Regexp.new('/foo/(.+)/baz/(.+)') =~ path
50
+ @sections[:bar] = $1
51
+ @sections[:id] = $2
52
+ return {:class=>"Test", :method=>"foo"}.merge(@sections)
53
+ end
54
+
55
+ if Regexp.new('/hey/(.+)/you/(.+)') =~ path
56
+ @sections[:there] = $1
57
+ @sections[:guys] = $2
58
+ return {:class=>"Test", :method=>"hello"}.merge(@sections)
59
+ end
60
+
61
+ if Regexp.new('/(.+)/(.+)/(.+)') =~ path
62
+ @sections[:class] = $1
63
+ @sections[:method] = $2
64
+ @sections[:id] = $3
65
+ return {}.merge(@sections)
66
+ end
67
+
68
+ if Regexp.new('/(.+)/(.+)') =~ path
69
+ @sections[:class] = $1
70
+ @sections[:method] = $2
71
+ return {}.merge(@sections)
72
+ end
73
+
74
+ if Regexp.new('.*') =~ path
75
+ return {:class=>"NoRouteFound", :method=>"noroute"}.merge(@sections)
76
+ end
77
+
78
+ return default_route
79
+ }
80
+
81
+ 3. Simple controller classes with built in render method and template handling
82
+ with instance vars available in the views automatically.
83
+
84
+ class Test < Merb::Controller
85
+ template_dir :test
86
+
87
+ def hello(args)
88
+ # args is a hash of the remaining templated
89
+ # parameters from the url matched in routing.
90
+ # @params and @cookies are avaialable here. string
91
+ # keys and not symbols just yet.
92
+ # Assign the parameter to an instance variable
93
+ @name = args[:name]
94
+ render
95
+ end
96
+ end
97
+
98
+ <html>
99
+ <head>
100
+ <title>Hello, <%= @name %></title>
101
+ </head>
102
+ <body>
103
+ <h1><%= @params.inspect %></h1>
104
+ <h1><%= @cookies.inspect %></h1>
105
+ <% 5.times do %>
106
+ <h1>Hello, <%= @name %>!</h1>
107
+ <% end %>
108
+ </body>
109
+ </html>
110
+
111
+ 4. the server. right now you add your routes in
112
+ the appdir/conf/router.rb file. So by default it runs on port 4000
113
+
114
+ $ cd /path/to/your/merb/app
115
+ $ merb start
116
+
117
+ 5. File uploads. This is the thing that Merb was written for. Rails doesn't allow
118
+ multiple concurrent file uploads at once without blocking an entire rails backend
119
+ for each file upload. Merb allows multiple file uploads at once. Start the server
120
+ and browse to http://localhost:4000/upload/start and you will get a file upload
121
+ form. There is a very simple upload controller example that handles this upload.
122
+ When a file is uploaded with Merb, it gets put in a Tempfile. So you just want to
123
+ copy it to the right place on the filesystem.
124
+
125
+ def upload(args)
126
+ puts @params[:file].inspect
127
+
128
+ FileUtils.mv @params[:file][:tempfile].path, MERB_ROOT+"/uploads/#{@params[:file][:filename]}"
129
+
130
+ render
131
+ end
132
+ </pre>
133
+ </body>
134
+ </html>
135
+
@@ -0,0 +1,126 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/testtask'
6
+ require 'fileutils'
7
+ include FileUtils
8
+
9
+ NAME = "merb"
10
+ VERS = "0.0.1"
11
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
12
+ RDOC_OPTS = ['--quiet', '--title', "Merb Documentation",
13
+ "--opname", "index.html",
14
+ "--line-numbers",
15
+ "--main", "README",
16
+ "--inline-source"]
17
+
18
+ desc "Packages up Merb."
19
+ task :default => [:package]
20
+ task :package => [:clean]
21
+
22
+ task :doc => [:rdoc]
23
+
24
+ Rake::RDocTask.new do |rdoc|
25
+ rdoc.rdoc_dir = 'doc/rdoc'
26
+ rdoc.options += RDOC_OPTS
27
+ rdoc.main = "README"
28
+ rdoc.title = "Merb Documentation"
29
+ rdoc.rdoc_files.add ['README', 'LICENSE', 'lib/*.rb', 'server.rb']
30
+ end
31
+
32
+ spec = Gem::Specification.new do |s|
33
+ s.name = NAME
34
+ s.version = VERS
35
+ s.platform = Gem::Platform::RUBY
36
+ s.has_rdoc = true
37
+ s.extra_rdoc_files = ["README", "LICENSE"]
38
+ s.rdoc_options += RDOC_OPTS +
39
+ ['--exclude', '^(app|uploads)']
40
+ s.summary = "Merb == Mongrel + Erb. Pocket rocket web framework."
41
+ s.description = s.summary
42
+ s.author = "Ezra Zygmuntowicz"
43
+ s.email = 'ez@engineyard.com'
44
+ s.homepage = 'http://merb.devjavu.com'
45
+ s.executables = ['merb']
46
+
47
+ s.add_dependency('mongrel')
48
+ s.required_ruby_version = '>= 1.8.4'
49
+
50
+ s.files = %w(LICENSE README Rakefile server.rb) + Dir.glob("{app,bin,doc,test,lib,examples}/**/*")
51
+
52
+ s.require_path = "lib"
53
+ s.bindir = "bin"
54
+ end
55
+
56
+ Rake::GemPackageTask.new(spec) do |p|
57
+ p.need_tar = true
58
+ p.gem_spec = spec
59
+ end
60
+
61
+ task :install do
62
+ sh %{rake package}
63
+ sh %{sudo gem install pkg/#{NAME}-#{VERS}}
64
+ end
65
+
66
+ task :uninstall => [:clean] do
67
+ sh %{sudo gem uninstall #{NAME}}
68
+ end
69
+
70
+ task :doc_rforge do
71
+ sh %{rake doc}
72
+ sh %{scp -r doc/rdoc/* ezmobius@rubyforge.org:/var/www/gforge-projects/merb}
73
+ end
74
+
75
+ desc 'Run unit tests'
76
+ Rake::TestTask.new('test_unit') do |t|
77
+ t.libs << 'test'
78
+ t.pattern = 'test/unit/*_test.rb'
79
+ t.verbose = true
80
+ end
81
+
82
+ desc 'Run functional tests'
83
+ Rake::TestTask.new('test_functional') do |t|
84
+ t.libs << 'test'
85
+ t.pattern = 'test/functional/*_test.rb'
86
+ t.verbose = true
87
+ end
88
+
89
+ desc 'Run all tests'
90
+ Rake::TestTask.new('test') do |t|
91
+ t.libs << 'test'
92
+ t.pattern = 'test/**/*_test.rb'
93
+ t.verbose = true
94
+ end
95
+
96
+ desc 'Run all tests, specs and finish with rcov'
97
+ task :aok do
98
+ sh %{rake rcov}
99
+ sh %{rake spec}
100
+ end
101
+
102
+ ##############################################################################
103
+ # Statistics
104
+ ##############################################################################
105
+
106
+ STATS_DIRECTORIES = [
107
+ %w(Code lib/),
108
+ %w(Unit\ tests test/unit),
109
+ %w(Functional\ tests test/functional)
110
+ ].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) }
111
+
112
+ desc "Report code statistics (KLOCs, etc) from the application"
113
+ task :stats do
114
+ require 'extra/stats'
115
+ verbose = true
116
+ CodeStatistics.new(*STATS_DIRECTORIES).to_s
117
+ end
118
+
119
+ ##############################################################################
120
+ # SVN
121
+ ##############################################################################
122
+
123
+ desc "Add new files to subversion"
124
+ task :svn_add do
125
+ system "svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add"
126
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/local/bin/ruby
2
+ require 'rubygems'
3
+ require 'merb'
4
+ #require 'merb_daemon'
5
+ require 'merb_config'
6
+
7
+ Merb::Server.run.join
@@ -0,0 +1,7 @@
1
+ class Noroutefound < Merb::Controller
2
+
3
+ def method_missing(*args)
4
+ "<html><body><h1>No Matching Route</h1></body></html>"
5
+ end
6
+
7
+ end
@@ -0,0 +1,21 @@
1
+
2
+ class Test < Merb::Controller
3
+ template_dir :test
4
+
5
+ def hello(args)
6
+ # Assign the parameter to an instance variable
7
+ @name = args[:id]
8
+ render
9
+ end
10
+
11
+ def foo(args)
12
+
13
+ @args = args
14
+ render
15
+ end
16
+
17
+ def to_s
18
+ "<html><body>Test controller, no mathing action</body></html>"
19
+ end
20
+ end
21
+
@@ -0,0 +1,21 @@
1
+ class Upload < Merb::Controller
2
+ template_dir :upload
3
+
4
+ def start(*args)
5
+ # Assign the parameter to an instance variable
6
+ @args = args
7
+ render
8
+ end
9
+
10
+ def upload(*args)
11
+ puts @params[:file].inspect
12
+
13
+ FileUtils.mv @params[:file][:tempfile].path, ::Merb::Server.config[:merb_root]+"/public/files/#{@params[:file][:filename]}"
14
+
15
+ render
16
+ end
17
+
18
+ def to_s
19
+ "<html><body>Upload controller, no mathing action</body></html>"
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ <html>
2
+ <body>
3
+
4
+ <%= @args.inspect %>
5
+ </body>
6
+ </html>
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <head>
3
+ <title>Hello, <%= @name %></title>
4
+ </head>
5
+ <body>
6
+ <h1><%= @params.inspect %></h1>
7
+ <% 5.times do %>
8
+ <h1>Hello, <%= @name %>!</h1>
9
+ <% end %>
10
+
11
+ </body>
12
+ </html>
@@ -0,0 +1,23 @@
1
+ <html>
2
+ <head>
3
+ <title>Upload#start</title>
4
+ </head>
5
+ <body>
6
+
7
+
8
+ <h2>Test file uploading.</h2>
9
+
10
+ <form action="/upload/upload" enctype="multipart/form-data" method="post">
11
+
12
+ <dl>
13
+ <dt><label>Select a file</label></dt>
14
+ <dd><input name="file" type="file" /></dd>
15
+ </dl>
16
+
17
+
18
+ <p>
19
+ <input name="commit" type="submit" value="Upload Asset" />
20
+ </p>
21
+ </form>
22
+ </body>
23
+ </html>
@@ -0,0 +1,4 @@
1
+ <h2>File uploaded successfully</h2>
2
+
3
+ <p>params for @params[:file].inspect</p>
4
+ <%=@params[:file].inspect %>
@@ -0,0 +1,3 @@
1
+ puts "merb init called"
2
+
3
+ # add your own ruby code here for app initialization and load path stuff
@@ -0,0 +1,7 @@
1
+ Merb::RouteMatcher.prepare do |r|
2
+ r.add '/foo/:bar/baz/:id', :class => 'Test', :method => 'foo'
3
+ r.add '/hey/:there/you/:guys', :class => 'Test', :method => 'hello'
4
+ r.add '/:class/:method/:id', {}
5
+ r.add '/:class/:method', {}
6
+ r.add '.*', {:class => 'NoRouteFound', :method => 'noroute'}
7
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'mongrel'
3
+ require 'fileutils'
4
+ require 'erb'
5
+ require 'merb_config'
6
+ require 'active_support'
7
+
8
+ module Merb
9
+ VERSION='0.0.1' unless defined?VERSION
10
+ end
11
+
12
+ lib = File.join(File.dirname(__FILE__), 'merb')
13
+ Dir.foreach(lib) {|fn| require File.join(lib, fn) if fn =~ /\.rb$/}
@@ -0,0 +1,87 @@
1
+ module Merb
2
+
3
+ class Controller
4
+ attr_accessor :response_code, :content_type
5
+ # Stolen from Camping without a twinge of remorse ;)
6
+ def initialize(req, env, method=(env['REQUEST_METHOD']||"GET")) #:nodoc:
7
+ @response_code = 200
8
+ @content_type = 'text/html'
9
+ env = MerbHash[env.to_hash]
10
+ puts env.inspect if $DEBUG
11
+ puts req.inspect if $DEBUG
12
+ @status, @method, @env, @headers, @root = 200, method.downcase, env,
13
+ {'Content-Type'=>'text/html'}, env['SCRIPT_NAME'].sub(/\/$/,'')
14
+ @k = query_parse(env['HTTP_COOKIE'], ';,')
15
+ qs = query_parse(env['QUERY_STRING'])
16
+ @in = req
17
+ if %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)|n.match(env['CONTENT_TYPE'])
18
+ b = /(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/
19
+ until @in.eof?
20
+ fh=MerbHash[]
21
+ for l in @in
22
+ case l
23
+ when "\r\n" : break
24
+ when /^Content-Disposition: form-data;/
25
+ fh.update MerbHash[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten]
26
+ when /^Content-Type: (.+?)(\r$|\Z)/m
27
+ puts "=> fh[type] = #$1"
28
+ fh[:type] = $1
29
+ end
30
+ end
31
+ fn=fh[:name]
32
+ o=if fh[:filename]
33
+ o=fh[:tempfile]=Tempfile.new(:Merb)
34
+ o.binmode
35
+ else
36
+ fh=""
37
+ end
38
+ while l=@in.read(16384)
39
+ if l=~b
40
+ o<<$`.chomp
41
+ @in.seek(-$'.size,IO::SEEK_CUR)
42
+ break
43
+ end
44
+ o<<l
45
+ end
46
+ qs[fn]=fh if fn
47
+ fh[:tempfile].rewind if fh.is_a?MerbHash
48
+ end
49
+ elsif @method == "post"
50
+ qs.merge!(query_parse(@in.read))
51
+ end
52
+ @cookies, @params = @k.dup, qs.dup
53
+ end
54
+
55
+ def query_parse(qs, d = '&;')
56
+ m = proc {|_,o,n|o.update(n,&m)rescue([*o]<<n)}
57
+ (qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p|
58
+ k, v=unescape(p).split('=',2)
59
+ h.update(k.split(/[\]\[]+/).reverse.
60
+ inject(v) { |x,i| MerbHash[i,x] },&m)
61
+ }
62
+ end
63
+
64
+ def escape(s)
65
+ Mongrel::HttpRequest.escape(s)
66
+ end
67
+
68
+ def unescape(s)
69
+ Mongrel::HttpRequest.unescape(s)
70
+ end
71
+
72
+ def self.template_dir(loc)
73
+ @@template_dir = File.expand_path(Merb::Server.config[:merb_root] + "/app/views/#{loc}")
74
+ end
75
+
76
+ def current_method_name(depth=0)
77
+ caller[depth] =~ /`(.*)'$/
78
+ $1
79
+ end
80
+
81
+ def render(template=current_method_name(1),b=binding)
82
+ template = ERB.new( IO.read( @@template_dir + "/#{template}.rhtml" ) )
83
+ template.result( b )
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,76 @@
1
+ require 'rubygems'
2
+ require 'daemons'
3
+ require 'merb'
4
+ require 'optparse'
5
+ require 'ostruct'
6
+
7
+ module Merb
8
+ end
9
+
10
+ module Daemons
11
+ class Controller
12
+ attr_accessor :app_part
13
+ end
14
+ end
15
+
16
+ class Merb::Server
17
+
18
+ def self.merb_config
19
+ options = Merb::Config.setup
20
+
21
+ opts = OptionParser.new do |opts|
22
+ opts.on("-c", "--config [String]") do |config|
23
+ options[:config] = config
24
+ end
25
+
26
+ opts.on("-p", "--port [Integer]") do |port|
27
+ options[:port] = port
28
+ end
29
+
30
+ opts.on("-n", "--num-procs [Integer]") do |numprocs|
31
+ options[:numprocs] = numprocs
32
+ end
33
+
34
+ opts.on("-h", "--host [String]") do |host|
35
+ options[:host] = host
36
+ end
37
+
38
+ opts.on("-m", "--merb-root [String]") do |merb_root|
39
+ options[:merb_root] = merb_root
40
+ end
41
+ end
42
+
43
+ opts.parse!(@@merb_raw_opts)
44
+
45
+
46
+ @@merb_opts = options
47
+ end
48
+
49
+ def self.run
50
+
51
+ options = {
52
+ :dir => Dir.pwd + '/log/',
53
+ :dir_mode => :normal
54
+ }
55
+
56
+ @@merb_raw_opts = Daemons::Controller.new(options, ARGV).app_part
57
+ merb_config
58
+
59
+ require @@merb_opts[:merb_root]+'/conf/router.rb'
60
+ require @@merb_opts[:merb_root]+'/conf/merb_init.rb'
61
+ $LOAD_PATH.unshift( File.join(@@merb_opts[:merb_root] , 'app/controllers') )
62
+ $LOAD_PATH.unshift( File.join(@@merb_opts[:merb_root] , 'lib') )
63
+
64
+
65
+ h = Mongrel::HttpServer.new((@@merb_opts[:host]||"0.0.0.0"), (@@merb_opts[:port]||4000), (@@merb_opts[:numprocs]||40))
66
+ h.register("/", MerbHandler.new)
67
+ h.register("/favicon.ico", Mongrel::Error404Handler.new(""))
68
+ h.run.join
69
+
70
+ end
71
+
72
+ def self.config
73
+ @@merb_opts
74
+ end
75
+
76
+ end
@@ -0,0 +1,58 @@
1
+ class MerbHandler < Mongrel::HttpHandler
2
+
3
+ def process(request, response)
4
+ if response.socket.closed?
5
+ return
6
+ end
7
+ controller, method, args = handle(request)
8
+ begin
9
+ output = if (controller && controller.kind_of?(Merb::Controller))
10
+ if method
11
+ ( args ? controller.send( method, args ) : controller.send(method) )
12
+ else
13
+ controller.to_s
14
+ end
15
+ else
16
+ response_code = 500
17
+ "<html><body>Error: no merb controller found for this url.</body></html>"
18
+ end
19
+ response_code = controller.response_code
20
+ content_type = controller.content_type
21
+ rescue Exception => e
22
+ response_code = 500
23
+ output = "<html><h2>Merb Error!</h2><pre>#{ e.message } - (#{ e.class })" <<
24
+ "\n" << "#{(e.backtrace or []).join('\n')}</pre></html>"
25
+ end
26
+ response.start(response_code||200) do |head,out|
27
+ head["Content-Type"] = content_type || "text/html"
28
+ out << output
29
+ end
30
+ end
31
+
32
+ def handle(request)
33
+ path = request.params["PATH_INFO"].sub(/\/+/, '/')
34
+ path = path[0..-2] if (path[-1] == ?/)
35
+ routes = Merb::RouteMatcher.new
36
+ route = routes.route_request(path)
37
+ if route
38
+ [ instantiate_controller(route.delete(:class), request.body, request.params),
39
+ route.delete(:method), route ]
40
+ else
41
+ ["<html><body>Error: no route matches!</body></html>", nil, nil]
42
+ end
43
+ end
44
+
45
+ def instantiate_controller(controller_name, req, env)
46
+ if !File.exist?(Merb::Server.config[:merb_root]+"/app/controllers/#{controller_name}.rb")
47
+ return Object.const_get(:Noroutefound).new(req, env)
48
+ end
49
+ controller_name.import
50
+ begin
51
+ return Object.const_get( controller_name.controller_class_name ).new(req, env)
52
+ rescue Exception
53
+ warn "Error getting instance of '#{controller_name.controller_class_name}': #{$!}"
54
+ raise $!
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,87 @@
1
+ module Merb
2
+
3
+ class RouteMatcher
4
+
5
+ attr_accessor :sections
6
+ SECTION_REGEX = /(?::([a-z]+))/ unless defined?SECTION_REGEX
7
+
8
+ def self.prepare
9
+ @@routes = Array.new
10
+ @@compiled_statement = String.new
11
+ yield self
12
+ compile_router
13
+ end
14
+
15
+ def initialize
16
+ @sections = Hash.new
17
+ end
18
+
19
+ def routes
20
+ @@routes
21
+ end
22
+
23
+ def compiled_statement
24
+ @@compiled_statement
25
+ end
26
+
27
+ def self.add(*route)
28
+ @@routes << route
29
+ end
30
+
31
+ def self.compile_router
32
+ router_lambda = @@routes.inject('lambda{|path| ') { |m,r|
33
+ m << compile(r)
34
+ } << "\n return {:class=>'NoRouteFound', :method=>'noroute'}\n}"
35
+ @@compiled_statement = router_lambda
36
+ define_method(:route_request, &eval(router_lambda))
37
+ end
38
+
39
+ def self.compile(route)
40
+ raise ArgumentError unless String === route[0]
41
+ code, count = '', 0
42
+ while route[0] =~ SECTION_REGEX
43
+ route[0] = route[0].dup
44
+ name = $1
45
+ count += 1
46
+ route[0].sub!(SECTION_REGEX, '(.+)')
47
+ code << " @sections[:#{name}] = $#{count}\n"
48
+ end
49
+ condition = " if Regexp.new('#{route[0]}') =~ path"
50
+ statement = "\n#{condition}\n#{code}"
51
+ statement << " return #{route[1].inspect}.merge(@sections)\n end\n"
52
+ statement
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ =begin
60
+ if __FILE__ == $0
61
+ Merb::RouteMatcher.prepare do |r|
62
+ r.add '/foo/:bar/baz/:id', :class => 'Test', :method => 'foo'
63
+ r.add '/hey/:there/you/:guys', :class => 'Test', :method => 'hello'
64
+ r.add '/these/:routes/are/:sweet', :class => 'Upload', :method => 'start'
65
+ r.add '/h/:a/b/:c/d/:f/g/:id', :class => 'Test'
66
+ r.add '/:class/:method/:id', {}
67
+ r.add '/:class/:method', {}
68
+ end
69
+
70
+ routes = Merb::RouteMatcher.new
71
+ puts routes.compiled_statement
72
+ p routes.route_request( "/foo/234/baz/dsdsd")
73
+ routes = Merb::RouteMatcher.new
74
+ p routes.route_request( "/hey/jon/you/girls")
75
+ routes = Merb::RouteMatcher.new
76
+ p routes.route_request( "/upload/test/12")
77
+ routes = Merb::RouteMatcher.new
78
+ p routes.route_request( "/these/234/are/yup")
79
+ routes = Merb::RouteMatcher.new
80
+ p routes.route_request( '/h/12/b/red/d/blue/g/12')
81
+ routes = Merb::RouteMatcher.new
82
+ p routes.route_request( '/hdsfvsdfsdfdsf')
83
+
84
+ p routes.routes
85
+
86
+ end
87
+ =end
@@ -0,0 +1,21 @@
1
+ class String
2
+ def import
3
+ Merb::Server.config[:allow_reloading] ? load( self + '.rb' ) : require( self )
4
+ end
5
+
6
+ def controller_class_name
7
+ self.capitalize
8
+ end
9
+ end
10
+
11
+ class MerbHash < HashWithIndifferentAccess
12
+ def method_missing(m,*a)
13
+ m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")
14
+ end
15
+ end
16
+
17
+ class Symbol
18
+ def to_s
19
+ @str_rep || (@str_rep = id2name.freeze)
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Merb
2
+ end
3
+
4
+ class Merb::Config
5
+ def self.setup
6
+ defaults = {
7
+ :host => "0.0.0.0",
8
+ :port => "4000",
9
+ :allow_reloading => true,
10
+ :merb_root => Dir.pwd
11
+ }
12
+
13
+ begin
14
+ options = defaults.merge(YAML.load(ERB.new(IO.read("#{defaults[:merb_root]}/conf/merb.yml")).result))
15
+ rescue
16
+ options = defaults
17
+ end
18
+
19
+ options
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/local/bin/ruby
2
+ require 'rubygems'
3
+ $LOAD_PATH.unshift( File.join( File.dirname( __FILE__ ) , 'lib' ) )
4
+ $LOAD_PATH.unshift( File.join( File.dirname( __FILE__ ) , 'app/controllers' ) )
5
+ require 'merb'
6
+ MERB_ROOT = File.dirname( __FILE__ )
7
+
8
+ h = Mongrel::HttpServer.new("0.0.0.0", PORT, 40)
9
+ h.register("/", MerbHandler.new)
10
+ h.register("/favicon.ico", Mongrel::Error404Handler.new(""))
11
+ h.run.join
12
+
13
+
@@ -0,0 +1 @@
1
+ require 'test/unit'
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ require File.dirname(__FILE__) + '/../../lib/merb'
3
+
4
+ class RouteMatcherTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ Merb::RouteMatcher.prepare do |r|
8
+ r.add '/foo/:bar/baz/:id', :class => 'Test', :method => 'foo'
9
+ r.add '/hey/:there/you/:guys', :class => 'Test', :method => 'hello'
10
+ r.add '/these/:routes/are/:sweet', :class => 'Upload', :method => 'start'
11
+ r.add '/h/:a/b/:c/d/:f/g/:id', :class => 'Test'
12
+ r.add '/:class/:method/:id', {}
13
+ r.add '/:class/:method', {}
14
+ r.add '.*', {:class => 'NoRouteFound', :method => 'noroute'}
15
+ end
16
+ end
17
+
18
+ def test_compiled_statement
19
+ r = Merb::RouteMatcher.new
20
+ assert( r.compiled_statement.kind_of?(String))
21
+ assert( r.compiled_statement =~ /lambda\{\|path/)
22
+ assert( r.compiled_statement =~ /Regexp\.new/)
23
+ assert( r.compiled_statement =~ /merge\(@sections\)/)
24
+ end
25
+
26
+ def test_route_matching
27
+ routes = Merb::RouteMatcher.new
28
+ assert_equal( {:class=>"Test", :bar=>"123", :id=>"456", :method=>"foo"}, routes.route_request("/foo/123/baz/456"))
29
+ routes = Merb::RouteMatcher.new
30
+ assert_equal( {:class=>"Test", :there=>"jon", :guys=>"girls", :method=>"hello"}, routes.route_request( "/hey/jon/you/girls"))
31
+ routes = Merb::RouteMatcher.new
32
+ assert_equal({:class=>"upload", :id=>"12", :method=>"test"},routes.route_request( "/upload/test/12"))
33
+ routes = Merb::RouteMatcher.new
34
+ assert_equal({:class=>"nik", :method=>"nak"},routes.route_request( "/nik/nak"))
35
+ routes = Merb::RouteMatcher.new
36
+ assert_equal({:class=>"lots", :method=>"of", :id => "12"},routes.route_request( "/lots/of/12"))
37
+ routes = Merb::RouteMatcher.new
38
+ assert_equal({:class => 'NoRouteFound', :method => 'noroute'},routes.route_request( "/"))
39
+ routes = Merb::RouteMatcher.new
40
+ assert_equal({:class => 'NoRouteFound', :method => 'noroute'},routes.route_request( "/dfnasfnafn"))
41
+ routes = Merb::RouteMatcher.new
42
+ assert_equal( {:class=>"Upload", :routes=>"234", :sweet=>"yup", :method=>"start"}, routes.route_request( "/these/234/are/yup"))
43
+ routes = Merb::RouteMatcher.new
44
+ assert_equal( {:class=>"Test", :a=>"12", :c=>"red", :id=>"12", :f=>"blue"}, routes.route_request( '/h/12/b/red/d/blue/g/12'))
45
+ end
46
+
47
+ def test_route_doesnt_match
48
+ routes = Merb::RouteMatcher.new
49
+ assert_equal({:class=>"NoRouteFound", :method=>"noroute"},routes.route_request( '/hdsfvsdfsdfdsf'))
50
+ end
51
+
52
+ def test_regexes_included_in_compiled_statement
53
+ routes = Merb::RouteMatcher.new
54
+ routes.routes.each do |r|
55
+ assert Regexp.new(r[0]) =~ routes.compiled_statement
56
+ end
57
+ end
58
+
59
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: merb
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2006-10-15 00:00:00 -07:00
8
+ summary: Merb == Mongrel + Erb. Pocket rocket web framework.
9
+ require_paths:
10
+ - lib
11
+ email: ez@engineyard.com
12
+ homepage: http://merb.devjavu.com
13
+ rubyforge_project:
14
+ description: Merb == Mongrel + Erb. Pocket rocket web framework.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.4
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Ezra Zygmuntowicz
31
+ files:
32
+ - LICENSE
33
+ - README
34
+ - Rakefile
35
+ - server.rb
36
+ - bin/merb
37
+ - test/test_helper.rb
38
+ - test/unit
39
+ - test/unit/route_matcher_test.rb
40
+ - lib/merb
41
+ - lib/merb.rb
42
+ - lib/merb_config.rb
43
+ - lib/merb/merb_controller.rb
44
+ - lib/merb/merb_daemon.rb
45
+ - lib/merb/merb_handler.rb
46
+ - lib/merb/merb_router.rb
47
+ - lib/merb/merb_utils.rb
48
+ - examples/sample_app
49
+ - examples/sample_app/app
50
+ - examples/sample_app/conf
51
+ - examples/sample_app/lib
52
+ - examples/sample_app/log
53
+ - examples/sample_app/public
54
+ - examples/sample_app/app/controllers
55
+ - examples/sample_app/app/views
56
+ - examples/sample_app/app/controllers/noroutefound.rb
57
+ - examples/sample_app/app/controllers/test.rb
58
+ - examples/sample_app/app/controllers/upload.rb
59
+ - examples/sample_app/app/views/test
60
+ - examples/sample_app/app/views/upload
61
+ - examples/sample_app/app/views/test/foo.rhtml
62
+ - examples/sample_app/app/views/test/hello.rhtml
63
+ - examples/sample_app/app/views/upload/start.rhtml
64
+ - examples/sample_app/app/views/upload/upload.rhtml
65
+ - examples/sample_app/conf/merb_init.rb
66
+ - examples/sample_app/conf/router.rb
67
+ - examples/sample_app/public/files
68
+ test_files: []
69
+
70
+ rdoc_options:
71
+ - --quiet
72
+ - --title
73
+ - Merb Documentation
74
+ - --opname
75
+ - index.html
76
+ - --line-numbers
77
+ - --main
78
+ - README
79
+ - --inline-source
80
+ - --exclude
81
+ - ^(app|uploads)
82
+ extra_rdoc_files:
83
+ - README
84
+ - LICENSE
85
+ executables:
86
+ - merb
87
+ extensions: []
88
+
89
+ requirements: []
90
+
91
+ dependencies:
92
+ - !ruby/object:Gem::Dependency
93
+ name: mongrel
94
+ version_requirement:
95
+ version_requirements: !ruby/object:Gem::Version::Requirement
96
+ requirements:
97
+ - - ">"
98
+ - !ruby/object:Gem::Version
99
+ version: 0.0.0
100
+ version: