guard-entangle 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +104 -0
- data/lib/guard/entangle.rb +94 -0
- data/lib/guard/entangle/entangler.rb +82 -0
- data/lib/guard/entangle/formatter.rb +71 -0
- data/lib/guard/entangle/runner.rb +191 -0
- data/lib/guard/entangle/templates/Guardfile +5 -0
- data/lib/guard/entangle/version.rb +5 -0
- data/lib/guard/entangle/writer.rb +140 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 27e56ed3b4c431a44f1767c95c4d75dcc6a5502b
|
4
|
+
data.tar.gz: 6be2f2e9ab8b6847f5bba4a4a61d9c962e4b1788
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1ef5d8ce547953d5c61618c345ca449ffaed937a38ba412182aebd4a40aad280694f8b1ab2be80c38244fd01d92e7ecc180d89b6916b98a005f4271ea401ed64
|
7
|
+
data.tar.gz: 8bddb67b1f907edfafd3a0f4d3864564fbfaac33e717aa3e32cc5f3fb5bef158de5f817afda17e98595923ef2835403fc05d1a594aad5c83bbd89065d87460ec
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Deshi Rahim
|
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,104 @@
|
|
1
|
+
# Guard::Entangle
|
2
|
+
|
3
|
+
This is a plugin for the Ruby gem [Guard](https://github.com/guard/guard). Guard-Entangle allows you to include one file inline into another. It uses a syntax of `//= path/to/file` to substitute that file in place of that line.
|
4
|
+
|
5
|
+
## Common usage
|
6
|
+
|
7
|
+
Often you might have separate JavaScript files that need to be included into one file. Or partials of a file that need to be included into a master file.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
NOTE: This is an innitial release - just to make sure the name isn't stolen now that it's on Github (I'm told this happens). There are a couple of Rpecs missing. It will be a full release once those files are added to this gem.
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'guard-entangle'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install guard-entangle
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
As mentioned above, this is only a plugin for [Guard](https://github.com/guard/guard) and does not run on it's own. This plugin will look for `//= path/to/file` in a file and then replace it with the contents of the file stated after `//= `. For example:
|
28
|
+
|
29
|
+
```
|
30
|
+
This is some content in the file
|
31
|
+
//= src/File1.js
|
32
|
+
|
33
|
+
This is someother content in the file.
|
34
|
+
```
|
35
|
+
When this is triggered, `//= src/File1.js` will be replaced with the contents of _src/File1.js_.
|
36
|
+
|
37
|
+
This functionality is not limited to JavaScript files. However, only JavaScript files can be run through Uglifier.
|
38
|
+
|
39
|
+
The rules that trigger this behavior are defined in a _Guardfile_ in your project. For further instructions visit the [Guard website](https://github.com/guard/guard). In the _Guardfile_ you can include the following rules:
|
40
|
+
|
41
|
+
### Run all / Run
|
42
|
+
Guard will trigger the files either on their own or by the _run all_ command (when you press enter in Guard).
|
43
|
+
|
44
|
+
When a single file is triggered, it will check if the file is a partial or not.
|
45
|
+
|
46
|
+
A partial is a file that is not meant to be complied on its own, but is included within another file. Guard-Entangle determines this by check if the file or folder has **_**
|
47
|
+
at the start of its name. For example a file named *_File1.js* will be considered to be a partial. Folders that start with _ will be skipped when runnign the run all command. All files within a partials folder should also start with an _.
|
48
|
+
|
49
|
+
If it is a partial, it will trigger the _run all_ command. If its not a partial, it will compile that file only. This is because partials don't get compiled on their own, but its their parent that needs to be compiled.
|
50
|
+
|
51
|
+
The _run all_ command will take all the file(s) in the input directory (that are not partials) and compile them into the output directory.
|
52
|
+
|
53
|
+
### Compile all files in a directory to the output directory
|
54
|
+
|
55
|
+
```
|
56
|
+
guard :entangle, output: 'output', all_on_start: false, input: 'src', uglifier_options: {} do
|
57
|
+
watch(%r{^src/.+\..+$})
|
58
|
+
end
|
59
|
+
```
|
60
|
+
This will watch all files that match the regex _%r{^src/.+\..+$}_ or its subdirectory and then compile them into the output directory (:output). If the _run all_ command is triggered, all the files in the source directory (:input) will get compiled into the output directory (:output). In this instance, the input directory is _src_ and the output directory is _output_.
|
61
|
+
|
62
|
+
### Compile only one file
|
63
|
+
|
64
|
+
```
|
65
|
+
guard :entangle, output: 'output', all_on_start: false, input: 'src/File1.js', uglifier_options: {} do
|
66
|
+
watch(%r{^src/.+\..+$})
|
67
|
+
end
|
68
|
+
```
|
69
|
+
This will watch all files that match the regex _%r{^src/.+\..+$}_ or its subdirectory and then when a file has been changed, it will compile _src/File1.js_ and write the compiled file into _output/File1.js_. This is because the __:output__ folder is defined as output.
|
70
|
+
|
71
|
+
### Specifying the output filename
|
72
|
+
When compiling one file you may choose to specify the name of the output file. This has 2 different behaviors depending on the input. If the input is a directory, then it will entangle all the files into the output file. If the input is a file, then it will take that file and entangle it into the output file.
|
73
|
+
|
74
|
+
```
|
75
|
+
guard :entangle, output: 'output/output.js', all_on_start: false, input: 'src', uglifier_options: {} do
|
76
|
+
watch(%r{^src/.+\..+$})
|
77
|
+
end
|
78
|
+
```
|
79
|
+
This will watch all files that match the regex _%r{^src/.+\..+$}_ or its subdirectory and then when a file has been changed, it will compile all the files in _src_ and write the compiled file into _output/output.js_. This is because the __:output__ is defined as that file.
|
80
|
+
|
81
|
+
```
|
82
|
+
guard :entangle, output: 'output/output.js', all_on_start: false, input: 'src/File.js', uglifier_options: {} do
|
83
|
+
watch(%r{^src/.+\..+$})
|
84
|
+
end
|
85
|
+
```
|
86
|
+
This will watch all files that match the regex _%r{^src/.+\..+$}_ or its subdirectory and then when a file has been changed, it will compile all the file _src/File.js_ and write the compiled file into _output/output.js_. This is because the __:output__ is defined as that file.
|
87
|
+
|
88
|
+
## Options
|
89
|
+
The options that can be passed are
|
90
|
+
|
91
|
+
* :output = The output file/folder
|
92
|
+
* :input = The input file/folder
|
93
|
+
* :uglify = If js files should be uglified
|
94
|
+
* :all_on_start = If all files should be engtangled when guard has started
|
95
|
+
* :uglifier_options = {} Pass a Hash of any [uglifier options](https://github.com/lautis/uglifier)
|
96
|
+
* :copy = Saves a copy of the non uglified file along with the min file
|
97
|
+
|
98
|
+
## Contributing
|
99
|
+
|
100
|
+
1. Fork it
|
101
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
102
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
103
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
104
|
+
5. Create new Pull Request
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'guard'
|
2
|
+
require 'guard/plugin'
|
3
|
+
|
4
|
+
module Guard
|
5
|
+
class Entangle < Plugin
|
6
|
+
|
7
|
+
attr_accessor :options, :runner
|
8
|
+
|
9
|
+
DEFAULTS = {
|
10
|
+
:output => 'js',
|
11
|
+
:input => 'input',
|
12
|
+
:uglify => true,
|
13
|
+
:run_all => { message: 'Entangling all files' },
|
14
|
+
:all_on_start => false,
|
15
|
+
:hide_success => false,
|
16
|
+
:uglifier_options => {},
|
17
|
+
:copy => true #save a copy of the original file
|
18
|
+
}
|
19
|
+
|
20
|
+
# Initializes a Guard plugin.
|
21
|
+
# Don't do any work here, especially as Guard plugins get initialized even if they are not in an active group!
|
22
|
+
#
|
23
|
+
# @param [Hash] options the custom Guard plugin options
|
24
|
+
# @option options [Array<Guard::Watcher>] watchers the Guard plugin file watchers
|
25
|
+
# @option options [Symbol] group the group this Guard plugin belongs to
|
26
|
+
# @option options [Boolean] any_return allow any object to be returned from a watcher
|
27
|
+
#
|
28
|
+
def initialize(options = {})
|
29
|
+
options = DEFAULTS.merge(options)
|
30
|
+
|
31
|
+
@runner = Runner.new(options)
|
32
|
+
super(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Called once when Guard starts. Please override initialize method to init stuff.
|
36
|
+
#
|
37
|
+
# @raise [:task_has_failed] when start has failed
|
38
|
+
# @return [Object] the task result
|
39
|
+
#
|
40
|
+
def start
|
41
|
+
::Guard::UI.info 'Guard::Entangle is running'
|
42
|
+
run_all if options[:all_on_start]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Called when `reload|r|z + enter` is pressed.
|
46
|
+
# This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
|
47
|
+
#
|
48
|
+
# @raise [:task_has_failed] when reload has failed
|
49
|
+
# @return [Object] the task result
|
50
|
+
#
|
51
|
+
def reload
|
52
|
+
runner.reload
|
53
|
+
end
|
54
|
+
|
55
|
+
# Called when just `enter` is pressed
|
56
|
+
# This method should be principally used for long action like running all specs/tests/...
|
57
|
+
#
|
58
|
+
# @raise [:task_has_failed] when run_all has failed
|
59
|
+
# @return [Object] the task result
|
60
|
+
#
|
61
|
+
def run_all
|
62
|
+
_throw_if_failed { runner.run_all }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Default behaviour on file(s) changes that the Guard plugin watches.
|
66
|
+
# @param [Array<String>] paths the changes files or paths
|
67
|
+
# @raise [:task_has_failed] when run_on_change has failed
|
68
|
+
# @return [Object] the task result
|
69
|
+
#
|
70
|
+
def run_on_changes(paths)
|
71
|
+
run_on_modifications(paths)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Called on file(s) modifications that the Guard plugin watches.
|
75
|
+
#
|
76
|
+
# @param [Array<String>] paths the changes files or paths
|
77
|
+
# @raise [:task_has_failed] when run_on_modifications has failed
|
78
|
+
# @return [Object] the task result
|
79
|
+
#
|
80
|
+
def run_on_modifications(paths)
|
81
|
+
return false if paths.empty?
|
82
|
+
_throw_if_failed { runner.run(paths) }
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def _throw_if_failed
|
88
|
+
throw :task_has_failed unless yield
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
require 'guard/entangle/runner'
|
94
|
+
require 'guard/entangle/formatter'
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Guard
|
2
|
+
class Entangle
|
3
|
+
class Entangler
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
# Initialize entangler
|
7
|
+
#
|
8
|
+
# @param [Hash] options The options passed in
|
9
|
+
#
|
10
|
+
def initialize(options={})
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
# Convert the file
|
15
|
+
#
|
16
|
+
# @param [String] path The path of file
|
17
|
+
# @return [String] The entangled content
|
18
|
+
#
|
19
|
+
def convert(path)
|
20
|
+
if not File.exists?(path) or not File.readable?(path)
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
pn = Pathname.new(path)
|
24
|
+
file = File.open(path, 'rb')
|
25
|
+
contents = file.read
|
26
|
+
contents = convert_file(contents, pn.dirname)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Convert the file
|
32
|
+
#
|
33
|
+
# @param [String] contents The content to replace in
|
34
|
+
# @param [String] base The base path
|
35
|
+
# @return [String] The replaced content
|
36
|
+
#
|
37
|
+
def convert_file(contents, base)
|
38
|
+
matches = Set.new search(contents)
|
39
|
+
if not matches.empty?
|
40
|
+
matches.each do |entry|
|
41
|
+
contents = replace(contents, entry, base)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
return contents
|
45
|
+
end
|
46
|
+
contents
|
47
|
+
end
|
48
|
+
|
49
|
+
# Search the contnet for any file hooks
|
50
|
+
#
|
51
|
+
# @param [String] contents The content to search
|
52
|
+
# @return [Array] The array of found hooks
|
53
|
+
#
|
54
|
+
def search(contents)
|
55
|
+
contents.scan(/\/\/=.+$/)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Replace the file hook with the contents of the file
|
59
|
+
#
|
60
|
+
# @param [String] content The content of the file
|
61
|
+
# @param [String] file The file hook
|
62
|
+
# @param [String] path The base path of the file
|
63
|
+
# @return [String] The replaced content
|
64
|
+
#
|
65
|
+
def replace(content, file, path)
|
66
|
+
name = file.sub '//=', ''
|
67
|
+
stripped = name.strip
|
68
|
+
file = "#{path}/#{stripped}"
|
69
|
+
if File.exists?(file) && File.readable?(file)
|
70
|
+
insert = File.open(file, 'rb')
|
71
|
+
insert_content = insert.read
|
72
|
+
pn = Pathname.new(insert)
|
73
|
+
insert = convert_file(insert_content, pn.dirname)
|
74
|
+
content.gsub! "//=#{name}", insert_content
|
75
|
+
else
|
76
|
+
content.gsub! "// #{name}: Does not exist or isn't readable!"
|
77
|
+
end
|
78
|
+
content
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Guard
|
2
|
+
class Entangle
|
3
|
+
|
4
|
+
# The formatter handles providing output to the user.
|
5
|
+
class Formatter
|
6
|
+
|
7
|
+
# Print an info message to the console.
|
8
|
+
#
|
9
|
+
# @param [String] message the message to print
|
10
|
+
# @param [Hash] options the output options
|
11
|
+
# @option options [Boolean] :reset reset the UI
|
12
|
+
#
|
13
|
+
def info(message, options = {})
|
14
|
+
::Guard::UI.info(message, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Print a debug message to the console.
|
18
|
+
#
|
19
|
+
# @param [String] message the message to print
|
20
|
+
# @param [Hash] options the output options
|
21
|
+
# @option options [Boolean] :reset reset the UI
|
22
|
+
#
|
23
|
+
def debug(message, options = {})
|
24
|
+
::Guard::UI.debug(message, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Print a red error message to the console.
|
28
|
+
#
|
29
|
+
# @param [String] message the message to print
|
30
|
+
# @param [Hash] options the output options
|
31
|
+
# @option options [Boolean] :reset reset the UI
|
32
|
+
#
|
33
|
+
def error(message, options = {})
|
34
|
+
::Guard::UI.error(color(message, ';31'), options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Print a green success message to the console.
|
38
|
+
#
|
39
|
+
# @param [String] message the message to print
|
40
|
+
# @param [Hash] options the output options
|
41
|
+
# @option options [Boolean] :reset reset the UI
|
42
|
+
#
|
43
|
+
def success(message, options = {})
|
44
|
+
::Guard::UI.info(color(message, ';32'), options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Outputs a system notification.
|
48
|
+
#
|
49
|
+
# @param [String] message the message to print
|
50
|
+
# @param [Hash] options the output options
|
51
|
+
# @option options [Symbol, String] :image the image to use, either :failed, :pending or :success, or an image path
|
52
|
+
# @option options [String] :title the title of the system notification
|
53
|
+
#
|
54
|
+
def notify(message, options = {})
|
55
|
+
::Guard::Notifier.notify(message, options)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Print a info message to the console.
|
61
|
+
#
|
62
|
+
# @param [String] text the text to colorize
|
63
|
+
# @param [String] color_code the color code
|
64
|
+
#
|
65
|
+
def color(text, color_code)
|
66
|
+
::Guard::UI.send(:color_enabled?) ? "\e[0#{ color_code }m#{ text }\e[0m" : text
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'guard/entangle/entangler'
|
2
|
+
require 'guard/entangle/formatter'
|
3
|
+
require 'guard/entangle/writer'
|
4
|
+
|
5
|
+
module Guard
|
6
|
+
class Entangle
|
7
|
+
class Runner
|
8
|
+
|
9
|
+
attr_accessor :options
|
10
|
+
|
11
|
+
# Initialize the object
|
12
|
+
#
|
13
|
+
# @param [hash] options The options passed in
|
14
|
+
#
|
15
|
+
def initialize(options={})
|
16
|
+
@options = options
|
17
|
+
@entangler = Entangler.new(options)
|
18
|
+
@formatter = Formatter.new
|
19
|
+
@writer = Writer.new(options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Run a changed file
|
23
|
+
#
|
24
|
+
# @param [array] files Contains the changed file
|
25
|
+
# @return [void]
|
26
|
+
#
|
27
|
+
def run(files)
|
28
|
+
# Check if it's a partial
|
29
|
+
if partial?(files.first)
|
30
|
+
run_all
|
31
|
+
else
|
32
|
+
# We need to check to see if the input is a
|
33
|
+
# directory, else only check that file
|
34
|
+
if File.directory?(options[:input])
|
35
|
+
# Check if the output is one file
|
36
|
+
if output_dir?
|
37
|
+
compile_files(files)
|
38
|
+
else
|
39
|
+
compile_all(files)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
compile(options[:input])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Run all the file(s) that are set in input
|
49
|
+
#
|
50
|
+
# @return [void]
|
51
|
+
#
|
52
|
+
def run_all
|
53
|
+
paths = options[:input]
|
54
|
+
if File.directory?(paths)
|
55
|
+
options = @options.merge(@options[:run_all]).freeze
|
56
|
+
return if paths.empty?
|
57
|
+
::Guard::UI.info(options[:message], reset: true)
|
58
|
+
if output_dir?
|
59
|
+
run_paths(paths)
|
60
|
+
else
|
61
|
+
compile_all(paths)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
compile(paths)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
|
71
|
+
# Run through all the paths
|
72
|
+
#
|
73
|
+
# @param [array] paths The paths array
|
74
|
+
# @return [void]
|
75
|
+
#
|
76
|
+
def run_paths(paths)
|
77
|
+
if paths.kind_of?(Array)
|
78
|
+
paths.each do |path|
|
79
|
+
process_dir(path)
|
80
|
+
end
|
81
|
+
elsif paths.kind_of?(String)
|
82
|
+
process_dir(paths)
|
83
|
+
else
|
84
|
+
::Guard::UI.info "Paths in configuration are incorrect"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# Process the entire directory
|
90
|
+
#
|
91
|
+
# @param [array] paths The paths array
|
92
|
+
# @param [boolen] only_compile If the file shouldn't be saved
|
93
|
+
# @return [void]
|
94
|
+
#
|
95
|
+
def process_dir(paths, only_compile = false)
|
96
|
+
return false unless File.directory?(paths)
|
97
|
+
skip = %w[. ..];
|
98
|
+
|
99
|
+
cwd = Dir.pwd
|
100
|
+
path = "#{cwd}/#{paths}"
|
101
|
+
compiled = ''
|
102
|
+
|
103
|
+
entries = Dir.entries(path)
|
104
|
+
entries.each do |file|
|
105
|
+
# Skip the dot files and the partials
|
106
|
+
if not skip.include?(file) and not partial?(file)
|
107
|
+
if File.directory?("#{path}/#{file}")
|
108
|
+
contents = process_dir("#{paths}/#{file}", only_compile)
|
109
|
+
else
|
110
|
+
contents = compile("#{path}/#{file}", only_compile)
|
111
|
+
end
|
112
|
+
compiled << contents if only_compile
|
113
|
+
end
|
114
|
+
end
|
115
|
+
if (only_compile)
|
116
|
+
return compiled
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Compile all files and save then in the same file
|
121
|
+
#
|
122
|
+
# @param [string] path The input path
|
123
|
+
# @return [void]
|
124
|
+
#
|
125
|
+
def compile_all(path)
|
126
|
+
compiled = process_dir(path, true)
|
127
|
+
saved = @writer.output(compiled, options[:output])
|
128
|
+
if saved
|
129
|
+
message = "Successfully compiled and saved #{ options[:output] }"
|
130
|
+
@formatter.success(message)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# Compile each of the files
|
136
|
+
#
|
137
|
+
# @param [array] files The array of files
|
138
|
+
# @return [void]
|
139
|
+
#
|
140
|
+
def compile_files(files)
|
141
|
+
files.each do |file|
|
142
|
+
compile(file)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
# Compile a file
|
148
|
+
#
|
149
|
+
# @param [string] file The file to compile
|
150
|
+
# @param [boolean] only_compile If the file shouldn't be saved
|
151
|
+
# @return [string] The saved file or contents
|
152
|
+
#
|
153
|
+
def compile(file, only_compile = false)
|
154
|
+
contents = @entangler.convert(file)
|
155
|
+
|
156
|
+
# If we are only compiling
|
157
|
+
return contents if only_compile
|
158
|
+
|
159
|
+
# save the contents to a file
|
160
|
+
if contents
|
161
|
+
saved = @writer.output(contents, file)
|
162
|
+
if saved
|
163
|
+
message = "Successfully compiled and saved #{ saved }"
|
164
|
+
@formatter.success(message)
|
165
|
+
end
|
166
|
+
else
|
167
|
+
message = "#{ file } does not exist or is not accessable"
|
168
|
+
@formatter.error(message)
|
169
|
+
end
|
170
|
+
saved
|
171
|
+
end
|
172
|
+
|
173
|
+
# Check if the file is a partial or not
|
174
|
+
#
|
175
|
+
# @param [string] path The path to check
|
176
|
+
# @return [boolean] If it is a partial
|
177
|
+
#
|
178
|
+
def partial?(path)
|
179
|
+
File.basename(path).start_with? '_'
|
180
|
+
end
|
181
|
+
|
182
|
+
# If the output is a directory
|
183
|
+
#
|
184
|
+
# @return [boolean]
|
185
|
+
#
|
186
|
+
def output_dir?
|
187
|
+
File.extname(options[:output]).empty?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'uglifier'
|
2
|
+
require 'guard/entangle/formatter'
|
3
|
+
|
4
|
+
module Guard
|
5
|
+
class Entangle
|
6
|
+
|
7
|
+
# The writter class to create an write files
|
8
|
+
class Writer
|
9
|
+
|
10
|
+
attr_accessor :options, :cwd
|
11
|
+
|
12
|
+
|
13
|
+
# Initialize entangler
|
14
|
+
#
|
15
|
+
# @param [Hash] options The options passed in
|
16
|
+
#
|
17
|
+
def initialize(options={})
|
18
|
+
@cwd = Dir.pwd
|
19
|
+
@options = options
|
20
|
+
@formatter = Formatter.new
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# Outputs the file in it's needed location
|
25
|
+
#
|
26
|
+
# @param [string] content The content to be written
|
27
|
+
# @param [string] file The file path
|
28
|
+
# @return [mixed] path on success else false
|
29
|
+
#
|
30
|
+
def output(content, file)
|
31
|
+
# Output the file
|
32
|
+
path = get_path(file)
|
33
|
+
if create_path?(path)
|
34
|
+
if File.writable?(File.dirname(path))
|
35
|
+
# Uglify the files if the flag is set
|
36
|
+
if options[:uglify]
|
37
|
+
uglify(content, file, path)
|
38
|
+
else
|
39
|
+
save(content, path)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
path.gsub! "#{cwd}/", ''
|
43
|
+
message = "The path #{ rel } is not writable."
|
44
|
+
@formatter.error(message)
|
45
|
+
return
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Uglify the js file
|
53
|
+
#
|
54
|
+
# @param [string] content The content
|
55
|
+
# @param [string] file The source file path
|
56
|
+
# @param [string] path The destination path
|
57
|
+
# @return [mixed] path on success, else false
|
58
|
+
#
|
59
|
+
def uglify(content, file, path)
|
60
|
+
if File.extname(path) == '.js'
|
61
|
+
min = path.gsub(/\.[^.]+$/, '.min.js')
|
62
|
+
begin
|
63
|
+
uglify = Uglifier.new(options[:uglifier_options]).compile(content)
|
64
|
+
save(uglify, min)
|
65
|
+
rescue Exception => e
|
66
|
+
# Get a readable message
|
67
|
+
message = e.message.split(/[\n\r]/).first
|
68
|
+
@formatter.error("Uglifier - #{message}")
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
|
72
|
+
# If it's specified to keep a copy of the original
|
73
|
+
if options[:copy]
|
74
|
+
save(content, path)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
save(content, path)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# Save the file
|
83
|
+
#
|
84
|
+
# @param [string] content The content to write
|
85
|
+
# @param [string] path The destination file
|
86
|
+
# @return [mixed] path on success, else false
|
87
|
+
#
|
88
|
+
def save(content, path)
|
89
|
+
file = path.gsub "#{cwd}/", ''
|
90
|
+
if content
|
91
|
+
if File.writable?(File.dirname(path))
|
92
|
+
output = File.new(path, 'w+')
|
93
|
+
output.write(content)
|
94
|
+
output.close
|
95
|
+
return file
|
96
|
+
else
|
97
|
+
message = "The path #{ file } is not writable."
|
98
|
+
@formatter.error(message)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
message = "Content for #{ file } was empty"
|
102
|
+
@formatter.error(message)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# Get the appropriate path depending on the settings
|
108
|
+
#
|
109
|
+
# @param [string] file The file path
|
110
|
+
# @return [string] The correct file path
|
111
|
+
#
|
112
|
+
def get_path(file)
|
113
|
+
path = "#{cwd}/#{options[:output]}"
|
114
|
+
if File.extname(options[:output]).empty?
|
115
|
+
filename = file.gsub "#{cwd}/", ''
|
116
|
+
source = filename.split('/').first
|
117
|
+
filename.gsub! "#{source}/", ''
|
118
|
+
path = "#{path}/#{filename}"
|
119
|
+
end
|
120
|
+
path
|
121
|
+
end
|
122
|
+
|
123
|
+
# Create the required directories
|
124
|
+
#
|
125
|
+
# @param [string] path The file path
|
126
|
+
# @return [boolean] If the folder was created
|
127
|
+
#
|
128
|
+
def create_path?(path)
|
129
|
+
begin
|
130
|
+
FileUtils.mkdir_p(File.dirname(path))
|
131
|
+
rescue Exception => e
|
132
|
+
message = e.message.split(/[\n\r]/).first
|
133
|
+
@formatter.error("Uglifier - #{message}")
|
134
|
+
return false
|
135
|
+
end
|
136
|
+
true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: guard-entangle
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Deshi Rahim
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: guard
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: uglifier
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: |2
|
70
|
+
|
71
|
+
This gem leverages Guard's watch ability to insert files inline within another file. When the parser incounters a //= path/to/file, it then gets the content of that file and then inserts the content replacing the comment. Optionally that file can then be passed through Uglifier. Files with no insertions will just be copied over.
|
72
|
+
email:
|
73
|
+
- deshi@deshiknaves.com
|
74
|
+
executables: []
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- LICENSE
|
79
|
+
- README.md
|
80
|
+
- lib/guard/entangle.rb
|
81
|
+
- lib/guard/entangle/entangler.rb
|
82
|
+
- lib/guard/entangle/formatter.rb
|
83
|
+
- lib/guard/entangle/runner.rb
|
84
|
+
- lib/guard/entangle/templates/Guardfile
|
85
|
+
- lib/guard/entangle/version.rb
|
86
|
+
- lib/guard/entangle/writer.rb
|
87
|
+
homepage: http://rubygems.org/gems/guard-entangle
|
88
|
+
licenses:
|
89
|
+
- MIT
|
90
|
+
metadata: {}
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 2.2.2
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Inserts file content inline into another document. Optionally uglifies the
|
111
|
+
output.
|
112
|
+
test_files: []
|