happy 0.1.0.pre16 → 0.1.0.pre19

Sign up to get free protection for your applications and to get access to all the features.
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
- ``` ruby
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.pre9'
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
- HappyHelpers::Templates.render(name, self, variables, &blk)
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
- attr_reader :request, :previous_path, :remaining_path
9
- attr_accessor :layout, :controller, :response
10
- delegate :params, :session, :to => :request
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 = nil
18
- @controller = nil
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 = self.controller
24
- self.controller = new_controller
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
- self.controller = old_controller
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
- # Don't serve if there are still bits of path remaining.
6
- return unless remaining_path.empty?
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
- # Don't serve is data is not a string.
9
- return unless data.is_a?(String)
9
+ # Mix in default options
10
+ options = {
11
+ :layout => context.layout
12
+ }.merge(options)
10
13
 
11
- # Mix in default options
12
- options = {
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
- # Add status code from options
17
- response.status = options.delete(:status) if options.has_key?(:status)
17
+ # Extract layout
18
+ layout = options.delete(:layout)
18
19
 
19
- # Extract layout
20
- layout = options.delete(:layout)
20
+ # Treat remaining options as headers
21
+ options.each { |k, v| header k, v }
21
22
 
22
- # Treat remaining options as headers
23
- options.each { |k, v| header k, v }
23
+ # Apply layout, if available
24
+ if layout
25
+ data = render(layout) { data }
26
+ end
24
27
 
25
- # Apply layout, if available
26
- if layout
27
- data = render(layout) { data }
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
- throw message
35
+ only_if_path_matches do
36
+ throw message
37
+ end
44
38
  end
45
39
 
46
40
  def redirect!(to, status = 302)
47
- header :location, url_for(to)
48
- response.status = status
49
- halt!
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
- halt!
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 call(env)
7
- @env = env
8
-
6
+ def handle_request
9
7
  catch :done do
10
- serve_or_404! perform
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.call(env)
25
+ new(env).handle_request
19
26
  end
20
27
  end
21
28
  end
@@ -34,7 +34,7 @@ module Happy
34
34
  previous_path << remaining_path.shift
35
35
  end
36
36
 
37
- serve_or_404! instance_exec(&blk)
37
+ serve!(instance_exec(&blk)) or raise Errors::NotFound
38
38
  end
39
39
  end
40
40
  end
@@ -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.from_env(@env)
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Happy
2
- VERSION = "0.1.0.pre16"
2
+ VERSION = "0.1.0.pre19"
3
3
  end
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
@@ -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.pre16
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-07 00:00:00.000000000 Z
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: &70251874709220 !ruby/object:Gem::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: *70251874709220
24
+ version_requirements: *70302468087940
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rack
27
- requirement: &70251874706120 !ruby/object:Gem::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: *70251874706120
35
+ version_requirements: *70302468086760
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: happy-helpers
38
- requirement: &70251874722540 !ruby/object:Gem::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.pre9
43
+ version: 0.1.0.pre10
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70251874722540
46
+ version_requirements: *70302468085720
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: allowance
49
- requirement: &70251874720680 !ruby/object:Gem::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: *70251874720680
57
+ version_requirements: *70302468084660
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: happy-cli
60
- requirement: &70251874719340 !ruby/object:Gem::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: *70251874719340
68
+ version_requirements: *70302468122420
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
- requirement: &70251874718000 !ruby/object:Gem::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: *70251874718000
79
+ version_requirements: *70302468120940
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &70251874735320 !ruby/object:Gem::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: *70251874735320
90
+ version_requirements: *70302468119720
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rspec-html-matchers
93
- requirement: &70251874732880 !ruby/object:Gem::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: *70251874732880
101
+ version_requirements: *70302468119000
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: rack-test
104
- requirement: &70251874731140 !ruby/object:Gem::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: *70251874731140
112
+ version_requirements: *70302468118500
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: watchr
115
- requirement: &70251874727440 !ruby/object:Gem::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: *70251874727440
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