mynyml-rack-respond_to 0.9.5 → 0.9.6

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 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