cehoffman-sinatra-respond_to 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Chris Hoffman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,88 @@
1
+ ## About
2
+
3
+
4
+ ## Examples
5
+
6
+ require 'sinatra'
7
+ require 'sinatra/respond_to'
8
+ register Sinatra::RespondTo # => Due to bug in sinatra for classic applications and extensions, see Issues
9
+
10
+ get '/posts' do
11
+ @posts = Post.recent
12
+
13
+ respond_to do |wants|
14
+ wants.html { haml :posts } # => views/posts.html.haml, also sets content_type to text/html
15
+ wants.rss { haml :posts } # => views/posts.rss.haml, also sets content_type to application/rss+xml
16
+ wants.atom { haml :posts } # => views/posts.atom.haml, also sets content_type to appliation/atom+xml
17
+ end
18
+ end
19
+
20
+ get '/post/:id' do
21
+ @post = Post.find(params[:id])
22
+
23
+ respond_to do |wants|
24
+ wants.html { haml :post } # => views/post.html.haml, also sets content_type to text/html
25
+ wants.xhtml { haml :post } # => views/post.xhtml.haml, also sets content_type to application/xhtml+xml
26
+ wants.xml { @post.to_xml } # => sets content_type to application/xml
27
+ wants.js { erb :post } # => views/post.js.erb, also sets content_type to application/javascript
28
+ end
29
+ end
30
+
31
+ get '/comments/:id' do
32
+ @comment = Comment.find(params[:id])
33
+
34
+ respond_to do |wants|
35
+ wants.html { haml :comment } # => views/comment.html.haml, also sets content_type to text/html
36
+ wants.json { @comment.to_json } # => sets content_type to application/json
37
+ wants.js { erb :comment } # => views/comment.js.erb, also sets content_type to application/javascript
38
+ end
39
+ end
40
+
41
+ ## Configuration
42
+
43
+ There a few options available for configuring the default behavior of respond_to using Sinatra's
44
+ <tt>set</tt> utility.
45
+
46
+ * <tt>default\_charset - utf-8</tt><br />
47
+ Assumes all text documents are encoded using this character set.
48
+ This can be overridden within the respond_to block for the appropriate format
49
+ * <tt>default\_content - :html</tt><br />
50
+ When a user vists a url without an extension, for example /post this will be
51
+ the assumed content to serve first. Expects a symbol as used in setting content_type.
52
+ * <tt>assume\_xhr\_is\_js - true</tt><br />
53
+ To avoid headaches with accept headers, and appending .js to urls, this will
54
+ cause the default format for all XmlHttpRequests to be classified as wanting Javascript
55
+ in the response.
56
+
57
+ ## Installing
58
+ sudo gem install cehoffman-sinatra-respond_to --source=http://gems.github.com
59
+
60
+ ## Cavaets
61
+ Due to the way respond\_to works, all incoming requests have the extension striped from the request.path\_info.
62
+ This causes routes like the following to fail.
63
+
64
+ get '/style.css' do
65
+ sass :style # => renders views/style.sass
66
+ end
67
+
68
+ They need to be changed to the following
69
+
70
+ get '/style' do
71
+ sass :style # => renders views/style.css.sass
72
+ end
73
+
74
+ If you want to ensure the route only gets called for css requests try this
75
+
76
+ get '/style', :provides => :css do
77
+ sass :style
78
+ end
79
+
80
+ ## Issues
81
+
82
+ Sinatra has a bug that affects Classic style applications and extensions see [#215][215] and [#180][180].
83
+ For this reason you'll have explicitly register Sinatra::RespondTo for classic applications just like for
84
+ non-classic applications.
85
+
86
+ [215]: https://sinatra.lighthouseapp.com/projects/9779/tickets/215-extensions-cannot-define-before-filters-for-classic-apps "Extensions cannot define before filters for classic apps"
87
+ [180]: https://sinatra.lighthouseapp.com/projects/9779/tickets/180-better-route-inheritence "Better route inheritence"
88
+
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |spec|
4
+ spec.name = 'sinatra-respond_to'
5
+ spec.summary = 'A respond_to style Rails block for baked-in web service support in Sinatra'
6
+ spec.email = 'cehoffman@gmail.com'
7
+ spec.homepage = 'http://github.com/cehoffman/sinatra-respond_to'
8
+ spec.description = spec.summary
9
+ spec.authors = ["Chris Hoffman"]
10
+ spec.add_dependency('sinatra', '>=0.9.1.3')
11
+ end
12
+ rescue LoadError
13
+ puts "Jewler not available. Install it with sugo gem install technicalpickles-jeweler -s http://gems.github.com"
14
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 0
4
+ :minor: 3
@@ -0,0 +1,185 @@
1
+ # Simple note, accept header parsing was looked at but deamed
2
+ # too much of an irregularity to deal with. Problems with the header
3
+ # differences from IE, Firefox, Safari, and every other UA causes
4
+ # problems with the expected output. The general expected behavior
5
+ # would be serve html when no extension provided, but most UAs say
6
+ # they will accept application/xml with out a quality indicator, meaning
7
+ # you'd get the xml block served insead. Just plain retarded, use the
8
+ # extension and you'll never be suprised.
9
+
10
+ module Sinatra
11
+ module RespondTo
12
+ class UnhandledFormat < Sinatra::NotFound; end
13
+ class MissingTemplate < Sinatra::NotFound
14
+ def code; 500 end
15
+ end
16
+
17
+ TEXT_MIME_TYPES = [:txt, :html, :js, :json, :xml, :rss, :atom, :css, :asm, :c, :cc, :conf,
18
+ :csv, :cxx, :diff, :dtd, :f, :f77, :f90, :for, :gemspec, :h, :hh, :htm,
19
+ :log, :mathml, :mml, :p, :pas, :pl, :pm, :py, :rake, :rb, :rdf, :rtf, :ru,
20
+ :s, :sgm, :sgml, :sh, :svg, :svgz, :text, :wsdl, :xhtml, :xsl, :xslt, :yaml,
21
+ :yml, :ics]
22
+
23
+ def self.registered(app)
24
+ app.helpers RespondTo::Helpers
25
+
26
+ app.set :default_charset, 'utf-8' unless app.respond_to?(:default_charset)
27
+ app.set :default_content, :html unless app.respond_to?(:default_content)
28
+ app.set :assume_xhr_is_js, true unless app.respond_to?(:assume_xhr_is_js)
29
+
30
+ # We remove the trailing extension so routes
31
+ # don't have to be of the style
32
+ #
33
+ # get '/resouce.:format'
34
+ #
35
+ # They can instead be of the style
36
+ #
37
+ # get '/resource'
38
+ #
39
+ # and the format will automatically be available in as <tt>format</tt>
40
+ app.before do
41
+ unless options.static? && options.public? && ["GET", "HEAD"].include?(request.request_method) && static_file?(unescape(request.path_info))
42
+ request.path_info.gsub! %r{\.([^\./]+)$}, ''
43
+ format $1 || options.default_content
44
+
45
+ # For the oh so common case of actually wanting Javascript from an XmlHttpRequest
46
+ format :js if request.xhr? && options.assume_xhr_is_js?
47
+ end
48
+ end
49
+
50
+ # Replace all routes that have an ending extension with one that doesn't
51
+ # Most generally a fix for the __sinatra__ routes in development
52
+ # app.routes.each_pair do |verb, subroutes|
53
+ # subroutes.each do |subroute|
54
+ # subroute[0] = Regexp.new(subroute[0].source.gsub(/\\\.[^\.\/]+\$$/, '$'))
55
+ # end
56
+ # end
57
+
58
+ app.configure :development do
59
+ # Very, very, very hackish but only for development at least
60
+ # Modifies the regex matching /__sinatra__/:image.png to not have the extension
61
+ ["GET", "HEAD"].each do |verb|
62
+ app.routes[verb][1][0] = Regexp.new(app.routes[verb][1][0].source.gsub(/\\\.[^\.\/]+\$$/, '$'))
63
+ end
64
+
65
+ app.error UnhandledFormat do
66
+ content_type :html, :charset => 'utf-8'
67
+
68
+ (<<-HTML).gsub(/^ {10}/, '')
69
+ <!DOCTYPE html>
70
+ <html>
71
+ <head>
72
+ <style type="text/css">
73
+ body { text-align:center;font-family:helvetica,arial;font-size:22px;
74
+ color:#888;margin:20px}
75
+ #c {margin:0 auto;width:500px;text-align:left}
76
+ </style>
77
+ </head>
78
+ <body>
79
+ <h2>Sinatra doesn't know this ditty.</h2>
80
+ <img src='/__sinatra__/404.png'>
81
+ <div id="c">
82
+ Try this:
83
+ <pre>#{request.request_method.downcase} '#{request.path_info}' do\n respond_to do |wants|\n wants.#{format} { "Hello World" }\n end\nend</pre>
84
+ </div>
85
+ </body>
86
+ </html>
87
+ HTML
88
+ end
89
+
90
+ app.error MissingTemplate do
91
+ content_type :html, :charset => 'utf-8'
92
+
93
+ engine = request.env['sinatra.error'].message[/\.([^\.]+)$/, 1]
94
+ path = request.path_info[/([^\/]+)$/, 1]
95
+
96
+ layout = case engine
97
+ when 'haml' then "!!!\n%html\n %body= yield"
98
+ when 'erb' then "<html>\n <body>\n <%= yield %>\n </body>\n</html>"
99
+ when 'builder' then "builder do |xml|\n xml << yield\nend"
100
+ end
101
+
102
+ layout = "<small>app.html.#{engine}</small>\n<pre>#{escape_html(layout)}</pre>" if layout
103
+
104
+ (<<-HTML).gsub(/^ {10}/, '')
105
+ <!DOCTYPE html>
106
+ <html>
107
+ <head>
108
+ <style type="text/css">
109
+ body { text-align:center;font-family:helvetica,arial;font-size:22px;
110
+ color:#888;margin:20px}
111
+ #c {margin:0 auto;width:500px;text-align:left;}
112
+ small {float:right;clear:both;}
113
+ pre {clear:both;}
114
+ </style>
115
+ </head>
116
+ <body>
117
+ <h2>Sinatra can't find #{request.env['sinatra.error'].message}</h2>
118
+ <img src='/__sinatra__/500.png'>
119
+ <div id="c">
120
+ Try this:<br />
121
+ #{layout if layout}
122
+ <small>#{path}.html.#{engine}</small>
123
+ <pre>Hello World!</pre>
124
+ <small>application.rb</small>
125
+ <pre>#{request.request_method.downcase} '#{request.path_info}' do\n respond_to do |wants|\n wants.#{engine == 'builder' ? 'xml' : 'html'} { #{engine} :#{path}#{",\n#{' '*32}layout => :app" if layout} }\n end\nend</pre>
126
+ </div>
127
+ </body>
128
+ </html>
129
+ HTML
130
+ end
131
+
132
+ end
133
+
134
+ app.class_eval do
135
+ private
136
+ def lookup_template_with_format(*args)
137
+ args[1] = "#{args[1]}.#{format}".to_sym
138
+ lookup_template_without_format *args
139
+ rescue Errno::ENOENT
140
+ raise MissingTemplate, "#{args[1]}.#{args[0]}"
141
+ end
142
+ alias_method :lookup_template_without_format, :lookup_template
143
+ alias_method :lookup_template, :lookup_template_with_format
144
+ end
145
+ end
146
+
147
+ module Helpers
148
+ def format(val=nil)
149
+ request.env['sinatra.respond_to.format'] = val.to_sym unless val.nil?
150
+ request.env['sinatra.respond_to.format']
151
+ end
152
+
153
+ def static_file?(path)
154
+ return false unless path =~ /.*[^\/]$/
155
+ public_dir = File.expand_path(options.public)
156
+ path = File.expand_path(File.join(public_dir, unescape(request.path_info)))
157
+ return false if path[0, public_dir.length] != public_dir
158
+ return false unless File.file?(path)
159
+ true
160
+ end
161
+
162
+ def respond_to(&block)
163
+ wants = {}
164
+ def wants.method_missing(type, *args, &block)
165
+ Sinatra::Base.send(:fail, "Unknown media type for respond_to: #{type}\nTry registering the extension with a mime type") if Sinatra::Base.media_type(type).nil?
166
+ self[type] = block
167
+ end
168
+
169
+ yield wants
170
+
171
+ handler = wants[format]
172
+ raise UnhandledFormat if handler.nil?
173
+
174
+ opts = [format]
175
+ opts << {:charset => options.default_charset} if TEXT_MIME_TYPES.include? format && response['Content-Type'] !~ /charset=/
176
+
177
+ content_type *opts
178
+
179
+ handler.call
180
+ end
181
+ end
182
+ end
183
+
184
+ register RespondTo
185
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cehoffman-sinatra-respond_to
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Chris Hoffman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-11 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sinatra
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.1.3
24
+ version:
25
+ description: A respond_to style Rails block for baked-in web service support in Sinatra
26
+ email: cehoffman@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.markdown
34
+ files:
35
+ - LICENSE
36
+ - README.markdown
37
+ - Rakefile
38
+ - VERSION.yml
39
+ - lib/sinatra/respond_to.rb
40
+ has_rdoc: true
41
+ homepage: http://github.com/cehoffman/sinatra-respond_to
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --charset=UTF-8
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.2.0
63
+ signing_key:
64
+ specification_version: 2
65
+ summary: A respond_to style Rails block for baked-in web service support in Sinatra
66
+ test_files: []
67
+