pipefitter 0.1.0

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