RailsBridge¶ ↑
DESCRIPTION¶ ↑
Easy embedding of remote content into your Rails app, plus exporting of your Rails layouts templatized for other languages (such as PHP).
SYNOPSIS¶ ↑
RailsBridge has two components.
RailsBridge::ContentBridge¶ ↑
Allows for easy embedding of content from remote servers directly into the HTML, XML, or Javascript returned by your Rails application. The remote server may be an external application, a web service, or any other resource available via HTTP on the network.
RailsBridge::LayoutBridge¶ ↑
Allows for easy exporting of your Rails HTML layouts as templates for other applications.
INSTALLATION¶ ↑
rails_bridge is installed as a Ruby gem.
gem install rails_bridge
Include the gem in your Gemfile.
gem 'rails_bridge'
(Note: RailsBridge depends on the Typhoeus gem. See CREDITS for more information about installation requirements for Typhoeus if you have difficulty with installation.)
CONTENT BRIDGE¶ ↑
The content bridge is used to retrieve and cache content from external HTTP servers. This is achieved by defining and executing content requests. Requests are defined using the RailsBridge::ContentBridge class.
Up and Running in 5 minutes¶ ↑
-
Install the gem in your Rails app.
-
Generate a content bridge using the provided Rails generator rails g content_bridge twitter_status_cacher
-
Edit the generated class file (app/rails_bridge/content_bridges/twitter_status_cacher.rb).
The following shows an example.
require 'json' class TwitterStatusCacher < RailsBridge::ContentBridge self.request_timeout = 1000 # miliseconds self.cache_timeout = 60 # seconds self.host = 'api.twitter.com' self.path = '/statuses/user_timeline.json' self.default_content = '<<Twitter unavailable>>' self.on_success {|content| JSON.parse(content).first["text"]} end
In your controller:
@soopa_latest_tweet = TwitterStatusCacher.get_remote_content(:params=>{:screen_name => 'soopa'})
That’s it! If the request time’s out before 1 second, “<<Twitter unavailable>>” will be returned instead. If the request succeeds, the content will be cached locally for 60 seconds and returned for each subsequent call to
TwitterStatusCacher.get_remote_content(:params=>{:screen_name => 'soopa'})
There are other more flexible ways to define reusable content requests. Read on for more details.
Defining reusable requests¶ ↑
Reusable requests can be defined on the ContentBridge class:
RailsBridge::ContentBridge.content_request( :server_com ) do |request| request.protocol = 'http' # can be 'http' or 'https'. 'http' is the default request.host = "server.com" request.port = 8080 # default is 80 request.path = "/some/path" request.params = {:param1=>'a value', :param2=>'another value'} # URL query params request.default_content = "Content unavailable at this time." request.request_timeout = 1000 # miliseconds request.cache_timeout = 5000 # seconds end
These requests can then be executed using the automatically generated class method:
content = RailsBridge::ContentBridge.get_server_com
This issues an HTTP GET to “server.com:8080/some/path?param1=a%20value¶m2=another%20value” and returns the content. If the server is unavailable or returns an error, or if the request times out, the default content is returned instead.
You can also simply use the url method to define the protocol
, host
, port
, path
, and params
values of a request. The following declaration is equivalent to the definition above.
RailsBridge::ContentBridge.content_request( :server_com ) do |request| request.url = "http://server.com:8080/some/path?p1=value" request.default_content = "Content unavailable at this time." request.request_timeout = 1000 # miliseconds request.cache_timeout = 5000 # seconds end
Pre-processing returned content¶ ↑
You may pre-process the content returned by a request by defining an on_success
block. The remote content is passed as a parameter and the return value of the block is used in place of the original content.
RailsBridge::ContentBridge.content_request( :server_com ) do |request| request.url = "http://server.com:8080/some/path" request.params = {:param1=>'a value', :param2=>'another value'} request.default_content = "Content unavailable at this time." request.request_timeout = 1000 # miliseconds request.on_success {|content| JSON.parse(content)} end
Caching¶ ↑
Content is automatically cached if a cache timeout is defined for the request.
RailsBridge::ContentBridge.content_request( :server_com ) do |request| request.url = "http://server.com:8080/some/path" request.params = {:param1=>'a value', :param2=>'another value'} request.default_content = "Content unavailable at this time." request.request_timeout = 1000 # miliseconds request.cache_timeout = 3600 # seconds end content = RailsBridge::ContentBridge.get_server_com
The content returned from first request will be cached for 3600 seconds. Subsequent requests issued during that time will return the cached content.
To explicitly skip the cache, override the cache_timeout with a value of 0. This will also remove any entry in the cache for this request.
content = RailsBridge::ContentBridge.get_server_com( :cache_timeout=>0 )
The cache key is derived from the complete URL of the request, including query params, so each request with a unique URL is cached independently.
When RailsBridge is loaded in a Rails application, the configured Rails cache is used by default. When loaded outside Rails, ActiveSupport::Cache::MemoryStore is used by default.
One-line requests¶ ↑
If desired, all of the above functionality can be achieved in one line:
content = RailsBridge::ContentBridge.get_remote_content("http://server.com:8080/some/path?param1=a%20value¶m2=another%20value", {:cache_timeout=>3600, :default_content=>"Content Unavailable", :request_timeout=>1000} )
Setting defaults for a group of requests.¶ ↑
Default values can be set for a group of requests by sub-classing RailsBridge::ContentBridge and defining them on the class:
class TwitterStatusCacher < RailsBridge::ContentBridge self.request_timeout = 500 # miliseconds self.cache_timeout = 60 # seconds self.host = 'api.twitter.com' self.path = '/statuses/user_timeline.json' self.on_success {|content| JSON.parse(content)} content_request( :hoonpark ) {|r| r.params = {:screen_name => 'hoonpark'}} content_request( :soopa ) {|r| r.params = {:screen_name => 'soopa'}} end soopa_latest_tweet = TwitterStatusCacher.get_soopa.first hoonpark_latest_tweet = TwitterStatusCacher.get_hoonpark.first
Setting global defaults¶ ↑
Global default values can be set directly on the RailsBridge::ContentBridge class. They are used by all inheriting sub-classes unless overridden by the sub-class.
RailsBridge::ContentBridge.logger = Logger.new(STDOUT) RailsBridge::ContentBridge.cache = ActiveSupport::Cache::MemCacheStore(:compress => true) RailsBridge::ContentBridge.cache_timeout = 5.minutes RailsBridge::ContentBridge.request_timeout = 500 # miliseconds
I recommend putting these declaration in an initializer file under config/initializers. For example config/initializers/content_bridge.rb.
Overriding request values on execution¶ ↑
All defined values for a request can be specified at runtime by passing them in as options to the get method. All parameters passed this way will override existing values, with the exception of :params, which are merged with those defined at the request declaration and class level.
content = RailsBridge::ContentBridge.get_server_com( :params=>{:p1=>'p1'}, :request_timeout=>2000, :cache_timeout=>0 )
Using the provided Rails generator to create ContentBridge classes.¶ ↑
A generator is included named ‘content_bridge’ that automates generation of content bridge classes in your application.
eg.
rails g content_bridge my_content_bridge my_request
will create a class named MyContentBridge
containing a prototype request definition named my_request
and place it under _app/rails_bridge/content_bridges.
Batching requests for parallel execution¶ ↑
You can also queue up a batch of multiple requests and execute them in parallel to minimize the time it takes to execute them all. In order to assign the results, you must use the request_<request_name> method and pass a block accepting the result as a parameter which assigns the result to a variable in your context. When you have scheduled all of your requests, call execute_requests
to execute them in parallel. When execute_requests
returns, all of the blocks passed to your request methods will have been executed.
class TwitterStatusCacher < RailsBridge::ContentBridge self.request_timeout = 2000 # miliseconds self.cache_timeout = 60 # seconds self.host = 'api.twitter.com' self.path = '/statuses/user_timeline.json' self.on_success {|content| JSON.parse(content).first['text']} content_request( :hoonpark ) {|r| r.params = {:screen_name => 'hoonpark'}} content_request( :soopa ) {|r| r.params = {:screen_name => 'soopa'}} end soopa_latest_tweet = hoonpark_latest_tweet = nil TwitterStatusCacher.request_soopa do |result| soopa_latest_tweet = result end TwitterStatusCacher.request_hoonpark do |result| hoonpark_latest_tweet = result end # soopa_latest_tweet and hoonpark_latest_tweet not assigned yet TwitterStatusCacher.execute_requests # executes requests in parallel # soopa_latest_tweet and hoonpark_latest_tweet will now be assigned
Loading your content bridge classes¶ ↑
All classes under app/rails_bridge/content_bridges will be loaded automatically during application initialization and reloaded when in running in development.
LAYOUT BRIDGE¶ ↑
The layout bridge lets you export your Rails application layouts templatized for other languages. A controller is automatically included in your app when the ‘rails_bridge’ gem is installed. You can access it at:
http://app_hostname/rails_bridge/layouts
This will show you all of your applications available layouts. To view a layout without content, click the link of one of the layouts. You will see the entire HTML layout with no content. To insert templating content, implement a view for that layout as app/rails_bridge/layouts/<layout_name>/content.html.erb.
In that view, define content for your content_for’s and provide the template code to be yielded to the main content area of your layout.
Also, any partials that exist under app/rails_bridge/views will take precedence over those in your normal views directory. Take advantage of this to override content for layout partials when rendering your templatized layout.
TODO¶ ↑
-
Create a ContentBridge class method to purge the cache.
-
Put the pre-processor block after the cache get so it can assign to variables on each execution.
-
Create a Rake task to export LayoutBridge layouts from the command line.
-
Add test to verify partial precedence and content precedence for layout bridge.
DEVELOPMENT¶ ↑
RailsBridge was developed and tested using Ruby v1.9.2 and Rails v3.0.3.
You may download the source from Github at github.com/capitalthought/rails_bridge.
You can start an IRB session with the RailsBridge code loaded and initialized by running:
script/console
TESTS¶ ↑
RSpec 2.x is used for the Rails Bridge test suite. To run the suite:
rake
or
rake spec
NOTE: The test suite has an additional dependency on the eventmachine gem to to implement a test HTTP server.
CREDITS¶ ↑
The ContentBridge component of RailsBridge is fundamentally a wrapper around the excellent Typhoeus gem; packaged as a Rails engine with conveniences added for managing content in the context of Rails. Typhoeus, in turn, depends on a native installation of curl.
Typhoeus usually installs nicely if you already have curl installed on your OS. If you have difficulty installing Typhoeus, please refer to the following sources.
LICENSE¶ ↑
Copyright 2010 Capital Thought, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use any part of this software or its source code except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.