pipefitter 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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pipefitter.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Lee Jones
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/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Pipefitter
2
+
3
+ A command-line tool that avoids unnecessary compiles when using the Rails Asset Pipeline.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'pipefitter'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install pipefitter
18
+
19
+ ## Usage
20
+
21
+ The `pipefitter` command is a smart wrapper to use when compiling your assets:
22
+
23
+ $ pipefitter
24
+ Running `bundle exec rake assets:precompile`...
25
+ Finished compiling assets!
26
+
27
+ It will automatically check if something changed that would require another compile:
28
+
29
+ $ pipefitter
30
+ Skipped compile because no changes were detected.
31
+
32
+ You can archive a compile in case it can be reused later (ex. switching back and forth between branches)
33
+
34
+ $ pipefitter --archive
35
+ $ rm -rf public/assets # oh no!
36
+ $ pipefitter
37
+ Used compiled assests from local archive!
38
+ # boom. didn't even need to recompile
39
+
40
+ Run a custom compile command:
41
+
42
+ $ pipefitter --command script/awesome_compile
43
+ Running `script/awesome_compile`...
44
+ Finished compiling assets!
45
+
46
+ If something seems out of sorts with the change detection, you can force asset compilation:
47
+
48
+ $ pipefitter --force
49
+ Running `bundle exec rake assets:precompile`...
50
+ Finished compiling assets!
51
+
52
+ ## Contributing
53
+
54
+ 1. Fork it
55
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
56
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
57
+ 4. Push to the branch (`git push origin my-new-feature`)
58
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/pipefitter ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/pipefitter.rb', __FILE__)
4
+
5
+ begin
6
+ Pipefitter::Cli.run(ARGV.clone, :environment => ENV.clone)
7
+ rescue Interrupt
8
+ exit 0
9
+ end
@@ -0,0 +1,32 @@
1
+ require 'pipefitter/error'
2
+
3
+ class Pipefitter
4
+ class Checksum
5
+ attr_reader :paths
6
+
7
+ def initialize(paths)
8
+ @paths = Array(paths)
9
+ end
10
+
11
+ def self.checksum(paths)
12
+ new(paths).checksum
13
+ end
14
+
15
+ def checksum
16
+ verify_paths
17
+ `find #{paths.join(' ')} -type f -exec md5 -q {} + | md5 -q`.strip
18
+ end
19
+
20
+ private
21
+
22
+ def verify_paths
23
+ paths.each do |path|
24
+ unless File.exists?(path)
25
+ raise PathNotFound, "Could not find #{path}"
26
+ end
27
+ end
28
+ end
29
+
30
+ class PathNotFound < Pipefitter::Error; end
31
+ end
32
+ end
@@ -0,0 +1,104 @@
1
+ require 'logger'
2
+
3
+ class Pipefitter
4
+ class Cli
5
+ attr_reader :arguments
6
+
7
+ def self.run(arguments, options = {})
8
+ new(arguments, options).run
9
+ end
10
+
11
+ def initialize(arguments, options = {})
12
+ @arguments = arguments
13
+ @options = options
14
+ end
15
+
16
+ def run
17
+ if help_requested?
18
+ logger.info help_text
19
+ else
20
+ Pipefitter.compile(path, compiler_options)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def path
27
+ path_argument || environment.fetch(:PWD)
28
+ end
29
+
30
+ def path_argument
31
+ arguments.reject do |arg|
32
+ arg =~ /\A\-\-/ || arg == command_argument
33
+ end.first
34
+ end
35
+
36
+ def environment
37
+ @environment ||= begin
38
+ environment = options.fetch(:environment)
39
+ symbolize_keys(environment)
40
+ end
41
+ end
42
+
43
+ def compiler_options
44
+ c_options = { :logger => logger }
45
+ if arguments.include?('--force')
46
+ c_options[:force] = true
47
+ end
48
+ if arguments.include?('--archive')
49
+ c_options[:archive] = true
50
+ end
51
+ if arguments.include?('--command')
52
+ c_options[:command] = command_argument
53
+ end
54
+ c_options
55
+ end
56
+
57
+ def options
58
+ @options_with_symbolized_keys ||= symbolize_keys(@options)
59
+ end
60
+
61
+ def command_argument
62
+ @command_argument ||= begin
63
+ if arguments.include?('--command')
64
+ arguments[arguments.index('--command') + 1]
65
+ else
66
+ nil
67
+ end
68
+ end
69
+ end
70
+
71
+ def symbolize_keys(hash)
72
+ hash.inject({}) do |memo, (k, v)|
73
+ memo[k.to_sym] = v
74
+ memo
75
+ end
76
+ end
77
+
78
+ def logger
79
+ @logger ||= options.fetch(:logger, Pipefitter::Logger.new)
80
+ end
81
+
82
+ def help_requested?
83
+ arguments.include?('--help')
84
+ end
85
+
86
+ def help_text
87
+ <<-EOS
88
+ # Pipefitter
89
+
90
+ ## Usage
91
+
92
+ pipefitter [project path] [options]
93
+
94
+ ### Arguments
95
+
96
+ * project root - optional project path to compile (default: current working directory)
97
+ * --force - forces a compile even when none is needed
98
+ * --archive - archives the compile assets to tmp in the project root
99
+ * --help - this help text :)
100
+ EOS
101
+
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,56 @@
1
+ class Pipefitter
2
+ class Compiler
3
+ attr_reader :base_path
4
+
5
+ def initialize(base_path, options = {})
6
+ @options = options
7
+ @base_path = base_path
8
+ end
9
+
10
+ def compile
11
+ logger.info "Running `#{compile_command}`..."
12
+ result = `cd #{base_path} && #{compile_command} 2>&1`.chomp
13
+ status = $?.to_i == 0
14
+ log_result(result, status)
15
+ status
16
+ end
17
+
18
+ private
19
+
20
+ def compile_command
21
+ options[:command] || default_compile_command
22
+ end
23
+
24
+ def default_compile_command
25
+ if using_bundler?
26
+ 'bundle exec rake assets:precompile'
27
+ else
28
+ 'rake assets:precompile'
29
+ end
30
+ end
31
+
32
+ def using_bundler?
33
+ File.exists?(gemfile)
34
+ end
35
+
36
+ def gemfile
37
+ gemfile = File.join(base_path, 'Gemfile')
38
+ end
39
+
40
+ def options
41
+ @options
42
+ end
43
+
44
+ def log_result(result, status = true)
45
+ if status
46
+ logger.info result unless result == ''
47
+ else
48
+ logger.error result unless result == ''
49
+ end
50
+ end
51
+
52
+ def logger
53
+ @logger ||= options.fetch(:logger, Pipefitter::Logger.new)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+ class Pipefitter
2
+ class Compressor
3
+ attr_reader :base_path
4
+
5
+ def initialize(base_path, target_path = nil)
6
+ @base_path = base_path
7
+ end
8
+
9
+ def compress(filename = nil)
10
+ `cd #{base_path}/public && tar -czf #{target(filename)} #{source}`
11
+ end
12
+
13
+ private
14
+
15
+ def source
16
+ './assets'
17
+ end
18
+
19
+ def target(filename = nil)
20
+ target_filename = filename || default_target_filename
21
+ File.join(target_path, target_filename)
22
+ end
23
+
24
+ def target_path
25
+ @target_path ||= begin
26
+ File.join(base_path, 'tmp', 'pipefitter')
27
+ end
28
+ end
29
+
30
+ def default_target_filename
31
+ "#{Time.now.to_i}.tar.gz"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ class Pipefitter
2
+ class Error < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,45 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ class Pipefitter
5
+ class Inventory
6
+ attr_reader :path
7
+
8
+ def initialize(path)
9
+ @path = path
10
+ FileUtils.mkdir_p(path)
11
+ end
12
+
13
+ def put(key, value)
14
+ data[key] = value
15
+ save
16
+ end
17
+
18
+ def get(key)
19
+ data[key]
20
+ end
21
+
22
+ private
23
+
24
+ def data
25
+ @data ||= begin
26
+ if File.exists?(data_file)
27
+ YAML.load_file(data_file) || {}
28
+ else
29
+ FileUtils.touch(data_file)
30
+ {}
31
+ end
32
+ end
33
+ end
34
+
35
+ def data_file
36
+ File.join(path, 'inventory.yml')
37
+ end
38
+
39
+ def save
40
+ File.open(data_file, 'w+') do |file|
41
+ file.write(data.to_yaml)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ require 'logger'
2
+
3
+ class Pipefitter
4
+ class Logger < ::Logger
5
+ def initialize(logger = STDOUT)
6
+ super(logger)
7
+ original_formatter = ::Logger::Formatter.new
8
+ self.formatter = proc { |severity, datetime, progname, message|
9
+ "#{message}\n"
10
+ }
11
+ self
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ class Pipefitter
2
+ VERSION = "0.1.0"
3
+ end
data/lib/pipefitter.rb ADDED
@@ -0,0 +1,156 @@
1
+ lib = File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'pipefitter/version'
4
+ require 'fileutils'
5
+
6
+ class Pipefitter
7
+ autoload 'Compiler', 'pipefitter/compiler'
8
+ autoload 'Checksum', 'pipefitter/checksum'
9
+ autoload 'Cli', 'pipefitter/cli'
10
+ autoload 'Compressor', 'pipefitter/compressor'
11
+ autoload 'Inventory', 'pipefitter/inventory'
12
+ autoload 'Error', 'pipefitter/error'
13
+ autoload 'Logger', 'pipefitter/logger'
14
+
15
+ attr_reader :base_path
16
+
17
+ def self.compile(base_path, options = {})
18
+ new(base_path, options).compile
19
+ end
20
+
21
+ def initialize(base_path, options = {})
22
+ @base_path = base_path
23
+ @options = options
24
+ end
25
+
26
+ def compile
27
+ setup
28
+ compile_if_necessary
29
+ end
30
+
31
+ def source_checksum
32
+ Checksum.checksum(source_paths)
33
+ end
34
+
35
+ def artifact_checksum
36
+ Checksum.checksum(artifact_paths)
37
+ end
38
+
39
+ private
40
+
41
+ def compile_if_necessary
42
+ if assets_need_compiling?
43
+ use_archive_or_compile
44
+ else
45
+ logger.info 'Skipped compile because no changes were detected.'
46
+ end
47
+ end
48
+
49
+ def use_archive_or_compile
50
+ if inventory_can_be_used?
51
+ move_archived_assets_into_place
52
+ logger.info 'Used compiled assests from local archive!'
53
+ else
54
+ compile_and_record_checksum
55
+ logger.info 'Finished compiling assets!'
56
+ archive
57
+ end
58
+ end
59
+
60
+ def compile_and_record_checksum
61
+ if compiler.compile
62
+ inventory.put(source_checksum, artifact_checksum)
63
+ else
64
+ raise CompilationError
65
+ end
66
+ end
67
+
68
+ def inventory_can_be_used?
69
+ ! compile_forced? && inventory_contains_compiled_assets?
70
+ end
71
+
72
+ def archive
73
+ if archiving_enabled?
74
+ logger.info 'Started archiving assets...'
75
+ compressor.compress("#{source_checksum}.tar.gz")
76
+ logger.info 'Finished archiving assets!'
77
+ end
78
+ end
79
+
80
+ def compiler
81
+ @compiler ||= Compiler.new(base_path,
82
+ :logger => logger,
83
+ :command => options[:command]
84
+ )
85
+ end
86
+
87
+ def compressor
88
+ @compressor ||= Compressor.new(base_path, :logger => logger)
89
+ end
90
+
91
+ def inventory
92
+ @inventory ||= Inventory.new(workspace)
93
+ end
94
+
95
+ def assets_need_compiling?
96
+ compile_forced? || inventory.get(source_checksum) != artifact_checksum
97
+ end
98
+
99
+ def archiving_enabled?
100
+ @archiving_enabled ||= options.fetch(:archive, false)
101
+ end
102
+
103
+ def workspace
104
+ File.join(base_path, 'tmp', 'pipefitter')
105
+ end
106
+
107
+ def setup
108
+ FileUtils.mkdir_p(workspace)
109
+ artifact_paths.each { |path| FileUtils.mkdir_p(path) }
110
+ end
111
+
112
+ def source_paths
113
+ paths = %w{
114
+ Gemfile
115
+ Gemfile.lock
116
+ app/assets
117
+ lib/assets
118
+ vendor/assets
119
+ }.map { |p| File.join(base_path, p) }
120
+ end
121
+
122
+ def artifact_paths
123
+ %w{
124
+ public/assets
125
+ }.map { |p| File.join(base_path, p) }
126
+ end
127
+
128
+ def inventory_contains_compiled_assets?
129
+ inventory_path = File.join(workspace, "#{source_checksum}.tar.gz")
130
+ inventory.get(source_checksum) && File.exists?(inventory_path)
131
+ end
132
+
133
+ def move_archived_assets_into_place
134
+ inventory_path = File.join(workspace, "#{source_checksum}.tar.gz")
135
+ FileUtils.rm_rf("#{base_path}/public/assets")
136
+ `cd #{base_path}/public && tar -xzf #{inventory_path}`
137
+ expected_artifact_checksum = inventory.get(source_checksum)
138
+ if expected_artifact_checksum != artifact_checksum
139
+ raise CompilationError, 'Archived assets did match stored checksum!'
140
+ end
141
+ end
142
+
143
+ def compile_forced?
144
+ options.fetch(:force, false)
145
+ end
146
+
147
+ def options
148
+ @options
149
+ end
150
+
151
+ def logger
152
+ @logger ||= options.fetch(:logger, Pipefitter::Logger.new)
153
+ end
154
+
155
+ class CompilationError < Pipefitter::Error; end
156
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pipefitter/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "pipefitter"
8
+ gem.version = Pipefitter::VERSION
9
+ gem.authors = ["Lee Jones"]
10
+ gem.email = ["scribblethink@gmail.com"]
11
+ gem.description = %q{A command-line tool that avoids unnecessary compiles when using the Rails Asset Pipeline.}
12
+ gem.summary = %q{Pipefitter answers the age old question of "To compile or not to compile?"}
13
+ gem.homepage = "https://github.com/leejones/pipefitter"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path('../../spec_helper.rb', __FILE__)
2
+
3
+ describe Pipefitter::Checksum do
4
+ include Pipefitter::SpecHelper
5
+
6
+ let(:test_root) { '/tmp/pipefitter_tests' }
7
+ let(:rails_root) { "#{test_root}/stubbed_rails_app" }
8
+
9
+ before(:each) do
10
+ stub_rails_app(test_root, :destroy_initially => true)
11
+ end
12
+
13
+ it 'checksums a group of files and directories' do
14
+ paths = %w{
15
+ Gemfile
16
+ Gemfile.lock
17
+ app/assets
18
+ lib/assets
19
+ vendor/assets
20
+ }.map { |p| File.join(rails_root, p) }
21
+ checksum = Pipefitter::Checksum.new(paths)
22
+ checksum.checksum.should eql('63af33df99e1f88bff6d3696f4ae6686')
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path('../../spec_helper.rb', __FILE__)
2
+
3
+ describe Pipefitter::Cli do
4
+ include Pipefitter::SpecHelper
5
+
6
+ it 'compiles a path' do
7
+ Pipefitter.should_receive(:compile).with('/tmp/pipefitter_app', kind_of(Hash)).and_return(true)
8
+ environment = { :PWD => '/tmp' }
9
+ Pipefitter::Cli.run(['/tmp/pipefitter_app'], :environment => environment)
10
+ end
11
+
12
+ it 'compiles the PWD' do
13
+ Pipefitter.should_receive(:compile).with('/tmp/pipefitter_app', kind_of(Hash)).and_return(true)
14
+ environment = { :PWD => '/tmp/pipefitter_app' }
15
+ Pipefitter::Cli.run([], :environment => environment)
16
+ end
17
+
18
+ it 'forces a compile' do
19
+ Pipefitter.should_receive(:compile).with('/tmp/pipefitter_app', {
20
+ :logger => kind_of(Logger),
21
+ :force => true
22
+ }).and_return(true)
23
+ environment = { :PWD => '/tmp/pipefitter_app' }
24
+ Pipefitter::Cli.run(['--force'], :environment => environment)
25
+ end
26
+
27
+ it 'archives' do
28
+ Pipefitter.should_receive(:compile).with('/tmp/pipefitter_app', {
29
+ :logger => kind_of(Logger),
30
+ :archive => true
31
+ }).and_return(true)
32
+ environment = { :PWD => '/tmp/pipefitter_app' }
33
+ Pipefitter::Cli.run(['--archive'], :environment => environment)
34
+ end
35
+
36
+ it 'helps' do
37
+ Pipefitter.should_not_receive(:compile)
38
+ environment = { :PWD => '/tmp/pipefitter_app' }
39
+ null_logger.should_receive(:info).with(/Usage/)
40
+ Pipefitter::Cli.run(['--help'], {
41
+ :environment => environment,
42
+ :logger => null_logger
43
+ })
44
+ end
45
+
46
+ it 'uses a custom command' do
47
+ Pipefitter.should_receive(:compile).with('/tmp/pipefitter_app', {
48
+ :logger => kind_of(Logger),
49
+ :command => 'script/precompile_assets'
50
+ }).and_return(true)
51
+ environment = { :PWD => '/tmp/pipefitter_app' }
52
+ Pipefitter::Cli.run(['--command', 'script/precompile_assets'], :environment => environment)
53
+ end
54
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../../spec_helper.rb', __FILE__)
2
+ require 'logger'
3
+
4
+ describe Pipefitter::Compiler do
5
+ include Pipefitter::SpecHelper
6
+
7
+ let(:test_root) { '/tmp/pipefitter_tests' }
8
+ let(:rails_root) { "#{test_root}/stubbed_rails_app" }
9
+
10
+ before(:each) do
11
+ stub_rails_app(test_root, :destroy_initially => true)
12
+ end
13
+
14
+ it 'compiles assets' do
15
+ compiler = Pipefitter::Compiler.new(rails_root, :logger => null_logger)
16
+ compiler.compile
17
+ File.exists?("#{rails_root}/public/assets/manifest.yml").should be_true
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path('../../../lib/pipefitter.rb', __FILE__)
2
+ require 'fileutils'
3
+
4
+ describe Pipefitter::Inventory do
5
+ before(:each) do
6
+ cleanup_inventory
7
+ end
8
+
9
+ it 'stores a record of a compilation' do
10
+ inventory = Pipefitter::Inventory.new('/tmp/pipefitter_tests')
11
+ inventory.put('source_checksum', 'artifact_checksum').should be_true
12
+ end
13
+
14
+ it 'retrieves a record of a compilation' do
15
+ inventory = Pipefitter::Inventory.new('/tmp/pipefitter_tests')
16
+ inventory.put('source_checksum', 'artifact_checksum')
17
+ inventory.get('source_checksum').should eql('artifact_checksum')
18
+ end
19
+
20
+ private
21
+
22
+ def cleanup_inventory
23
+ FileUtils.rm_rf('/tmp/pipefitter_tests')
24
+ end
25
+ end
@@ -0,0 +1,82 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+ require 'yaml'
3
+
4
+ describe Pipefitter do
5
+ include Pipefitter::SpecHelper
6
+
7
+ let(:test_root) { '/tmp/pipefitter_tests' }
8
+ let(:rails_root) { "#{test_root}/stubbed_rails_app" }
9
+
10
+ before(:each) do
11
+ stub_rails_app(test_root, :destroy_initially => true)
12
+ end
13
+
14
+ it 'compiles assets' do
15
+ Pipefitter.compile(rails_root, :logger => null_logger)
16
+ manifest_file = "#{rails_root}/public/assets/manifest.yml"
17
+ File.exists?(manifest_file).should be_true
18
+ end
19
+
20
+ it 'records a checksum' do
21
+ Pipefitter.compile(rails_root, :logger => null_logger)
22
+ inventory_file = "#{rails_root}/tmp/pipefitter/inventory.yml"
23
+ checksums = YAML.load_file(inventory_file)
24
+ checksums.has_key?('63af33df99e1f88bff6d3696f4ae6686').should be_true
25
+ end
26
+
27
+ it 'stores an archived copy of compiled assets' do
28
+ Pipefitter.compile(rails_root, :archive => true, :logger => null_logger)
29
+ archive_file = "#{rails_root}/tmp/pipefitter/63af33df99e1f88bff6d3696f4ae6686.tar.gz"
30
+ File.exists?(archive_file).should be_true
31
+ `cd #{test_root} && tar -xzf #{archive_file}`
32
+ archived_assets_checksum = `find #{test_root}/assets -type f -exec md5 -q {} + | md5 -q`.strip
33
+ compiled_assets_checksum = `find #{rails_root}/public/assets -type f -exec md5 -q {} + | md5 -q`.strip
34
+ archived_assets_checksum.should eql(compiled_assets_checksum)
35
+ end
36
+
37
+ it 'uses an archived copy of compiled assets when available' do
38
+ Pipefitter.compile(rails_root, :archive => true, :logger => null_logger)
39
+ original_checksum = Pipefitter::Checksum.new("#{rails_root}/public/assets").checksum
40
+ FileUtils.rm_rf("#{rails_root}/public/assets/manifest.yml")
41
+ compiler_stub = stub
42
+ Pipefitter::Compiler.stub(:new => compiler_stub)
43
+ compiler_stub.should_not_receive(:compile)
44
+ Pipefitter.compile(rails_root, :archive => true, :logger => null_logger)
45
+ final_checksum = Pipefitter::Checksum.new("#{rails_root}/public/assets").checksum
46
+ final_checksum.should eql(original_checksum)
47
+ end
48
+
49
+ it 'only compiles when needed' do
50
+ FileUtils.mkdir_p("#{rails_root}/tmp/pipefitter")
51
+ File.open("#{rails_root}/tmp/pipefitter/inventory.yml", 'w+') do |file|
52
+ file.write({
53
+ '63af33df99e1f88bff6d3696f4ae6686' => 'd41d8cd98f00b204e9800998ecf8427e'
54
+ }.to_yaml)
55
+ end
56
+ compiler_stub = stub
57
+ Pipefitter::Compiler.stub(:new => compiler_stub)
58
+ compiler_stub.should_not_receive(:compile)
59
+ Pipefitter.compile(rails_root, :logger => null_logger)
60
+ end
61
+
62
+ it 'does not record a checksum if the compile fails' do
63
+ FileUtils.rm("#{rails_root}/Rakefile")
64
+ expect { Pipefitter.compile(rails_root, :logger => null_logger) }.to raise_error(Pipefitter::CompilationError)
65
+ File.exists?("#{rails_root}/tmp/pipefitter/checksum.txt").should be_false
66
+ end
67
+
68
+ it 'forces a compile' do
69
+ Pipefitter.compile(rails_root, :logger => null_logger)
70
+ compiler_stub = stub
71
+ Pipefitter::Compiler.stub(:new => compiler_stub)
72
+ compiler_stub.should_receive(:compile).and_return(true)
73
+ Pipefitter.compile(rails_root, :force => true, :logger => null_logger)
74
+ end
75
+
76
+ it 'uses a custom compile command', :focus => true do
77
+ compiler_stub = stub
78
+ Pipefitter::Compiler.should_receive(:new).with('/tmp/pipefitter_tests/stubbed_rails_app', :logger => kind_of(Logger), :command => 'script/precompile_assets').and_return(compiler_stub)
79
+ compiler_stub.should_receive(:compile).and_return(true)
80
+ Pipefitter.compile(rails_root, :command => 'script/precompile_assets', :logger => null_logger)
81
+ end
82
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../../lib/pipefitter.rb', __FILE__)
2
+ require 'fileutils'
3
+ require 'logger'
4
+
5
+ class Pipefitter
6
+ module SpecHelper
7
+ private
8
+
9
+ def stub_rails_app(base_working_directory, options = {})
10
+ if options[:destroy_initially]
11
+ FileUtils.rm_rf(base_working_directory)
12
+ end
13
+ FileUtils.mkdir_p(base_working_directory)
14
+ app_source_path = File.expand_path('../support/stubbed_rails_app', __FILE__)
15
+ FileUtils.cp_r(app_source_path, base_working_directory)
16
+ end
17
+
18
+ def null_logger
19
+ @null_logger ||= ::Logger.new('/dev/null')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', '3.2.12'
4
+
5
+ # Bundle edge Rails instead:
6
+ # gem 'rails', :git => 'git://github.com/rails/rails.git'
7
+
8
+ gem 'sqlite3'
9
+
10
+
11
+ # Gems used only for assets and not required
12
+ # in production environments by default.
13
+ group :assets do
14
+ gem 'sass-rails', '~> 3.2.3'
15
+ gem 'coffee-rails', '~> 3.2.1'
16
+
17
+ # See https://github.com/sstephenson/execjs#readme for more supported runtimes
18
+ # gem 'therubyracer', :platforms => :ruby
19
+
20
+ gem 'uglifier', '>= 1.0.3'
21
+ end
22
+
23
+ gem 'jquery-rails'
24
+
25
+ # To use ActiveModel has_secure_password
26
+ # gem 'bcrypt-ruby', '~> 3.0.0'
27
+
28
+ # To use Jbuilder templates for JSON
29
+ # gem 'jbuilder'
30
+
31
+ # Use unicorn as the app server
32
+ # gem 'unicorn'
33
+
34
+ # Deploy with Capistrano
35
+ # gem 'capistrano'
36
+
37
+ # To use debugger
38
+ # gem 'debugger'
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env rake
2
+ require 'fileutils'
3
+
4
+ namespace :assets do
5
+ task :precompile do
6
+ rails_root = File.expand_path('..', __FILE__)
7
+ asset_root = File.join(rails_root, 'public', 'assets')
8
+ FileUtils.mkdir_p(File.join(asset_root))
9
+ File.open(File.join(asset_root, 'manifest.yml'), 'w+') do |file|
10
+ file.write('list of compiled assets...')
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
File without changes
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pipefitter
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Lee Jones
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2013-03-24 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: A command-line tool that avoids unnecessary compiles when using the Rails Asset Pipeline.
17
+ email:
18
+ - scribblethink@gmail.com
19
+ executables:
20
+ - pipefitter
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - .gitignore
27
+ - Gemfile
28
+ - LICENSE.txt
29
+ - README.md
30
+ - Rakefile
31
+ - bin/pipefitter
32
+ - lib/pipefitter.rb
33
+ - lib/pipefitter/checksum.rb
34
+ - lib/pipefitter/cli.rb
35
+ - lib/pipefitter/compiler.rb
36
+ - lib/pipefitter/compressor.rb
37
+ - lib/pipefitter/error.rb
38
+ - lib/pipefitter/inventory.rb
39
+ - lib/pipefitter/logger.rb
40
+ - lib/pipefitter/version.rb
41
+ - pipefitter.gemspec
42
+ - spec/pipefitter/checksum_spec.rb
43
+ - spec/pipefitter/cli_spec.rb
44
+ - spec/pipefitter/compiler_spec.rb
45
+ - spec/pipefitter/inventory_spec.rb
46
+ - spec/pipefitter_spec.rb
47
+ - spec/spec_helper.rb
48
+ - spec/support/stubbed_rails_app/Gemfile
49
+ - spec/support/stubbed_rails_app/Rakefile
50
+ - spec/support/stubbed_rails_app/app/assets/images/rails.png
51
+ - spec/support/stubbed_rails_app/app/assets/javascripts/application.js
52
+ - spec/support/stubbed_rails_app/app/assets/stylesheets/application.css
53
+ - spec/support/stubbed_rails_app/lib/assets/.gitkeep
54
+ - spec/support/stubbed_rails_app/public/.gitkeep
55
+ - spec/support/stubbed_rails_app/vendor/assets/javascripts/.gitkeep
56
+ - spec/support/stubbed_rails_app/vendor/assets/stylesheets/.gitkeep
57
+ - spec/support/stubbed_rails_app/vendor/plugins/.gitkeep
58
+ homepage: https://github.com/leejones/pipefitter
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options: []
63
+
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.8.24
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Pipefitter answers the age old question of "To compile or not to compile?"
85
+ test_files:
86
+ - spec/pipefitter/checksum_spec.rb
87
+ - spec/pipefitter/cli_spec.rb
88
+ - spec/pipefitter/compiler_spec.rb
89
+ - spec/pipefitter/inventory_spec.rb
90
+ - spec/pipefitter_spec.rb
91
+ - spec/spec_helper.rb
92
+ - spec/support/stubbed_rails_app/Gemfile
93
+ - spec/support/stubbed_rails_app/Rakefile
94
+ - spec/support/stubbed_rails_app/app/assets/images/rails.png
95
+ - spec/support/stubbed_rails_app/app/assets/javascripts/application.js
96
+ - spec/support/stubbed_rails_app/app/assets/stylesheets/application.css
97
+ - spec/support/stubbed_rails_app/lib/assets/.gitkeep
98
+ - spec/support/stubbed_rails_app/public/.gitkeep
99
+ - spec/support/stubbed_rails_app/vendor/assets/javascripts/.gitkeep
100
+ - spec/support/stubbed_rails_app/vendor/assets/stylesheets/.gitkeep
101
+ - spec/support/stubbed_rails_app/vendor/plugins/.gitkeep