lib/closure/script.rb in closure-1.2.701 vs lib/closure/script.rb in closure-1.3.0
- old
+ new
@@ -10,39 +10,38 @@
# 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.
+require 'pathname'
class Closure
- # @private
- class ScriptNotFoundError < StandardError
- end
-
- # @private
- class ScriptCallStackTooDeepError < StandardError
- end
-
# A Closure::Script instance is the context in which scripts are rendered.
- # It inherits everything from Rack::Request and supplies a response instance
+ # It inherits everything from Rack::Request and supplies a Response instance
# you can use for redirects, cookies, and other controller actions.
class Script < Rack::Request
+
+ class NotFound < StandardError
+ end
+
+ class RenderStackOverflow < StandardError
+ end
ENV_ERROR_CONTENT_TYPE = 'closure.error.content_type'
def initialize(env, sources, filename)
super(env)
- @closure_script_render_stack = []
- @goog = Goog.new(env, sources, @closure_script_render_stack)
+ @render_stack = []
+ @goog = Goog.new(env, sources, @render_stack)
@response = original_response = Rack::Response.new
rendering = render(filename)
if @response == original_response and @response.empty?
@response.write rendering
end
- rescue ScriptCallStackTooDeepError, ScriptNotFoundError => e
- if @closure_script_render_stack.size > 0
+ rescue RenderStackOverflow, NotFound => e
+ if @render_stack.size > 0
# Make errors appear from the render instead of the engine.call
e.set_backtrace e.backtrace[1..-1]
env[ENV_ERROR_CONTENT_TYPE] = @response.finish[1]["Content-Type"] rescue nil
raise e
end
@@ -63,85 +62,100 @@
# All the cool stuff lives here.
# @return [Goog]
attr_accessor :goog
- # Render another script. The same Closure::Script instance is
- # used for all internally rendered scripts so you can pass
- # information with instance variables.
+ # An array of filenames representing the current render stack.
+ # @example
+ # <%= if render_stack.size == 1
+ # render 'html_version'
+ # else
+ # render 'included_version'
+ # end
+ # %>
+ # @return [<Array>]
+ attr_reader :render_stack
+
+ # Render another Script.
# @example view_test.erb
# <%= render 'util/logger_popup' %>
- # @param (String) filename Relative to current script.
- def render(filename)
- if @closure_script_render_stack.size > 100
- # Since nobody sane would recurse through here, this mainly
+ # @param (String) filename Relative to current Script.
+ # @param (Hash) locals Local variables for the Script.
+ def render(filename, locals = {})
+ if render_stack.size > 100
+ # Since nobody sane should recurse through here, this mainly
# finds a render self that you might get after a copy and paste
- raise ScriptCallStackTooDeepError
- elsif @closure_script_render_stack.size > 0
+ raise RenderStackOverflow
+ elsif render_stack.size > 0
# Hooray for relative paths and easily movable files
- filename = File.expand_path(filename, File.dirname(@closure_script_render_stack.last))
+ filename = File.expand_path(filename, File.dirname(render_stack.last))
else
# Underbar scripts are partials by convention; keep them from rendering at root
filename = File.expand_path(filename)
- raise ScriptNotFoundError if File.basename(filename) =~ /^_/
+ raise NotFound if File.basename(filename) =~ /^_/
end
ext = File.extname(filename)
files1 = [filename]
files1 << filename + '.html' if ext == ''
files1 << filename.sub(/.html$/,'') if ext == '.html'
files1.each do |filename1|
Closure.config.engines.each do |ext, engine|
files2 = [filename1+ext]
files2 << filename1.gsub(/.html$/, ext) if File.extname(filename1) == '.html'
- unless filename1 =~ /^_/ or @closure_script_render_stack.empty?
+ unless filename1 =~ /^_/ or render_stack.empty?
files2 = files2 + files2.collect {|f| "#{File.dirname(f)}/_#{File.basename(f)}"}
end
files2.each do |filename2|
if File.file?(filename2) and File.readable?(filename2)
- if @closure_script_render_stack.empty?
+ if render_stack.empty?
response.header["Content-Type"] = Rack::Mime.mime_type(File.extname(filename1), 'text/html')
end
+ render_stack.push filename2
@goog.add_dependency filename2
- @closure_script_render_stack.push filename2
- result = engine.call self, filename2
- @closure_script_render_stack.pop
+ result = engine.call self, locals
+ render_stack.pop
return result
end
end
end
end
- raise ScriptNotFoundError
+ raise NotFound
end
- # Helper for URL escaping.
- # @param [String]
- # @return [String]
- def escape(s)
- Rack::Utils.escape(s)
+ # Helper for finding files relative to Scripts.
+ # @param [String] filename
+ # @return [String] absolute filesystem path
+ def expand_path(filename, dir=nil)
+ dir ||= File.dirname render_stack.last
+ File.expand_path filename, dir
end
- # Helper and alias for HTML escaping.
- # @param [String]
- # @return [String]
- def escape_html(s)
- Rack::Utils.escape_html(s)
+ # Helper to locate a file as a file server path.
+ # @param [String] filename
+ # @return [String] absolute http path
+ def expand_src(filename, dir=nil)
+ found = false
+ filename = expand_path filename, dir
+ src = nil
+ @goog.each do |dir, path|
+ dir_range = (dir.length..-1)
+ if filename.index(dir) == 0
+ src = "#{path}#{filename.slice(dir_range)}"
+ break
+ end
+ end
+ raise Errno::ENOENT unless src
+ src
end
- alias :h :escape_html
- # Helper for relative filenames.
- # @param [String]
- # @return [String]
- def expand_path(s)
- File.expand_path(s, File.dirname(@closure_script_render_stack.last))
+ # Helper to locate a file as a file server path.
+ # @param [String] filename
+ # @return [String] relative http path
+ def relative_src(filename, dir=nil)
+ file = expand_src filename, dir
+ base = Pathname.new File.dirname path_info
+ Pathname.new(file).relative_path_from(base).to_s
end
-
- # Helper to add file mtime as query for future-expiry caching.
- # @param [String]
- # @return [String]
- def expand_src(s)
- @goog.path_for(expand_path(s)) rescue s
- end
-
end
end