deltacloud-core 0.0.6 → 0.0.7

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