torba 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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