torba 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c693882d1f37d1cdf2ea156a9d6fd90aaa42409e
4
+ data.tar.gz: d57f3ec19cf7274292a3bee7440f9102ddd2e3a6
5
+ SHA512:
6
+ metadata.gz: 3191c0a6b2610b78e148fe7f3cbf936f8fd3a82707e62d914c01378ea464bee951b3d6412530089792d10188fba62faa633b52a0ec81e72f1506f548c9418af5
7
+ data.tar.gz: aa2e6571108e54d3fc76d35e99e5b8d90f23e47ee70c8dd456d7e3aa6cfb06831d6ad5e821b72c0c746f3f80c4b463a0d19e0c2a61c7acb8968b731b0a1c929f
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1
6
+ - 2.2
7
+ - jruby-19mode
8
+ - rbx-2
9
+ bundler_args: --without doc debug
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,47 @@
1
+ # Contributing
2
+
3
+ ## Adding a feature
4
+
5
+ 1. Open an issue and explain what you're planning to do. It is better to discuss new idea first,
6
+ rather when diving into code.
7
+ 2. Add some tests.
8
+ 3. Write the code.
9
+ 4. Make sure all tests pass.
10
+ 5. Commit with detailed explanation what you've done in a message.
11
+ 6. Open pull request.
12
+
13
+ ## Breaking/removing a feature
14
+
15
+ 1. Add deprecation warning and fallback to old behaivour if possible.
16
+ 2. Explain how to migrate to the new code in CHANGELOG.
17
+ 3. Update/remove tests.
18
+ 4. Update the code.
19
+ 5. Make sure all tests pass.
20
+ 6. Commit with detailed explanation what you've done in a message.
21
+ 7. Open pull request.
22
+
23
+ ## Fixing a bug
24
+
25
+ 1. Add failing test.
26
+ 2. Fix the bug.
27
+ 3. Make sure all tests pass.
28
+ 4. Commit with detailed explanation what you've done in a message.
29
+ 5. Open pull request.
30
+
31
+ ## Fixing a typo
32
+
33
+ 1. Commit with a message that include "[ci skip]" remark.
34
+ 2. Open pull request.
35
+
36
+ ## Running the tests
37
+
38
+ ```
39
+ rake test
40
+ ```
41
+
42
+ ## Working with documentation
43
+
44
+ ```
45
+ yard server -dr
46
+ open http://localhost:8808
47
+ ```
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem "pry", :group => :debug
6
+ gem "yard", "~> 0.8", :group => :doc
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Andrii Malyshko
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+ task :test do
5
+ $LOAD_PATH.unshift("test")
6
+ Dir.glob("./test/**/*_test.rb").each { |file| require file}
7
+ end
data/Readme.md ADDED
@@ -0,0 +1,211 @@
1
+ # Torba
2
+
3
+ [![Build Status](https://img.shields.io/travis/torba-rb/torba.svg)](https://travis-ci.org/torba-rb/torba)
4
+ [![Gem version](https://img.shields.io/gem/v/torba.svg)](https://rubygems.org/gems/torba)
5
+
6
+ **Torba** is a [Bower][bower]-less asset manager for [Sprockets][sprockets]. It makes a local copy
7
+ of a JS/CSS library and puts it under Sprockets' [load path][sprockets-load-path].
8
+
9
+ ## Name origin
10
+
11
+ "Торба" [[tǒːrba][torba-pronounce]] in Ukrainian and "torba" in Polish, Turkic languages can mean
12
+ "duffel bag", "gunny sack" or, more generally, any flexible container.
13
+
14
+ ## Status
15
+
16
+ Not tested in production.
17
+
18
+ ## Documentation
19
+
20
+ http://rubydoc.info/github/torba-rb/torba/
21
+
22
+ ## Why
23
+
24
+ De facto approach, i.e. wrapping JS and CSS libraries in a gem, requires from a
25
+ maintainer to constantly track changes in upstream repository. Even more, if the
26
+ maintainer stops using this gem itself, it will eventually become abandoned.
27
+ Many libraries still have no gem wrappers.
28
+
29
+ Among other alternatives:
30
+
31
+ * [rails-assets][rails-assets] project relies on Bower *and* it is quite complex,
32
+ * [bower-rails][bower-rails] project relies on Bower.
33
+
34
+ Problems with the Bower:
35
+
36
+ * it is not a part of Ruby ecosystem,
37
+ * frontend JS libraries are usually standalone (except for jQuery dependency), so there's
38
+ no need for complex Bundler-like solution with tree-dependency resolution,
39
+ * often we can't use optimistic version constraints, because JS community still doesn't
40
+ fully grasp the idea of [Semver][semver]. By specifying strict versions we use Bower
41
+ as a complex facade for functionality that could be done by curl.
42
+
43
+ ## External dependencies
44
+
45
+ * curl
46
+ * unzip
47
+
48
+ ## Design limitations
49
+
50
+ * Torba doesn't do any version dependency resolution, it's up to you to specify correct version of
51
+ each asset package,
52
+ * Torba doesn't do any builds, use remote sources with pre-built assets.
53
+
54
+ ## Installation
55
+
56
+ Add this line to your application's Gemfile and run `bundle`:
57
+
58
+ ```ruby
59
+ gem 'torba'
60
+ ```
61
+
62
+ ### Rails
63
+
64
+ in boot.rb
65
+
66
+ ```diff
67
+ require 'bundler/setup' # Set up gems listed in the Gemfile.
68
+ +require 'torba/verify'
69
+ ```
70
+
71
+ it config/application.rb
72
+
73
+ ```diff
74
+ # Require the gems listed in Gemfile, including any gems
75
+ # you've limited to :test, :development, or :production.
76
+ Bundler.require(*Rails.groups)
77
+ +
78
+ +require 'torba/rails'
79
+ ```
80
+
81
+ ## Usage
82
+
83
+ 1. Create Torbafile at the project root and commit it.
84
+
85
+ 2. Run `bundle exec torba pack`.
86
+
87
+ 3. Add "require" [Sprockets directives][sprockets-directives] to your "application.js"
88
+ and/or "@import" [Sass directives][sass-import] to "application.css".
89
+
90
+ If any changes made to the Torbafile, run `bundle exec torba pack` again.
91
+
92
+ ### Torbafile
93
+
94
+ Torbafile is an assets specification. It is a plain text file that contains one or more
95
+ sections, each of them describes one remote source of assets.
96
+
97
+ Currently only zip archives and [Github releases][github-releases] are supported.
98
+
99
+ #### Zip archive package
100
+
101
+ Allows to download and unpack asset package from any source accessible by curl.
102
+
103
+ The syntax is:
104
+
105
+ ```
106
+ zip "name", url: "..." [, import: %w(...)]
107
+ ```
108
+
109
+ where "name" is an arbitrary name for the package, more on "import" below. For example,
110
+
111
+ ```
112
+ zip "scroll_magic", url: "https://github.com/janpaepke/ScrollMagic/archive/v2.0.0.zip"
113
+ ```
114
+
115
+ #### Github release package
116
+
117
+ This is a more readable version/shortcut for "https://github.com/.../archive/..." URLs.
118
+
119
+ The syntax is:
120
+
121
+ ```
122
+ gh_release "name", source: "...", tag: "..." [, import: %w(...)]
123
+ ```
124
+
125
+ where "source" is the user + repository and "tag" is the repository tag (exactly as on Github,
126
+ i.e. with "v" prefix if present), more on "import" below. For example,
127
+
128
+ ```
129
+ gh_release "scroll_magic", source: "janpaepke/ScrollMagic", tag: "v.2.0.0"
130
+ ```
131
+
132
+ ### "Packing the torba" process
133
+
134
+ When you run `torba pack` the following happens:
135
+
136
+ 1. All remote sources are cached locally.
137
+
138
+ 2. Archives are unpacked with top level directory removed. This is done for good cause it
139
+ usually contains package version, e.g. "react-0.13.2", and you don't want to reference versions
140
+ inside your application code (except Torbafile).
141
+
142
+ 3. Remote source's content is copied as is to the `Torba.home_path` location with **package name used
143
+ as a namespace**.
144
+
145
+ This is also done for good in order to avoid name collisions (since many JS projects can have
146
+ assets with the same names and all packages are placed into shared Sprockets' virtual filesystem).
147
+ The downside is that you have to use namespace in each require directive, which can lead to
148
+ duplication:
149
+
150
+ ```javascript
151
+ // application.js
152
+ //= require 'underscore/underscore'
153
+ ```
154
+
155
+ Hint: use "require_directory" if you strongly against such duplication:
156
+
157
+ ```javascript
158
+ //= require_directory 'underscore'
159
+ ```
160
+
161
+ 4. Stylesheets (if any) are converted to ".css.erb" with "asset_path" helpers used in "url(...)"
162
+ statements.
163
+
164
+ ### :import option
165
+
166
+ Copying whole remote source's content has one disadvantage of using remote source specific paths in your
167
+ require/import directives. For example, if an archive contains file in "dist/css" directory, you'll have
168
+ to mention it:
169
+
170
+ ```css
171
+ /* application.css */
172
+ @import 'lightslider/dist/css/lightslider';
173
+ ```
174
+
175
+ To mitigate this you can cherry-pick files from the source via "import" option, for example:
176
+
177
+ ```
178
+ gh_release "lightslider", source: "sachinchoolur/lightslider", tag: "1.1.2", import: %w[
179
+ dist/css/lightslider.css
180
+ ]
181
+ ```
182
+
183
+ Such files will be copied directly to the package root (i.e. file tree becomes flatten), thus you
184
+ can omit unnesseccary paths:
185
+
186
+ ```css
187
+ @import 'lightslider/lightslider';
188
+ ```
189
+
190
+ You can use any Dir.glob pattern:
191
+
192
+ ```
193
+ gh_release "lightslider", source: "sachinchoolur/lightslider", tag: "1.1.2", import: %w[
194
+ dist/css/lightslider.css
195
+ dist/img/*.png
196
+ ]
197
+ ```
198
+
199
+ In addition to this "path/" is treated as a shortcut for "path/**/*" glob pattern.
200
+
201
+
202
+ [bower]: http://bower.io/
203
+ [sprockets]: https://github.com/sstephenson/sprockets/
204
+ [sprockets-load-path]: https://github.com/sstephenson/sprockets#the-load-path
205
+ [torba-pronounce]: http://upload.wikimedia.org/wikipedia/commons/2/28/Uk-%D1%82%D0%BE%D1%80%D0%B1%D0%B0.ogg
206
+ [github-releases]: https://help.github.com/articles/about-releases/
207
+ [sprockets-directives]: https://github.com/sstephenson/sprockets#the-directive-processor
208
+ [sass-import]: http://sass-lang.com/documentation/file.SASS_REFERENCE.html#import
209
+ [rails-assets]: https://rails-assets.org/
210
+ [bower-rails]: https://github.com/rharriso/bower-rails
211
+ [semver]: http://semver.org/
data/bin/torba ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "torba/cli"
4
+
5
+ Torba::Cli.start
data/lib/torba/cli.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "thor"
2
+ require "torba"
3
+
4
+ module Torba
5
+ class Cli < Thor
6
+ desc "pack", "download and prepare all packages defined in Torbafile"
7
+ def pack
8
+ Torba.pretty_errors { Torba.pack }
9
+ Torba.ui.confirm "Torba has been packed!"
10
+ end
11
+
12
+ desc "verify", "check if all packages are prepared"
13
+ def verify
14
+ Torba.pretty_errors { Torba.verify }
15
+ Torba.ui.confirm "Torba is prepared!"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ module Torba
2
+ # Parses content of CSS file and converts its image assets paths into Sprockets'
3
+ # {https://github.com/sstephenson/sprockets#logical-paths logical paths}.
4
+ class CssUrlToErbAssetPath
5
+ URL_RE =
6
+ /
7
+ url\( # url(
8
+ \s* # optional space
9
+ (?!data) # no data URIs
10
+ ['"]? # optional quote
11
+ (?!\/) # only relative location
12
+ ([^'"]+?) # location
13
+ ['"]? # optional quote
14
+ \s* # optional space
15
+ \) # )
16
+ /xm
17
+
18
+ # @return [String] CSS file content where image "url(...)" paths are replaced by ERB
19
+ # interpolations "url(<%= asset_path(...) %>)".
20
+ # @param content [String] content of original CSS file
21
+ # @param file_path [String] absolute path to original CSS file
22
+ # @yield [image_file_path]
23
+ # @yieldparam image_file_path [String] absolute path to original image file which is mentioned
24
+ # within CSS file
25
+ # @yieldreturn [String] logical path to image file within Sprockets' virtual filesystem.
26
+ #
27
+ # @example
28
+ # content = \
29
+ # ".react-toolbar {
30
+ # width: 100%;
31
+ # background: url('./images/toolbar.png');
32
+ # }"
33
+ #
34
+ # new_content = CssUrlToErbAssetPath.call(content, "/var/downloads/react_unzipped/styles.css") do |url|
35
+ # url.sub("/var/downloads/react_unzipped/images", "react-toolbar-js"
36
+ # end
37
+ #
38
+ # new_content #=>
39
+ # ".react-toolbar {
40
+ # width: 100%;
41
+ # background: url('<%= asset_path('react-toolbar-js/toolbar.png') %>');
42
+ # }"
43
+ def self.call(content, file_path)
44
+ content.gsub(URL_RE) do
45
+ absolute_image_file_path = File.expand_path($1, File.dirname(file_path))
46
+ sprockets_file_path = yield absolute_image_file_path
47
+ "url('<%= asset_path('#{sprockets_file_path}') %>')"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ module Torba
2
+ module Errors
3
+ AssetNotFound = Class.new(StandardError)
4
+ end
5
+
6
+ # Represents a list of assets to be imported from a remote source.
7
+ class ImportList
8
+ class Asset < Struct.new(:absolute_path, :subpath)
9
+ def css?
10
+ absolute_path.end_with?(".css")
11
+ end
12
+ end
13
+
14
+ # @return [Array<Asset>] full list of assets to be imported.
15
+ attr_reader :assets
16
+
17
+ def initialize(assets)
18
+ @assets = assets
19
+ end
20
+
21
+ # @return [Asset] asset with given path.
22
+ # @param path [String] absolute path of an asset.
23
+ # @raise [Errors::AssetNotFound] if nothing found
24
+ def find_by_absolute_path(path)
25
+ assets.find { |asset| asset.absolute_path == path } || raise(Errors::AssetNotFound.new(path))
26
+ end
27
+
28
+ # @return [Array<Asset>] list of stylesheets to be imported.
29
+ def css_assets
30
+ assets.find_all { |asset| asset.css? }
31
+ end
32
+
33
+ # @return [Array<Asset>] list of assets to be imported except stylesheets.
34
+ def non_css_assets
35
+ assets.find_all { |asset| !asset.css? }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,65 @@
1
+ require "torba/package"
2
+ require "torba/remote_sources/zip"
3
+ require "torba/remote_sources/github_release"
4
+
5
+ module Torba
6
+ # Represents Torbafile.
7
+ class Manifest
8
+ # all packages defined in Torbafile
9
+ attr_reader :packages
10
+
11
+ # Reads Torbafile and evaluates it.
12
+ # @return [Manifest]
13
+ #
14
+ # @overload self.build(file_path)
15
+ # @param file_path [String] absolute path to Torbafile
16
+ #
17
+ # @overload self.build
18
+ # Reads Torbafile from current directory
19
+ def self.build(file_path = nil)
20
+ file_path ||= File.join(Dir.pwd, "Torbafile")
21
+
22
+ manifest = new
23
+ content = File.read(file_path)
24
+ manifest.instance_eval(content, file_path)
25
+ manifest
26
+ end
27
+
28
+ def initialize
29
+ @packages = []
30
+ end
31
+
32
+ # Adds {Package} with {RemoteSources::Zip} to {#packages}
33
+ def zip(name, options = {})
34
+ url = options.fetch(:url)
35
+ remote_source = RemoteSources::Zip.new(url)
36
+ packages << Package.new(name, remote_source, options)
37
+ end
38
+
39
+ # Adds {Package} with {RemoteSources::GithubRelease} to {#packages}
40
+ def gh_release(name, options = {})
41
+ source = options.fetch(:source)
42
+ tag = options.fetch(:tag)
43
+ remote_source = RemoteSources::GithubRelease.new(source, tag)
44
+ packages << Package.new(name, remote_source, options)
45
+ end
46
+
47
+ # Builds all {#packages}
48
+ # @return [void]
49
+ def pack
50
+ packages.each(&:build)
51
+ end
52
+
53
+ # @return [Array<String>] list of paths to each prepared asset package.
54
+ # It should be appended to the Sprockets' load_path.
55
+ def load_path
56
+ packages.map(&:load_path)
57
+ end
58
+
59
+ # Verifies all {#packages}
60
+ # @return [void]
61
+ def verify
62
+ packages.each(&:verify)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,173 @@
1
+ require "fileutils"
2
+
3
+ require "torba/css_url_to_erb_asset_path"
4
+ require "torba/import_list"
5
+
6
+ module Torba
7
+ module Errors
8
+ UnbuiltPackage = Class.new(StandardError)
9
+
10
+ class NothingToImport < StandardError
11
+ attr_reader :package, :path
12
+
13
+ def initialize(options)
14
+ @package = options.fetch(:package)
15
+ @path = options.fetch(:path)
16
+ super
17
+ end
18
+ end
19
+ end
20
+
21
+ # Represents remote source with explicit paths/files to be imported (i.e.
22
+ # copied from an archive, git repository etc).
23
+ # Stylesheets (if any) are treated specially because static "url(...)"
24
+ # definitions should be replaced with Sprockets-aware "asset_path" helpers.
25
+ class Package
26
+ # @return [String] short package name, acts as as namespace within Sprockets' load path.
27
+ # Doesn't need to be equal to remote package name.
28
+ attr_reader :name
29
+
30
+ # @return instance that implements {RemoteSources::Common}
31
+ attr_reader :remote_source
32
+
33
+ # @return [Array<String>] list of file paths to import (relative to remote source root).
34
+ # @example Direct path to a file
35
+ # ["build/underscore.js"]
36
+ # @example {http://www.rubydoc.info/stdlib/core/Dir#glob-class_method Dir.glob} pattern
37
+ # ["build/*.js", "**/*.css"]
38
+ # @example Any file within directory (including subdirectories)
39
+ # ["build/"] # same as ["build/**/*"]
40
+ attr_reader :import_paths
41
+
42
+ # @param name [String] see {#name}
43
+ # @param remote_source [#[]] see {#remote_source}
44
+ # @param options [Hash]
45
+ # @option options [Array<String>] :import list assigned to {#import_paths}
46
+ def initialize(name, remote_source, options = {})
47
+ @name = name
48
+ @remote_source = remote_source
49
+ @import_paths = (options[:import] || ["**/*"]).sort.map do |path|
50
+ if path.end_with?("/")
51
+ File.join(path, "**/*")
52
+ else
53
+ path
54
+ end
55
+ end
56
+ end
57
+
58
+ # @raise [Errors::UnbuiltPackage] if package is not build.
59
+ def verify
60
+ raise Errors::UnbuiltPackage.new(name) unless built?
61
+ end
62
+
63
+ # Cache remote source and import specified assets to {#load_path}.
64
+ # @return [void]
65
+ # @note Directories explicitly specified in {#import_paths} are not preserved after importing,
66
+ # i.e. resulted file tree becomes flatten. This way you can omit build specific directories
67
+ # when requiring assets in your project. If you want to preserve remote source file tree,
68
+ # use glob patterns without mentioning subdirectories in them.
69
+ #
70
+ # In addition {#name} is used as a namespace folder within {#load_path} to protect file names
71
+ # clashing across packages.
72
+ #
73
+ # package.name #=> "datepicker"
74
+ # package.import_paths #=> ["css/stylesheet.css", "js/*.js"]
75
+ # Dir[package.load_path + "/**/*"] #=> ["datepicker/stylesheet.css", "datepicker/script.js"]
76
+ #
77
+ # package.name #=> "datepicker"
78
+ # package.import_paths #=> ["**/*.{js,css}"]
79
+ # Dir[package.load_path + "/**/*"] #=> ["datepicker/css/stylesheet.css", "datepicker/js/script.js"]
80
+ def build
81
+ return if built?
82
+ process_stylesheets
83
+ process_other_assets
84
+ rescue
85
+ remove
86
+ raise
87
+ end
88
+
89
+ # @return [String] path where processed files of the package reside. It's located within
90
+ # {Torba.home_path} directory.
91
+ def load_path
92
+ @load_path ||= File.join(Torba.home_path, folder_name)
93
+ end
94
+
95
+ # @return [ImportList]
96
+ def import_list
97
+ @import_list ||= build_import_list
98
+ end
99
+
100
+ # Remove self from filesystem.
101
+ # @return [void]
102
+ def remove
103
+ FileUtils.rm_rf(load_path)
104
+ end
105
+
106
+ private
107
+
108
+ def built?
109
+ Dir.exist?(load_path)
110
+ end
111
+
112
+ def folder_name
113
+ digest = Torba.digest(import_paths.join << remote_source.digest)
114
+ "#{name}-#{digest}"
115
+ end
116
+
117
+ def build_import_list
118
+ assets = import_paths.flat_map do |import_path|
119
+ path_wo_glob_metacharacters = import_path.sub(/\*.+$/, "")
120
+
121
+ assets = remote_source[import_path].map do |absolute_path, relative_path|
122
+ subpath =
123
+ if relative_path == import_path
124
+ File.basename(relative_path)
125
+ else
126
+ relative_path.sub(path_wo_glob_metacharacters, "")
127
+ end
128
+
129
+ ImportList::Asset.new(absolute_path, subpath)
130
+ end
131
+
132
+ if assets.empty?
133
+ raise Errors::NothingToImport.new(package: name, path: import_path)
134
+ end
135
+
136
+ assets
137
+ end
138
+
139
+ ImportList.new(assets)
140
+ end
141
+
142
+ def process_stylesheets
143
+ import_list.css_assets.each do |asset|
144
+ content = File.read(asset.absolute_path)
145
+
146
+ new_content = CssUrlToErbAssetPath.call(content, asset.absolute_path) do |image_file_path|
147
+ image_asset = import_list.find_by_absolute_path(image_file_path)
148
+ with_namespace(image_asset.subpath)
149
+ end
150
+
151
+ new_absolute_path = File.join(load_path, with_namespace(asset.subpath + ".erb"))
152
+ ensure_directory(new_absolute_path)
153
+ File.write(new_absolute_path, new_content)
154
+ end
155
+ end
156
+
157
+ def process_other_assets
158
+ import_list.non_css_assets.each do |asset|
159
+ new_absolute_path = File.join(load_path, with_namespace(asset.subpath))
160
+ ensure_directory(new_absolute_path)
161
+ FileUtils.cp(asset.absolute_path, new_absolute_path)
162
+ end
163
+ end
164
+
165
+ def with_namespace(file_name)
166
+ File.join(name, file_name)
167
+ end
168
+
169
+ def ensure_directory(file)
170
+ FileUtils.mkdir_p(File.dirname(file))
171
+ end
172
+ end
173
+ end