lib/sprockets/context.rb in sprockets-2.12.5 vs lib/sprockets/context.rb in sprockets-3.0.0.beta.1

- old
+ new

@@ -1,17 +1,15 @@ -require 'base64' -require 'rack/utils' -require 'sprockets/errors' -require 'sprockets/utils' require 'pathname' +require 'rack/utils' require 'set' +require 'sprockets/errors' module Sprockets - # `Context` provides helper methods to all `Tilt` processors. They - # are typically accessed by ERB templates. You can mix in custom - # helpers by injecting them into `Environment#context_class`. Do not - # mix them into `Context` directly. + # Deprecated: `Context` provides helper methods to all `Template` processors. + # They are typically accessed by ERB templates. You can mix in custom helpers + # by injecting them into `Environment#context_class`. Do not mix them into + # `Context` directly. # # environment.context_class.class_eval do # include MyHelper # def asset_url; end # end @@ -19,53 +17,56 @@ # <%= asset_url "foo.png" %> # # The `Context` also collects dependencies declared by # assets. See `DirectiveProcessor` for an example of this. class Context - attr_reader :environment, :pathname - attr_reader :_required_paths, :_stubbed_assets - attr_reader :_dependency_paths, :_dependency_assets - attr_writer :__LINE__ + attr_reader :environment, :filename, :pathname - def initialize(environment, logical_path, pathname) - @environment = environment - @logical_path = logical_path - @pathname = pathname - @__LINE__ = nil + def initialize(input) + @environment = input[:environment] + @metadata = input[:metadata] + @load_path = input[:load_path] + @logical_path = input[:name] + @filename = input[:filename] + @dirname = File.dirname(@filename) + @pathname = Pathname.new(@filename) + @content_type = input[:content_type] - @_required_paths = [] - @_stubbed_assets = Set.new - @_dependency_paths = Set.new - @_dependency_assets = Set.new([pathname.to_s]) + @required = Set.new(@metadata[:required]) + @stubbed = Set.new(@metadata[:stubbed]) + @links = Set.new(@metadata[:links]) + @dependency_paths = Set.new(@metadata[:dependency_paths]) end + def metadata + { required: @required, + stubbed: @stubbed, + links: @links, + dependency_paths: @dependency_paths } + end + # Returns the environment path that contains the file. # # If `app/javascripts` and `app/stylesheets` are in your path, and - # current file is `app/javascripts/foo/bar.js`, `root_path` would + # current file is `app/javascripts/foo/bar.js`, `load_path` would # return `app/javascripts`. - def root_path - environment.paths.detect { |path| pathname.to_s[path] } - end + attr_reader :load_path + alias_method :root_path, :load_path # Returns logical path without any file extensions. # # 'app/javascripts/application.js' # # => 'application' # - def logical_path - @logical_path.chomp(File.extname(@logical_path)) - end + attr_reader :logical_path # Returns content type of file # # 'application/javascript' # 'text/css' # - def content_type - environment.content_type_of(pathname) - end + attr_reader :content_type # Given a logical path, `resolve` will find and return the fully # expanded path. Relative paths will also be resolved. An optional # `:content_type` restriction can be supplied to restrict the # search. @@ -74,51 +75,36 @@ # # => "/path/to/app/javascripts/foo.js" # # resolve("./bar.js") # # => "/path/to/app/javascripts/bar.js" # - def resolve(path, options = {}, &block) - pathname = Pathname.new(path) - attributes = environment.attributes_for(pathname) + def resolve(path, options = {}) + options[:content_type] = self.content_type if options[:content_type] == :self + options[:accept] = options.delete(:content_type) - if pathname.absolute? - if environment.stat(pathname) - pathname + if environment.absolute_path?(path) + path + elsif environment.relative_path?(path) + path = File.expand_path(path, @dirname) + if logical_path = @environment.split_subpath(load_path, path) + environment.resolve_in_load_path(load_path, logical_path, options) else - raise FileNotFound, "couldn't find file '#{pathname}'" + raise FileOutsidePaths, "#{path} isn't under path: #{load_path}" end - - elsif content_type = options[:content_type] - content_type = self.content_type if content_type == :self - - if attributes.format_extension - if content_type != attributes.content_type - raise ContentTypeMismatch, "#{path} is " + - "'#{attributes.content_type}', not '#{content_type}'" - end - end - - resolve(path) do |candidate| - if self.content_type == environment.content_type_of(candidate) - return candidate - end - end - - raise FileNotFound, "couldn't find file '#{path}'" else - environment.resolve(path, {:base_path => self.pathname.dirname}.merge(options), &block) + environment.resolve(path, options) end end # `depend_on` allows you to state a dependency on a file without # including it. # # This is used for caching purposes. Any changes made to # the dependency file with invalidate the cache of the # source file. def depend_on(path) - @_dependency_paths << resolve(path).to_s + @dependency_paths << resolve(path).to_s nil end # `depend_on_asset` allows you to state an asset dependency # without including it. @@ -126,12 +112,13 @@ # This is used for caching purposes. Any changes that would # invalidate the dependency asset will invalidate the source # file. Unlike `depend_on`, this will include recursively include # the target asset's dependencies. def depend_on_asset(path) - filename = resolve(path).to_s - @_dependency_assets << filename + if asset = @environment.find_asset(resolve(path)) + @dependency_paths.merge(asset.metadata[:dependency_paths]) + end nil end # `require_asset` declares `path` as a dependency of the file. The # dependency will be inserted before the file and will only be @@ -141,69 +128,35 @@ # require assets. # # <%= require_asset "#{framework}.js" %> # def require_asset(path) - pathname = resolve(path, :content_type => :self) - depend_on_asset(pathname) - @_required_paths << pathname.to_s + filename = resolve(path, accept: @content_type) + @required << @environment.resolve_asset_uri(filename, accept: @content_type, bundle: false) nil end # `stub_asset` blacklists `path` from being included in the bundle. # `path` must be an asset which may or may not already be included # in the bundle. def stub_asset(path) - @_stubbed_assets << resolve(path, :content_type => :self).to_s + filename = resolve(path, accept: @content_type) + @stubbed << @environment.resolve_asset_uri(filename, accept: @content_type, bundle: false) nil end - # Tests if target path is able to be safely required into the - # current concatenation. - def asset_requirable?(path) - pathname = resolve(path) - content_type = environment.content_type_of(pathname) - stat = environment.stat(path) - return false unless stat && stat.file? - self.content_type.nil? || self.content_type == content_type - end - - # Reads `path` and runs processors on the file. + # `link_asset` declares an external dependency on an asset without directly + # including it. The target asset is returned from this function making it + # easy to construct a link to it. # - # This allows you to capture the result of an asset and include it - # directly in another. - # - # <%= evaluate "bar.js" %> - # - def evaluate(path, options = {}) - pathname = resolve(path) - attributes = environment.attributes_for(pathname) - processors = options[:processors] || attributes.processors - - if options[:data] - result = options[:data] - else - if environment.respond_to?(:default_external_encoding) - mime_type = environment.mime_types(pathname.extname) - encoding = environment.encoding_for_mime_type(mime_type) - result = Sprockets::Utils.read_unicode(pathname, encoding) - else - result = Sprockets::Utils.read_unicode(pathname) - end + # Returns an Asset or nil. + def link_asset(path) + if asset = @environment.find_asset(resolve(path)) + @dependency_paths.merge(asset.metadata[:dependency_paths]) + @links << asset.uri end - - processors.each do |processor| - begin - template = processor.new(pathname.to_s) { result } - result = template.render(self, {}) - rescue Exception => e - annotate_exception! e - raise - end - end - - result + asset end # Returns a Base64-encoded `data:` URI with the contents of the # asset at the specified path, and marks that path as a dependency # of the current file. @@ -214,13 +167,12 @@ # # $('<img>').attr('src', '<%= asset_data_uri 'avatar.jpg' %>') # def asset_data_uri(path) depend_on_asset(path) - asset = environment.find_asset(path) - base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "") - "data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}" + asset = environment.find_asset(path, accept_encoding: 'base64') + "data:#{asset.content_type};base64,#{Rack::Utils.escape(asset.to_s)}" end # Expands logical path to full url to asset. # # NOTE: This helper is currently not implemented and should be @@ -241,49 +193,34 @@ raise NotImplementedError, message end # Expand logical image asset path. def image_path(path) - asset_path(path, :type => :image) + asset_path(path, type: :image) end # Expand logical video asset path. def video_path(path) - asset_path(path, :type => :video) + asset_path(path, type: :video) end # Expand logical audio asset path. def audio_path(path) - asset_path(path, :type => :audio) + asset_path(path, type: :audio) end # Expand logical font asset path. def font_path(path) - asset_path(path, :type => :font) + asset_path(path, type: :font) end # Expand logical javascript asset path. def javascript_path(path) - asset_path(path, :type => :javascript) + asset_path(path, type: :javascript) end # Expand logical stylesheet asset path. def stylesheet_path(path) - asset_path(path, :type => :stylesheet) + asset_path(path, type: :stylesheet) end - - private - # Annotates exception backtrace with the original template that - # the exception was raised in. - def annotate_exception!(exception) - location = pathname.to_s - location << ":#{@__LINE__}" if @__LINE__ - - exception.extend(Sprockets::EngineError) - exception.sprockets_annotation = " (in #{location})" - end - - def logger - environment.logger - end end end