renee-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # Renee Core
2
+
3
+ ## Routing
4
+
5
+ Routing in `Renee` is different from any web framework you are likely to have used in the past. The syntax is most familiar to Sinatra but allows
6
+ for far more flexibility and freedom in the way that routes and actions are defined. In a Renee, routes are defined using the `path`, `var`, `query_string`, `extension`, `remainder` and request methods.
7
+
8
+ **Request Methods**
9
+
10
+ The bread and butter of Renee are the request verbs reminiscent of Sinatra:
11
+
12
+ ```ruby
13
+ run Renee::Core.new {
14
+ get { halt "a get!" }
15
+ post { halt "a post!" }
16
+ put { halt "a put!" }
17
+ delete { halt "a delete!" }
18
+ }
19
+ ```
20
+
21
+ These will declare the response to "/" for each of the common request types. Notice the use of the request method to
22
+ specify the http verb and the use of `halt` inside the block to send back the body of the response.
23
+
24
+ **Path**
25
+
26
+ Path is how Renee describes the basic uri path for a route:
27
+
28
+ ```ruby
29
+ run Renee::Core.new {
30
+ path('blog') { ... }
31
+ }
32
+ ```
33
+
34
+ All declarations inside that block will start with `/blog`. Paths can also be nested within one another:
35
+
36
+ ```ruby
37
+ run Renee::Core.new {
38
+ path('blog') {
39
+ path('foo') { get { halt "path is /blog/foo" } }
40
+ }
41
+ }
42
+ ```
43
+
44
+ You can also use `exact_path` for more precise path matching and/or `part` which doesn't look for leading slashes.
45
+
46
+ **Query String**
47
+
48
+ In addition to defining paths, you may find yourself wanting to describe the state of the query string for a request within the path:
49
+
50
+ ```ruby
51
+ path 'foo' do
52
+ query_string 'bar' do
53
+ get { halt 'BAR!' }
54
+ end
55
+
56
+ query_string 'baz' do
57
+ get { halt 'BAZ!' }
58
+ end
59
+ end
60
+ ```
61
+
62
+ This will respond to `/foo?bar` with "BAR!" and `/foo?baz` with "BAZ!". You can also specify query_string in a variety of other ways:
63
+
64
+ ```ruby
65
+ # Check key and value of query param
66
+ query_string 'foo=bar' do
67
+ post { halt [200,{},'foo'] }
68
+ end
69
+
70
+ # Declare query params as a hash
71
+ query :foo => "bar" do
72
+ halt 200
73
+ end
74
+
75
+ # Switch based on a query parameter
76
+ query :foo do |var|
77
+ case var
78
+ when 'bar' then halt 200
79
+ when 'bar2' then halt 500
80
+ end
81
+ end
82
+ ```
83
+
84
+ **Variables**
85
+
86
+ In Renee, you specify parameters for your request as explicit variables. Variables are declared like this:
87
+
88
+ ```ruby
89
+ path('blog') {
90
+ var { |id| get { halt "path is /blog/#{id}" } }
91
+ }
92
+ ```
93
+
94
+ You can access the variables (passed into the request) using the local variables yielded to the block. Variables are a powerful
95
+ way to express expected parameters for a given set of requests. You can specify variables that match a regex:
96
+
97
+ ```ruby
98
+ path('blog') {
99
+ var(/\d+/) { |id| get { halt "path is /blog/#{id}" } }
100
+ }
101
+ ```
102
+
103
+ and even explicitly cast your variable types:
104
+
105
+ ```ruby
106
+ path('blog') {
107
+ var :type => Integer do |id|
108
+ get { halt "path is /blog/#{id} and id is an integer" }
109
+ end
110
+ end
111
+ ```
112
+
113
+ **Extensions**
114
+
115
+ You can also use `extension` as a way to define formats:
116
+
117
+ ```ruby
118
+ path '/test' do
119
+ extension 'html' do
120
+ halt 'html'
121
+ end
122
+ extension 'json' do
123
+ halt 'json'
124
+ end
125
+ end
126
+ ```
127
+
128
+ This will have `test.html` respond with 'html' and `test.json` respond with 'json'.
129
+
130
+ **Remainder**
131
+
132
+ In the event that no route has been matched, the `remainder` keyword makes defining the else case rather easy:
133
+
134
+ ```ruby
135
+ path 'foo' do
136
+ path 'bar' do
137
+ halt "BAR!"
138
+ end
139
+
140
+ remainder do |rest|
141
+ halt "Rest was #{rest}"
142
+ end
143
+ end
144
+ ```
145
+
146
+ Notice this allows you to handle the cases within a particular route scope and manage them based on the "rest" of the uri yielded in the `remainder` block. You
147
+ can handle different remainders in all the different path blocks.
148
+
149
+ **Named Routes**
150
+
151
+ Once you have defined your routes, you can then "register" a particular path mapping that to a symbol. This is useful for referencing routes without
152
+ having to specify the entire path:
153
+
154
+ ```ruby
155
+ run Renee::Core.new {
156
+ register(:test, '/test/time')
157
+ register(:test_var, '/test/:id')
158
+ }
159
+ ```
160
+
161
+ You can then access these using the `path` method in a route or template:
162
+
163
+ ```ruby
164
+ path(:test) # => '/test/time'
165
+ path(:test_var, :id => 123) # => '/test/123'
166
+ ```
167
+
168
+ Using named routes makes referencing and modifying routes within an application much simpler to manage.
169
+
170
+ ## Responding
171
+
172
+ Responding to a request within a route can be managed with the `respond`, `halt`, `redirect` commands:
173
+
174
+ **Respond**
175
+
176
+ The `respond` command makes returning a rack response very explicit, you can respond as if you were constructing a Rack::Response
177
+
178
+ ```ruby
179
+ run Renee {
180
+ get { respond!("hello!", 403, "foo" => "bar") }
181
+ }
182
+ ```
183
+
184
+ or use the block DSL for convenience:
185
+
186
+ ```ruby
187
+ run Renee {
188
+ get { respond! { status 403; headers :foo => "bar"; body "hello!" } }
189
+ }
190
+ ```
191
+
192
+ **Halt**
193
+
194
+ Halting is the easiest way to render data within a route:
195
+
196
+ ```ruby
197
+ run Renee::Core.new {
198
+ get { halt 'easy' }
199
+ }
200
+ ```
201
+
202
+ This will return a 200 status code and 'easy' as the body. You can also specify status code and header explicitly in the halt response:
203
+
204
+ ```ruby
205
+ get { halt [200, {}, 'body'] }
206
+ ```
207
+
208
+ This will set the status code to 200, pass no headers and return 'body'. You can also use several variations of halt:
209
+
210
+ ```ruby
211
+ # Return just status code
212
+ halt 200
213
+
214
+ # Return status with symbol
215
+ halt :not_found
216
+
217
+ # Return 200 with body
218
+ halt "hello!"
219
+
220
+ # Return 500 with body
221
+ halt 500, "hello!"
222
+ ```
223
+
224
+ Halt is the most straightforward way to control the response for a request.
225
+
226
+ **Redirect**
227
+
228
+ A redirect is a common action within a web route and can be achieved with the convenience method `redirect` command:
229
+
230
+ ```ruby
231
+ get {
232
+ halt redirect('/hello')
233
+ }
234
+ ```
235
+
236
+ You can also specify the status code for the redirect:
237
+
238
+ ```ruby
239
+ get {
240
+ halt redirect('/hello', 303)
241
+ }
242
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push "lib"
6
+ t.test_files = FileList[File.expand_path('../test/**/*_test.rb', __FILE__)]
7
+ t.verbose = true
8
+ end
data/ideal.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'benchmark'
3
+
4
+ $: << 'lib'
5
+ require 'renee'
6
+
7
+ router = Renee::Core.new {
8
+ path 'test/time' do
9
+ query_string 'ok' do
10
+ get { halt "ok" }
11
+ post { halt [200, {}, ['POSTED!']] }
12
+ end
13
+ end
14
+ variable do |id1, id2|
15
+ path 'more' do
16
+ get {
17
+ halt [200, {}, "this is the id1: #{id1} id2: #{id2}" ]
18
+ }
19
+ end
20
+ end
21
+ remainder do |rest|
22
+ halt "the rest is #{rest}"
23
+ end
24
+ }.setup {
25
+ view_path('views')
26
+ environment(:development)
27
+ }
28
+
29
+ app = Renee do
30
+ path "add" do
31
+ variable Integer do |first, second|
32
+ "#{first} + #{second} = #{first + second}"
33
+ end
34
+ end
35
+ end
36
+
37
+ p router.call(Rack::MockRequest.env_for('/add/3/4')) # => "3 + 4 = 7"
38
+
39
+ p router.call(Rack::MockRequest.env_for('/test/time?ok'))
40
+ p router.call(Rack::MockRequest.env_for('/test/josh/more'))
41
+ p router.call(Rack::MockRequest.env_for('/'))
42
+
43
+
44
+ #puts Benchmark.measure {
45
+ #50_000.times do
46
+ # router.call(Rack::MockRequest.env_for('/test/josh/more'))
47
+ #router.call(Rack::MockRequest.env_for('/test/time?ok', :method => 'POST' ))
48
+ #end
49
+ #}
@@ -0,0 +1,39 @@
1
+ class Renee
2
+ class Core
3
+ class Application
4
+ # A module that defines useful Rack interaction methods.
5
+ module RackInteraction
6
+
7
+ # Creates an ad-hoc Rack application within the context of a Rack::Builder.
8
+ # @example
9
+ # get { halt build { use Rack::ContentLength; run proc { |env| Rack::Response.new("Hello!").finish } } }
10
+ #
11
+ def build(&blk)
12
+ run Rack::Builder.new(&blk).to_app
13
+ end
14
+
15
+ def build!(&blk)
16
+ run! build(&blk)
17
+ end
18
+
19
+ # Runs a rack application
20
+ # @example
21
+ # get { halt run proc { |env| Renee::Core::Response.new("Hello!").finish } }
22
+ #
23
+ def run(app = nil, &blk)
24
+ (app || blk).call(env)
25
+ end
26
+
27
+ # Runs a rack application and responds immediately.
28
+ #
29
+ # @see #run
30
+ # @example
31
+ # get { run proc { |env| Renee::Core::Response.new("Hello!").finish } }
32
+ #
33
+ def run!(*args)
34
+ halt! run(*args)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ class Renee
2
+ class Core
3
+ class Application
4
+ # This module deals with the Rack#call compilance. It defines #call and also defines several critical methods
5
+ # used by interaction by other application modules.
6
+ module RequestContext
7
+ attr_reader :env, :request, :detected_extension, :is_index_request
8
+ alias_method :is_index_request?, :is_index_request
9
+
10
+ # Provides a rack interface compliant call method.
11
+ # @param[Hash] env The rack environment.
12
+ def call(env)
13
+ @env, @request = env, Rack::Request.new(env)
14
+ @detected_extension = env['PATH_INFO'][/\.([^\.\/]+)$/, 1]
15
+ @is_index_request = env['PATH_INFO'][/^\/?$/]
16
+ # TODO clear template cache in development? `template_cache.clear`
17
+ catch(:halt) { instance_eval(&application_block); Renee::Core::Response.new("Not found", 404).finish }
18
+ end # call
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,122 @@
1
+ class Renee
2
+ class Core
3
+ class Application
4
+ # Collection of useful methods for responding within a {Renee::Core} app.
5
+ module Responding
6
+
7
+ # Codes used by Symbol lookup in interpret_response.
8
+ # @example
9
+ # halt! :unauthorized # would return a 401.
10
+ #
11
+ HTTP_CODES = {
12
+ :ok => 200,
13
+ :created => 201,
14
+ :accepted => 202,
15
+ :no_content => 204,
16
+ :no_content => 204,
17
+ :bad_request => 400,
18
+ :unauthorized => 401,
19
+ :payment_required => 403,
20
+ :not_found => 404,
21
+ :method_not_found => 405,
22
+ :not_acceptable => 406,
23
+ :gone => 410,
24
+ :error => 500,
25
+ :not_implemented => 501}.freeze
26
+
27
+ # Halts current processing to the top-level calling Renee application and uses that as a response. This method requies that
28
+ # the PATH_INFO be completely consumed.
29
+ # @param [Object...] response The response to use.
30
+ # @see #interpret_response
31
+ def halt(*response)
32
+ raise "PATH_INFO hasn't been entirely consumed, you still have #{env['PATH_INFO'].inspect} left. Try putting a #remainder block around it. " if env['PATH_INFO'] != ''
33
+ halt! *response
34
+ end
35
+
36
+ # Halts current processing to the top-level calling Renee application and uses that as a response. Unlike #halt, this does
37
+ # not require the path to be consumed.
38
+ # @param [Object...] response The response to use.
39
+ # @see #interpret_response
40
+ def halt!(*response)
41
+ throw :halt, interpret_response(response.size == 1 ? response.first : response)
42
+ end
43
+
44
+ ##
45
+ # Creates a response by allowing the response header, body and status to be passed into the block.
46
+ #
47
+ # @param [Array] body The contents to return.
48
+ # @param [Integer] status The status code to return.
49
+ # @param [Hash] header The headers to return.
50
+ # @param [Proc] &blk The response options to specify
51
+ #
52
+ # @example
53
+ # respond { status 200; body "Yay!" }
54
+ # respond("Hello", 200, "foo" => "bar")
55
+ #
56
+ def respond(body=[], status=200, header={}, &blk)
57
+ Renee::Core::Response.new(body, status, header).tap { |r| r.instance_eval(&blk) if block_given? }.finish
58
+ end
59
+
60
+ ##
61
+ # Creates a response by allowing the response header, body and status to be passed into the block.
62
+ #
63
+ # @example
64
+ # respond! { status 200; body "Yay!" }
65
+ #
66
+ # @param (see #respond)
67
+ # @see #respond
68
+ def respond!(*args, &blk)
69
+ halt respond(*args, &blk)
70
+ end
71
+
72
+ # Interprets responses returns by #halt.
73
+ #
74
+ # * If it is a Symbol, it will be looked up in {HTTP_CODES}.
75
+ # * If it is a Symbol, it will use Rack::Response to return the value.
76
+ # * If it is a Symbol, it will either be used as a Rack response or as a body and status code.
77
+ # * If it is an Integer, it will use Rack::Response to return the status code.
78
+ # * Otherwise, #to_s will be called on it and it will be treated as a Symbol.
79
+ #
80
+ # @param [Object] response This can be either a Symbol, String, Array or any Object.
81
+ #
82
+ def interpret_response(response)
83
+ case response
84
+ when Array then
85
+ case response.size
86
+ when 3 then response
87
+ when 2 then Renee::Core::Response.new(response[1], HTTP_CODES[response[0]] || response[0]).finish
88
+ else raise "I don't know how to render #{response.inspect}"
89
+ end
90
+ when String then Renee::Core::Response.new(response).finish
91
+ when Integer then Renee::Core::Response.new("Status code #{response}", response).finish
92
+ when Symbol then interpret_response(HTTP_CODES[response] || response.to_s)
93
+ else interpret_response(response.to_s)
94
+ end
95
+ end
96
+
97
+ # Returns a rack-based response for redirection.
98
+ # @param [String] path The URL to redirect to.
99
+ # @param [Integer] code The HTTP code to use.
100
+ # @example
101
+ # r = Renee::Core.new { get { halt redirect '/index' } }
102
+ # r.call(Rack::MockResponse("/")) # => [302, {"Location" => "/index"}, []]
103
+ def redirect(path, code = 302)
104
+ response = ::Rack::Response.new
105
+ response.redirect(path, code)
106
+ response.finish
107
+ end
108
+
109
+ # Halts with a rack-based response for redirection.
110
+ # @see #redirect
111
+ # @param [String] path The URL to redirect to.
112
+ # @param [Integer] code The HTTP code to use.
113
+ # @example
114
+ # r = Renee::Core.new { get { redirect! '/index' } }
115
+ # r.call(Rack::MockResponse("/")) # => [302, {"Location" => "/index"}, []]
116
+ def redirect!(path, code = 302)
117
+ halt redirect(path, code)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end