happy 0.1.0.pre.3 → 0.1.0.pre.4

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/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' # TODO: , '~> 0.1.0'
20
+ gem.add_dependency 'happy-helpers', '~> 0.1.0.pre.5'
21
21
  gem.add_dependency 'allowance', '>= 0.1.1'
22
22
 
23
23
  gem.add_development_dependency 'rake'
@@ -7,6 +7,7 @@ module Happy
7
7
 
8
8
  def render(what, options = {}, &blk)
9
9
  case what
10
+ when NilClass then ''
10
11
  when String then render_template(what, options, &blk)
11
12
  when Enumerable then what.map { |i| render(i, options, &blk) }.join
12
13
  else render_resource(what, options)
@@ -14,7 +14,7 @@ module Happy
14
14
  :render, :url_for,
15
15
  :to => :context
16
16
 
17
- def initialize(env = nil, options = {}, &blk)
17
+ def initialize(env = {}, options = {}, &blk)
18
18
  @env = env
19
19
  @options = options
20
20
  instance_exec(&blk) if blk
@@ -10,16 +10,21 @@ module Happy
10
10
 
11
11
  # Mix in default options
12
12
  options = {
13
- layout: context.layout
13
+ :layout => context.layout
14
14
  }.merge(options)
15
15
 
16
- # Add optional headers et al
17
- response.status = options[:status] if options.has_key?(:status)
18
- response['Content-type'] = options[:content_type] if options.has_key?(:content_type)
16
+ # Add status code from options
17
+ response.status = options.delete(:status) if options.has_key?(:status)
18
+
19
+ # Extract layout
20
+ layout = options.delete(:layout)
21
+
22
+ # Treat remaining options as headers
23
+ options.each { |k, v| header k, v }
19
24
 
20
25
  # Apply layout, if available
21
- if options[:layout]
22
- data = render(options[:layout]) { data }
26
+ if layout
27
+ data = render(layout) { data }
23
28
  end
24
29
 
25
30
  # Set response body and finish request
@@ -32,7 +37,7 @@ module Happy
32
37
  end
33
38
 
34
39
  def redirect!(to, status = 302)
35
- header "Location", url_for(to)
40
+ header :location, url_for(to)
36
41
  response.status = status
37
42
  halt!
38
43
  end
@@ -42,7 +47,7 @@ module Happy
42
47
  end
43
48
 
44
49
  def content_type(type)
45
- header 'Content-type', type
50
+ header :content_type, type
46
51
  end
47
52
 
48
53
  def max_age(t, options = {})
@@ -60,10 +65,11 @@ module Happy
60
65
  end
61
66
 
62
67
  def cache_control(s)
63
- header 'Cache-control', s
68
+ header :cache_control, s
64
69
  end
65
70
 
66
71
  def header(name, value)
72
+ name = name.to_s.dasherize.humanize if name.is_a?(Symbol)
67
73
  response[name] = value
68
74
  end
69
75
 
@@ -2,8 +2,9 @@ module Happy
2
2
  module ControllerExtensions
3
3
  module Routing
4
4
  def path_to_regexp(path)
5
- path = ":#{path}" if path.is_a?(Symbol)
6
- Regexp.compile('^'+path.gsub(/\)/, ')?').gsub(/\//, '\/').gsub(/\./, '\.').gsub(/:(\w+)/, '(?<\\1>.+)')+'$')
5
+ # Since we want to be compatible with Ruby 1.8.7, we unfortunately can't use named captures like this:
6
+ # Regexp.compile('^'+path.gsub(/\)/, ')?').gsub(/\//, '\/').gsub(/\./, '\.').gsub(/:(\w+)/, '(?<\\1>.+)')+'$')
7
+ Regexp.compile('^'+path.gsub(/\)/, ')?').gsub(/\//, '\/').gsub(/\./, '\.').gsub(/:(\w+)/, '(.+)')+'$')
7
8
  end
8
9
 
9
10
  def path(*args, &blk)
@@ -11,18 +12,25 @@ module Happy
11
12
  args = [nil] if args.empty?
12
13
 
13
14
  args.each do |name|
15
+ # If a path name has been given, match it against the next request path part.
14
16
  if name.present?
17
+ # convert symbols to ":foo" type string
18
+ name = ":#{name}" if name.is_a?(Symbol)
15
19
  path_match = path_to_regexp(name).match(remaining_path.first)
16
20
  end
17
21
 
22
+ # Match the request method, if specified
18
23
  method_matched = [nil, request.request_method.downcase.to_sym].include?(options[:method])
24
+
19
25
  path_matched = (path_match || (name.nil? && remaining_path.empty?))
20
26
 
21
27
  # Only do something here if method and requested path both match
22
28
  if path_matched && method_matched
23
29
  # Transfer variables contained in path name to params hash
24
30
  if path_match
25
- path_match.names.each { |k| request.params[k] = path_match[k] }
31
+ name.scan(/:(\w+)/).flatten.each do |var|
32
+ request.params[var] = path_match.captures.shift
33
+ end
26
34
  remaining_path.shift
27
35
  end
28
36
 
@@ -35,22 +43,22 @@ module Happy
35
43
  end
36
44
 
37
45
  def get(*args, &blk)
38
- args.last.is_a?(Hash) ? args.last.merge(method: :get) : args.push(method: :get)
46
+ args.last.is_a?(Hash) ? args.last.merge(:method => :get) : args.push(:method => :get)
39
47
  path(*args, &blk)
40
48
  end
41
49
 
42
50
  def post(*args, &blk)
43
- args.last.is_a?(Hash) ? args.last.merge(method: :post) : args.push(method: :post)
51
+ args.last.is_a?(Hash) ? args.last.merge(:method => :post) : args.push(:method => :post)
44
52
  path(*args, &blk)
45
53
  end
46
54
 
47
55
  def put(*args, &blk)
48
- args.last.is_a?(Hash) ? args.last.merge(method: :put) : args.push(method: :put)
56
+ args.last.is_a?(Hash) ? args.last.merge(:method => :put) : args.push(:method => :put)
49
57
  path(*args, &blk)
50
58
  end
51
59
 
52
60
  def delete(*args, &blk)
53
- args.last.is_a?(Hash) ? args.last.merge(method: :delete) : args.push(method: :delete)
61
+ args.last.is_a?(Hash) ? args.last.merge(:method => :delete) : args.push(:method => :delete)
54
62
  path(*args, &blk)
55
63
  end
56
64
  end
@@ -91,8 +91,8 @@ module Happy
91
91
 
92
92
  def route
93
93
  @options = {
94
- singular_name: options[:class].to_s.tableize.singularize,
95
- plural_name: options[:class].to_s.tableize.pluralize
94
+ :singular_name => options[:class].to_s.tableize.singularize,
95
+ :plural_name => options[:class].to_s.tableize.pluralize
96
96
  }.merge(@options)
97
97
 
98
98
  path options[:plural_name] do
data/lib/happy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Happy
2
- VERSION = "0.1.0.pre.3"
2
+ VERSION = "0.1.0.pre.4"
3
3
  end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ module Happy
4
+ describe ControllerExtensions::Actions do
5
+ subject { Controller.new }
6
+
7
+ describe '#serve!' do
8
+ def app
9
+ build_controller do
10
+ path('simple') { serve! "Simple response" }
11
+ path('with_headers') { serve! "body { color: red }", :content_type => 'text/css' }
12
+ path('with_status') { serve! "Not Allowed", :status => 401 }
13
+ path('with_layout') { serve! "content", :layout => 'layout.erb' }
14
+ end
15
+ end
16
+
17
+ it "serves the provided string as the response body" do
18
+ response_for { get '/simple' }.body.should == 'Simple response'
19
+ end
20
+
21
+ it "responds with a status code of 200 by default" do
22
+ response_for { get '/simple' }.status.should == 200
23
+ end
24
+
25
+ it "sets the response status code to its :status option" do
26
+ response_for { get '/with_status' }.status.should == 401
27
+ end
28
+
29
+ it "uses the layout provided through the :layout option" do
30
+ instance = Controller.new
31
+ instance.send(:context).should_receive(:render).with('layout.erb')
32
+
33
+ catch(:done) { instance.serve! "content", :layout => 'layout.erb' }
34
+ end
35
+
36
+ it "sets extra options as response headers" do
37
+ response_for { get '/with_headers' }['Content-type'].should == 'text/css'
38
+ end
39
+
40
+ it "finishes the rendering by throwing :done" do
41
+ expect { subject.serve! "body" }.to throw_symbol :done
42
+ end
43
+ end
44
+
45
+ describe '#redirect!' do
46
+ it "triggers a redirection to the specified URL" do
47
+ def app
48
+ build_controller { redirect! 'http://www.test.com' }
49
+ end
50
+
51
+ get '/'
52
+ last_response.status.should == 302
53
+ last_response.headers['Location'].should == 'http://www.test.com'
54
+ end
55
+
56
+ it "sets the provided status code" do
57
+ def app
58
+ build_controller { redirect! 'http://www.test.com', 301 }
59
+ end
60
+
61
+ get '/'
62
+ last_response.status.should == 301
63
+ end
64
+ end
65
+
66
+ describe '#header' do
67
+ it "sets the specified header in the response" do
68
+ subject.send(:context).response.should_receive(:[]=).with('Content-type', 'text/css')
69
+ subject.header 'Content-type', 'text/css'
70
+ end
71
+
72
+ it "also accepts the header name as a symbol" do
73
+ subject.send(:context).response.should_receive(:[]=).with('Content-type', 'text/css')
74
+ subject.header :content_type, 'text/css'
75
+ end
76
+ end
77
+
78
+ describe '#content_type' do
79
+ it "sets the Content-type header" do
80
+ subject.should_receive(:header).with(:content_type, 'text/css')
81
+ subject.content_type 'text/css'
82
+ end
83
+ end
84
+
85
+ describe '#layout' do
86
+ it "sets the layout to be used by the current context" do
87
+ subject.send(:context).should_receive(:layout=).with('layout.erb')
88
+ subject.layout 'layout.erb'
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ module Happy
4
+ describe ControllerExtensions::Routing do
5
+ describe '#path' do
6
+ subject do
7
+ Controller.build do
8
+ route do
9
+ path('foo') { serve! 'bar' }
10
+ path('one', 'two') { serve! 'onetwo' }
11
+ path('hello') do
12
+ path(:name) { serve! "Hello #{params['name']}" }
13
+ serve! "Please provide a name."
14
+ end
15
+ path('number-:num') { serve! "num = #{params['num']}" }
16
+ serve! "root"
17
+ end
18
+ end
19
+ end
20
+
21
+ it "routes requests to the specified path to its contained block" do
22
+ response_for { get '/' }.body.should == 'root'
23
+ response_for { get '/foo' }.body.should == 'bar'
24
+ end
25
+
26
+ it "routes independently from the request method" do
27
+ response_for { post '/foo' }.body.should == 'bar'
28
+ end
29
+
30
+ it "supports multiple path parameters" do
31
+ response_for { get '/one' }.body.should == 'onetwo'
32
+ response_for { get '/two' }.body.should == 'onetwo'
33
+ end
34
+
35
+ it "routes to the root code if no path matches" do
36
+ response_for { get '/' }.body.should == 'root'
37
+ end
38
+
39
+ it "executes the contained serve! call if no sub-paths match" do
40
+ response_for { get '/hello' }.body.should == 'Please provide a name.'
41
+ end
42
+
43
+ it "parses parameters when the provided path is a symbol" do
44
+ response_for { get '/hello/hendrik' }.body.should == 'Hello hendrik'
45
+ end
46
+
47
+ it "parses parameters within path names" do
48
+ response_for { get '/number-123' }.body.should == 'num = 123'
49
+ end
50
+ end
51
+
52
+ %w(get post put delete).each do |what|
53
+ describe "##{what}" do
54
+ subject { Controller.new }
55
+
56
+ it "merely invokes #path with the :method => :#{what} option" do
57
+ subject.should_receive(:path).with('path', :method => what.to_sym)
58
+ subject.send(what, 'path')
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -2,6 +2,20 @@ require 'spec_helper'
2
2
 
3
3
  module Happy
4
4
  describe Controller do
5
+ subject do
6
+ Controller.build do
7
+ route do
8
+ serve! "it works"
9
+ end
10
+ end
11
+ end
12
+
13
+ it "is mountable as a Rack app" do
14
+ subject.should respond_to(:call)
15
+ get '/'
16
+ last_response.body.should == 'it works'
17
+ end
18
+
5
19
  describe ".build" do
6
20
  subject do
7
21
  Controller.build do
@@ -9,10 +23,6 @@ module Happy
9
23
  end
10
24
  end
11
25
 
12
- def app
13
- subject
14
- end
15
-
16
26
  it "creates a new controller class" do
17
27
  subject.ancestors.should include(Controller)
18
28
  end
@@ -22,19 +32,5 @@ module Happy
22
32
  last_response.body.should == 'yay!'
23
33
  end
24
34
  end
25
-
26
- it "is also a Rack app" do
27
- def app
28
- Controller.build do
29
- route do
30
- serve! "it works"
31
- end
32
- end
33
- end
34
-
35
- get '/'
36
- last_response.body.should == 'it works'
37
- end
38
-
39
35
  end
40
36
  end
data/spec/spec_helper.rb CHANGED
@@ -8,6 +8,26 @@ require 'rack/test'
8
8
 
9
9
  require 'happy'
10
10
 
11
+ module SpecHelpers
12
+ def app
13
+ subject
14
+ end
15
+
16
+ def response_for
17
+ yield if block_given?
18
+ last_response
19
+ end
20
+
21
+ def build_controller(&blk)
22
+ Happy::Controller.build do
23
+ route do
24
+ instance_exec(&blk)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
11
30
  RSpec.configure do |conf|
12
31
  conf.include Rack::Test::Methods
32
+ conf.include SpecHelpers
13
33
  end
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.pre.3
4
+ version: 0.1.0.pre.4
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-05-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70362048389880 !ruby/object:Gem::Requirement
16
+ requirement: &70114163872540 !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: *70362048389880
24
+ version_requirements: *70114163872540
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rack
27
- requirement: &70362048389020 !ruby/object:Gem::Requirement
27
+ requirement: &70114163872020 !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: *70362048389020
35
+ version_requirements: *70114163872020
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: happy-helpers
38
- requirement: &70362048388280 !ruby/object:Gem::Requirement
38
+ requirement: &70114163868380 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
- - - ! '>='
41
+ - - ~>
42
42
  - !ruby/object:Gem::Version
43
- version: '0'
43
+ version: 0.1.0.pre.5
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70362048388280
46
+ version_requirements: *70114163868380
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: allowance
49
- requirement: &70362048386840 !ruby/object:Gem::Requirement
49
+ requirement: &70114163867700 !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: *70362048386840
57
+ version_requirements: *70114163867700
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rake
60
- requirement: &70362048386200 !ruby/object:Gem::Requirement
60
+ requirement: &70114163867160 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70362048386200
68
+ version_requirements: *70114163867160
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
- requirement: &70362048385440 !ruby/object:Gem::Requirement
71
+ requirement: &70114163866580 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '2.8'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70362048385440
79
+ version_requirements: *70114163866580
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec-html-matchers
82
- requirement: &70362048384620 !ruby/object:Gem::Requirement
82
+ requirement: &70114163866080 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70362048384620
90
+ version_requirements: *70114163866080
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rack-test
93
- requirement: &70362048384120 !ruby/object:Gem::Requirement
93
+ requirement: &70114163865340 !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: *70362048384120
101
+ version_requirements: *70114163865340
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: watchr
104
- requirement: &70362048383520 !ruby/object:Gem::Requirement
104
+ requirement: &70114163864500 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,7 +109,7 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70362048383520
112
+ version_requirements: *70114163864500
113
113
  description: A happy little toolkit for writing web applications.
114
114
  email:
115
115
  - hendrik@mans.de
@@ -139,6 +139,8 @@ files:
139
139
  - lib/happy/ext/static.rb
140
140
  - lib/happy/request.rb
141
141
  - lib/happy/version.rb
142
+ - spec/controller_ext/actions_spec.rb
143
+ - spec/controller_ext/routing_spec.rb
142
144
  - spec/controller_spec.rb
143
145
  - spec/spec_helper.rb
144
146
  homepage: https://github.com/hmans/happy
@@ -166,6 +168,8 @@ signing_key:
166
168
  specification_version: 3
167
169
  summary: A happy little toolkit for writing web applications.
168
170
  test_files:
171
+ - spec/controller_ext/actions_spec.rb
172
+ - spec/controller_ext/routing_spec.rb
169
173
  - spec/controller_spec.rb
170
174
  - spec/spec_helper.rb
171
175
  has_rdoc: