mynyml-rack-respond_to 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,7 +1,21 @@
1
1
  ===== Summary
2
2
 
3
3
  Rack convenience middleware that allows triggering different actions based on
4
- requested mime type. Standalone version of the equivalent Rails functionality.
4
+ requested media type. Standalone version of the equivalent Rails functionality.
5
+
6
+ RespondTo gets the requested media type from the Accept header, which contains
7
+ a list of media types accepted/requested by the client, ordered by level of
8
+ preference (see http://github.com/mynyml/rack-accept-media-types).
9
+
10
+ ===== Features
11
+
12
+ * Based on familiar API (Rails)
13
+ * Cascades down priority list of accepted media types
14
+ * Simple to use
15
+ * Simple code (~50 LOCs)
16
+ * Flexible (standalone use)
17
+ * Decently documented (examples/ dir, source docs/rdocs)
18
+ * Compatible with other media type handling middleware (uses Rack::AcceptMediaTypes)
5
19
 
6
20
  ===== Installation
7
21
 
@@ -17,30 +31,32 @@ requested mime type. Standalone version of the equivalent Rails functionality.
17
31
  include Rack::RespondTo
18
32
 
19
33
  def call(env)
20
- # Pass in the env, and RespondTo will retrieve the requested mime type
34
+ # Pass in the env, and RespondTo will retrieve the requested media types
21
35
  Rack::RespondTo.env = env
22
36
 
23
- # Alternatively, to use standalone you can also assign the mime type
37
+ # Alternatively, to use standalone you can also assign the media types
24
38
  # directly (this will take precedence over the env)
25
- #Rack::RespondTo.mime_type = 'text/html'
39
+ #Rack::RespondTo.media_types = ['text/html']
26
40
 
27
41
  body = respond_to do |format|
28
42
  format.html { '<em>html</em>' }
29
43
  format.xml { '<body>xml</body>' }
30
44
  end
31
45
 
32
- [200, {'Content-Type' => Rack::RespondTo.mime_type}, [body]]
46
+ [200, {'Content-Type' => Rack::RespondTo.selected_media_type}, [body]]
33
47
  end
34
48
  end
35
49
 
36
50
  run App.new
37
51
 
52
+ See examples/simple_app.ru for an executable example.
53
+
38
54
  ===== Tips
39
55
 
40
56
  Use together with Rack::AbstractFormat to respond to routes based on url
41
57
  extensions. For example, if you want <tt>example.com/foo.xml</tt> to trigger
42
- the <tt>format.xml</tt> block (Rack::AbstractFormat moves the extension into
43
- HTTP_ACCEPT).
58
+ the <tt>format.xml</tt> block (Rack::AbstractFormat moves the extension's media
59
+ type into HTTP_ACCEPT and makes it the highest ranked).
44
60
 
45
61
  sudo gem install mynyml-rack-abstract-format --source=http://gems.github.com/
46
62
 
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  spec = Gem::Specification.new do |s|
24
24
  s.name = 'rack-respond_to'
25
- s.version = '0.9.5'
25
+ s.version = '0.9.6'
26
26
  s.summary = "Rack middleware port of Rails's respond_to feature"
27
27
  s.description = "Rack middleware port of Rails's respond_to feature"
28
28
  s.author = "Martin Aumont"
data/TODO CHANGED
@@ -1,4 +1,4 @@
1
- o handle wildcard content-types?
1
+ o handle wildcard content-types
2
2
  e.g. */* matches first format block
3
3
 
4
4
  Rack::RespondTo.mime_type = '*/*'
@@ -15,8 +15,8 @@ o handle wildcard content-types?
15
15
  end
16
16
  body #=> 'xml'
17
17
 
18
- o cascade down accept types priority instead of only responding to prefered
19
- type. e.g:
18
+ x cascade down accept types priority instead of only responding to prefered
19
+ type. e.g:
20
20
 
21
21
  env['HTTP_ACCEPT'] #=> 'text/plain,text/html;q=0.9,application/xml;q=0.8'
22
22
 
@@ -25,6 +25,8 @@ class App
25
25
  request = Rack::Request.new(env)
26
26
 
27
27
  body = case request.path_info
28
+ when '/'
29
+ "try foo<em>.html</em> and foo<em>.xml</em>"
28
30
  when '/foo'
29
31
  respond_to do |format|
30
32
  format.html { '<em>html</em>' }
@@ -32,10 +34,9 @@ class App
32
34
  end
33
35
  end
34
36
 
35
- [200, {'Content-Type' => Rack::RespondTo.mime_type}, [body]]
37
+ [200, {'Content-Type' => Rack::RespondTo.selected_media_type || 'text/html'}, [body]]
36
38
  end
37
39
  end
38
40
 
39
41
  use Rack::AbstractFormat
40
42
  run App.new
41
-
@@ -13,7 +13,7 @@ class App
13
13
  include Rack::RespondTo
14
14
 
15
15
  def call(env)
16
- Rack::RespondTo.mime_type = 'application/xml'
16
+ Rack::RespondTo.media_types = %w( application/xml )
17
17
 
18
18
  body = case env['PATH_INFO']
19
19
  when '/'
@@ -23,7 +23,7 @@ class App
23
23
  end
24
24
  end
25
25
 
26
- [200, {'Content-Type' => Rack::RespondTo.mime_type}, [body]]
26
+ [200, {'Content-Type' => Rack::RespondTo.selected_media_type}, [body]]
27
27
  end
28
28
  end
29
29
 
@@ -8,26 +8,39 @@ module Rack
8
8
  #
9
9
  module RespondTo
10
10
  class << self
11
- # Assign the environment directly to fetch the mime type from
11
+ # Assign the environment directly to fetch the requested media types from
12
12
  # env['HTTP_ACCEPT'] ('Accept:' request header).
13
13
  #
14
14
  # ===== Example
15
15
  #
16
16
  # def call(env)
17
17
  # Rack::RespondTo.env = env
18
+ # #...
18
19
  # end
19
20
  #
20
21
  attr_accessor :env
21
22
 
22
- # If used completely standalone, you can assign the request mime_type directly.
23
+ # If used completely standalone, you can assign the requested media types
24
+ # directly.
23
25
  #
24
26
  # ===== Example
25
27
  #
26
- # RespondTo.mime_type = 'application/xml'
28
+ # RespondTo.media_types = ['application/xml']
27
29
  #
28
- attr_accessor :mime_type
29
- alias :media_type= :mime_type=
30
- alias :media_type :mime_type
30
+ attr_accessor :media_types
31
+ alias :mime_types= :media_types=
32
+ alias :mime_types :media_types
33
+
34
+ # Contains the media type that was responded to. Set after the respond_to
35
+ # block is called.
36
+ #
37
+ # Useful for setting the response's Content-Type:
38
+ #
39
+ # [200, {'Content-Type' => RespondTo.selected_media_type}, [body]]
40
+ #
41
+ attr_accessor :selected_media_type
42
+ alias :selected_mime_type= :selected_media_type=
43
+ alias :selected_mime_type :selected_media_type
31
44
 
32
45
  def included(base) #:nodoc:
33
46
  base.extend(ClassMethods)
@@ -36,34 +49,35 @@ module Rack
36
49
  end
37
50
  end
38
51
 
39
- # Mime type requested.
52
+ # Cast format to media type
40
53
  #
41
- # Useful for setting content type:
54
+ # ===== Example
42
55
  #
43
- # [200, {'Content-Type' => Rack::RespondTo.mime_type}, [body]]
56
+ # RespondTo::MediaType('html') #=> 'text/html'
57
+ # RespondTo::MediaType('htm') #=> 'text/html'
44
58
  #
45
- def mime_type
46
- @mime_type || accept
59
+ def MediaType(format)
60
+ Rack::Mime.mime_type(format.sub(/^\./,'').insert(0,'.'))
47
61
  end
62
+ alias :MimeType :MediaType
48
63
 
49
- # Cast format to mime type
64
+ # Requested media types, in preferencial order
50
65
  #
51
- # ===== Example
66
+ # ===== Examples
52
67
  #
53
- # RespondTo::MimeType('html') #=> 'text/html'
68
+ # RespondTo.env['HTTP_ACCEPT'] #=> 'text/html,application/xml'
69
+ # RespondTo.media_types #=> ['text/html', 'application/xml']
54
70
  #
55
- def MimeType(format)
56
- Rack::Mime.mime_type(format.sub(/^\./,'').insert(0,'.'))
71
+ # RespondTo.env['HTTP_ACCEPT'] #=> 'text/html;q=0.7,application/xml;q=0.9,application/json;q=0.8'
72
+ # RespondTo.media_types #=> ['application/xml', 'application/json', 'text/html']
73
+ #
74
+ def media_types
75
+ @media_types || accept_list
57
76
  end
58
77
 
59
78
  private
60
- # The mime type retained from the HTTP_ACCEPT header's list
61
- #
62
- # ===== Returns
63
- # String:: first mime type from header's list or nil if none
64
- #
65
- def accept
66
- Rack::AcceptMediaTypes.new(self.env['HTTP_ACCEPT'] || '').prefered unless self.env.nil?
79
+ def accept_list
80
+ self.env.nil? ? [] : Rack::AcceptMediaTypes.new(self.env['HTTP_ACCEPT'] || '')
67
81
  end
68
82
  end
69
83
 
@@ -75,30 +89,53 @@ module Rack
75
89
  end
76
90
 
77
91
  module ClassMethods
92
+
78
93
  # Allows defining different actions and returns the one which corresponds
79
- # to the current RespondTo.mime_type.
94
+ # to the highest ranking value in the RespondTo.media_types list.
80
95
  #
81
- # ===== Example
96
+ # If no handler is defined for the highest ranking value, respond_to will
97
+ # cascade down the RespondTo.media_types list until it finds a match.
98
+ # Returns nil if there is no match.
82
99
  #
83
- # RespondTo.mime_type = 'text/html'
100
+ # ===== Examples
101
+ #
102
+ # RespondTo.media_types = ['text/html', 'application/xml']
84
103
  #
85
104
  # respond_to do |format|
86
- # format.html { '<em>html</em>' }
87
- # format.xml { '<content>xml</content>' }
105
+ # format.html { 'html' }
106
+ # format.xml { 'xml' }
88
107
  # end
89
- # #=> '<em>html</em>'
108
+ # #=> 'html'
90
109
  #
91
- def respond_to(&block)
110
+ # RespondTo.media_types = ['text/html', 'application/xml']
111
+ #
112
+ # respond_to do |format|
113
+ # format.xml { 'xml' }
114
+ # format.txt { 'txt' }
115
+ # end
116
+ # #=> 'xml'
117
+ #
118
+ # RespondTo.media_types = ['text/html', 'application/json']
119
+ #
120
+ # respond_to do |format|
121
+ # format.xml { 'xml' }
122
+ # format.txt { 'txt' }
123
+ # end
124
+ # #=> nil
125
+ #
126
+ def respond_to
92
127
  format = Format.new
93
- block.call(format)
94
- handler = format[RespondTo.mime_type]
128
+ yield format
129
+ type = RespondTo.media_types.detect {|type| format[type] }
130
+ RespondTo.selected_media_type = type
131
+ handler = format[type]
95
132
  handler.nil? ? nil : handler.call
96
133
  end
97
134
  end
98
135
 
99
136
  class Format < Hash #:nodoc:
100
137
  def method_missing(format, *args, &handler)
101
- self[RespondTo::MimeType(format.to_s)] = handler
138
+ self[RespondTo::MediaType(format.to_s)] = handler
102
139
  end
103
140
  end
104
141
  end
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-respond_to
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Aumont
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-08 00:00:00 -04:00
12
+ date: 2009-06-09 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -7,7 +7,9 @@ end
7
7
  class TestRespondTo < Test::Unit::TestCase
8
8
 
9
9
  def setup
10
- Rack::RespondTo.mime_type = nil
10
+ Rack::RespondTo.selected_media_type = nil
11
+ Rack::RespondTo.media_types = nil
12
+ Rack::RespondTo.env = nil
11
13
  end
12
14
 
13
15
  ## api
@@ -18,13 +20,21 @@ class TestRespondTo < Test::Unit::TestCase
18
20
  assert_equal env, Rack::RespondTo.env
19
21
  end
20
22
 
21
- test "mime type accessor" do
22
- Rack::RespondTo.mime_type = 'application/xml'
23
- assert_equal 'application/xml', Rack::RespondTo.mime_type
23
+ test "media types accessor" do
24
+ Rack::RespondTo.media_types = %w( application/xml )
25
+ assert_equal %w( application/xml ), Rack::RespondTo.media_types
24
26
 
25
27
  # alias
26
- Rack::RespondTo.media_type = 'application/xml'
27
- assert_equal 'application/xml', Rack::RespondTo.media_type
28
+ Rack::RespondTo.media_types = %w( application/xml )
29
+ assert_equal %w( application/xml ), Rack::RespondTo.media_types
30
+ end
31
+
32
+ test "selected media type reader" do
33
+ Rack::RespondTo.media_types = %w( application/xml )
34
+ body = App.respond_to do |format|
35
+ format.xml { 'xml' }
36
+ end
37
+ assert_equal 'application/xml', Rack::RespondTo.selected_media_type
28
38
  end
29
39
 
30
40
  test "mixin injects respond_to class method" do
@@ -35,59 +45,54 @@ class TestRespondTo < Test::Unit::TestCase
35
45
  assert App.new.respond_to?(:respond_to)
36
46
  end
37
47
 
38
- ## mime type
48
+ ## requested media types
39
49
 
40
- test "mime type is extracted from header (first in list)" do
41
- Rack::RespondTo.env = {'HTTP_ACCEPT' => 'text/html,application/xml'}
42
- assert_equal 'text/html', Rack::RespondTo.mime_type
43
-
44
- Rack::RespondTo.env = {'HTTP_ACCEPT' => 'application/xml,text/html'}
45
- assert_equal 'application/xml', Rack::RespondTo.mime_type
50
+ test "explicitly specified media types take precedence over header's" do
51
+ Rack::RespondTo.env = {'HTTP_ACCEPT' => 'text/html'}
52
+ Rack::RespondTo.media_types = %w( text/plain )
53
+ assert_equal %w( text/plain ), Rack::RespondTo.media_types
46
54
  end
47
55
 
48
- test "mime type with empty header" do
49
- assert_nothing_raised do
50
- Rack::RespondTo.env = {'HTTP_ACCEPT' => ''}
51
- Rack::RespondTo.mime_type = nil
52
- assert_equal nil, Rack::RespondTo.mime_type
53
- end
54
- end
56
+ ## respond_to
55
57
 
56
- test "mime type with nil header" do
57
- assert_nothing_raised do
58
- Rack::RespondTo.env = {}
59
- Rack::RespondTo.mime_type = nil
60
- assert_equal nil, Rack::RespondTo.mime_type
58
+ test "respond_to returns block for highest ranking format" do
59
+ Rack::RespondTo.env = {'HTTP_ACCEPT' => 'application/xml;q=0.8,text/plain;q=0.9'}
60
+ body = App.respond_to do |format|
61
+ format.xml { 'xml' }
62
+ format.txt { 'txt' }
61
63
  end
64
+ assert_equal 'txt', body
62
65
  end
63
66
 
64
- test "mime type without source" do
65
- assert_nothing_raised do
66
- Rack::RespondTo.env = nil
67
- Rack::RespondTo.mime_type = nil
68
- assert_equal nil, Rack::RespondTo.mime_type
67
+ test "cascades down the Accept header's list to find suitable type" do
68
+ Rack::RespondTo.env = {'HTTP_ACCEPT' => 'text/html,text/plain;q=0.9'}
69
+ body = App.respond_to do |format|
70
+ format.xml { 'xml' }
71
+ format.txt { 'txt' }
69
72
  end
73
+ assert_equal 'txt', body
70
74
  end
71
75
 
72
- test "explicitly specified mime type takes precedence over env's" do
73
- Rack::RespondTo.env = {'HTTP_ACCEPT' => 'text/html'}
74
- Rack::RespondTo.mime_type = 'text/plain'
75
- assert_equal 'text/plain', Rack::RespondTo.mime_type
76
+ test "respond_to with no matching format" do
77
+ Rack::RespondTo.media_types = %w( text/html )
78
+ body = App.respond_to do |format|
79
+ format.xml { 'xml' }
80
+ format.txt { 'txt' }
81
+ end
82
+ assert_equal nil, body
76
83
  end
77
84
 
78
- ## respond_to
79
-
80
- test "respond_to returns block for requested format" do
81
- Rack::RespondTo.mime_type = 'application/xml'
85
+ test "respond_to with empty type list" do
86
+ Rack::RespondTo.media_types = []
82
87
  body = App.respond_to do |format|
83
88
  format.xml { 'xml' }
84
89
  format.txt { 'txt' }
85
90
  end
86
- assert_equal 'xml', body
91
+ assert_equal nil, body
87
92
  end
88
93
 
89
- test "respond_to with no matching format" do
90
- Rack::RespondTo.mime_type = 'text/html'
94
+ test "respond_to with nil type list" do
95
+ Rack::RespondTo.media_types = nil
91
96
  body = App.respond_to do |format|
92
97
  format.xml { 'xml' }
93
98
  format.txt { 'txt' }
@@ -96,7 +101,7 @@ class TestRespondTo < Test::Unit::TestCase
96
101
  end
97
102
 
98
103
  test "respond_to handles synonymous formats" do
99
- Rack::RespondTo.mime_type = 'text/html'
104
+ Rack::RespondTo.media_types = %w( text/html )
100
105
 
101
106
  body = App.respond_to do |format|
102
107
  format.htm { 'htm' } # htm/html
@@ -110,4 +115,15 @@ class TestRespondTo < Test::Unit::TestCase
110
115
  end
111
116
  assert_equal 'html', body
112
117
  end
118
+
119
+ test "repond_to sets selected media type" do
120
+ Rack::RespondTo.media_types = %w( text/html text/plain )
121
+ assert_equal nil, Rack::RespondTo.selected_media_type
122
+ body = App.respond_to do |format|
123
+ format.xml { 'xml' }
124
+ format.txt { 'txt' }
125
+ end
126
+ assert_equal 'txt', body
127
+ assert_equal 'text/plain', Rack::RespondTo.selected_media_type
128
+ end
113
129
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mynyml-rack-respond_to
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Aumont
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-07 21:00:00 -07:00
12
+ date: 2009-06-08 21:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency