deltacloud-core 0.0.6 → 0.0.7

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.
@@ -180,7 +180,7 @@ class OpennebulaDriver < Deltacloud::BaseDriver
180
180
 
181
181
  imageid = computehash['STORAGE/DISK[@type="disk"]'].attributes['href'].split("/").last
182
182
 
183
- state = (computehash['STATE'].text == 'ACTIVE') ? 'RUNNING' : computehash['STATE'].text
183
+ state = (computehash['STATE'].text == "ACTIVE") ? "RUNNING" : "STOPPED"
184
184
 
185
185
  hwp_name = computehash['INSTANCE_TYPE'] || 'small'
186
186
 
@@ -18,6 +18,7 @@
18
18
 
19
19
  require 'deltacloud/base_driver'
20
20
  require 'deltacloud/drivers/rackspace/rackspace_client'
21
+ require 'cloudfiles'
21
22
 
22
23
  module Deltacloud
23
24
  module Drivers
@@ -27,8 +28,13 @@ class RackspaceDriver < Deltacloud::BaseDriver
27
28
 
28
29
  feature :instances, :user_name
29
30
 
31
+ def supported_collections
32
+ DEFAULT_COLLECTIONS + [ :buckets ]
33
+ end
34
+
30
35
  def hardware_profiles(credentials, opts = nil)
31
36
  racks = new_client( credentials )
37
+ results=""
32
38
  safely do
33
39
  results = racks.list_flavors.map do |flav|
34
40
  HardwareProfile.new(flav["id"].to_s) do
@@ -43,6 +49,7 @@ class RackspaceDriver < Deltacloud::BaseDriver
43
49
 
44
50
  def images(credentials, opts=nil)
45
51
  racks = new_client( credentials )
52
+ results=""
46
53
  safely do
47
54
  results = racks.list_images.map do |img|
48
55
  Image.new( {
@@ -75,8 +82,8 @@ class RackspaceDriver < Deltacloud::BaseDriver
75
82
  end
76
83
  Instance.new( {
77
84
  :id => id,
78
- :state => "REBOOT",
79
- :actions => instance_actions_for( state ),
85
+ :state => "RUNNING",
86
+ :actions => instance_actions_for( "RUNNING" ),
80
87
  } )
81
88
  end
82
89
 
@@ -141,6 +148,88 @@ class RackspaceDriver < Deltacloud::BaseDriver
141
148
  end
142
149
 
143
150
 
151
+
152
+
153
+ define_instance_states do
154
+ start.to( :pending ) .on( :create )
155
+
156
+ pending.to( :running ) .automatically
157
+
158
+ running.to( :running ) .on( :reboot )
159
+ running.to( :shutting_down ) .on( :stop )
160
+
161
+ shutting_down.to( :stopped ) .automatically
162
+
163
+ stopped.to( :finish ) .automatically
164
+ end
165
+
166
+ #--
167
+ # Buckets
168
+ #--
169
+ def buckets(credentials, opts)
170
+ bucket_list = []
171
+ cf = cloudfiles_client(credentials)
172
+ safely do
173
+ cf.containers.each do |container_name|
174
+ current = cf.container(container_name)
175
+ bucket_list << convert_container(current)
176
+ end #containers.each
177
+ end #safely
178
+ bucket_list = filter_on(bucket_list, :id, opts)
179
+ bucket_list
180
+ end
181
+
182
+ #--
183
+ # Create Bucket
184
+ #--
185
+ def create_bucket(credentials, name, opts)
186
+ bucket = nil
187
+ cf = cloudfiles_client(credentials)
188
+ safely do
189
+ new_bucket = cf.create_container(name)
190
+ bucket = convert_container(new_bucket)
191
+ end
192
+ bucket
193
+ end
194
+
195
+ #--
196
+ # Delete Bucket
197
+ #--
198
+ def delete_bucket(credentials, name, opts)
199
+ cf = cloudfiles_client(credentials)
200
+ safely do
201
+ cf.delete_container(name)
202
+ end
203
+ end
204
+
205
+ #--
206
+ # Blobs
207
+ #--
208
+ def blobs(credentials, opts)
209
+ cf = cloudfiles_client(credentials)
210
+ blobs = []
211
+ safely do
212
+ cf_container = cf.container(opts['bucket'])
213
+ cf_container.objects.each do |object_name|
214
+ blobs << convert_object(cf_container.object(object_name))
215
+ end
216
+ end
217
+ blobs = filter_on(blobs, :id, opts)
218
+ blobs
219
+ end
220
+
221
+ #-
222
+ # Blob data
223
+ #-
224
+ def blob_data(credentials, bucket_id, blob_id, opts)
225
+ cf = cloudfiles_client(credentials)
226
+ cf.container(bucket_id).object(blob_id).data_stream do |chunk|
227
+ yield chunk
228
+ end
229
+ end
230
+
231
+ private
232
+
144
233
  def convert_srv_to_instance(srv)
145
234
  inst = Instance.new(:id => srv["id"].to_s,
146
235
  :owner_id => "root",
@@ -163,17 +252,27 @@ class RackspaceDriver < Deltacloud::BaseDriver
163
252
  end
164
253
  end
165
254
 
166
- define_instance_states do
167
- start.to( :pending ) .on( :create )
168
-
169
- pending.to( :running ) .automatically
170
-
171
- running.to( :running ) .on( :reboot )
172
- running.to( :shutting_down ) .on( :stop )
255
+ def convert_container(cf_container)
256
+ Bucket.new({ :id => cf_container.name,
257
+ :name => cf_container.name,
258
+ :size => cf_container.count,
259
+ :blob_list => cf_container.objects
260
+ })
261
+ end
173
262
 
174
- shutting_down.to( :stopped ) .automatically
263
+ def convert_object(cf_object)
264
+ Blob.new({ :id => cf_object.name,
265
+ :bucket => cf_object.container.name,
266
+ :content_length => cf_object.bytes,
267
+ :content_type => cf_object.content_type,
268
+ :last_modified => cf_object.last_modified
269
+ })
270
+ end
175
271
 
176
- stopped.to( :finish ) .automatically
272
+ def cloudfiles_client(credentials)
273
+ safely do
274
+ CloudFiles::Connection.new(credentials.user, credentials.password)
275
+ end
177
276
  end
178
277
 
179
278
  def safely(&block)
@@ -78,10 +78,13 @@ class RHEVMDriver < Deltacloud::BaseDriver
78
78
 
79
79
  def statify(state)
80
80
  st = state.nil? ? "" : state.upcase()
81
- return "running" if st == "UP"
82
- return "stopped" if st == "DOWN"
83
- return "pending" if st == "POWERING UP"
84
- st
81
+ case st
82
+ when "UP"
83
+ "RUNNING"
84
+ when "DOWN"
85
+ "STOPPED"
86
+ when "POWERING UP"
87
+ "PENDING"
85
88
  end
86
89
 
87
90
  define_hardware_profile 'rhevm'
@@ -54,6 +54,13 @@ module ApplicationHelper
54
54
  return 'password' if driver_has_feature?(:authentication_password)
55
55
  end
56
56
 
57
+ def driver_has_bucket_location_feature?
58
+ driver.features(:buckets).each do |feat|
59
+ return true if feat.name == :bucket_location
60
+ end
61
+ false
62
+ end
63
+
57
64
  def filter_all(model)
58
65
  filter = {}
59
66
  filter.merge!(:id => params[:id]) if params[:id]
@@ -100,8 +107,8 @@ module ApplicationHelper
100
107
  return redirect(instances_url) if name.eql?(:destroy) or @instance.class!=Instance
101
108
 
102
109
  respond_to do |format|
103
- format.html { haml :"instances/show" }
104
110
  format.xml { haml :"instances/show" }
111
+ format.html { haml :"instances/show" }
105
112
  format.json {convert_to_json(:instance, @instance) }
106
113
  end
107
114
  end
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2010 Red Hat, Inc.
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one or more
4
+ # contributor license agreements. See the NOTICE file distributed with
5
+ # this work for additional information regarding copyright ownership. The
6
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance with the
8
+ # License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ #--
19
+ # based on the example from http://macournoyer.com/blog/2009/06/04/pusher-and-async-with-thin/
20
+ #--
21
+ require 'eventmachine'
22
+ class BlobStream
23
+ AsyncResponse = [-1, {}, []].freeze
24
+ def self.call(env, credentials, params)
25
+ body = DeferrableBody.new
26
+ #Get the headers out asap. Don't specify a content-type let
27
+ #the client guess and if they can't they SHOULD default to
28
+ #'application/octet-stream' anyway as per:
29
+ #http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
30
+ EM.next_tick { env['async.callback'].call [200, {'Content-Type' => "#{params['content_type']}", 'Content-Length' => "#{params['content_length']}"}, body] }
31
+ #call the driver from here. the driver method yields for every chunk of blob it receives. We then
32
+ #use body.call to write that chunk as received.
33
+ driver.blob_data(credentials, params[:bucket], params[:blob], params) {|chunk| body.call ["#{chunk}"]} #close blob_data block
34
+ body.succeed
35
+ AsyncResponse # Tells Thin to not close the connection and continue it's work on other request
36
+ end
37
+ end
38
+
39
+ class DeferrableBody
40
+ include EventMachine::Deferrable
41
+
42
+ def call(body)
43
+ body.each do |chunk|
44
+ @body_callback.call(chunk)
45
+ end
46
+ end
47
+
48
+ def each(&blk)
49
+ @body_callback = blk
50
+ end
51
+ end
@@ -0,0 +1,26 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one or more
5
+ # contributor license agreements. See the NOTICE file distributed with
6
+ # this work for additional information regarding copyright ownership. The
7
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance with the
9
+ # License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
+ # License for the specific language governing permissions and limitations
17
+ # under the License.
18
+
19
+ class Blob < BaseModel
20
+ #already has an id from basemodel (for the key)
21
+ attr_accessor :bucket
22
+ attr_accessor :content_length
23
+ attr_accessor :content_type
24
+ attr_accessor :last_modified
25
+ attr_accessor :content
26
+ end
@@ -0,0 +1,24 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # Licensed to the Apache Software Foundation (ASF) under one or more
5
+ # contributor license agreements. See the NOTICE file distributed with
6
+ # this work for additional information regarding copyright ownership. The
7
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance with the
9
+ # License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
+ # License for the specific language governing permissions and limitations
17
+ # under the License.
18
+
19
+ class Bucket < BaseModel
20
+
21
+ attr_accessor :name
22
+ attr_accessor :size
23
+ attr_accessor :blob_list
24
+ end
@@ -6,6 +6,7 @@ DRIVERS = {
6
6
  :rimuhosting => { :name => "RimuHosting"},
7
7
  :opennebula => { :name => "Opennebula", :class => "OpennebulaDriver" },
8
8
  :terremark => { :name => "Terremark"},
9
+ :azure => { :name => "Azure" },
9
10
  :mock => { :name => "Mock" }
10
11
  }
11
12
 
@@ -1,93 +1,132 @@
1
+ # respond_to (The MIT License)
2
+
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software
4
+ # and associated documentation files (the 'Software'), to deal in the Software without restriction,
5
+ # including without limitation the rights to use, copy, modify, merge, publish, distribute,
6
+ # sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
7
+ # furnished to do so, subject to the following conditions:
8
+ #
9
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
10
+ # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
11
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
12
+ # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
14
+
1
15
  require 'sinatra/base'
2
- require 'sinatra/accept_media_types'
16
+ require 'rack/accept'
3
17
 
4
- # Accept header parsing was looked at but deemed
5
- # too much of an irregularity to deal with. Problems with the header
6
- # differences from IE, Firefox, Safari, and every other UA causes
7
- # problems with the expected output. The general expected behavior
8
- # would be serve html when no extension provided, but most UAs say
9
- # they will accept application/xml with out a quality indicator, meaning
10
- # you'd get the xml block served insead. Just plain retarded, use the
11
- # extension and you'll never be suprised.
18
+ use Rack::Accept
12
19
 
13
20
  module Sinatra
14
21
  module RespondTo
15
- class UnhandledFormat < Sinatra::NotFound; end
16
- class MissingTemplate < Sinatra::NotFound
17
- def code; 500 end
22
+
23
+ class MissingTemplate < Sinatra::NotFound; end
24
+
25
+ # Define all MIME types you want to support here.
26
+ # This conversion table will be used for auto-negotiation
27
+ # with browser in sinatra when no 'format' parameter is specified.
28
+
29
+ SUPPORTED_ACCEPT_HEADERS = {
30
+ :xml => [
31
+ 'text/xml',
32
+ 'application/xml'
33
+ ],
34
+ :html => [
35
+ 'text/html',
36
+ 'application/xhtml+xml'
37
+ ],
38
+ :json => [
39
+ 'application/json'
40
+ ]
41
+ }
42
+
43
+ # We need to pass array of available response types to
44
+ # best_media_type method
45
+ def accept_to_array
46
+ SUPPORTED_ACCEPT_HEADERS.keys.collect do |key|
47
+ SUPPORTED_ACCEPT_HEADERS[key]
48
+ end.flatten
18
49
  end
19
50
 
20
- TEXT_MIME_TYPES = [:txt, :html, :js, :json, :xml, :rss, :atom, :css, :asm, :c, :cc, :conf,
21
- :csv, :cxx, :diff, :dtd, :f, :f77, :f90, :for, :gemspec, :h, :hh, :htm,
22
- :log, :mathml, :mml, :p, :pas, :pl, :pm, :py, :rake, :rb, :rdf, :rtf, :ru,
23
- :s, :sgm, :sgml, :sh, :svg, :svgz, :text, :wsdl, :xhtml, :xsl, :xslt, :yaml,
24
- :yml, :ics, :png]
51
+ # Then, when we get best media type for response, we need
52
+ # to know which format to choose
53
+ def lookup_format_from_mime(mime)
54
+ SUPPORTED_ACCEPT_HEADERS.keys.each do |format|
55
+ return format if SUPPORTED_ACCEPT_HEADERS[format].include?(mime)
56
+ end
57
+ end
25
58
 
26
59
  def self.registered(app)
60
+
27
61
  app.helpers RespondTo::Helpers
28
62
 
29
- app.set :default_charset, 'utf-8'
30
- app.set :default_content, :html
31
- app.set :assume_xhr_is_js, true
32
-
33
- # We remove the trailing extension so routes
34
- # don't have to be of the style
35
- #
36
- # get '/resouce.:format'
37
- #
38
- # They can instead be of the style
39
- #
40
- # get '/resource'
41
- #
42
- # and the format will automatically be available in <tt>format</tt>
43
63
  app.before do
44
- # Let through sinatra image urls in development
64
+
65
+ # Skip development error image and static content
45
66
  next if self.class.development? && request.path_info =~ %r{/__sinatra__/.*?.png}
67
+ next if options.static? && options.public? && (request.get? || request.head?) && static_file?(request.path_info)
46
68
 
47
- unless options.static? && options.public? && (request.get? || request.head?) && static_file?(request.path_info)
48
- rpi = request.path_info.sub(%r{\.([^\./]+)$}, '')
49
-
50
- if (not $1) or ($1 and TEXT_MIME_TYPES.include?($1.to_sym))
51
- request.path_info, ext = rpi, nil
52
- if ! $1.nil?
53
- ext = $1
54
- elsif env['HTTP_ACCEPT'].nil? || env['HTTP_ACCEPT'].empty?
55
- ext = options.default_content
56
- end
57
- if ext
58
- @mime_types = [ Helpers::mime_type(ext) ]
59
- format ext
60
- end
61
- end
69
+ # Remove extension from URI
70
+ # Extension will be available as a 'extension' method (extension=='txt')
71
+
72
+ extension request.path_info.match(/\.([^\.\/]+)$/).to_a.first
73
+
74
+ # If ?format= is present, ignore all Accept negotiations because
75
+ # we are not dealing with browser
76
+ if request.params.has_key? 'format'
77
+ format params['format'].to_sym
78
+ end
79
+
80
+ # Let's make a little exception here to handle
81
+ # /api/instance_states[.gv/.png] calls
82
+ if extension.eql?('gv')
83
+ format :gv
84
+ elsif extension.eql?('png')
85
+ format :png
62
86
  end
63
- end
64
87
 
65
- app.configure :development do |dev|
66
- dev.error UnhandledFormat do
67
- content_type :html, :charset => 'utf-8'
88
+ # Get Rack::Accept::Response object and find best possible
89
+ # mime type to output.
90
+ # This negotiation works fine with latest rest-client gem:
91
+ #
92
+ # RestClient.get 'http://localhost:3001/api', {:accept => :json } =>
93
+ # 'application/json'
94
+ # RestClient.get 'http://localhost:3001/api', {:accept => :xml } =>
95
+ # 'application/xml'
96
+ #
97
+ # Also browsers like Firefox (3.6.x) and Chromium reporting
98
+ # 'application/xml+xhtml' which is recognized as :html reponse
99
+ # In browser you can force output using ?format=[format] parameter.
100
+
101
+ rack_accept = env['rack-accept.request']
102
+
103
+ if rack_accept.media_type.to_s.strip.eql?('Accept:')
104
+ format :xml
105
+ else
106
+ format lookup_format_from_mime(rack_accept.best_media_type(accept_to_array))
107
+ end
68
108
 
69
- (<<-HTML).gsub(/^ {10}/, '')
70
- <!DOCTYPE html>
71
- <html>
72
- <head>
73
- <style type="text/css">
74
- body { text-align:center;font-family:helvetica,arial;font-size:22px;
75
- color:#888;margin:20px}
76
- #c {margin:0 auto;width:500px;text-align:left}
77
- </style>
78
- </head>
79
- <body>
80
- <h2>Sinatra doesn't know this ditty.</h2>
81
- <img src='/__sinatra__/404.png'>
82
- <div id="c">
83
- Try this:
84
- <pre>#{request.request_method.downcase} '#{request.path_info}' do\n respond_to do |wants|\n wants.#{format} { "Hello World" }\n end\nend</pre>
85
- </div>
86
- </body>
87
- </html>
88
- HTML
109
+ end
110
+
111
+ app.class_eval do
112
+ # This code was copied from respond_to plugin
113
+ # http://github.com/cehoffman/sinatra-respond_to
114
+ # MIT License
115
+ alias :render_without_format :render
116
+ def render(*args, &block)
117
+ assumed_layout = args[1] == :layout
118
+ args[1] = "#{args[1]}.#{format}".to_sym if args[1].is_a?(::Symbol)
119
+ render_without_format *args, &block
120
+ rescue Errno::ENOENT => e
121
+ raise MissingTemplate, "#{args[1]}.#{args[0]}" unless assumed_layout
122
+ raise e
89
123
  end
124
+ private :render
125
+ end
90
126
 
127
+ # This code was copied from respond_to plugin
128
+ # http://github.com/cehoffman/sinatra-respond_to
129
+ app.configure :development do |dev|
91
130
  dev.error MissingTemplate do
92
131
  content_type :html, :charset => 'utf-8'
93
132
  response.status = request.env['sinatra.error'].code
@@ -103,7 +142,6 @@ module Sinatra
103
142
  layout = case engine
104
143
  when 'haml' then "!!!\n%html\n %body= yield"
105
144
  when 'erb' then "<html>\n <body>\n <%= yield %>\n </body>\n</html>"
106
- when 'builder' then ::Sinatra::VERSION =~ /^1.0/ ? "xml << yield" : "builder do |xml|\n xml << yield\nend"
107
145
  end
108
146
 
109
147
  layout = "<small>app.#{format}.#{engine}</small>\n<pre>#{escape_html(layout)}</pre>"
@@ -117,19 +155,16 @@ module Sinatra
117
155
  color:#888;margin:20px}
118
156
  #c {margin:0 auto;width:500px;text-align:left;}
119
157
  small {float:right;clear:both;}
120
- pre {clear:both;}
158
+ pre {clear:both;text-align:left;font-size:70%;width:500px;margin:0 auto;}
121
159
  </style>
122
160
  </head>
123
161
  <body>
124
162
  <h2>Sinatra can't find #{request.env['sinatra.error'].message}</h2>
125
163
  <img src='/__sinatra__/500.png'>
164
+ <pre>#{request.env['sinatra.error'].backtrace.join("\n")}</pre>
126
165
  <div id="c">
127
- Try this:<br />
128
- #{layout}
129
- <small>#{path}.#{format}.#{engine}</small>
130
- <pre>Hello World!</pre>
131
166
  <small>application.rb</small>
132
- <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>
167
+ <pre>#{request.request_method.downcase} '#{request.path_info}' do\n respond_to do |wants|\n wants.#{format} { #{engine} :#{path} }\n end\nend</pre>
133
168
  </div>
134
169
  </body>
135
170
  </html>
@@ -137,75 +172,23 @@ module Sinatra
137
172
  end
138
173
 
139
174
  end
140
-
141
- app.class_eval do
142
- private
143
- def accept_list
144
- @mime_types || Rack::AcceptMediaTypes.new(env['HTTP_ACCEPT'] || '')
145
- end
146
-
147
- # Changes in 1.0 Sinatra reuse render for layout so we store
148
- # the original value to tell us if this is an automatic attempt
149
- # to do a layout call. If it is, it might fail with Errno::ENOENT
150
- # and we want to pass that back to sinatra since it isn't a MissingTemplate
151
- # error
152
- def render_with_format(*args, &block)
153
- assumed_layout = args[1] == :layout
154
- args[1] = "#{args[1]}.#{format}".to_sym if args[1].is_a?(::Symbol)
155
- render_without_format *args, &block
156
- rescue Errno::ENOENT => e
157
- raise MissingTemplate, "#{args[1]}.#{args[0]}" unless assumed_layout
158
- raise e
159
- end
160
- alias_method :render_without_format, :render
161
- alias_method :render, :render_with_format
162
-
163
- if ::Sinatra::VERSION =~ /^0\.9/
164
- def lookup_layout_with_format(*args)
165
- args[1] = "#{args[1]}.#{format}".to_sym if args[1].is_a?(::Symbol)
166
- lookup_layout_without_format *args
167
- end
168
- alias_method :lookup_layout_without_format, :lookup_layout
169
- alias_method :lookup_layout, :lookup_layout_with_format
170
- end
171
- end
172
175
  end
173
176
 
174
177
  module Helpers
175
- # Patch the content_type function to remember the set type
176
- # This helps cut down on time in the format helper so it
177
- # doesn't have to do a reverse lookup on the header
178
+
179
+ # This code was copied from respond_to plugin
180
+ # http://github.com/cehoffman/sinatra-respond_to
178
181
  def self.included(klass)
179
182
  klass.class_eval do
180
- def content_type_with_save(*args)
183
+ alias :content_type_without_save :content_type
184
+ def content_type(*args)
181
185
  content_type_without_save *args
182
186
  @_format = args.first.to_sym
183
187
  response['Content-Type']
184
188
  end
185
- alias_method :content_type_without_save, :content_type
186
- alias_method :content_type, :content_type_with_save
187
- end if ::Sinatra::VERSION =~ /^1.0/
188
- end
189
-
190
- def self.mime_type(sym)
191
- ::Sinatra::Base.respond_to?(:mime_type) && ::Sinatra::Base.mime_type(sym) || ::Sinatra::Base.media_type(sym)
192
- end
193
-
194
- def format(val=nil)
195
- unless val.nil?
196
- mime_type = ::Sinatra::RespondTo::Helpers.mime_type(val)
197
- fail "Unknown media type #{val}\nTry registering the extension with a mime type" if mime_type.nil?
198
-
199
- @_format = val.to_sym
200
- response['Content-Type'].sub!(/^[^;]+/, mime_type)
201
- charset options.default_charset if Sinatra::RespondTo::TEXT_MIME_TYPES.include?(format) and format!=:png
202
189
  end
203
-
204
- @_format
205
190
  end
206
191
 
207
- # This is mostly just a helper so request.path_info isn't changed when
208
- # serving files from the public directory
209
192
  def static_file?(path)
210
193
  public_dir = File.expand_path(options.public)
211
194
  path = File.expand_path(File.join(public_dir, unescape(path)))
@@ -213,60 +196,43 @@ module Sinatra
213
196
  path[0, public_dir.length] == public_dir && File.file?(path)
214
197
  end
215
198
 
216
- def charset(val=nil)
217
- fail "Content-Type must be set in order to specify a charset" if response['Content-Type'].nil?
218
-
219
- if response['Content-Type'] =~ /charset=[^;]+/
220
- response['Content-Type'].sub!(/charset=[^;]+/, (val == '' && '') || "charset=#{val}")
221
- else
222
- response['Content-Type'] += ";charset=#{val}"
223
- end unless val.nil?
224
199
 
225
- response['Content-Type'][/charset=([^;]+)/, 1]
200
+ # Extension holds trimmed extension. This is extra usefull
201
+ # when you want to build original URI (with extension)
202
+ # You can simply call "#{request.env['REQUEST_URI']}.#{extension}"
203
+ def extension(val=nil)
204
+ @_extension ||= val
205
+ @_extension
226
206
  end
227
-
228
- def respond_to(&block)
229
- wants = Format.new
230
- yield wants
231
- fmt, type, handler = match_accept_type(accept_list, wants)
232
- raise UnhandledFormat if fmt.nil?
233
- format fmt
234
- handler.nil? ? nil : handler.call
207
+
208
+ # This helper will holds current format. Helper should be
209
+ # accesible from all places in Sinatra
210
+ def format(val=nil)
211
+ @_format ||= val
212
+ @_format
235
213
  end
236
214
 
237
- def match_accept_type(mime_types, format)
238
- selected = []
239
- accepted_types = mime_types.map {|type| Regexp.escape(type).gsub(/\\\*/,'.*') }
240
- # Fix for Chrome based browsers which returns XML when 'xhtml' is requested.
241
- if env['HTTP_USER_AGENT'] =~ /Chrome/ and accepted_types.size>1
242
- accepted_types[0], accepted_types[1] = accepted_types[1], accepted_types[0]
243
- if accepted_types[0].eql?('application/xhtml\\+xml')
244
- accepted_types[0] = 'text/html'
245
- end
215
+ def respond_to(&block)
216
+ wants = {}
217
+
218
+ def wants.method_missing(type, *args, &handler)
219
+ self[type] = handler
246
220
  end
247
- accepted_types.each do |at|
248
- format.each do |fmt, ht, handler|
249
- (selected = [fmt, ht, handler]) and break if ht.match(at)
250
- end
251
- break unless selected.empty?
221
+
222
+ # Set proper content-type and encoding for
223
+ # text based formats
224
+ if [:xml, :gv, :html, :json].include?(format)
225
+ content_type format, :charset => 'utf-8'
252
226
  end
253
- selected
254
- end
227
+ yield wants
228
+ # Raise this error if requested format is not defined
229
+ # in respond_to { } block.
230
+ raise MissingTemplate if wants[format].nil?
255
231
 
256
- # NOTE Array instead of hash because order matters (wildcard type
257
- # matches first handler)
258
- class Format < Array #:nodoc:
259
- def method_missing(format, *args, &handler)
260
- mt = Sinatra::RespondTo::Helpers.mime_type(format)
261
- if mt.nil?
262
- Sinatra::Base.send(:fail, "Unknown media type for respond_to: #{format}\nTry registering the extension with a mime type")
263
- end
264
- self << [format.to_s, mt, handler]
265
- end
232
+ wants[format].call
266
233
  end
234
+
267
235
  end
236
+
268
237
  end
269
238
  end
270
-
271
- Rack::Mime::MIME_TYPES.merge!({ ".gv" => "text/plain" })
272
- Sinatra::Application.register Sinatra::RespondTo