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

Sign up to get free protection for your applications and to get access to all the features.
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: