linner 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad0db2767b837ef41fc611523a7208c1ae2eb7dc
4
- data.tar.gz: 0b0171fa4c551709cd155859cc9723033a2ef3b6
3
+ metadata.gz: 163b9428e612081d56821206cf90610b4d5653c1
4
+ data.tar.gz: ef2a80cc220dc6ebfc834368b5f1a3726295300a
5
5
  SHA512:
6
- metadata.gz: 6a505783f236a3a1ea6a9390a5b40dc9d62de55798aa2cc2cf83a9d3e4b695ec5af96dcf267c70b2d72d64ed8bdd2b01a42c096d2a5cd4944b9e7aac0727908f
7
- data.tar.gz: 7b8751ebbbafd8ec0db858d793d80ab1e16513be93331bd14e363cdd77a294867807a140d9814abb057f05724a4368cc196d44867aad676d3ffde81637872d43
6
+ metadata.gz: ba2dd94fda915cd2d24b44ac1313193516140e094510676e1276b1cffb1726f3b40b5c324a7c82baa084c34ef77f5f5e3cb766f93c1bbb7b33e6da7db17b297e
7
+ data.tar.gz: 23321997a50676a923bc9517365f74559f0078d31342efd9f05953cff8f1eca05cd60168285e003dcf096515f6cb3c37ce6387d84a3906de217530080a372796
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ v 0.4.1
2
+ - `manifest.yml` support
3
+ - lots of badges support
4
+
1
5
  v 0.4.0
2
6
  - revision support
3
7
  - fix memory leak in `cache_miss?`
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Linner
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/linner.png)](http://badge.fury.io/rb/linner) [![Build Status](https://travis-ci.org/SaitoWu/linner.png)](https://travis-ci.org/SaitoWu/linner) [![Code Climate](https://codeclimate.com/repos/520fd56e56b10241f50f15a3/badges/e8beb45f55b5c1fa6142/gpa.png)](https://codeclimate.com/repos/520fd56e56b10241f50f15a3/feed) [![Dependency Status](https://gemnasium.com/SaitoWu/linner.png)](https://gemnasium.com/SaitoWu/linner)
4
+
3
5
  Linner is a full-featured HTML5 application assembler.
4
6
 
5
7
  ![Linner](http://d.pr/i/bWPA+)
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ desc "test"
4
+ task :test do
5
+ sh 'rspec'
6
+ end
7
+
8
+ task :default => [:test]
@@ -3,6 +3,7 @@ require "nokogiri"
3
3
  require "linner/version"
4
4
  require "linner/command"
5
5
  require "linner/asset"
6
+ require "linner/cache"
6
7
  require "linner/helper"
7
8
  require "linner/reactor"
8
9
  require "linner/wrapper"
@@ -23,12 +24,30 @@ module Linner
23
24
  @root ||= Pathname('.').expand_path
24
25
  end
25
26
 
27
+ def env
28
+ @env ||= Environment.new begin
29
+ linner_file = root.join("Linnerfile")
30
+ config_file = root.join("config.yml")
31
+ File.exist?(linner_file) ? linner_file : config_file
32
+ end
33
+ end
34
+
26
35
  def cache
27
- @cache ||= {}
36
+ @cache ||= Cache.new
28
37
  end
29
38
 
30
- def env
31
- @env ||= Environment.new root.join("config.yml")
39
+ def manifest
40
+ @manifest ||= begin
41
+ hash = {}
42
+ env.groups.each do |config|
43
+ config["concat"].to_h.each do |dest, pattern|
44
+ asset = Asset.new(File.join env.public_folder, dest)
45
+ hash[dest] = asset.relative_digest_path
46
+ asset.revision!
47
+ end
48
+ end
49
+ hash
50
+ end
32
51
  end
33
52
 
34
53
  def compile?
@@ -36,11 +55,13 @@ module Linner
36
55
  end
37
56
 
38
57
  def sass_engine_options
39
- @options ||= Compass.configuration.to_sass_engine_options
40
- env.paths.each do |load_path|
41
- @options[:load_paths] << Sass::Importers::Filesystem.new(load_path)
58
+ @options ||= begin
59
+ options = Compass.configuration.to_sass_engine_options
60
+ env.paths.each do |load_path|
61
+ options[:load_paths] << Sass::Importers::Filesystem.new(load_path)
62
+ end
63
+ options
42
64
  end
43
- @options
44
65
  end
45
66
 
46
67
  def perform(*asset)
@@ -56,19 +77,15 @@ private
56
77
  config["concat"].each_with_index do |pair, index|
57
78
  dest, pattern, order = pair.first, pair.last, config["order"]||[]
58
79
  matches = Dir.glob(pattern).order_by(order)
59
- next if matches.select {|p| cache_miss? p}.empty?
60
- dest = Asset.new(File.join env.public_folder, dest)
61
- definition = Wrapper.definition if dest.path == env.definition
62
- dest.content = matches.inject(definition || "") {|s, m| s << cache[m].content}
63
- dest.compress if compile?
64
- dest.write
80
+ next if matches.select {|p| cache.miss? p}.empty?
81
+ write_asset(dest, matches)
65
82
  end
66
83
  end
67
84
 
68
85
  def copy(config)
69
86
  config["copy"].each do |dest, pattern|
70
87
  Dir.glob(pattern).each do |path|
71
- next if not cache_miss?(path)
88
+ next if not cache.miss?(path)
72
89
  logical_path = Asset.new(path).logical_path
73
90
  dest_path = File.join(env.public_folder, dest, logical_path)
74
91
  FileUtils.mkdir_p File.dirname(dest_path)
@@ -78,29 +95,34 @@ private
78
95
  end
79
96
 
80
97
  def revision
81
- revision = File.join env.public_folder, env.revision
82
- doc = Nokogiri::HTML.parse(File.read revision)
83
- doc.search("script").each do |x|
84
- next unless src = x.attributes["src"]
85
- asset = Asset.new(File.join env.public_folder, src)
86
- x.set_attribute "src", asset.revision!
87
- end
88
- doc.search("link").each do |x|
89
- next unless href = x.attributes["href"]
90
- asset = Asset.new(File.join env.public_folder, href)
91
- x.set_attribute "href", asset.revision!
92
- end
93
- File.open(revision, "w") do |f|
94
- f.write doc.to_html
98
+ dump_manifest
99
+ return unless File.exist?(rev = File.join(env.public_folder, env.revision.to_s))
100
+ doc = Nokogiri::HTML.parse(File.read rev)
101
+ replace_tag_with_manifest_value doc, "script", "src"
102
+ replace_tag_with_manifest_value doc, "link", "href"
103
+ File.open(rev, "w") {|f| f.write doc.to_html}
104
+ end
105
+
106
+ private
107
+
108
+ def write_asset(dest, child_assets)
109
+ asset = Asset.new(File.join env.public_folder, dest)
110
+ definition = (asset.path == env.definition ? Wrapper.definition : "")
111
+ asset.content = child_assets.inject(definition) {|s, m| s << cache[m].content}
112
+ asset.compress if compile?
113
+ asset.write
114
+ end
115
+
116
+ def replace_tag_with_manifest_value doc, tag, attribute
117
+ doc.search(tag).each do |x|
118
+ next unless node = x.attributes[attribute]
119
+ x.set_attribute attribute, manifest[node.value]
95
120
  end
96
121
  end
97
122
 
98
- def cache_miss?(path)
99
- asset = Asset.new(path)
100
- if cache[path] and cache[path].mtime == asset.mtime
101
- false
102
- else
103
- cache[path] = asset
123
+ def dump_manifest
124
+ File.open(File.join(env.public_folder, env.manifest), "w") do |f|
125
+ YAML.dump(manifest, f)
104
126
  end
105
127
  end
106
128
  end
@@ -7,9 +7,7 @@ module Linner
7
7
 
8
8
  def initialize(path)
9
9
  @path = path
10
- if File.exist? @path
11
- @mtime ||= File.mtime(path).to_i
12
- end
10
+ @mtime = File.mtime(path).to_i if File.exist?(path)
13
11
  end
14
12
 
15
13
  def mtime
@@ -17,17 +15,20 @@ module Linner
17
15
  end
18
16
 
19
17
  def extname
20
- File.extname @path
18
+ @extname = File.extname path
19
+ end
20
+
21
+ def digest_path
22
+ digest = Digest::MD5.hexdigest content
23
+ path.chomp(extname) << "-#{digest}" << extname
21
24
  end
22
25
 
23
- def digest
24
- Digest::MD5.hexdigest content
26
+ def relative_digest_path
27
+ digest_path.gsub /#{Linner.env.public_folder}/, ""
25
28
  end
26
29
 
27
30
  def revision!
28
- revision = @path.chomp(extname) << "-#{digest}" << extname
29
- File.rename @path, revision
30
- revision.gsub /#{Linner.env.public_folder}/, ""
31
+ File.rename path, digest_path
31
32
  end
32
33
 
33
34
  def content
@@ -0,0 +1,19 @@
1
+ module Linner
2
+ class Cache < Hash
3
+ def miss? path
4
+ asset = Asset.new path
5
+ if self[path] and self[path].mtime == asset.mtime
6
+ false
7
+ else
8
+ self[path] = asset
9
+ end
10
+ end
11
+
12
+ def expire_by paths
13
+ is_include_partial_styles = paths.any? do |path|
14
+ Asset.new(path).stylesheet? and File.basename(path).start_with? "_"
15
+ end
16
+ self.reject! {|k, v| v.stylesheet?} if is_include_partial_styles
17
+ end
18
+ end
19
+ end
@@ -18,46 +18,18 @@ module Linner
18
18
  desc "build", "build assets"
19
19
  def build
20
20
  Linner.compile = true
21
-
22
21
  clean
23
-
24
- Notifier.profile do
25
- Linner.perform
26
- end
22
+ Notifier.profile { Linner.perform }
27
23
  end
28
24
 
29
25
  desc "watch", "watch assets"
30
26
  def watch
31
- trap :INT do
32
- Notifier.exit
33
- Process.kill("QUIT", 0)
34
- end
35
-
27
+ trap(:INT) { exit! }
36
28
  clean
37
-
38
- @proc = Proc.new do |modified, added, removed|
39
- begin
40
- Notifier.profile{ Linner.perform }
41
- rescue
42
- Notifier.error $!
43
- end
44
- end
45
- @proc.call
46
-
47
- Listen.to env.app_folder, env.vendor_folder, env.test_folder do |modified, added, removed|
48
- is_include_partial_styles = (modified + added + removed).any? do |path|
49
- Asset.new(path).stylesheet? and File.basename(path).start_with? "_"
50
- end
51
- Linner.cache.reject! {|k, v| v.stylesheet?} if is_include_partial_styles
52
- @proc.call
53
- end
54
-
55
- @reactor = Reactor.supervise_as(:reactor).actors.first
56
- Listen.to env.public_folder, relative_path: true do |modified, added, removed|
57
- @reactor.reload_browser(modified + added + removed)
58
- end
59
-
60
- sleep
29
+ perform_proc.call
30
+ watch_for_perform
31
+ watch_for_reload
32
+ Process.wait
61
33
  end
62
34
 
63
35
  desc "clean", "clean assets"
@@ -75,6 +47,35 @@ module Linner
75
47
  def env
76
48
  Linner.env
77
49
  end
50
+
51
+ def perform_proc
52
+ @proc ||= Proc.new do |modified, added, removed|
53
+ begin
54
+ Notifier.profile{ Linner.perform }
55
+ rescue
56
+ Notifier.error $!
57
+ end
58
+ end
59
+ end
60
+
61
+ def watch_for_perform
62
+ Listen.to env.watched_paths do |modified, added, removed|
63
+ Linner.cache.expire_by(modified + added + removed)
64
+ perform_proc.call
65
+ end
66
+ end
67
+
68
+ def watch_for_reload
69
+ reactor = Reactor.supervise_as(:reactor).actors.first
70
+ Listen.to env.public_folder, relative_path: true do |modified, added, removed|
71
+ reactor.reload_browser(modified + added + removed)
72
+ end
73
+ end
74
+
75
+ def exit!
76
+ Notifier.exit
77
+ Kernel::exit
78
+ end
78
79
  end
79
80
  end
80
81
 
@@ -9,22 +9,26 @@ module Linner
9
9
  @env = @convension.rmerge!(@env)
10
10
  end
11
11
 
12
- def paths
13
- groups.map { |group| group["paths"] }.flatten.uniq
14
- end
15
-
16
12
  %w(app test vendor public).each do |method|
17
13
  define_method("#{method}_folder") do
18
14
  @env["paths"][method]
19
15
  end
20
16
  end
21
17
 
22
- def revision
23
- @env["revision"]
18
+ def paths
19
+ groups.map { |group| group["paths"] }.flatten.uniq
20
+ end
21
+
22
+ def watched_paths
23
+ [app_folder, vendor_folder, test_folder].select do |path|
24
+ File.exists? path
25
+ end
24
26
  end
25
27
 
26
- def notification
27
- @env["notification"]
28
+ %w(revision notification manifest).each do |method|
29
+ define_method("#{method}") do
30
+ @env[method]
31
+ end
28
32
  end
29
33
 
30
34
  def modules_ignored
@@ -13,25 +13,18 @@ module Linner
13
13
  order_ary = ary.inject([[]]) do |a, x|
14
14
  x != "..." ? a.last << x : a<< []; a
15
15
  end
16
- order_by_before(self, order_ary.first)
17
- order_by_after(self, order_ary.last)
16
+ order_by_direction(order_ary.first, :before)
17
+ order_by_direction(order_ary.last, :after)
18
18
  self
19
19
  end
20
20
 
21
21
  private
22
- def order_by_before(list, before)
23
- before.reverse.each do |f|
24
- if i = list.index {|x| x =~ /#{f}/i}
25
- list.unshift list.delete_at i
26
- end
27
- end
28
- end
29
-
30
- def order_by_after(list, after)
31
- after.reverse.each do |f|
32
- if i = list.index {|x| x =~ /#{f}/i}
33
- list.push list.delete_at i
34
- end
22
+ def order_by_direction(ary, direction)
23
+ ary = ary.reverse if direction == :before
24
+ ary.each do |f|
25
+ next unless i = self.index {|x| x =~ /#{f}/i}
26
+ item = self.delete_at i
27
+ direction == :before ? self.unshift(item) : self.push(item)
35
28
  end
36
29
  end
37
30
  end
@@ -6,7 +6,7 @@
6
6
  <meta name="description" content="">
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1">
8
8
 
9
- <link rel="stylesheet" href="styles/app.css">
9
+ <link rel="stylesheet" href="/styles/app.css">
10
10
  </head>
11
11
  <body>
12
12
  <!--[if lt IE 7]>
@@ -15,8 +15,7 @@
15
15
 
16
16
  <p>Hello world! This is Linner Boilerplate.</p>
17
17
 
18
- <script src="scripts/vendor.js"></script>
19
- <script src="scripts/app.js"></script>
20
- <script>require("app")()</script>
18
+ <script src="/scripts/vendor.js"></script>
19
+ <script src="/scripts/app.js"></script>
21
20
  </body>
22
21
  </html>
@@ -3,21 +3,24 @@ paths:
3
3
  groups:
4
4
  scripts:
5
5
  concat:
6
- "scripts/app.js": "app/**/*.{js,coffee}"
7
- "scripts/vendor.js": "vendor/**/*.{js,coffee}"
6
+ "/scripts/app.js": "app/**/*.{js,coffee}"
7
+ "/scripts/vendor.js": "vendor/**/*.{js,coffee}"
8
8
  order:
9
9
  - "vendor/jquery-1.10.2.js"
10
10
  - "..."
11
+ - "app/scripts/app.coffee"
11
12
  styles:
12
13
  concat:
13
- "styles/app.css": "app/styles/**/[a-z]*.{css,scss,sass}"
14
+ "/styles/app.css": "app/styles/**/[a-z]*.{css,scss,sass}"
14
15
  images:
15
16
  copy:
16
- "images/": "app/images/**/*.{png,gif}"
17
+ "/images/": "app/images/**/*.{png,gif}"
17
18
  views:
18
19
  copy:
19
20
  "/": "app/views/**/*.{html,hbs}"
20
21
  modules:
21
22
  wrapper: "cmd"
23
+ ignored: "{vendor/**/*,app/scripts/app.{js,coffee}"
24
+ definition: "/scripts/app.js"
22
25
  revision: "index.html"
23
26
  notification: true
@@ -1,3 +1,3 @@
1
1
  module Linner
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1"
3
3
  end
@@ -22,15 +22,16 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency "thor", "~> 0.18"
23
23
  spec.add_dependency "tilt", "~> 1.4"
24
24
  spec.add_dependency "sass", "~> 3.2"
25
- spec.add_dependency "listen", "~> 1.2"
25
+ spec.add_dependency "listen", "~> 1.3"
26
26
  spec.add_dependency "nio4r", "~> 0.5.0"
27
- spec.add_dependency "uglifier", "~> 2.1"
27
+ spec.add_dependency "uglifier", "~> 2.2"
28
28
  spec.add_dependency "nokogiri", "~> 1.6.0"
29
29
  spec.add_dependency "compass", "~> 0.12.2"
30
30
  spec.add_dependency "cssminify", "~> 1.0.2"
31
31
  spec.add_dependency "coffee-script", "~> 2.2"
32
- spec.add_dependency "terminal-notifier", "~> 1.4"
32
+ spec.add_dependency "terminal-notifier", "~> 1.5"
33
33
 
34
+ spec.add_development_dependency "pry"
34
35
  spec.add_development_dependency "rake"
35
36
  spec.add_development_dependency "rspec", "~> 2.14"
36
37
  spec.add_development_dependency "bundler", "~> 1.3"
@@ -5,13 +5,19 @@ describe Asset do
5
5
  before(:each) do
6
6
  @script_asset = Asset.new("app/scripts/app.js")
7
7
  @style_asset = Asset.new("app/styles/app.css")
8
+ @dest_asset = Asset.new("public/app.js")
8
9
  end
9
10
 
10
- it "should be return right logical_path" do
11
+ it "should return right logical_path" do
11
12
  @script_asset.logical_path.should == "app.js"
12
13
  @style_asset.logical_path.should == "app.css"
13
14
  end
14
15
 
16
+ it "should return right digest_path" do
17
+ @dest_asset.digest_path.should == "public/app-cdd1c27fabeede75d12b8adf46e987fd.js"
18
+ @dest_asset.relative_digest_path.should == "/app-cdd1c27fabeede75d12b8adf46e987fd.js"
19
+ end
20
+
15
21
  it "should be javascript" do
16
22
  @script_asset.javascript?.should be_true
17
23
  @style_asset.stylesheet?.should be_true
@@ -17,6 +17,7 @@ describe Environment do
17
17
  it "should equals default config" do
18
18
  @env.notification.should be_true
19
19
  @env.wrapper.should == "cmd"
20
+ @env.manifest.should == "manifest.yml"
20
21
  @env.revision.should == false
21
22
  @env.groups.should respond_to(:each)
22
23
  end
@@ -6,21 +6,21 @@ describe Array do
6
6
  end
7
7
 
8
8
  it "won't change when before and after are empty array" do
9
- @array.order_by([]).should =~ @array
9
+ @array.order_by([]).should == @array
10
10
  end
11
11
 
12
12
  it "will change by before items" do
13
13
  @array.order_by(["jquery.js", "vendor.js"])
14
- @array.should =~ %w[jquery.js vendor.js app.js bootstrap.css reset.css]
14
+ @array.should == %w[jquery.js vendor.js app.js bootstrap.css reset.css]
15
15
  end
16
16
 
17
17
  it "will change by after items" do
18
18
  @array.order_by(["...", "reset.css", "bootstrap.css"])
19
- @array.should =~ %w[app.js jquery.js vendor.js reset.css bootstrap.css]
19
+ @array.should == %w[app.js jquery.js vendor.js reset.css bootstrap.css]
20
20
  end
21
21
 
22
22
  it "will change by before and after items" do
23
23
  @array.order_by(["jquery.js", "vendor.js", "...", "reset.css", "bootstrap.css"])
24
- @array.should =~ %w[jquery.js vendor.js app.js reset.css bootstrap.css]
24
+ @array.should == %w[jquery.js vendor.js app.js reset.css bootstrap.css]
25
25
  end
26
26
  end
@@ -24,8 +24,8 @@ groups:
24
24
  modules:
25
25
  wrapper: "cmd"
26
26
  ignored: "vendor/**/*"
27
- definition: "scripts/app.js"
27
+ definition: "/scripts/app.js"
28
28
 
29
+ manifest: "manifest.yml"
29
30
  revision: false
30
-
31
31
  notification: true
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saito
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-16 00:00:00.000000000 Z
11
+ date: 2013-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reel
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - ~>
74
74
  - !ruby/object:Gem::Version
75
- version: '1.2'
75
+ version: '1.3'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
- version: '1.2'
82
+ version: '1.3'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: nio4r
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - ~>
102
102
  - !ruby/object:Gem::Version
103
- version: '2.1'
103
+ version: '2.2'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ~>
109
109
  - !ruby/object:Gem::Version
110
- version: '2.1'
110
+ version: '2.2'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: nokogiri
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -170,14 +170,28 @@ dependencies:
170
170
  requirements:
171
171
  - - ~>
172
172
  - !ruby/object:Gem::Version
173
- version: '1.4'
173
+ version: '1.5'
174
174
  type: :runtime
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - ~>
179
179
  - !ruby/object:Gem::Version
180
- version: '1.4'
180
+ version: '1.5'
181
+ - !ruby/object:Gem::Dependency
182
+ name: pry
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: rake
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -230,6 +244,7 @@ extra_rdoc_files: []
230
244
  files:
231
245
  - .gitignore
232
246
  - .rspec
247
+ - .travis.yml
233
248
  - CHANGELOG
234
249
  - Gemfile
235
250
  - Gemfile.lock
@@ -241,6 +256,7 @@ files:
241
256
  - docs/config.md
242
257
  - lib/linner.rb
243
258
  - lib/linner/asset.rb
259
+ - lib/linner/cache.rb
244
260
  - lib/linner/command.rb
245
261
  - lib/linner/compressor.rb
246
262
  - lib/linner/environment.rb