merb 0.0.1

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/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: