happy 0.1.0.pre16 → 0.1.0.pre19
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/README.md +1 -2
- data/happy.gemspec +1 -1
- data/lib/happy/context/helpers.rb +2 -1
- data/lib/happy/context.rb +67 -20
- data/lib/happy/controller/actions.rb +40 -33
- data/lib/happy/controller/configurable.rb +21 -0
- data/lib/happy/controller/rackable.rb +12 -5
- data/lib/happy/controller/routing.rb +1 -1
- data/lib/happy/controller.rb +21 -13
- data/lib/happy/errors.rb +64 -0
- data/lib/happy/extras/code_reloader.rb +26 -0
- data/lib/happy/files/error.erb +131 -0
- data/lib/happy/request.rb +4 -2
- data/lib/happy/version.rb +1 -1
- data/lib/happy.rb +4 -5
- data/spec/controller/actions_spec.rb +29 -0
- data/spec/controller_spec.rb +0 -19
- metadata +27 -23
data/README.md
CHANGED
@@ -25,7 +25,7 @@ run Happy
|
|
25
25
|
|
26
26
|
How about something a little bit closer to reality?
|
27
27
|
|
28
|
-
```
|
28
|
+
```ruby
|
29
29
|
# config.ru
|
30
30
|
require 'happy'
|
31
31
|
|
@@ -112,7 +112,6 @@ Happy is being extracted from a web application that I've been working on. I'm t
|
|
112
112
|
|
113
113
|
FWIW, here's a list of important things still missing right now:
|
114
114
|
|
115
|
-
* Nicer error pages for 404s, 401s etc.
|
116
115
|
* Better logging.
|
117
116
|
* Improved view engine compatibility.
|
118
117
|
|
data/happy.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
|
18
18
|
gem.add_dependency 'activesupport', '~> 3.1'
|
19
19
|
gem.add_dependency 'rack', '~> 1.4'
|
20
|
-
gem.add_dependency 'happy-helpers', '~> 0.1.0.
|
20
|
+
gem.add_dependency 'happy-helpers', '~> 0.1.0.pre10'
|
21
21
|
gem.add_dependency 'allowance', '>= 0.1.1'
|
22
22
|
|
23
23
|
gem.add_dependency 'happy-cli', '>= 0.1.0.pre1'
|
@@ -15,7 +15,8 @@ module Happy
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def render_template(name, variables = {}, &blk)
|
18
|
-
|
18
|
+
path = @controller ? @controller.config[:views] : './views'
|
19
|
+
HappyHelpers::Templates.render(File.join(path, name), self, variables, &blk)
|
19
20
|
end
|
20
21
|
|
21
22
|
def render_resource(resource, options = {})
|
data/lib/happy/context.rb
CHANGED
@@ -2,40 +2,87 @@ require 'happy/request'
|
|
2
2
|
require 'happy/context/helpers'
|
3
3
|
|
4
4
|
module Happy
|
5
|
+
# Represents the current request and its respective application state.
|
6
|
+
# Not only does this class wrap around both the incoming {#request} and the
|
7
|
+
# generated {#response}, but it is also used as the scope for all templates
|
8
|
+
# rendered through {#render_template}.
|
9
|
+
#
|
10
|
+
# (In case you're wondering, a particular request's instance of #{Context} is
|
11
|
+
# created from the Rack environment by #{Controller#context} when first accessed.)
|
12
|
+
#
|
13
|
+
# == View Helpers
|
14
|
+
#
|
15
|
+
# If you're coming from other web frameworks and looking for the right place
|
16
|
+
# to add "view helpers", this is it, since all templates being rendered use
|
17
|
+
# the request's instance of {Context} as their scope.
|
18
|
+
#
|
19
|
+
# The most convenient way of extending this class is through #{Happy.context}.
|
20
|
+
#
|
21
|
+
# Happy.context do
|
22
|
+
# def some_helper
|
23
|
+
# "I'm a view helper!"
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# In addition to view helpers, the context is the place to add methods
|
28
|
+
# dealing with the current request scope, eg. methods like 'current_user'.
|
29
|
+
#
|
5
30
|
class Context
|
6
31
|
include Helpers
|
7
32
|
|
8
|
-
|
9
|
-
|
10
|
-
|
33
|
+
# Instance of {Happy::Request} representing the current HTTP request.
|
34
|
+
attr_reader :request
|
35
|
+
|
36
|
+
# The Rack::Response instance being used to compose the response.
|
37
|
+
attr_accessor :response
|
38
|
+
|
39
|
+
# The current layout template to be used when rendering the response.
|
40
|
+
attr_accessor :layout
|
41
|
+
|
42
|
+
# Array containing path parts that are yet to be handled.
|
43
|
+
attr_reader :remaining_path
|
44
|
+
|
45
|
+
# Array of path parts that have been handled so far.
|
46
|
+
attr_reader :previous_path
|
47
|
+
|
48
|
+
delegate :params, :session, :to => :request
|
49
|
+
|
50
|
+
# Initializes a new {Context} instance from a Rack environment hash.
|
51
|
+
#
|
52
|
+
# @param [Hash] env Rack environment hash
|
53
|
+
#
|
54
|
+
def initialize(env)
|
55
|
+
@request = Happy::Request.new(env)
|
56
|
+
@response = Rack::Response.new
|
11
57
|
|
12
|
-
def initialize(request, response)
|
13
|
-
@request = request
|
14
|
-
@response = response
|
15
58
|
@remaining_path = @request.path.split('/').reject {|s| s.blank? }
|
16
|
-
@previous_path
|
17
|
-
@layout
|
18
|
-
@controller
|
59
|
+
@previous_path = []
|
60
|
+
@layout = nil
|
61
|
+
@controller = nil
|
19
62
|
end
|
20
63
|
|
64
|
+
# @note
|
65
|
+
# This method is mostly used internally by Happy. You will not need
|
66
|
+
# to call it from your own controllers or applications.
|
67
|
+
#
|
68
|
+
# Execute the provided block, but register the provided {Controller}
|
69
|
+
# instance as the controller currently handling the request. Call this
|
70
|
+
# whenever you're passing control from one controller to another.
|
71
|
+
#
|
72
|
+
# @param [Controller] new_controller The {Controller} instance to set as the current controller
|
73
|
+
# @return Results of provided block.
|
74
|
+
#
|
21
75
|
def with_controller(new_controller)
|
22
76
|
# remember previous controller
|
23
|
-
old_controller =
|
24
|
-
|
77
|
+
old_controller = @controller
|
78
|
+
@controller = new_controller
|
25
79
|
|
26
80
|
# execute block
|
27
|
-
yield
|
81
|
+
yield if block_given?
|
28
82
|
ensure
|
29
83
|
# switch back to previous controller
|
30
|
-
|
84
|
+
@controller = old_controller
|
31
85
|
end
|
32
86
|
|
33
|
-
private
|
34
|
-
|
35
|
-
class << self
|
36
|
-
def from_env(env)
|
37
|
-
new(Happy::Request.new(env), Rack::Response.new)
|
38
|
-
end
|
39
|
-
end
|
40
87
|
end
|
41
88
|
end
|
@@ -2,51 +2,47 @@ module Happy
|
|
2
2
|
class Controller
|
3
3
|
module Actions
|
4
4
|
def serve!(data, options = {})
|
5
|
-
|
6
|
-
|
5
|
+
only_if_path_matches do
|
6
|
+
# Don't serve is data is not a string.
|
7
|
+
return unless data.is_a?(String)
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
# Mix in default options
|
10
|
+
options = {
|
11
|
+
:layout => context.layout
|
12
|
+
}.merge(options)
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
:layout => context.layout
|
14
|
-
}.merge(options)
|
14
|
+
# Add status code from options
|
15
|
+
response.status = options.delete(:status) if options.has_key?(:status)
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
# Extract layout
|
18
|
+
layout = options.delete(:layout)
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
# Treat remaining options as headers
|
21
|
+
options.each { |k, v| header k, v }
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
# Apply layout, if available
|
24
|
+
if layout
|
25
|
+
data = render(layout) { data }
|
26
|
+
end
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
# Set response body and finish request
|
29
|
+
response.body = [data]
|
30
|
+
halt!
|
28
31
|
end
|
29
|
-
|
30
|
-
# Set response body and finish request
|
31
|
-
response.body = [data]
|
32
|
-
halt!
|
33
|
-
end
|
34
|
-
|
35
|
-
def serve_or_404!(*args)
|
36
|
-
serve!(*args)
|
37
|
-
|
38
|
-
# If we get here, #serve! decided not to serve, so let's raise a 404.
|
39
|
-
raise Errors::NotFound
|
40
32
|
end
|
41
33
|
|
42
34
|
def halt!(message = :done)
|
43
|
-
|
35
|
+
only_if_path_matches do
|
36
|
+
throw message
|
37
|
+
end
|
44
38
|
end
|
45
39
|
|
46
40
|
def redirect!(to, status = 302)
|
47
|
-
|
48
|
-
|
49
|
-
|
41
|
+
only_if_path_matches do
|
42
|
+
header :location, url_for(to)
|
43
|
+
response.status = status
|
44
|
+
halt!
|
45
|
+
end
|
50
46
|
end
|
51
47
|
|
52
48
|
def layout(name)
|
@@ -87,13 +83,24 @@ module Happy
|
|
87
83
|
elsif thing.respond_to?(:call)
|
88
84
|
# Rack apps!
|
89
85
|
context.response = thing.call(request.env)
|
90
|
-
|
86
|
+
throw :done
|
91
87
|
elsif thing.respond_to?(:to_s)
|
92
88
|
thing.to_s
|
93
89
|
else
|
94
90
|
raise "Don't know how to run #{thing.inspect} :("
|
95
91
|
end
|
96
92
|
end
|
93
|
+
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Execute the provided block, unless there are still bits of
|
98
|
+
# unprocessed path left (which indicates that the current path
|
99
|
+
# is not the path the user requested.)
|
100
|
+
#
|
101
|
+
def only_if_path_matches
|
102
|
+
yield if remaining_path.empty?
|
103
|
+
end
|
97
104
|
end
|
98
105
|
end
|
99
106
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Happy
|
2
|
+
class Controller
|
3
|
+
module Configurable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def config
|
7
|
+
self.class.config
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def config
|
12
|
+
@config ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def set(k, v)
|
16
|
+
config[k.to_sym] = v
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,19 +3,26 @@ module Happy
|
|
3
3
|
module Rackable
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
def
|
7
|
-
@env = env
|
8
|
-
|
6
|
+
def handle_request
|
9
7
|
catch :done do
|
10
|
-
|
8
|
+
serve!(perform) or raise Errors::NotFound
|
11
9
|
end
|
12
10
|
|
13
11
|
response
|
12
|
+
rescue Errors::NotFound => e
|
13
|
+
html = Errors.html e, env,
|
14
|
+
:title => "Path not found",
|
15
|
+
:message => '',
|
16
|
+
:friendly_message => "You performed a <strong>#{context.request.request_method}</strong> request on <strong>#{context.request.path}</strong>, but your application did not know how to handle this request."
|
17
|
+
[404, {'Content-type' => 'text/html'}, [html]]
|
18
|
+
rescue ::Exception => e
|
19
|
+
html = Errors.html e, env
|
20
|
+
[500, {'Content-type' => 'text/html'}, [html]]
|
14
21
|
end
|
15
22
|
|
16
23
|
module ClassMethods
|
17
24
|
def call(env)
|
18
|
-
new
|
25
|
+
new(env).handle_request
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
data/lib/happy/controller.rb
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
require 'happy/controller/routing'
|
2
2
|
require 'happy/controller/actions'
|
3
3
|
require 'happy/controller/rackable'
|
4
|
+
require 'happy/controller/configurable'
|
4
5
|
|
5
6
|
module Happy
|
7
|
+
# Base class for Happy controllers. A controller's primary job is to act
|
8
|
+
# upon an incoming request, navigating the request URL's path, and finally
|
9
|
+
# deciding on a course of action (eg. rendering something, redirecting the
|
10
|
+
# client, passing control over to another controller, and so on.)
|
11
|
+
#
|
6
12
|
class Controller
|
7
13
|
include Routing
|
8
14
|
include Actions
|
9
15
|
include Rackable
|
16
|
+
include Configurable
|
10
17
|
|
11
18
|
attr_reader :options, :env, :root_path
|
12
19
|
|
@@ -15,6 +22,15 @@ module Happy
|
|
15
22
|
:render, :url_for,
|
16
23
|
:to => :context
|
17
24
|
|
25
|
+
# Creates a new instance of {Controller}. When a block is provided,
|
26
|
+
# it is run against the new instance, allowing custom controller classes
|
27
|
+
# to provide DSL-like configuration.
|
28
|
+
#
|
29
|
+
# @param env [Hash]
|
30
|
+
# Rack environment hash.
|
31
|
+
# @param options [Hash]
|
32
|
+
# Controller options.
|
33
|
+
#
|
18
34
|
def initialize(env = {}, options = {}, &blk)
|
19
35
|
@env = env
|
20
36
|
@options = options
|
@@ -27,14 +43,16 @@ module Happy
|
|
27
43
|
instance_exec(&blk) if blk
|
28
44
|
end
|
29
45
|
|
46
|
+
protected
|
47
|
+
|
48
|
+
# Run this controller, performing its routing logic.
|
49
|
+
#
|
30
50
|
def perform
|
31
51
|
context.with_controller(self) do
|
32
52
|
route
|
33
53
|
end
|
34
54
|
end
|
35
55
|
|
36
|
-
protected
|
37
|
-
|
38
56
|
def root_url(extras = nil)
|
39
57
|
url_for(root_path, extras)
|
40
58
|
end
|
@@ -46,22 +64,12 @@ module Happy
|
|
46
64
|
end
|
47
65
|
|
48
66
|
def context
|
49
|
-
@env['happy.context'] ||= Happy::Context.
|
67
|
+
@env['happy.context'] ||= Happy::Context.new(@env)
|
50
68
|
end
|
51
69
|
|
52
70
|
def route
|
53
71
|
# override this in subclasses
|
54
72
|
end
|
55
73
|
|
56
|
-
class << self
|
57
|
-
# Create a new subclass of Happy::Controller, using the provided
|
58
|
-
# block for defining class methods et al.
|
59
|
-
#
|
60
|
-
def build(&blk)
|
61
|
-
Class.new(self).tap do |klass|
|
62
|
-
klass.class_eval(&blk) if blk
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
74
|
end
|
67
75
|
end
|
data/lib/happy/errors.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Happy
|
2
|
+
module Errors
|
3
|
+
class Base < StandardError ; end
|
4
|
+
class NotFound < Base ; end
|
5
|
+
|
6
|
+
# Render a HTML page for the given exception.
|
7
|
+
#
|
8
|
+
# @param [Exception] exception
|
9
|
+
# The exception to display.
|
10
|
+
# @param [Hash] env
|
11
|
+
# The current Rack environment hash (used to display information on request parameters, session contents and such.)
|
12
|
+
#
|
13
|
+
# @option options [String] :title
|
14
|
+
# Title of error page
|
15
|
+
# @option options [String] :message
|
16
|
+
# Message to be displayed on error page, right underneath the title.
|
17
|
+
# @option options [String] :friendly_message
|
18
|
+
# Friendly error message to be displayed below title and message.
|
19
|
+
# If left blank, will be generated from the exception message.
|
20
|
+
#
|
21
|
+
def self.html(exception, env, options = {})
|
22
|
+
options = {
|
23
|
+
:title => exception.class.to_s,
|
24
|
+
:message => exception.message,
|
25
|
+
:friendly_message => nil
|
26
|
+
}.merge(options)
|
27
|
+
|
28
|
+
context = env['happy.context']
|
29
|
+
|
30
|
+
# Load and cache error template.
|
31
|
+
@html = begin
|
32
|
+
File.read(File.expand_path(File.join(__FILE__, '../files/error.erb')))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Generate friendly message from exception.
|
36
|
+
options[:friendly_message] ||= friendly_message_for options[:message]
|
37
|
+
|
38
|
+
# Render error page.
|
39
|
+
ERB.new(@html).result(binding)
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def self.friendly_message_for(msg)
|
45
|
+
case msg
|
46
|
+
when /^undefined local variable or method `(.+)'/
|
47
|
+
"You called a method called \"#{$1}\", and this method did not exist. This could simply be a typo. If it's not, please check that you're calling the method from within the correct scope."
|
48
|
+
when /^undefined method `(.+)' for nil:NilClass$/
|
49
|
+
"You called a method called <strong>\"#{$1}\"</strong> on <strong>nil</strong>. In most cases, this is due to simple typos; please check your variable names. Otherwise, make sure the receiving object has been initialized correctly."
|
50
|
+
when /^undefined method `(.+)' for (.+)$/
|
51
|
+
method = $1
|
52
|
+
var = $2
|
53
|
+
klass = case var
|
54
|
+
when /^#<(.+):0x.+>$/ then $1
|
55
|
+
when /^.+:(.+)$/ then $1
|
56
|
+
else var
|
57
|
+
end
|
58
|
+
"You called a method called <strong>\"#{h method}\"</strong> on an instance of class <strong>#{h klass}</strong>, which doesn't know how to respond to that method. Please check for typos."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.h(t) ; Rack::Utils.escape_html(t) ; end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Happy
|
2
|
+
module Extras
|
3
|
+
class CodeReloader < Happy::Controller
|
4
|
+
class << self
|
5
|
+
def reload_app_code
|
6
|
+
Dir[config[:directory]].each do |f|
|
7
|
+
load f
|
8
|
+
end
|
9
|
+
@app_code_loaded = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def reload_app_code?
|
13
|
+
Happy.env.development? || !@app_code_loaded
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def route
|
18
|
+
raise "no directory specified" unless config[:directory]
|
19
|
+
raise "no controller specified" unless config[:controller]
|
20
|
+
|
21
|
+
self.class.reload_app_code if self.class.reload_app_code?
|
22
|
+
run eval(config[:controller])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<style>
|
4
|
+
body {
|
5
|
+
font: 18px/24px "Helvetica Neue",Arial,Helvetica,sans-serif;
|
6
|
+
color: #555;
|
7
|
+
background-color: #fffef8;
|
8
|
+
padding: 30px 0 100px 0;
|
9
|
+
}
|
10
|
+
.container {
|
11
|
+
width: 800px;
|
12
|
+
margin: 0 auto;
|
13
|
+
}
|
14
|
+
h1, h2, h3 {
|
15
|
+
color: #222;
|
16
|
+
}
|
17
|
+
h1 {
|
18
|
+
margin: 0 0 0px -4px;
|
19
|
+
font-size: 80px;
|
20
|
+
line-height: 80px;
|
21
|
+
letter-spacing: -0.05em;
|
22
|
+
text-shadow: 2px 1px 4px rgba(0, 0, 0, 0.2);
|
23
|
+
}
|
24
|
+
h2 {
|
25
|
+
margin-bottom: 0;
|
26
|
+
margin-top: 2em;
|
27
|
+
text-shadow: 2px 1px 4px rgba(0, 0, 0, 0.1);
|
28
|
+
}
|
29
|
+
h3 {
|
30
|
+
margin-bottom: 0;
|
31
|
+
color: #999;
|
32
|
+
}
|
33
|
+
p.message {
|
34
|
+
margin-top: 0px !important;
|
35
|
+
font-size: 30px;
|
36
|
+
line-height: 40px;
|
37
|
+
margin: 20px 0;
|
38
|
+
}
|
39
|
+
.friendly_message {
|
40
|
+
font-size: 20px;
|
41
|
+
line-height: 30px;
|
42
|
+
color: #777;
|
43
|
+
margin: 20px 0;
|
44
|
+
}
|
45
|
+
code {
|
46
|
+
font-family: Menlo,Monaco,"Lucida Console",Consolas,"Courier New",monotype;
|
47
|
+
}
|
48
|
+
.output {
|
49
|
+
display: block;
|
50
|
+
width: 820px;
|
51
|
+
margin-left: -20px;
|
52
|
+
|
53
|
+
background-color: #222;
|
54
|
+
color: #ccc;
|
55
|
+
padding: 20px;
|
56
|
+
border-radius: 15px;
|
57
|
+
margin-top: 1em;
|
58
|
+
|
59
|
+
font: 14px/20px Menlo,Monaco,"Lucida Console",Consolas,"Courier New",monotype;
|
60
|
+
}
|
61
|
+
.backtrace {
|
62
|
+
list-style: none;
|
63
|
+
height: 192px;
|
64
|
+
overflow: auto;
|
65
|
+
}
|
66
|
+
.backtrace li.app { color: #fff; }
|
67
|
+
.backtrace li.ruby { color: #c55; }
|
68
|
+
.backtrace li.happy { color: #5c5; }
|
69
|
+
.backtrace .where { font-weight: bold; }
|
70
|
+
.backtrace .location { color: #666; }
|
71
|
+
</style>
|
72
|
+
</head>
|
73
|
+
<body>
|
74
|
+
<div class="container">
|
75
|
+
<h1><%= h options[:title] %></h1>
|
76
|
+
|
77
|
+
<p class="message">
|
78
|
+
<%= h options[:message] %>
|
79
|
+
</p>
|
80
|
+
|
81
|
+
<% if options[:friendly_message] %>
|
82
|
+
<p class="friendly_message">
|
83
|
+
<%= options[:friendly_message] %>
|
84
|
+
</p>
|
85
|
+
<% end %>
|
86
|
+
|
87
|
+
<h2>Backtrace:</h2>
|
88
|
+
<ul class="output backtrace">
|
89
|
+
<% exception.backtrace.each do |l| %>
|
90
|
+
<%
|
91
|
+
file, line, where = l.split(':')
|
92
|
+
|
93
|
+
klass = case file
|
94
|
+
when %r{happy/lib/happy} then 'happy'
|
95
|
+
when %r{lib/ruby/gems} then 'ruby'
|
96
|
+
else 'app'
|
97
|
+
end
|
98
|
+
%>
|
99
|
+
<% where =~ /^in `(.+)'$/ %>
|
100
|
+
<li class="<%= klass %>">
|
101
|
+
<span class="where">
|
102
|
+
<%= h ($1 || where) %>
|
103
|
+
</span>
|
104
|
+
<span class="location">
|
105
|
+
@ <%= h file %>:<%= h line %>
|
106
|
+
</span>
|
107
|
+
</li>
|
108
|
+
<% end %>
|
109
|
+
</ul>
|
110
|
+
|
111
|
+
<h2>Request Parameters:</h2>
|
112
|
+
<div class="output params">
|
113
|
+
<%= h context.request.params.inspect %>
|
114
|
+
</div>
|
115
|
+
|
116
|
+
<h2>Session:</h2>
|
117
|
+
<div class="output session">
|
118
|
+
<%= h context.request.session.inspect %>
|
119
|
+
</div>
|
120
|
+
|
121
|
+
<h2>Environment:</h2>
|
122
|
+
<div class="output env">
|
123
|
+
<%= h env %>
|
124
|
+
</div>
|
125
|
+
|
126
|
+
<p>
|
127
|
+
If you need help, please drop by <code>#happy</code> on FreeNode.
|
128
|
+
</p>
|
129
|
+
</div>
|
130
|
+
</body>
|
131
|
+
</html>
|
data/lib/happy/request.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# SMELL: really?
|
2
2
|
require 'happy-helpers/utils/date_parameter_converter'
|
3
3
|
|
4
|
-
# SMELL: do we really need our own request class?
|
5
|
-
|
6
4
|
module Happy
|
5
|
+
# Happy's own little request class. It extends {Rack::Request} with
|
6
|
+
# a bit of convenience functionality.
|
7
|
+
#
|
7
8
|
class Request < Rack::Request
|
9
|
+
|
8
10
|
protected
|
9
11
|
|
10
12
|
def parse_query(qs)
|
data/lib/happy/version.rb
CHANGED
data/lib/happy.rb
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'rack'
|
2
2
|
require 'happy/version'
|
3
|
+
require 'happy/errors'
|
3
4
|
require 'happy/context'
|
4
5
|
require 'happy/controller'
|
5
6
|
|
6
7
|
module Happy
|
7
|
-
module Errors
|
8
|
-
class Base < StandardError ; end
|
9
|
-
class NotFound < Base ; end
|
10
|
-
end
|
11
|
-
|
12
8
|
def self.env
|
13
9
|
ActiveSupport::StringInquirer.new(ENV['RACK_ENV'] || 'development')
|
14
10
|
end
|
@@ -33,3 +29,6 @@ module Happy
|
|
33
29
|
@last_controller_class_created.try(:call, env) or raise "Please use Happy.route to define some routes."
|
34
30
|
end
|
35
31
|
end
|
32
|
+
|
33
|
+
__END__
|
34
|
+
yooooooo!
|
@@ -40,6 +40,19 @@ module Happy
|
|
40
40
|
it "finishes the rendering by throwing :done" do
|
41
41
|
expect { subject.serve! "body" }.to throw_symbol :done
|
42
42
|
end
|
43
|
+
|
44
|
+
it "doesn't do anything if the current path does not match the request path" do
|
45
|
+
def app
|
46
|
+
Happy.route do
|
47
|
+
serve! "This should not render"
|
48
|
+
path 'test' do
|
49
|
+
serve! "But this should render"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
response_for { get '/test' }.body.should == "But this should render"
|
55
|
+
end
|
43
56
|
end
|
44
57
|
|
45
58
|
describe '#redirect!' do
|
@@ -61,6 +74,22 @@ module Happy
|
|
61
74
|
get '/'
|
62
75
|
last_response.status.should == 301
|
63
76
|
end
|
77
|
+
|
78
|
+
it "doesn't do anything if the current path does not match the request path" do
|
79
|
+
def app
|
80
|
+
Happy.route do
|
81
|
+
redirect! "http://mans.de"
|
82
|
+
|
83
|
+
path 'test' do
|
84
|
+
redirect! "http://schnitzelpress.org"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
get '/test'
|
90
|
+
last_response.should be_redirect
|
91
|
+
last_response.headers['Location'].should == 'http://schnitzelpress.org'
|
92
|
+
end
|
64
93
|
end
|
65
94
|
|
66
95
|
describe '#run' do
|
data/spec/controller_spec.rb
CHANGED
@@ -18,25 +18,6 @@ module Happy
|
|
18
18
|
last_response.body.should == 'it works'
|
19
19
|
end
|
20
20
|
|
21
|
-
describe ".build" do
|
22
|
-
subject do
|
23
|
-
Controller.build do
|
24
|
-
def route
|
25
|
-
serve! "yay!"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
it "creates a new controller class" do
|
31
|
-
subject.ancestors.should include(Controller)
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should use the passed block to initialize the new controller class" do
|
35
|
-
get "/"
|
36
|
-
last_response.body.should == 'yay!'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
21
|
describe '#url' do
|
41
22
|
it "returns the current URL" do
|
42
23
|
def app
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: happy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre19
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-
|
12
|
+
date: 2012-06-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &70302468087940 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.1'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70302468087940
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rack
|
27
|
-
requirement: &
|
27
|
+
requirement: &70302468086760 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,21 +32,21 @@ dependencies:
|
|
32
32
|
version: '1.4'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70302468086760
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: happy-helpers
|
38
|
-
requirement: &
|
38
|
+
requirement: &70302468085720 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.1.0.
|
43
|
+
version: 0.1.0.pre10
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70302468085720
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: allowance
|
49
|
-
requirement: &
|
49
|
+
requirement: &70302468084660 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.1.1
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70302468084660
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: happy-cli
|
60
|
-
requirement: &
|
60
|
+
requirement: &70302468122420 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.1.0.pre1
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70302468122420
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rake
|
71
|
-
requirement: &
|
71
|
+
requirement: &70302468120940 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70302468120940
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: rspec
|
82
|
-
requirement: &
|
82
|
+
requirement: &70302468119720 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '2.8'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70302468119720
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: rspec-html-matchers
|
93
|
-
requirement: &
|
93
|
+
requirement: &70302468119000 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70302468119000
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: rack-test
|
104
|
-
requirement: &
|
104
|
+
requirement: &70302468118500 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70302468118500
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: watchr
|
115
|
-
requirement: &
|
115
|
+
requirement: &70302468118060 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,7 +120,7 @@ dependencies:
|
|
120
120
|
version: '0'
|
121
121
|
type: :development
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *70302468118060
|
124
124
|
description: A happy little toolkit for writing web applications.
|
125
125
|
email:
|
126
126
|
- hendrik@mans.de
|
@@ -143,12 +143,16 @@ files:
|
|
143
143
|
- lib/happy/context/helpers.rb
|
144
144
|
- lib/happy/controller.rb
|
145
145
|
- lib/happy/controller/actions.rb
|
146
|
+
- lib/happy/controller/configurable.rb
|
146
147
|
- lib/happy/controller/rackable.rb
|
147
148
|
- lib/happy/controller/routing.rb
|
149
|
+
- lib/happy/errors.rb
|
150
|
+
- lib/happy/extras/code_reloader.rb
|
148
151
|
- lib/happy/extras/permissions.rb
|
149
152
|
- lib/happy/extras/resources.rb
|
150
153
|
- lib/happy/extras/scriptable.rb
|
151
154
|
- lib/happy/extras/static.rb
|
155
|
+
- lib/happy/files/error.erb
|
152
156
|
- lib/happy/request.rb
|
153
157
|
- lib/happy/version.rb
|
154
158
|
- spec/controller/actions_spec.rb
|