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,88 @@
1
+ require File.dirname(__FILE__) + '/context/renderer'
2
+
3
+ module Sinatra
4
+
5
+ class EventContext
6
+
7
+ cattr_accessor :logger
8
+ attr_reader :request
9
+
10
+ include Sinatra::Renderer
11
+
12
+ def initialize(request) #:nodoc:
13
+ @request = request
14
+ @headers = {}
15
+ end
16
+
17
+ # Sets or returns the status
18
+ def status(value = nil)
19
+ @status = value if value
20
+ @status || 200
21
+ end
22
+
23
+ # Sets or returns the body
24
+ # *Usage*
25
+ # body 'test'
26
+ # or
27
+ # body do
28
+ # 'test'
29
+ # end
30
+ # both are the same
31
+ #
32
+ def body(value = nil, &block)
33
+ @body = value if value
34
+ @body = block.call if block
35
+ @body
36
+ end
37
+
38
+ # Renders an exception to +body+ and sets status to 500
39
+ def error(value = nil)
40
+ if value
41
+ status 500
42
+ @error = value
43
+ erb :error, :views_directory => SINATRA_ROOT + '/files/'
44
+ end
45
+ @error
46
+ end
47
+
48
+ # Sets or returns response headers
49
+ #
50
+ # *Usage*
51
+ # header 'Content-Type' => 'text/html'
52
+ # header 'Foo' => 'Bar'
53
+ # or
54
+ # headers 'Content-Type' => 'text/html',
55
+ # 'Foo' => 'Bar'
56
+ #
57
+ # Whatever blows your hair back
58
+ def headers(value = nil)
59
+ @headers.merge!(value) if value
60
+ @headers
61
+ end
62
+ alias :header :headers
63
+
64
+ # Returns a Hash of session data. Keys are symbolized
65
+ def session
66
+ request.env['rack.session']
67
+ end
68
+
69
+ # Returns a Hash of params. Keys are symbolized
70
+ def params
71
+ @params ||= @request.params.symbolize_keys
72
+ end
73
+
74
+ # Redirect to a url
75
+ def redirect(path)
76
+ logger.info "Redirecting to: #{path}"
77
+ status 302
78
+ header 'Location' => path
79
+ end
80
+
81
+ def log_event #:nodoc:
82
+ logger.info "#{request.request_method} #{request.path_info} | Status: #{status} | Params: #{params.inspect}"
83
+ logger.exception(error) if error
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,75 @@
1
+ Layouts = Hash.new # :nodoc:
2
+
3
+ module Sinatra
4
+
5
+ # The magic or rendering happens here. This is included in Sinatra::EventContext on load.
6
+ #
7
+ # These methods are the foundation for Sinatra::Erb and Sinatra::Haml and allow you to quickly
8
+ # create custom wrappers for your favorite rendering engines outside of erb and haml.
9
+
10
+ module Renderer
11
+
12
+ DEFAULT_OPTIONS = {
13
+ :views_directory => 'views',
14
+ :layout => :layout
15
+ }
16
+
17
+
18
+ # Renders templates from a string or file and handles their layouts:
19
+ #
20
+ # Example:
21
+ # module MyRenderer
22
+ # def my(template, options, &layout)
23
+ # render(template, :my, options, &layout)
24
+ # end
25
+ #
26
+ # def render_my(template)
27
+ # template.capitalize # It capitalizes templates!!!!! WOW!
28
+ # end
29
+ # end
30
+ # Sinatra::EventContext.send :include, MyRenderer
31
+ #
32
+ # get '/' do
33
+ # my "something"
34
+ # end
35
+ #
36
+ # get_it '/' # => 'Something'
37
+ #
38
+ # The second method is named render_extname. render will call this dynamicly
39
+ #
40
+ # paramaters:
41
+ # * +template+ If String, renders the string. If Symbol, reads from file with the basename of the Symbol; uses +renderer+ for extension.
42
+ # * +renderer+ A symbol defining the render_ method to call and the extension append to +template+ when looking in the +views_directory+
43
+ # * +options+ An optional Hash of options (see next section)
44
+ #
45
+ # options:
46
+ # * +:views_directory+ Allows you to override the default 'views' directory an look for the template in another
47
+ # * +:layout+ Which layout to use (see Sinatra::Dsl). false to force a render with no layout. Defaults to :default layout
48
+ #
49
+ def render(template, renderer, options = {})
50
+ options = DEFAULT_OPTIONS.merge(options)
51
+
52
+ layout = block_given? ? yield : Layouts[options[:layout]]
53
+
54
+ result_method = 'render_%s' % renderer
55
+
56
+ if layout
57
+ send(result_method, layout) { send(result_method, determine_template(template, renderer, options)) }
58
+ else
59
+ send(result_method, determine_template(template, renderer, options))
60
+ end
61
+ end
62
+
63
+ protected
64
+
65
+ def determine_template(template, ext, options)
66
+ if template.is_a?(Symbol)
67
+ File.read("%s/%s.%s" % [options[:views_directory], template, ext])
68
+ else
69
+ template
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def to_hash
3
+ self.inject({}) { |h, (k, v)| h[k] = v; h }
4
+ end
5
+ end
@@ -0,0 +1,49 @@
1
+ # Extends the class object with class and instance accessors for class attributes,
2
+ # just like the native attr* accessors for instance attributes.
3
+ class Class # :nodoc:
4
+ def cattr_reader(*syms)
5
+ syms.flatten.each do |sym|
6
+ next if sym.is_a?(Hash)
7
+ class_eval(<<-EOS, __FILE__, __LINE__)
8
+ unless defined? @@#{sym}
9
+ @@#{sym} = nil
10
+ end
11
+
12
+ def self.#{sym}
13
+ @@#{sym}
14
+ end
15
+
16
+ def #{sym}
17
+ @@#{sym}
18
+ end
19
+ EOS
20
+ end
21
+ end
22
+
23
+ def cattr_writer(*syms)
24
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
25
+ syms.flatten.each do |sym|
26
+ class_eval(<<-EOS, __FILE__, __LINE__)
27
+ unless defined? @@#{sym}
28
+ @@#{sym} = nil
29
+ end
30
+
31
+ def self.#{sym}=(obj)
32
+ @@#{sym} = obj
33
+ end
34
+
35
+ #{"
36
+ def #{sym}=(obj)
37
+ @@#{sym} = obj
38
+ end
39
+ " unless options[:instance_writer] == false }
40
+ EOS
41
+ end
42
+ end
43
+
44
+ def cattr_accessor(*syms)
45
+ cattr_reader(*syms)
46
+ cattr_writer(*syms)
47
+ end
48
+
49
+ end
@@ -0,0 +1,7 @@
1
+ class Hash
2
+
3
+ def symbolize_keys
4
+ self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
5
+ end
6
+
7
+ end
@@ -0,0 +1,16 @@
1
+ module Kernel
2
+ # (Taken from ActiveSupport)
3
+ # Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
4
+ #
5
+ # silence_warnings do
6
+ # value = noisy_call # no warning voiced
7
+ # end
8
+ #
9
+ # noisy_call # warning voiced
10
+ def silence_warnings
11
+ old_verbose, $VERBOSE = $VERBOSE, nil
12
+ yield
13
+ ensure
14
+ $VERBOSE = old_verbose
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # Compliments to why for this:
2
+ # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
3
+
4
+ class Object
5
+ # The hidden singleton lurks behind everyone
6
+ def metaclass; class << self; self; end; end
7
+ def meta_eval &blk; metaclass.instance_eval &blk; end
8
+
9
+ # Adds methods to a metaclass
10
+ def meta_def name, &blk
11
+ meta_eval { define_method name, &blk }
12
+ end
13
+
14
+ # Defines an instance method within a class
15
+ def class_def name, &blk
16
+ class_eval { define_method name, &blk }
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ class Module
2
+ def attr_with_default(sym, default)
3
+ define_method "#{sym}=" do |obj|
4
+ instance_variable_set("@#{sym}", obj)
5
+ end
6
+
7
+ define_method sym do
8
+ instance_variable_get("@#{sym}") || default
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class Symbol
2
+ def to_proc
3
+ Proc.new { |*args| args.shift.__send__(self, *args) }
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ module Sinatra
2
+
3
+ class Dispatcher
4
+
5
+ cattr_accessor :logger
6
+
7
+ def default_headers
8
+ { 'Content-Type' => 'text/html' }
9
+ end
10
+
11
+ def call(env)
12
+ Loader.reload! if Options.environment == :development
13
+
14
+ @request = Rack::Request.new(env)
15
+
16
+ event = EventManager.determine_event(
17
+ @request.request_method.downcase.intern,
18
+ @request.path_info
19
+ )
20
+
21
+ result = event.attend(@request)
22
+ [result.status, default_headers.merge(result.headers), result.body]
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,163 @@
1
+
2
+ module Sinatra
3
+
4
+ module Dsl
5
+
6
+ # Define an Event that responds to a +path+ on GET method
7
+ #
8
+ # The +path+ can be a template (i.e. '/:foo/bar/:baz'). When recognized, it will add <tt>:foo</tt> and <tt>:baz</tt> to +params+ with their values.
9
+ #
10
+ # Example:
11
+ # # Going RESTful
12
+ #
13
+ # get '/' do
14
+ # .. show stuff ..
15
+ # end
16
+ #
17
+ # post '/' do
18
+ # .. add stuff ..
19
+ # redirect '/'
20
+ # end
21
+ #
22
+ # put '/:id' do
23
+ # .. update params[:id] ..
24
+ # redirect '/'
25
+ # end
26
+ #
27
+ # delete '/:id' do
28
+ # .. delete params[:id] ..
29
+ # redirect '/'
30
+ # end
31
+ #
32
+ # BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
33
+ #
34
+ def get(path, &block)
35
+ Sinatra::Event.new(:get, path, &block)
36
+ end
37
+
38
+ # Same as get but responds to POST
39
+ def post(path, &block)
40
+ Sinatra::Event.new(:post, path, &block)
41
+ end
42
+
43
+ # Same as get but responds to PUT
44
+ #
45
+ # BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
46
+ def put(path, &block)
47
+ Sinatra::Event.new(:put, path, &block)
48
+ end
49
+
50
+ # Same as get but responds to DELETE
51
+ #
52
+ # BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
53
+ def delete(path, &block)
54
+ Sinatra::Event.new(:delete, path, &block)
55
+ end
56
+
57
+ # Run given block after each Event's execution
58
+ # Example:
59
+ # after_attend do
60
+ # logger.debug "After event attend!"
61
+ # end
62
+ def after_attend(filter_name = nil, &block)
63
+ Sinatra::Event.after_attend(filter_name, &block)
64
+ end
65
+
66
+ # Add methods to each event for use during execution
67
+ #
68
+ # Example:
69
+ # helpers do
70
+ # def foo
71
+ # 'foo!'
72
+ # end
73
+ # end
74
+ #
75
+ # get '/bar' do
76
+ # foo
77
+ # end
78
+ #
79
+ # get_it '/bar' # => 'foo!'
80
+ #
81
+ def helpers(&block)
82
+ Sinatra::EventContext.class_eval(&block)
83
+ end
84
+
85
+ # Maps a path to a physical directory containing static files
86
+ #
87
+ # Example:
88
+ # static '/p', 'public'
89
+ #
90
+ def static(path, root)
91
+ Sinatra::StaticEvent.new(path, root)
92
+ end
93
+
94
+ # Execute block if in development mode (Used for configuration)
95
+ def development
96
+ yield if Sinatra::Options.environment == :development
97
+ end
98
+
99
+ # Execute block if in production mode (Used for configuration)
100
+ def production
101
+ yield if Sinatra::Options.environment == :production
102
+ end
103
+
104
+ # Execute block if in test mode (Used for configuration)
105
+ def test
106
+ yield if Sinatra::Options.environment == :test
107
+ end
108
+
109
+ # Define named layouts (default name is <tt>:layout</tt>)
110
+ #
111
+ # Examples:
112
+ # # Default layout in Erb
113
+ # layout do
114
+ # '-- <%= yield %> --'
115
+ # end
116
+ #
117
+ # # Named layout in Haml
118
+ # layout :for_haml do
119
+ # '== XXXX #{yield} XXXX'
120
+ # end
121
+ #
122
+ # # Loads layout named <tt>:"foo.erb"</tt> from file (default behaviour if block is omitted)
123
+ # layout 'foo.erb' # looks for foo.erb. This is odd an is being re-thought
124
+ #
125
+ # def layout(name = :layout, options = {})
126
+ # Layouts[name] = unless block_given?
127
+ # File.read("%s/%s" % [options[:views_directory] || 'views', name])
128
+ # else
129
+ # yield
130
+ # end
131
+ # end
132
+ #
133
+ # Cool trick:
134
+ #
135
+ # # Send a one-time layout to renderer method
136
+ # get '/cooltrick' do
137
+ # erb 'wicked' do
138
+ # 'Cool <%= yield %> Trick'
139
+ # end
140
+ # end
141
+ #
142
+ # get_it '/cooltrick' # => 'Cool wicked Trick'
143
+ #
144
+ def layout(name = :layout, options = {})
145
+ Layouts[name] = unless block_given?
146
+ File.read("%s/%s" % [options[:views_directory] || 'views', name])
147
+ else
148
+ yield
149
+ end
150
+ end
151
+
152
+ # Turn sessions <tt>:on</tt> or <tt>:off</tt>
153
+ #
154
+ # NOTE: There is currently no way to turn it on or off per Event... patches anyone?)
155
+ def sessions(on_off)
156
+ Sinatra::Session::Cookie.use = on_off
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+
163
+ include Sinatra::Dsl