Rhodes provides another utility for connecting to backend services besides the SyncEngine
called AsyncHttp
. Your application can use the AsyncHttp
library to interact with web services, pull remote images, etc.
Refer to the AsyncHttp API methods to asynchronously make calls to http(s) services.
You can perform an HTTP GET request to the specified :url
. You can also provide an optional hash of :headers
and :callback_param
.
Rho::AsyncHttp.get( :url => "http://www.example.com", :headers => {"Cookie" => cookie}, :callback => (url_for :action => :httpget_callback) )
Example using Basic Auth:
Rho::AsyncHttp.get( :url => "http://www.example.com", :headers => {"Cookie" => cookie}, :callback => (url_for :action => :httpget_callback), :authentication => { :type => :basic, :username => "john", :password => "secret" } )
Example of synchronous call:
result = Rho::AsyncHttp.get( :url => "http://www.apache.org/licenses/LICENSE-2.0" ) @get_result = res["body"]
WARNING! Do NOT use synchronous calls unless you know what you are doing. This is a blocking call and will cause your UI to freeze.
You can perform an HTTP POST request to the specified :url
. As with get, you can specify optional arguments.
# :post HTTP POST body to send with request. # :http_command (optional) Use different HTTP method # (i.e. "put"). Rho::AsyncHttp.post( :url => "https://www.example.com", :headers => {"Cookie" => cookie}, :body => "username=john&password=secret", :callback => url_for(:action => :httppost_callback), :callback_param => "post=complete" )
You can download a file to the specified filename.
file_name = File.join(Rho::RhoApplication::get_base_app_path, "test.jpg") # :filename Full path to download file target. Rho::AsyncHttp.download_file( :url => "http://www.google.com/images/logos/ps_logo2.png", :filename => file_name, :headers => {}, :callback => url_for(:action => :httpdownload_callback), )
Upload the specified file using HTTP POST:
file_name = File.join(Rho::RhoApplication::get_base_app_path, "myfile.txt") # :filename Full path to download file target. # :post HTTP POST body to send with request. Rho::AsyncHttp.upload_file( :url => "http://example.com/receive_file", :filename => file_name, :body => "" #=> leave blank, AsyncHttp will fill in multipart body :headers => {"Content-Type"=>"text/plain"}, #=> used as body text content type :callback => url_for(:action => :httpupload_callback), :callback_param => "" )
You can also send multiple files in a single upload_file
request:
# :multipart Array of hashes containing # file information. # # :multipart[:filename] Name of file to be uploaded. # # :multipart[:filename_base] (optional) Base directory containing # the :filename. # :multipart[:name] (optional) File type, defaults # to "blob". # # :multipart[:content_type] (optional) Content-Type header, # defaults to "application/octet-stream". Rho::AsyncHttp.upload_file( :url => "some_url", :multipart => [ { :filename => file_name, # if missed base name from file path used :filename_base => "files_to_upload", :name => "image", :content_type => "application/octet-stream" }, # You can specify file content inline. { :body => "upload test", :name => "upload_body_test", :content_type => "plain/text" } ] )
As you noticed with each of the code samples above, we specified a :callback
action. This callback will execute when the AsyncHttp
request is completed.
Refer to AsyncHttp API for a discussion of the callback parameters.
Adding an animated transition to an AsyncHttp
request requires some small setup and is useful for displaying a smoother user experience.
To enable an animated transition, the controller action must set a "Wait-Page"
response header after making the AsyncHttp
call. The response header tells the user interface that an AsyncHttp
request has been spawned and that the rendered view should be treated as a transient page, it will not be added to the navigation history.
def async_show Rho::AsyncHttp.get( :url => "http://rhostore.herokuapp.com/products/#{@params["product_id"]}.json", :callback => url_for(:action => :show_callback), ) @response["headers"]["Wait-Page"] = "true" render :action => :waiting end
This example renders a waiting screen while awaiting a response from the AsyncHttp
request. The :waiting
page is transient and will not be added to the navigation history, which means clicking back won’t open the page.
The AsyncHttp
callback can render the response by calling render_transition
. This function is defined in ApplicationHelper
so make sure you include
it in your controller. The render_transition
function works much like render
except that it will animate a transition from the previous page.
Below, a product model is created using the response from the web service and then calling render_transition
, which leverages the show view template:
include ApplicationHelper def show_callback if @params["status"] == "ok" @product = Product.new(@params["body"]["product"]) @product.object = @product.id render_transition :action => :show else # In this example, an error just navigates back to the index w/o transition. WebView.navigate url_for :action => :index end end
To disable jQuery Mobile page caching (by default jQuery Mobile cachepages in the DOM) globally, look for cache control options in jQuery Mobile documentation
You can disable page caching globally by using jQuery Mobile initialization option in layout.erb file:
$.mobile.page.prototype.options.domCache = true;
Also, you can disable caching on exact page transition with data-dom-cache
attribute, like that: link text
If you deploy to platforms that don"t handle animated transitions (like Windows Mobile and BlackBerry), the controller will need to handle both cases. In your AsyncHttp
request, you"ll need to set the callback_param
with the @request
variable.
There’s a helper function called caller_request_hash_to_query
defined in ApplicationHelper
that you can invoke. The returned value is a string that looks like “_request=<json_request>
is the URL-encoded JSON representation of the @request
value. This parameter is used to give the callback function some context of whether the user interface made the request with or without transition enabled.
include ApplicationHelper def async_show Rho::AsyncHttp.get( :url => "http://rhostore.herokuapp.com/products/#{@params["product_id"]}.json", :callback => url_for(:action => :show_callback), :callback_param => caller_request_hash_to_query ) @response["headers"]["Wait-Page"] = "true" render :action => :waiting end
In your callback function, the first thing you need to do is invoke caller_request_query_to_hash
(also defined in ApplicationHelper
) that deserializes the @request
query parameter value passed in via callback_param
shown in the example above. The function sets a @caller_request
in the current context. You can then use it to determine if the user interface had transition enabled by inspecting the "Transition-Enabled"
request header. For transitions, call render_transition
, otherwise call WebView.navigate
.
def show_callback caller_request_query_to_hash if @params["status"] == "ok" @product = Product.new(@params["body"]["product"]) @product.object = @product.id if @caller_request["headers"]["Transition-Enabled"] == "true" render_transition :action => :show else WebView.navigate( url_for(:action => :show, :id => @product.object) ) end else WebView.navigate( url_for(:action => :index) ) end end
Here is a controller in the Rexml sample from the System API Samples. It makes a AsyncHttp.get
call to a test web service. Then it parses the web service response with rexml and displays the result.
def webservicetest Rho::AsyncHttp.get( :url => "http://rhostore.herokuapp.com/products.xml", :callback => url_for(:action => :httpget_callback), ) render :action => :wait end def get_res @@get_result end def get_error @@error_params end def httpget_callback if @params["status"] != "ok" @@error_params = @params WebView.navigate( url_for(:action => :show_error) ) else @@get_result = @params["body"] begin require "rexml/document" doc = REXML::Document.new(@@get_result) puts "doc : #{doc}" rescue Exception => e puts "Error: #{e}" @@get_result = "Error: #{e}" end WebView.navigate( url_for(:action => :show_result) ) end end def show_result render :action => :webservicetest, :back => "/app/RexmlTest" end