cobranding 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/License.txt +674 -0
- data/README.rdoc +69 -0
- data/Rakefile +37 -0
- data/lib/cobranding.rb +11 -0
- data/lib/cobranding/helper.rb +28 -0
- data/lib/cobranding/layout.rb +208 -0
- data/lib/cobranding/persistent_layout.rb +53 -0
- data/spec/helper_spec.rb +98 -0
- data/spec/layout_spec.rb +151 -0
- data/spec/persistent_layout_spec.rb +111 -0
- data/spec/spec_helper.rb +34 -0
- metadata +106 -0
data/README.rdoc
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
= Cobranding
|
2
|
+
|
3
|
+
This gem allows you too pull marked up HTML from a URL and use it as a layout in a Rails view.
|
4
|
+
|
5
|
+
== Fetching the layout
|
6
|
+
|
7
|
+
To fetch the layout from a service, you can cal
|
8
|
+
|
9
|
+
Cobranding::Layout.get(url, options)
|
10
|
+
|
11
|
+
Where +url+ and +options+ are values passed to +SimpleHttpClient+. Additional options available are
|
12
|
+
|
13
|
+
* base_url: set the base url for expanding any relative URLs in the markup
|
14
|
+
* method: set to :post to perform a POST instead of a GET request
|
15
|
+
|
16
|
+
== Caching
|
17
|
+
|
18
|
+
If you specify +:ttl+ in the options, the layout will be cached for that many seconds. The cache algorithm also tries to prevent race conditions when the cache expires. Using it can greatly reduce load on the servers.
|
19
|
+
|
20
|
+
You must use a Rails.cache that supports +:expires_in+ on writes to the cache. In Rails 2 the only out of the box cache that will work properly is MemCacheStore.
|
21
|
+
|
22
|
+
== Markup
|
23
|
+
|
24
|
+
The HTML markup in the layout cannot contain any ERB code (<% %>). If any is found, it will be escaped.
|
25
|
+
|
26
|
+
You can put limited markup into the HTML using {{ }} style tags. These tags must be exactly one word and will trigger calling helper methods like *_for_cobranding.
|
27
|
+
|
28
|
+
=== Example
|
29
|
+
|
30
|
+
<html>
|
31
|
+
<head>
|
32
|
+
<title>{{ page_title }}</title>
|
33
|
+
{{ stylesheets }}
|
34
|
+
</head>
|
35
|
+
<body>
|
36
|
+
{{ content }}
|
37
|
+
</body>
|
38
|
+
</html>
|
39
|
+
|
40
|
+
The tags in this layout will result in calls to
|
41
|
+
|
42
|
+
* page_title_for_cobranding
|
43
|
+
* stylesheets_for_cobranding
|
44
|
+
* content_for_cobranding
|
45
|
+
|
46
|
+
If any of these helper methods aren't defined, they will be silently ignored. If you need a different naming convention for you helper methods, you can pass in a :prefix or :suffix option to the +evaluate+ or +cobranding_layout+ helper call.
|
47
|
+
|
48
|
+
== Using in a view
|
49
|
+
|
50
|
+
The gem automatically adds a universal helper method that allows you to call the layout. It should be added to a regular old layout view like this:
|
51
|
+
|
52
|
+
<%= cobranding_layout(url, options) do %>
|
53
|
+
<html>
|
54
|
+
<head>
|
55
|
+
<title><%= page_title_for_cobranding %></title>
|
56
|
+
<%= stylesheets_for_cobranding %>
|
57
|
+
</head>
|
58
|
+
<body>
|
59
|
+
<div id="warning">Warning the layout was not available!</div>
|
60
|
+
<%= content_for_cobranding %>
|
61
|
+
</body>
|
62
|
+
</html>
|
63
|
+
<% end %>
|
64
|
+
|
65
|
+
If the tag includes a block, it will only be called if there was an error evaluating the template. This way, you can provide a default failsafe layout in case the layout service is unavailable. Providing one will keep your site up in this case and can also serve as documentation for what the layouts are expected to look like and what tags they should use.
|
66
|
+
|
67
|
+
== Persisting
|
68
|
+
|
69
|
+
If you have layouts that don't need to be real time, you can persist them to a data store and update them asynchronously via a background job. You simply need to include Cobranding::PersistentLayout in your model. To render the layout, you can then pass in model.layout to the cobranding_layout helper instead of a URL.
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
desc 'Default: run unit tests'
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'rspec'
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
desc 'Run the unit tests'
|
11
|
+
RSpec::Core::RakeTask.new(:test)
|
12
|
+
rescue LoadError
|
13
|
+
task :test do
|
14
|
+
raise "You must have rspec 2.0 installed to run the tests"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
require 'jeweler'
|
20
|
+
Jeweler::Tasks.new do |gem|
|
21
|
+
gem.name = "cobranding"
|
22
|
+
gem.summary = %Q{Provides Rails view layouts from an HTTP service}
|
23
|
+
gem.description = %Q{Provides Rails view layouts from an HTTP service.}
|
24
|
+
gem.authors = ["Brian Durand"]
|
25
|
+
gem.email = ["mdobrota@tribune.com", "ddpr@tribune.com"]
|
26
|
+
gem.files = FileList["lib/**/*", "spec/**/*", "README.rdoc", "Rakefile", "License.txt"].to_a
|
27
|
+
gem.has_rdoc = true
|
28
|
+
gem.rdoc_options << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc'
|
29
|
+
gem.extra_rdoc_files = ["README.rdoc"]
|
30
|
+
gem.add_dependency('actionpack', '>=3.0.0')
|
31
|
+
gem.add_dependency('rest-client')
|
32
|
+
gem.add_development_dependency('rspec', '>= 2.0.0')
|
33
|
+
gem.add_development_dependency('webmock')
|
34
|
+
end
|
35
|
+
Jeweler::RubygemsDotOrgTasks.new
|
36
|
+
rescue LoadError
|
37
|
+
end
|
data/lib/cobranding.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'active_support/all' unless defined?(ActiveSupport::HashWithIndifferentAccess)
|
2
|
+
require 'action_view'
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
module Cobranding
|
6
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'cobranding', 'layout')
|
7
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'cobranding', 'helper')
|
8
|
+
autoload :PersistentLayout, File.join(File.expand_path(File.dirname(__FILE__)), 'cobranding', 'persistent_layout')
|
9
|
+
|
10
|
+
ActionView::Base.send(:include, Helper)
|
11
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Cobranding
|
2
|
+
# This module gets mixed in to ActionView::Helpers so its methods are available in all Rails views.
|
3
|
+
module Helper
|
4
|
+
# Helper method to render a layout. The +url_or_layout+ can either be a URL to a layout service or a Layout object.
|
5
|
+
# The options parameter will only be used if a URL is passed in.
|
6
|
+
#
|
7
|
+
# This method can take a block which should be a fail safe ERB version of the layout that will only be used if the
|
8
|
+
# layout service is unavailable.
|
9
|
+
#
|
10
|
+
# Note that for Rails 2.x applications you must call the tag with a block as <% cobranding_layout do %> while in Rails 3.0
|
11
|
+
# and later you must call it as <%= cobranding_layout do %>.
|
12
|
+
def cobranding_layout (url_or_layout, options = nil, &block)
|
13
|
+
options = options.dup if options
|
14
|
+
evaluate_options = {:prefix => options.delete(:prefix), :suffix => options.delete(:suffix)} if options
|
15
|
+
layout = url_or_layout.is_a?(Layout) ? url_or_layout : Layout.get(url_or_layout, options)
|
16
|
+
layout.evaluate(self, evaluate_options).html_safe
|
17
|
+
rescue SystemExit, Interrupt, NoMemoryError
|
18
|
+
raise
|
19
|
+
rescue Exception => e
|
20
|
+
if block_given?
|
21
|
+
Rails.logger.warn(e) if Rails.logger
|
22
|
+
capture(&block).html_safe
|
23
|
+
else
|
24
|
+
raise e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'rest-client'
|
4
|
+
|
5
|
+
module Cobranding
|
6
|
+
# This class is used to get layout HTML markup from a service and compile it into Ruby code that can be used
|
7
|
+
# as the layout in a Rails view.
|
8
|
+
class Layout
|
9
|
+
QUOTED_RELATIVE_URL = /(<\w+\s((src)|(href))=(['"]))\/(.*?)(\5[^>]*?>)/i
|
10
|
+
UNQUOTED_RELATIVE_URL = /(<\w+\s((src)|(href))=)\/(.*?)(>|(\s[^>]*?>))/i
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Get the layout HTML from a service. The options can be any of the options accepted by SimpleHttpClient
|
14
|
+
# or +:base_url+. Any relative URLs found in the HTML will be expanded to absolute URLs using either the
|
15
|
+
# +:base_url+ option or the +url+ as the base.
|
16
|
+
#
|
17
|
+
# If +:ttl+ is specified in the options, the layout will be cached for that many seconds.
|
18
|
+
#
|
19
|
+
# By default the request will be a GET request. If you need to do a POST, you can pass :method => :post in the options.
|
20
|
+
#
|
21
|
+
# If a block is passed, it will be called with the layout html before the layout is created. This can be used
|
22
|
+
# to munge the layout HTML code if necessary.
|
23
|
+
def get (url, options = {}, &block)
|
24
|
+
return nil if url.blank?
|
25
|
+
options ||= {}
|
26
|
+
options = options.is_a?(HashWithIndifferentAccess) ? options.dup : options.with_indifferent_access
|
27
|
+
ttl = options.delete(:ttl)
|
28
|
+
race_ttl = options.delete(:race_condition_ttl)
|
29
|
+
if Rails.cache && ttl
|
30
|
+
cache_options = {}
|
31
|
+
cache_options[:expires_in] = ttl if ttl
|
32
|
+
cache_options[:race_condition_ttl] = race_ttl if race_ttl
|
33
|
+
key = cache_key(url, options)
|
34
|
+
Rails.cache.fetch(key, cache_options) do
|
35
|
+
layout = fetch_layout(url, options, &block)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
return fetch_layout(url, options, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generate a unique cache key for the layout request.
|
43
|
+
def cache_key (url, options = {})
|
44
|
+
options = options.is_a?(HashWithIndifferentAccess) ? options.dup : options.with_indifferent_access
|
45
|
+
full_uri = full_uri(url, options)
|
46
|
+
params = options.delete(:params)
|
47
|
+
options.delete(:host)
|
48
|
+
options.delete(:port)
|
49
|
+
options.delete(:scheme)
|
50
|
+
options.delete(:base)
|
51
|
+
options.delete(:ttl)
|
52
|
+
options.delete(:timeout)
|
53
|
+
options.delete(:read_timeout)
|
54
|
+
options.delete(:open_timeout)
|
55
|
+
append_params_to_uri!(full_uri, params) if params
|
56
|
+
|
57
|
+
options_key, query = full_uri.to_s.split('?', 2)
|
58
|
+
if query
|
59
|
+
options_key << '?'
|
60
|
+
options_key << query.split('&').sort.join('&')
|
61
|
+
end
|
62
|
+
|
63
|
+
options.keys.sort{|a,b| a.to_s <=> b.to_s}.each do |key|
|
64
|
+
options_key << " #{key}=#{options[key]}"
|
65
|
+
end
|
66
|
+
|
67
|
+
"#{name}.#{Digest::MD5.hexdigest(options_key)}"
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
# Fetch the layout HTML from the service. The block is optional and will be called with the html code.
|
73
|
+
def fetch_layout (url, options, &block)
|
74
|
+
method = options.delete(:method) || :get
|
75
|
+
full_url = full_uri(url, options).to_s
|
76
|
+
base_uri = options[:base_url] ? URI.parse(options.delete(:base_url)) : full_uri(url, options)
|
77
|
+
base_uri.userinfo = nil
|
78
|
+
base_uri.path = "/"
|
79
|
+
base_uri.query = nil
|
80
|
+
base_uri.fragment = nil
|
81
|
+
response = method.to_sym == :post ? RestClient.post(full_url, options) : RestClient.get(full_url, options)
|
82
|
+
html = expand_base_url(response, base_uri.to_s)
|
83
|
+
html = block.call(html) if block
|
84
|
+
layout = new(html)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Expand any relative URL's found in HTML tags to be absolute URLs with the specified base.
|
88
|
+
def expand_base_url (html, base_url)
|
89
|
+
return html unless base_url
|
90
|
+
base_url = "#{base_url}/" unless base_url.end_with?("/")
|
91
|
+
html.gsub(QUOTED_RELATIVE_URL){|match| "#{$1}#{base_url}#{$6}#{$7}"}.gsub(UNQUOTED_RELATIVE_URL){|match| "#{$1}#{base_url}#{$5}#{$6}"}
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def full_uri (url, options)
|
97
|
+
return url if url.kind_of?(URI)
|
98
|
+
uri = URI.parse(url)
|
99
|
+
base = URI.parse(options[:base]) if options[:base]
|
100
|
+
|
101
|
+
if uri.scheme == nil
|
102
|
+
host = options[:host]
|
103
|
+
port = options[:port]
|
104
|
+
scheme = options[:scheme]
|
105
|
+
if base and base.scheme
|
106
|
+
host ||= base.host
|
107
|
+
port ||= base.port
|
108
|
+
scheme ||= base.scheme
|
109
|
+
end
|
110
|
+
if host
|
111
|
+
full_url = "#{scheme ? scheme : 'http'}://#{host}"
|
112
|
+
full_url << ":#{port}" if port
|
113
|
+
if base
|
114
|
+
unless uri.to_s[0,1] == '/'
|
115
|
+
full_url << base.path
|
116
|
+
full_url << '/' unless base.path.last == '/'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
full_url << uri.to_s
|
120
|
+
uri = URI.parse(full_url)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
return uri
|
125
|
+
end
|
126
|
+
|
127
|
+
def append_params_to_uri! (uri, params)
|
128
|
+
unless params.blank?
|
129
|
+
if uri.query.blank?
|
130
|
+
uri.query = url_encode_parameters(params)
|
131
|
+
else
|
132
|
+
uri.query << "&"
|
133
|
+
uri.query << url_encode_parameters(params)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def url_encode_parameters (params)
|
139
|
+
params.collect{|name, value| url_encoded_param(name, value)}.join('&')
|
140
|
+
end
|
141
|
+
|
142
|
+
def url_encoded_param (name, value)
|
143
|
+
if value.kind_of?(Array)
|
144
|
+
return value.collect{|v| url_encoded_param(name, v)}.join('&')
|
145
|
+
else
|
146
|
+
return "#{Rack::Utils.escape(name.to_s)}=#{Rack::Utils.escape(value.to_s)}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
attr_accessor :src
|
152
|
+
|
153
|
+
# Create the Layout. The src will be defined from the HTML passed in.
|
154
|
+
def initialize (html = nil)
|
155
|
+
self.html = html if html
|
156
|
+
end
|
157
|
+
|
158
|
+
# Set the src by compiling HTML into RHTML and then into Ruby code.
|
159
|
+
def html= (html)
|
160
|
+
self.src = compile(html)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Evaluate the RHTML source code in the specified context. Any yields will call a helper method
|
164
|
+
# corresponding to the value yielded if it exists. The options :prefix and :suffix can be set to
|
165
|
+
# determine the full method name to call. The default is to suffix values with +_for_cobranding+
|
166
|
+
# so that <tt>yield title</tt> will call +title_for_cobranding+. Setting a different prefix or
|
167
|
+
# suffix can be useful if you are pulling in templates from different sources which use the same
|
168
|
+
# variable names but need different values.
|
169
|
+
def evaluate (context, options = nil)
|
170
|
+
if src
|
171
|
+
prefix = options[:prefix] if options
|
172
|
+
suffix = options[:suffix] if options
|
173
|
+
suffix = "_for_cobranding" unless prefix or suffix
|
174
|
+
evaluator = Object.new
|
175
|
+
eval <<-EOS
|
176
|
+
def evaluator.evaluate
|
177
|
+
#{src}
|
178
|
+
end
|
179
|
+
EOS
|
180
|
+
evaluator.evaluate do |var|
|
181
|
+
method = "#{prefix}#{var}#{suffix}"
|
182
|
+
context.send(method) if context.respond_to?(method)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
# Turn markup in the html into rhtml yield statments. Markup will be in HTML comments containing
|
190
|
+
# listings:var where var is a variable name set using content_for.
|
191
|
+
def rhtml (html)
|
192
|
+
return nil unless html
|
193
|
+
# Strip blank lines 'cuz theres just so many of them
|
194
|
+
rhtml_code = html.gsub(/^\s+$/, "\n").gsub(/[\n\r]+/, "\n")
|
195
|
+
# Escape things that look like ERB since it could be a mistake or it could be malicious
|
196
|
+
rhtml_code.gsub!("<%", "<%")
|
197
|
+
rhtml_code.gsub!("%>", "%>")
|
198
|
+
# Replace special comments with yield tags.
|
199
|
+
rhtml_code.gsub!(/\{\{\s*(\w+)\s*\}\}/){|match| "<%=yield :#{$1}%>"}
|
200
|
+
return rhtml_code
|
201
|
+
end
|
202
|
+
|
203
|
+
# Compile RHTML into ruby code.
|
204
|
+
def compile (html)
|
205
|
+
ERB.new(rhtml(html), nil, '-').src.freeze if html
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Cobranding
|
2
|
+
# This module can be mixed in to persistent objects so that layouts can be persisted to a data store. This is very
|
3
|
+
# useful when there are only a few layouts and they don't need to be updated in real time. In this case, you can run
|
4
|
+
# a background task to call fetch_layout on all your persistent layouts to update them asynchronously.
|
5
|
+
#
|
6
|
+
# By default, it will be assumed that the URL for the layout service will be stored in a field called +url+ and
|
7
|
+
# the compiled Layout ruby code will be stored in +src+. You can override these values with +layout_src_attribute+
|
8
|
+
# and +layout_url_attriubte+. In addition, if the URL takes options, you can specify the field that stores the Hash
|
9
|
+
# with +layout_url_options_attribute+.
|
10
|
+
#
|
11
|
+
# If the layout code needs to be munged, set the +:layout_preprocessor+ class attribute to either a symbol that
|
12
|
+
# matches a method name or to a +Proc+.
|
13
|
+
module PersistentLayout
|
14
|
+
def self.included (base)
|
15
|
+
base.class_attribute :layout_src_attribute, :layout_url_attribute, :layout_url_options_attribute, :layout_preprocessor
|
16
|
+
end
|
17
|
+
|
18
|
+
# Fetch a loyout from the service and store the ruby src code in the src attribute.
|
19
|
+
def fetch_layout
|
20
|
+
layout_url = send(layout_url_attribute || :url)
|
21
|
+
unless layout_url.blank?
|
22
|
+
options = send(layout_url_options_attribute) unless layout_url_options_attribute.blank?
|
23
|
+
options ||= {}
|
24
|
+
preprocessor = self.class.layout_preprocessor
|
25
|
+
if preprocessor && !preprocessor.is_a?(Proc)
|
26
|
+
preprocessor = method(preprocessor)
|
27
|
+
end
|
28
|
+
@layout = preprocessor ? Layout.get(layout_url, options, &preprocessor) : Layout.get(layout_url, options)
|
29
|
+
send("#{self.class.layout_src_attribute || :src}=", @layout.src)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the layout defined by the src attribute.
|
34
|
+
def layout
|
35
|
+
unless @layout
|
36
|
+
layout_src = send(layout_src_attribute || :src)
|
37
|
+
unless layout_src.blank?
|
38
|
+
@layout = Layout.new
|
39
|
+
@layout.src = layout_src
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@layout
|
43
|
+
end
|
44
|
+
|
45
|
+
def layout_html= (html)
|
46
|
+
preprocessor = self.class.layout_preprocessor
|
47
|
+
preprocessor = method(preprocessor) if preprocessor && !preprocessor.is_a?(Proc)
|
48
|
+
html = preprocessor.call(html) if preprocessor
|
49
|
+
@layout = Layout.new(html)
|
50
|
+
send("#{self.class.layout_src_attribute || :src}=", @layout.src)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/spec/helper_spec.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Cobranding::Helper do
|
4
|
+
|
5
|
+
module Cobranding::Helper::TestMethods
|
6
|
+
def content_for_cobranding
|
7
|
+
"Content!"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:view) do
|
12
|
+
view = ActionView::Base.new
|
13
|
+
view.extend(Cobranding::Helper::TestMethods)
|
14
|
+
view
|
15
|
+
end
|
16
|
+
|
17
|
+
def template(rhtml)
|
18
|
+
handler = ActionView::Template.handler_class_for_extension("erb")
|
19
|
+
ActionView::Template.new(rhtml, "test template", handler, {})
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_template(rhtml)
|
23
|
+
handler = Template.handler_for_extension("erb")
|
24
|
+
template = Template.new(rhtml, "test template", handler, {})
|
25
|
+
template.extend(Cobranding::Helper)
|
26
|
+
def template.content_for_cobranding
|
27
|
+
"Content!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
before :each do
|
32
|
+
cache = ActiveSupport::Cache::MemoryStore.new
|
33
|
+
Rails.stub!(:cache).and_return(cache)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should render a layout in a view with a Layout" do
|
37
|
+
rhtml = '<%= cobranding_layout(layout) %>'
|
38
|
+
layout = Cobranding::Layout.new("<html><title>Success</title><body>{{content}}</body></html>")
|
39
|
+
view.stub(:layout => layout)
|
40
|
+
view.render(:inline => rhtml).should == "<html><title>Success</title><body>Content!</body></html>"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should render a layout in a view with a Layout using custom prefix and suffix on helper methods" do
|
44
|
+
def view._content!
|
45
|
+
"Content with custom prefix/suffix"
|
46
|
+
end
|
47
|
+
url = "http://test.host/layout"
|
48
|
+
rhtml = "<%= cobranding_layout('#{url}', :params => {:x => 1}, :prefix => '_', :suffix => '!') %>"
|
49
|
+
layout = Cobranding::Layout.new("<html><title>Success</title><body>{{content}}</body></html>")
|
50
|
+
Cobranding::Layout.should_receive(:get).with(url, :params => {:x => 1}).and_return(layout)
|
51
|
+
view.stub(:layout => layout)
|
52
|
+
view.render(:inline => rhtml).should == "<html><title>Success</title><body>Content with custom prefix/suffix</body></html>"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should render a layout in a view with a URL" do
|
56
|
+
rhtml = '<%= cobranding_layout("http://localhost/layout", :params => {:v => 1}, :ttl => 300) %>'
|
57
|
+
layout = Cobranding::Layout.new("<html><title>Success</title><body>{{content}}</body></html>")
|
58
|
+
key = Cobranding::Layout.cache_key("http://localhost/layout?v=1")
|
59
|
+
Rails.cache.write(key, layout)
|
60
|
+
view.render(:inline => rhtml).should == "<html><title>Success</title><body>Content!</body></html>"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should render a layout in a view with an alternate failsafe layout in the body of the cobranding_layout tag" do
|
64
|
+
rhtml = '<%= cobranding_layout("http://localhost/layout", :params => {:v => 1}, :ttl => 300) do -%><html><title>FAIL</title><body><%= content_for_cobranding %></body></html><% end -%>'
|
65
|
+
layout = Cobranding::Layout.new("<html><title>Success</title><body>{{content}}</body></html>")
|
66
|
+
key = Cobranding::Layout.cache_key("http://localhost/layout?v=1")
|
67
|
+
Rails.cache.write(key, layout)
|
68
|
+
view.render(:inline => rhtml).should == "<html><title>Success</title><body>Content!</body></html>"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should render a default layout in the body of the cobranding_layout tag in a view" do
|
72
|
+
rhtml = '<%= cobranding_layout("invalid url", :params => {:v => 1}, :ttl => 300) do -%><html><title>FAIL</title><body><%= content_for_cobranding %></body></html><% end -%>'
|
73
|
+
view.render(:inline => rhtml).should == "<html><title>FAIL</title><body>Content!</body></html>"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should raise an error if no failsafe layout is specified" do
|
77
|
+
rhtml = '<%= cobranding_layout("http://localhost/layout", :params => {:v => 1}, :ttl => 300) %>'
|
78
|
+
lambda{view.render(:inline => rhtml)}.should raise_error
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when rendering a failsafe layout" do
|
82
|
+
let(:action_view) { ActionView::Base.new }
|
83
|
+
|
84
|
+
def block_helper(block, content)
|
85
|
+
"<%= #{block} do %>#{content}<% end %>"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should render the default block only once when an exception is raised" do
|
89
|
+
url = "http://p2p.cobranding.bad"
|
90
|
+
expected_output = "Tribune Tower"
|
91
|
+
Cobranding::Layout.should_receive(:get).with(url, nil).and_raise(Exception)
|
92
|
+
|
93
|
+
output = action_view.render(:inline => block_helper(%(cobranding_layout("#{url}")), expected_output))
|
94
|
+
output.should == expected_output
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|