Skip to content
This repository
tree: da7f0426ec
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 147 lines (120 sloc) 4.857 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/file'
require 'action_view/helpers/tag_helper'

module ActionView
  module Helpers
    module AssetTagHelper

      class AssetIncludeTag
        include TagHelper

        attr_reader :config, :asset_paths
        class_attribute :expansions

        def self.inherited(base)
          base.expansions = { }
        end

        def initialize(config, asset_paths)
          @config = config
          @asset_paths = asset_paths
        end

        def asset_name
          raise NotImplementedError
        end

        def extension
          raise NotImplementedError
        end

        def custom_dir
          raise NotImplementedError
        end

        def asset_tag(source, options)
          raise NotImplementedError
        end

        def include_tag(*sources)
          options = sources.extract_options!.stringify_keys
          concat = options.delete("concat")
          cache = concat || options.delete("cache")
          recursive = options.delete("recursive")

          if concat || (config.perform_caching && cache)
            joined_name = (cache == true ? "all" : cache) + ".#{extension}"
            joined_path = File.join((joined_name[/^#{File::SEPARATOR}/] ? config.assets_dir : custom_dir), joined_name)
            unless config.perform_caching && File.exists?(joined_path)
              write_asset_file_contents(joined_path, compute_paths(sources, recursive))
            end
            asset_tag(joined_name, options)
          else
            sources = expand_sources(sources, recursive)
            ensure_sources!(sources) if cache
            sources.collect { |source| asset_tag(source, options) }.join("\n").html_safe
          end
        end

        private

          def path_to_asset(source, options = {})
            asset_paths.compute_public_path(source, asset_name.to_s.pluralize, options.merge(:ext => extension))
          end

          def path_to_asset_source(source)
            asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension)
          end

          def compute_paths(*args)
            expand_sources(*args).collect { |source| path_to_asset_source(source) }
          end

          def expand_sources(sources, recursive)
            if sources.first == :all
              collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}")
            else
              sources.inject([]) do |list, source|
                determined_source = determine_source(source, expansions)
                update_source_list(list, determined_source)
              end
            end
          end

          def update_source_list(list, source)
            case source
            when String
              list.delete(source)
              list << source
            when Array
              updated_sources = source - list
              list.concat(updated_sources)
            end
          end

          def ensure_sources!(sources)
            sources.each do |source|
              asset_file_path!(path_to_asset_source(source))
            end
          end

          def collect_asset_files(*path)
            dir = path.first

            Dir[File.join(*path.compact)].collect do |file|
              file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
            end.sort
          end

          def determine_source(source, collection)
            case source
            when Symbol
              collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
            else
              source
            end
          end

          def join_asset_file_contents(paths)
            paths.collect { |path| File.read(asset_file_path!(path, true)) }.join("\n\n")
          end

          def write_asset_file_contents(joined_asset_path, asset_paths)
            FileUtils.mkdir_p(File.dirname(joined_asset_path))
            File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }

            # Set mtime to the latest of the combined files to allow for
            # consistent ETag without a shared filesystem.
            mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max
            File.utime(mt, mt, joined_asset_path)
          end

          def asset_file_path!(absolute_path, error_if_file_is_uri = false)
            if asset_paths.is_uri?(absolute_path)
              raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
            else
              raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
              return absolute_path
            end
          end
      end

    end
  end
end
Something went wrong with that request. Please try again.