sinatra 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

Files changed (54) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +22 -0
  3. data/Manifest +53 -0
  4. data/README +99 -0
  5. data/RakeFile +36 -0
  6. data/Rakefile +36 -0
  7. data/examples/hello/hello.rb +30 -0
  8. data/examples/hello/views/hello.erb +1 -0
  9. data/examples/todo/todo.rb +38 -0
  10. data/files/default_index.erb +42 -0
  11. data/files/error.erb +9 -0
  12. data/files/logo.png +0 -0
  13. data/files/not_found.erb +52 -0
  14. data/lib/sinatra.rb +47 -0
  15. data/lib/sinatra/context.rb +88 -0
  16. data/lib/sinatra/context/renderer.rb +75 -0
  17. data/lib/sinatra/core_ext/array.rb +5 -0
  18. data/lib/sinatra/core_ext/class.rb +49 -0
  19. data/lib/sinatra/core_ext/hash.rb +7 -0
  20. data/lib/sinatra/core_ext/kernel.rb +16 -0
  21. data/lib/sinatra/core_ext/metaid.rb +18 -0
  22. data/lib/sinatra/core_ext/module.rb +11 -0
  23. data/lib/sinatra/core_ext/symbol.rb +5 -0
  24. data/lib/sinatra/dispatcher.rb +27 -0
  25. data/lib/sinatra/dsl.rb +163 -0
  26. data/lib/sinatra/environment.rb +15 -0
  27. data/lib/sinatra/event.rb +184 -0
  28. data/lib/sinatra/irb.rb +55 -0
  29. data/lib/sinatra/loader.rb +31 -0
  30. data/lib/sinatra/logger.rb +22 -0
  31. data/lib/sinatra/options.rb +43 -0
  32. data/lib/sinatra/rack_ext/request.rb +15 -0
  33. data/lib/sinatra/route.rb +65 -0
  34. data/lib/sinatra/server.rb +54 -0
  35. data/lib/sinatra/sessions.rb +21 -0
  36. data/lib/sinatra/test_methods.rb +55 -0
  37. data/sinatra.gemspec +60 -0
  38. data/site/index.htm +100 -0
  39. data/site/index.html +100 -0
  40. data/site/logo.png +0 -0
  41. data/test/helper.rb +17 -0
  42. data/test/sinatra/dispatcher_test.rb +91 -0
  43. data/test/sinatra/event_test.rb +37 -0
  44. data/test/sinatra/renderer_test.rb +47 -0
  45. data/test/sinatra/request_test.rb +21 -0
  46. data/test/sinatra/route_test.rb +21 -0
  47. data/test/sinatra/static_files/foo.txt +1 -0
  48. data/test/sinatra/static_files_test.rb +41 -0
  49. data/test/sinatra/url_test.rb +18 -0
  50. data/vendor/erb/init.rb +3 -0
  51. data/vendor/erb/lib/erb.rb +41 -0
  52. data/vendor/haml/init.rb +3 -0
  53. data/vendor/haml/lib/haml.rb +41 -0
  54. metadata +121 -0
@@ -0,0 +1,15 @@
1
+ module Sinatra
2
+ module Environment
3
+ extend self
4
+
5
+ def prepare
6
+ Options.parse!(ARGV)
7
+ end
8
+
9
+ def prepare_loggers(logger = Logger.new(open(Options.log_file, 'w')))
10
+ [Server, EventContext, Event, Dispatcher].each do |klass|
11
+ klass.logger = logger
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,184 @@
1
+ module Sinatra
2
+
3
+ module EventManager # :nodoc:
4
+ extend self
5
+
6
+ def reset!
7
+ @events.clear if @events
8
+ end
9
+
10
+ def events
11
+ @events || []
12
+ end
13
+
14
+ def register_event(event)
15
+ (@events ||= []) << event
16
+ end
17
+
18
+ def determine_event(verb, path, if_nil = :present_error)
19
+ event = events.find(method(if_nil)) do |e|
20
+ e.verb == verb && e.recognize(path)
21
+ end
22
+ end
23
+
24
+ def present_error
25
+ determine_event(:get, '404', :not_found)
26
+ end
27
+
28
+ def not_found
29
+ Event.new(:get, 'not_found', false) do
30
+ status 404
31
+
32
+ if request.path_info == '/' && request.request_method == 'GET'
33
+ erb :default_index, :views_directory => SINATRA_ROOT + '/files'
34
+ else
35
+ erb :not_found, :views_directory => SINATRA_ROOT + '/files'
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ class Event # :nodoc:
43
+
44
+ cattr_accessor :logger
45
+ cattr_accessor :after_filters
46
+
47
+ self.after_filters = []
48
+
49
+ def self.after_attend(filter)
50
+ after_filters << filter
51
+ end
52
+
53
+ after_attend :log_event
54
+
55
+ attr_reader :path, :verb
56
+
57
+ def initialize(verb, path, register = true, &block)
58
+ @verb = verb
59
+ @path = path
60
+ @route = Route.new(path)
61
+ @block = block
62
+ EventManager.register_event(self) if register
63
+ end
64
+
65
+ def attend(request)
66
+ request.params.merge!(@route.params)
67
+ context = EventContext.new(request)
68
+ begin
69
+ result = context.instance_eval(&@block) if @block
70
+ context.body context.body || result || ''
71
+ rescue => e
72
+ context.error e
73
+ end
74
+ run_through_after_filters(context)
75
+ context
76
+ end
77
+ alias :call :attend
78
+
79
+ def recognize(path)
80
+ @route.recognize(path)
81
+ end
82
+
83
+ private
84
+
85
+ def run_through_after_filters(context)
86
+ after_filters.each { |filter| context.send(filter) }
87
+ end
88
+
89
+ end
90
+
91
+ class StaticEvent < Event # :nodoc:
92
+
93
+ def initialize(path, root, register = true)
94
+ @root = root
95
+ super(:get, path, register)
96
+ end
97
+
98
+ def recognize(path)
99
+ File.exists?(physical_path_for(path))
100
+ end
101
+
102
+ def physical_path_for(path)
103
+ path.gsub(/^#{@path}/, @root)
104
+ end
105
+
106
+ def attend(request)
107
+ @filename = physical_path_for(request.path_info)
108
+ context = EventContext.new(request)
109
+ context.body self
110
+ context.header 'Content-Type' => MIME_TYPES[File.extname(@filename)[1..-1]]
111
+ context.header 'Content-Length' => File.size(@filename).to_s
112
+ context
113
+ end
114
+
115
+ def each
116
+ File.open(@filename, "rb") do |file|
117
+ while part = file.read(8192)
118
+ yield part
119
+ end
120
+ end
121
+ end
122
+
123
+ # :stopdoc:
124
+ # From WEBrick.
125
+ MIME_TYPES = {
126
+ "ai" => "application/postscript",
127
+ "asc" => "text/plain",
128
+ "avi" => "video/x-msvideo",
129
+ "bin" => "application/octet-stream",
130
+ "bmp" => "image/bmp",
131
+ "class" => "application/octet-stream",
132
+ "cer" => "application/pkix-cert",
133
+ "crl" => "application/pkix-crl",
134
+ "crt" => "application/x-x509-ca-cert",
135
+ #"crl" => "application/x-pkcs7-crl",
136
+ "css" => "text/css",
137
+ "dms" => "application/octet-stream",
138
+ "doc" => "application/msword",
139
+ "dvi" => "application/x-dvi",
140
+ "eps" => "application/postscript",
141
+ "etx" => "text/x-setext",
142
+ "exe" => "application/octet-stream",
143
+ "gif" => "image/gif",
144
+ "htm" => "text/html",
145
+ "html" => "text/html",
146
+ "jpe" => "image/jpeg",
147
+ "jpeg" => "image/jpeg",
148
+ "jpg" => "image/jpeg",
149
+ "lha" => "application/octet-stream",
150
+ "lzh" => "application/octet-stream",
151
+ "mov" => "video/quicktime",
152
+ "mpe" => "video/mpeg",
153
+ "mpeg" => "video/mpeg",
154
+ "mpg" => "video/mpeg",
155
+ "pbm" => "image/x-portable-bitmap",
156
+ "pdf" => "application/pdf",
157
+ "pgm" => "image/x-portable-graymap",
158
+ "png" => "image/png",
159
+ "pnm" => "image/x-portable-anymap",
160
+ "ppm" => "image/x-portable-pixmap",
161
+ "ppt" => "application/vnd.ms-powerpoint",
162
+ "ps" => "application/postscript",
163
+ "qt" => "video/quicktime",
164
+ "ras" => "image/x-cmu-raster",
165
+ "rb" => "text/plain",
166
+ "rd" => "text/plain",
167
+ "rtf" => "application/rtf",
168
+ "sgm" => "text/sgml",
169
+ "sgml" => "text/sgml",
170
+ "tif" => "image/tiff",
171
+ "tiff" => "image/tiff",
172
+ "txt" => "text/plain",
173
+ "xbm" => "image/x-xbitmap",
174
+ "xls" => "application/vnd.ms-excel",
175
+ "xml" => "text/xml",
176
+ "xpm" => "image/x-xpixmap",
177
+ "xwd" => "image/x-xwindowdump",
178
+ "zip" => "application/zip",
179
+ }
180
+ # :startdoc:
181
+
182
+ end
183
+
184
+ end
@@ -0,0 +1,55 @@
1
+ module Sinatra
2
+
3
+ # Sinatra Irb is entered via <tt>ruby myapp.rb -c</tt> (replace myapp.rb with your app filename)
4
+ #
5
+ # Be sure to also check out Sinatra::TestMethods for more cool stuff when your in Irb
6
+ #
7
+ module Irb
8
+ extend self
9
+
10
+ # taken from merb
11
+ def start! #:nodoc:
12
+
13
+ Object.send(:include, TestMethods) # added to allow post_to in console
14
+
15
+ Object.class_eval do
16
+ # Reload all Sinatra and App specific files
17
+ def reload!
18
+ Loader.reload!
19
+ end
20
+
21
+ # Show the +body+ with result info in your text editor!!! Great Job!
22
+ def show!(editor = nil)
23
+ editor = editor || ENV['EDITOR']
24
+ IO.popen(editor, 'w') do |f|
25
+ f.puts "<!--"
26
+ f.puts result_info
27
+ f.puts "-->"
28
+ f.puts
29
+ f.puts body
30
+ end
31
+ end
32
+ alias :mate :show!
33
+
34
+ def result_info #:nodoc:
35
+ info = <<-end_info
36
+ # Status: #{status}
37
+ # Headers: #{headers.inspect}
38
+ end_info
39
+ end
40
+ end
41
+
42
+ ARGV.clear # Avoid passing args to IRB
43
+ require 'irb'
44
+ require 'irb/completion'
45
+ def exit
46
+ exit!
47
+ end
48
+ if File.exists? ".irbrc"
49
+ ENV['IRBRC'] = ".irbrc"
50
+ end
51
+ IRB.start
52
+ exit!
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ require 'set'
2
+
3
+ module Sinatra
4
+ module Loader
5
+ extend self
6
+
7
+ def reload!
8
+ silence_warnings do
9
+ EventManager.reset!
10
+ load_files loaded_files
11
+ load $0
12
+ end
13
+ end
14
+
15
+ def load_files(*files)
16
+ files = files.flatten
17
+ files = files.first if files.first.is_a? Set
18
+
19
+ files.each do |file|
20
+ file = File.expand_path(file)
21
+ load file
22
+ loaded_files << file
23
+ end
24
+ end
25
+ alias_method :load_file, :load_files
26
+
27
+ def loaded_files
28
+ @loaded_files ||= Set.new
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ module Sinatra
2
+
3
+ class Logger
4
+
5
+ def initialize(steam)
6
+ @stream = steam
7
+ end
8
+
9
+ %w(info debug error warn).each do |n|
10
+ define_method n do |message|
11
+ @stream.puts message
12
+ @stream.flush
13
+ end
14
+ end
15
+
16
+ def exception(e)
17
+ error "#{e.message}:\n\t#{e.backtrace.join("\n\t")}"
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,43 @@
1
+ require 'optparse'
2
+
3
+ module Sinatra
4
+ module Options
5
+ extend self
6
+
7
+ attr_with_default :port, 4567
8
+ attr_with_default :environment, :development
9
+ attr_with_default :console, nil
10
+
11
+ def parse!(args)
12
+ return if @environment == :test
13
+ OptionParser.new do |opts|
14
+ opts.on '-p port', '--port port', 'Set the port (default is 4567)' do |port|
15
+ @port = port
16
+ end
17
+ opts.on '-e environment', 'Set the environment (default if development)' do |env|
18
+ @environment = env.intern
19
+ end
20
+ opts.on '-c', '--console', 'Run in console mode' do
21
+ @console = true
22
+ end
23
+ opts.on '-h', '--help', '-?', 'Show this message' do
24
+ puts opts
25
+ exit!
26
+ end
27
+ end.parse!(ARGV)
28
+ end
29
+
30
+ def log_file
31
+ # TODO find a better way that this
32
+ if File.basename($0, '.rb') == 'rake_test_loader' # hack to satisfy rake
33
+ '%s.log' % environment
34
+ else
35
+ File.dirname($0) + ('/%s.log' % environment)
36
+ end
37
+ end
38
+
39
+ def set_environment(env)
40
+ @environment = env
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ module Rack #:nodoc:
2
+
3
+ class Request #:nodoc:
4
+
5
+ def request_method
6
+ if @env['REQUEST_METHOD'] == 'POST' && %w(PUT DELETE).include?(params['_method'])
7
+ params['_method'].upcase
8
+ else
9
+ @env['REQUEST_METHOD']
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,65 @@
1
+ module Sinatra
2
+
3
+ class Route
4
+
5
+ SYMBOL_FIND = /:[a-z_]+/.freeze
6
+ PARENTHETICAL_SEGMENT_STRING = "([^\/.,;?]+)".freeze
7
+
8
+ attr_reader :regex, :params
9
+
10
+ def initialize(template)
11
+ @template = template.to_s.strip
12
+ @default_params = { :format => 'html' }
13
+ @params = {}
14
+ extract_keys
15
+ genereate_route
16
+ end
17
+
18
+ def recognize(path)
19
+ @params.clear
20
+
21
+ param_values = path.match(@regex).captures.compact rescue nil
22
+
23
+ if param_values
24
+ keys = @keys.size < param_values.size ? @keys.concat([:format]) : @keys
25
+ @params = @default_params.merge(@keys.zip(param_values).to_hash)
26
+ true
27
+ else
28
+ false
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def extract_keys
35
+ @keys = @template.scan(SYMBOL_FIND).map { |raw| eval(raw) }
36
+ @keys
37
+ end
38
+
39
+ def genereate_route_without_format
40
+ template = @template.dup
41
+ template.gsub!(/\.:format$/, '')
42
+ to_regex_route(template)
43
+ end
44
+
45
+ def genereate_route_with_format
46
+ template = @template.dup
47
+ if template =~ /\.:format$|\.([\w\d]+)$/
48
+ @default_params[:format] = $1 if $1
49
+ else
50
+ template << '.:format'
51
+ end
52
+ to_regex_route(template)
53
+ end
54
+
55
+ def to_regex_route(template)
56
+ /^#{template.gsub(/\./, '\.').gsub(SYMBOL_FIND, PARENTHETICAL_SEGMENT_STRING)}$/
57
+ end
58
+
59
+ def genereate_route
60
+ @regex = Regexp.union(genereate_route_without_format, genereate_route_with_format)
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,54 @@
1
+ require 'fileutils'
2
+
3
+ module Sinatra
4
+
5
+ class Server
6
+
7
+ cattr_accessor :logger
8
+ cattr_accessor :running
9
+
10
+ def start
11
+ begin
12
+ tail_thread = tail(Options.log_file)
13
+ Rack::Handler::Mongrel.run(Sinatra::Session::Cookie.new(Dispatcher.new), :Port => Options.port) do |server|
14
+ puts "== Sinatra has taken the stage on port #{server.port}!"
15
+ trap("INT") do
16
+ server.stop
17
+ self.class.running = false
18
+ puts "\n== Sinatra has ended his set (crowd applauds)"
19
+ end
20
+ end
21
+ self.class.running = true
22
+ rescue => e
23
+ logger.exception e
24
+ ensure
25
+ tail_thread.kill if tail_thread
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def tail(log_file)
32
+ FileUtils.touch(log_file)
33
+ cursor = File.size(log_file)
34
+ last_checked = Time.now
35
+ tail_thread = Thread.new do
36
+ File.open(log_file, 'r') do |f|
37
+ loop do
38
+ f.seek cursor
39
+ if f.mtime > last_checked
40
+ last_checked = f.mtime
41
+ contents = f.read
42
+ cursor += contents.length
43
+ print contents
44
+ end
45
+ sleep 1
46
+ end
47
+ end
48
+ end
49
+ tail_thread
50
+ end
51
+
52
+ end
53
+
54
+ end