Class: Slinky::Manifest
- Inherits:
-
Object
- Object
- Slinky::Manifest
- Defined in:
- lib/slinky/manifest.rb
Instance Attribute Summary collapse
-
#config ⇒ Object
Returns the value of attribute config.
-
#dir ⇒ Object
Returns the value of attribute dir.
-
#manifest_dir ⇒ Object
Returns the value of attribute manifest_dir.
Instance Method Summary collapse
-
#add_all_by_path(paths) ⇒ Object
Adds a file to the manifest, updating the dependency graph.
- #build ⇒ Object
- #compress_product(product) ⇒ Object
-
#dependency_graph ⇒ [ManifestFile, ManifestFile]
Builds the directed graph representing the dependencies of all files in the manifest that contain a slinky_require declaration.
- #dependency_list ⇒ Object
-
#files(include_ignores = true) ⇒ ManifestFile
Returns a list of all files contained in this manifest.
- #files_for_all_products ⇒ Object
-
#files_for_product(product) ⇒ Object
Finds all the matching manifest files for a particular product.
-
#find_by_path(path, allow_multiple = false) ⇒ Object
Finds the file at the given path in the manifest if one exists, otherwise nil.
-
#find_by_pattern(pattern) ⇒ Object
Finds all files that match the given pattern.
-
#initialize(dir, config, options = {}) ⇒ Manifest
constructor
A new instance of Manifest.
-
#md5 ⇒ Object
Returns a md5 encompassing the current state of the manifest.
-
#product_string(product) ⇒ Object
Produces a string of HTML that includes all of the files for the given product.
-
#remove_all_by_path(paths) ⇒ Object
Removes a file from the manifest.
-
#scripts_string ⇒ Object
These are special cases for simplicity and backwards compatability.
-
#styles_string ⇒ Object
These are special cases for simplicity and backwards compatability.
-
#update_all_by_path(paths) ⇒ Object
Notifies of an update to a file in the manifest.
Constructor Details
#initialize(dir, config, options = {}) ⇒ Manifest
Returns a new instance of Manifest.
27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/slinky/manifest.rb', line 27 def initialize dir, config, = {} @dir = dir @build_to = if d = [:build_to] File.(d) else dir end @manifest_dir = ManifestDir.new dir, self, @build_to, self @devel = ([:devel].nil?) ? true : [:devel] @config = config @no_minify = [:no_minify] || config.dont_minify end |
Instance Attribute Details
#config ⇒ Object
Returns the value of attribute config.
25 26 27 |
# File 'lib/slinky/manifest.rb', line 25 def config @config end |
#dir ⇒ Object
Returns the value of attribute dir.
25 26 27 |
# File 'lib/slinky/manifest.rb', line 25 def dir @dir end |
#manifest_dir ⇒ Object
Returns the value of attribute manifest_dir.
25 26 27 |
# File 'lib/slinky/manifest.rb', line 25 def manifest_dir @manifest_dir end |
Instance Method Details
#add_all_by_path(paths) ⇒ Object
Adds a file to the manifest, updating the dependency graph
56 57 58 59 60 61 |
# File 'lib/slinky/manifest.rb', line 56 def add_all_by_path paths manifest_update paths do |path| md = find_by_path(File.dirname(path)).first mf = md.add_file(File.basename(path)) end end |
#build ⇒ Object
280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/slinky/manifest.rb', line 280 def build @manifest_dir.build unless @devel @config.produce.keys.each{|product| compress_product(product) } # clean up the files that have been processed files_for_all_products.each{|mf| FileUtils.rm(mf.build_to, :force => true)} end end |
#compress_product(product) ⇒ Object
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/slinky/manifest.rb', line 207 def compress_product product compressor = compressor_for_product product post_processor = post_processor_for_product product s = files_for_product(product).map{|mf| f = File.open(mf.build_to.to_s, 'rb'){|f| f.read} post_processor ? (post_processor.call(mf, f)) : f }.join("\n") # Make the directory the product is in FileUtils.mkdir_p("#{@build_to}/#{Pathname.new(product).dirname}") File.open("#{@build_to}/#{product}", "w+"){|f| unless @no_minify f.write(compressor[s]) else f.write(s) end } end |
#dependency_graph ⇒ [ManifestFile, ManifestFile]
Builds the directed graph representing the dependencies of all files in the manifest that contain a slinky_require declaration. The graph is represented as a list of pairs (required, by), each of which describes an edge.
263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/slinky/manifest.rb', line 263 def dependency_graph return @dependency_graph if @dependency_graph graph = [] files(false).each{|mf| mf.dependencies.each{|d| graph << [d, mf] } } @dependency_graph = Graph.new(files(false), graph) end |
#dependency_list ⇒ Object
276 277 278 |
# File 'lib/slinky/manifest.rb', line 276 def dependency_list dependency_graph.dependency_list end |
#files(include_ignores = true) ⇒ ManifestFile
Returns a list of all files contained in this manifest
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/slinky/manifest.rb', line 43 def files include_ignores = true unless @files @files = [] files_rec @manifest_dir end if include_ignores @files else @files.reject{|f| @config.ignore.any?{|p| f.in_tree? p}} end end |
#files_for_all_products ⇒ Object
198 199 200 201 202 203 204 205 |
# File 'lib/slinky/manifest.rb', line 198 def files_for_all_products return @files_for_all_products if @files_for_all_products SlinkyError.batch_errors do @files_for_all_products = @config.produce.keys.map{|product| files_for_product(product) }.flatten.uniq end end |
#files_for_product(product) ⇒ Object
Finds all the matching manifest files for a particular product. This does not take into account dependencies.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/slinky/manifest.rb', line 141 def files_for_product product p = @config.produce[product] if p.nil? raise NoSuchProductError.new( "Product '#{product}' has not been configured") end type = type_for_product product if type != ".js" && type != ".css" raise InvalidConfigError.new("Only .js and .css products are supported") end g = dependency_graph.transitive_closure # Topological indices for each file indices = {} dependency_list.each_with_index{|f, i| indices[f] = i} # Compute the set of excluded files excludes = Set.new((p["exclude"] || []).map{|p| find_by_pattern(p) }.flatten.uniq) SlinkyError.batch_errors do # First find the list of files that have been explictly # included/excluded p["include"].map{|f| mfs = find_by_pattern(f) .map{|mf| [mf] + g[f]} .flatten .reject{|f| f.output_path.extname != type} if mfs.empty? SlinkyError.raise FileNotFoundError, "No files matched by include #{f} in product #{product}" end mfs.flatten }.flatten.reject{|f| excludes.include?(f) # Then add all the files these require }.map{|f| # Find all of the downstream files # check that we're not excluding any required files g[f].each{|rf| if p["exclude"] && r = p["exclude"].find{|ex| rf.matches_path?(ex, true)} SlinkyError.raise DependencyError, "File #{f} requires #{rf} which is excluded by exclusion rule #{r}" end } [f] + g[f] }.flatten.uniq.sort_by{|f| # Sort by topological order indices[f] } end end |
#find_by_path(path, allow_multiple = false) ⇒ Object
Finds the file at the given path in the manifest if one exists, otherwise nil.
84 85 86 |
# File 'lib/slinky/manifest.rb', line 84 def find_by_path path, allow_multiple = false @manifest_dir.find_by_path path, allow_multiple end |
#find_by_pattern(pattern) ⇒ Object
Finds all files that match the given pattern. The match rules are similar to those for .gitignore and given by
-
If the pattern ends with a slash, it will only match directories; e.g. ‘foo/` would match a directory `foo/` but not a file `foo`. In a file context, matching a directory is equivalent to matching all files under that directory, recursively.
-
If the pattern does not contain a slash, slinky treats it as a relative pathname which can match files in any directory. For example, the rule ‘test.js` will matching `/test.js` and
`/component/test.js`.
-
If the pattern begins with a slash, it will be treated as an absolute path starting at the root of the source directory.
-
If the pattern does not begin with a slash, but does contain one or more slashes, it will be treated as a path relative to any directory. For example, ‘test/*.js` will match `/test/main.js`, and /component/test/component.js`, but not `main.js`.
-
A single star ‘*` in a pattern will match any number of characters within a single path component. For example, `/test/*.js` will match `/test/main_test.js` but not `/test/component/test.js`.
-
A double star ‘**` will match any number of characters including path separators. For example `/scripts/**/main.js` will match any file named `main.js` under the `/scripts` directory, including
`/scripts/main.js` and `/scripts/component/main.js`.
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 |
# File 'lib/slinky/manifest.rb', line 112 def find_by_pattern pattern # The strategy here is to convert the pattern into an equivalent # regex and run that against the pathnames of all the files in # the manifest. regex_str = Regexp.escape(pattern) .gsub('\*\*/', ".*") .gsub('\*\*', ".*") .gsub('\*', "[^/]*") if regex_str[0] != '/' regex_str = '.*/' + regex_str end if regex_str[-1] == '/' regex_str += '.*' end regex_str = "^#{regex_str}$" regex = Regexp.new(regex_str) files(false).reject{|f| !regex.match('/' + f.relative_source_path.to_s) && !regex.match('/' + f.relative_output_path.to_s) } end |
#md5 ⇒ Object
Returns a md5 encompassing the current state of the manifest. Any change to the manifest should produce a different hash. This can be used to determine if the manifest has changed.
295 296 297 298 299 300 301 302 |
# File 'lib/slinky/manifest.rb', line 295 def md5 if @md5 @md5 else @md5 = Digest::MD5.hexdigest(files.map{|f| [f.source, f.md5]} .sort.flatten.join(":")) end end |
#product_string(product) ⇒ Object
Produces a string of HTML that includes all of the files for the given product.
247 248 249 250 251 252 253 254 255 |
# File 'lib/slinky/manifest.rb', line 247 def product_string product if @devel files_for_product(product).map{|f| html_for_path("/#{f.relative_output_path}") }.join("\n") else html_for_path("#{product}?#{rand(999999999)}") end end |
#remove_all_by_path(paths) ⇒ Object
Removes a file from the manifest
69 70 71 72 73 74 75 76 |
# File 'lib/slinky/manifest.rb', line 69 def remove_all_by_path paths manifest_update paths do |path| mf = find_by_path(path).first() if mf mf.parent.remove_file(mf) end end end |
#scripts_string ⇒ Object
These are special cases for simplicity and backwards compatability. If no products are defined, we have two default products, one which includes are .js files in the repo and one that includes all .css files. This method produces an HTML include string for all of the .js files.
232 233 234 |
# File 'lib/slinky/manifest.rb', line 232 def scripts_string product_string ConfigReader::DEFAULT_SCRIPT_PRODUCT end |
#styles_string ⇒ Object
These are special cases for simplicity and backwards compatability. If no products are defined, we have two default products, one which includes are .js files in the repo and one that includes all .css files. This method produces an HTML include string for all of the .css files.
241 242 243 |
# File 'lib/slinky/manifest.rb', line 241 def styles_string product_string ConfigReader::DEFAULT_STYLE_PRODUCT end |
#update_all_by_path(paths) ⇒ Object
Notifies of an update to a file in the manifest
64 65 66 |
# File 'lib/slinky/manifest.rb', line 64 def update_all_by_path paths manifest_update paths end |