sass 3.3.0 → 3.4.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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +58 -50
- data/Rakefile +1 -4
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass/cache_stores/filesystem.rb +6 -2
- data/lib/sass/css.rb +1 -3
- data/lib/sass/engine.rb +37 -46
- data/lib/sass/environment.rb +13 -17
- data/lib/sass/error.rb +6 -9
- data/lib/sass/exec/base.rb +187 -0
- data/lib/sass/exec/sass_convert.rb +264 -0
- data/lib/sass/exec/sass_scss.rb +424 -0
- data/lib/sass/exec.rb +5 -771
- data/lib/sass/features.rb +7 -0
- data/lib/sass/importers/base.rb +7 -2
- data/lib/sass/importers/filesystem.rb +9 -25
- data/lib/sass/importers.rb +0 -1
- data/lib/sass/media.rb +1 -4
- data/lib/sass/plugin/compiler.rb +200 -83
- data/lib/sass/plugin/staleness_checker.rb +1 -1
- data/lib/sass/plugin.rb +3 -3
- data/lib/sass/script/css_lexer.rb +1 -1
- data/lib/sass/script/functions.rb +622 -268
- data/lib/sass/script/lexer.rb +99 -34
- data/lib/sass/script/parser.rb +24 -23
- data/lib/sass/script/tree/funcall.rb +1 -1
- data/lib/sass/script/tree/interpolation.rb +20 -2
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +1 -1
- data/lib/sass/script/tree.rb +1 -0
- data/lib/sass/script/value/base.rb +7 -5
- data/lib/sass/script/value/bool.rb +0 -5
- data/lib/sass/script/value/color.rb +39 -21
- data/lib/sass/script/value/helpers.rb +107 -0
- data/lib/sass/script/value/list.rb +0 -15
- data/lib/sass/script/value/null.rb +0 -5
- data/lib/sass/script/value/number.rb +62 -14
- data/lib/sass/script/value/string.rb +59 -11
- data/lib/sass/script/value.rb +0 -1
- data/lib/sass/scss/css_parser.rb +8 -2
- data/lib/sass/scss/parser.rb +190 -328
- data/lib/sass/scss/rx.rb +15 -6
- data/lib/sass/scss/static_parser.rb +298 -1
- data/lib/sass/selector/abstract_sequence.rb +28 -13
- data/lib/sass/selector/comma_sequence.rb +92 -13
- data/lib/sass/selector/pseudo.rb +256 -0
- data/lib/sass/selector/sequence.rb +94 -24
- data/lib/sass/selector/simple.rb +14 -25
- data/lib/sass/selector/simple_sequence.rb +97 -33
- data/lib/sass/selector.rb +57 -194
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +26 -12
- data/lib/sass/stack.rb +0 -6
- data/lib/sass/supports.rb +2 -3
- data/lib/sass/tree/at_root_node.rb +1 -0
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/directive_node.rb +8 -2
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +4 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/prop_node.rb +1 -1
- data/lib/sass/tree/rule_node.rb +12 -7
- data/lib/sass/tree/visitors/check_nesting.rb +38 -10
- data/lib/sass/tree/visitors/convert.rb +16 -18
- data/lib/sass/tree/visitors/cssize.rb +29 -29
- data/lib/sass/tree/visitors/deep_copy.rb +5 -0
- data/lib/sass/tree/visitors/perform.rb +45 -33
- data/lib/sass/tree/visitors/set_options.rb +14 -0
- data/lib/sass/tree/visitors/to_css.rb +15 -14
- data/lib/sass/util/subset_map.rb +1 -1
- data/lib/sass/util.rb +222 -99
- data/lib/sass/version.rb +5 -5
- data/lib/sass.rb +0 -5
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +19 -10
- data/test/sass/conversion_test.rb +58 -1
- data/test/sass/css2sass_test.rb +23 -4
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +136 -199
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +236 -19
- data/test/sass/functions_test.rb +295 -253
- data/test/sass/importer_test.rb +31 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +14 -13
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +10 -7
- data/test/sass/script_test.rb +288 -74
- data/test/sass/scss/css_test.rb +141 -24
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +457 -18
- data/test/sass/source_map_test.rb +115 -25
- data/test/sass/superselector_test.rb +191 -0
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/normalized_map_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +31 -1
- data/test/sass/value_helpers_test.rb +5 -7
- data/test/test_helper.rb +2 -2
- data/vendor/listen/CHANGELOG.md +1 -228
- data/vendor/listen/Gemfile +5 -15
- data/vendor/listen/README.md +111 -77
- data/vendor/listen/Rakefile +0 -42
- data/vendor/listen/lib/listen/adapter.rb +195 -82
- data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
- data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
- data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
- data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
- data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
- data/vendor/listen/lib/listen/directory_record.rb +96 -61
- data/vendor/listen/lib/listen/listener.rb +135 -37
- data/vendor/listen/lib/listen/turnstile.rb +9 -5
- data/vendor/listen/lib/listen/version.rb +1 -1
- data/vendor/listen/lib/listen.rb +33 -19
- data/vendor/listen/listen.gemspec +6 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
- data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
- data/vendor/listen/spec/listen/listener_spec.rb +128 -39
- data/vendor/listen/spec/listen_spec.rb +15 -21
- data/vendor/listen/spec/spec_helper.rb +4 -0
- data/vendor/listen/spec/support/adapter_helper.rb +52 -15
- data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
- data/vendor/listen/spec/support/listeners_helper.rb +30 -7
- metadata +25 -22
- data/ext/mkrf_conf.rb +0 -27
- data/lib/sass/importers/deprecated_path.rb +0 -51
- data/lib/sass/script/value/deprecated_false.rb +0 -55
- data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
- data/vendor/listen/lib/listen/multi_listener.rb +0 -143
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
- data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -4,17 +4,12 @@ module Listen
|
|
4
4
|
# Listener implementation for Linux `inotify`.
|
5
5
|
#
|
6
6
|
class Linux < Adapter
|
7
|
-
extend DependencyManager
|
8
|
-
|
9
|
-
# Declare the adapter's dependencies
|
10
|
-
dependency 'rb-inotify', '~> 0.9'
|
11
|
-
|
12
7
|
# Watched inotify events
|
13
8
|
#
|
14
9
|
# @see http://www.tin.org/bin/man.cgi?section=7&topic=inotify
|
15
10
|
# @see https://github.com/nex3/rb-inotify/blob/master/lib/rb-inotify/notifier.rb#L99-L177
|
16
11
|
#
|
17
|
-
EVENTS =
|
12
|
+
EVENTS = [:recursive, :attrib, :create, :delete, :move, :close_write]
|
18
13
|
|
19
14
|
# The message to show when the limit of inotify watchers is not enough
|
20
15
|
#
|
@@ -25,63 +20,31 @@ module Listen
|
|
25
20
|
for information on how to solve this issue.
|
26
21
|
EOS
|
27
22
|
|
28
|
-
|
23
|
+
def self.target_os_regex; /linux/i; end
|
24
|
+
def self.adapter_gem; 'rb-inotify'; end
|
25
|
+
|
26
|
+
# Initializes the Adapter.
|
27
|
+
#
|
28
|
+
# @see Listen::Adapter#initialize
|
29
29
|
#
|
30
30
|
def initialize(directories, options = {}, &callback)
|
31
31
|
super
|
32
|
-
@worker = init_worker
|
33
32
|
rescue Errno::ENOSPC
|
34
33
|
abort(INOTIFY_LIMIT_MESSAGE)
|
35
34
|
end
|
36
35
|
|
37
|
-
|
38
|
-
#
|
39
|
-
# @param [Boolean] blocking whether or not to block the current thread after starting
|
40
|
-
#
|
41
|
-
def start(blocking = true)
|
42
|
-
@mutex.synchronize do
|
43
|
-
return if @stop == false
|
44
|
-
super
|
45
|
-
end
|
46
|
-
|
47
|
-
@worker_thread = Thread.new { @worker.run }
|
48
|
-
@poll_thread = Thread.new { poll_changed_dirs } if @report_changes
|
49
|
-
|
50
|
-
@worker_thread.join if blocking
|
51
|
-
end
|
52
|
-
|
53
|
-
# Stops the adapter.
|
54
|
-
#
|
55
|
-
def stop
|
56
|
-
@mutex.synchronize do
|
57
|
-
return if @stop == true
|
58
|
-
super
|
59
|
-
end
|
60
|
-
|
61
|
-
@worker.stop
|
62
|
-
Thread.kill(@worker_thread) if @worker_thread
|
63
|
-
@poll_thread.join if @poll_thread
|
64
|
-
end
|
65
|
-
|
66
|
-
# Checks if the adapter is usable on the current OS.
|
67
|
-
#
|
68
|
-
# @return [Boolean] whether usable or not
|
69
|
-
#
|
70
|
-
def self.usable?
|
71
|
-
return false unless RbConfig::CONFIG['target_os'] =~ /linux/i
|
72
|
-
super
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
36
|
+
private
|
76
37
|
|
77
38
|
# Initializes a INotify worker and adds a watcher for
|
78
39
|
# each directory passed to the adapter.
|
79
40
|
#
|
80
41
|
# @return [INotify::Notifier] initialized worker
|
81
42
|
#
|
82
|
-
|
43
|
+
# @see Listen::Adapter#initialize_worker
|
44
|
+
#
|
45
|
+
def initialize_worker
|
83
46
|
callback = lambda do |event|
|
84
|
-
if
|
47
|
+
if paused || (
|
85
48
|
# Event on root directory
|
86
49
|
event.name == ""
|
87
50
|
) || (
|
@@ -89,24 +52,29 @@ module Listen
|
|
89
52
|
# on the directories themselves too.
|
90
53
|
#
|
91
54
|
# @see http://linux.die.net/man/7/inotify
|
92
|
-
event.flags.include?(:isdir) and event.flags & [:close, :modify]
|
55
|
+
event.flags.include?(:isdir) and (event.flags & [:close, :modify]).any?
|
93
56
|
)
|
94
57
|
# Skip all of these!
|
95
58
|
next
|
96
59
|
end
|
97
60
|
|
98
|
-
|
99
|
-
@
|
61
|
+
mutex.synchronize do
|
62
|
+
@changed_directories << File.dirname(event.absolute_name)
|
100
63
|
end
|
101
64
|
end
|
102
65
|
|
103
66
|
INotify::Notifier.new.tap do |worker|
|
104
|
-
|
105
|
-
worker.watch(directory, *EVENTS.map(&:to_sym), &callback)
|
106
|
-
end
|
67
|
+
directories.each { |dir| worker.watch(dir, *EVENTS, &callback) }
|
107
68
|
end
|
108
69
|
end
|
109
70
|
|
71
|
+
# Starts the worker in a new thread.
|
72
|
+
#
|
73
|
+
# @see Listen::Adapter#start_worker
|
74
|
+
#
|
75
|
+
def start_worker
|
76
|
+
@worker_thread = Thread.new { worker.run }
|
77
|
+
end
|
110
78
|
end
|
111
79
|
|
112
80
|
end
|
@@ -1,66 +1,57 @@
|
|
1
1
|
module Listen
|
2
2
|
module Adapters
|
3
3
|
|
4
|
-
# The default delay between checking for changes.
|
5
4
|
DEFAULT_POLLING_LATENCY = 1.0
|
6
5
|
|
7
6
|
# Polling Adapter that works cross-platform and
|
8
7
|
# has no dependencies. This is the adapter that
|
9
8
|
# uses the most CPU processing power and has higher
|
10
|
-
# file IO
|
9
|
+
# file IO than the other implementations.
|
11
10
|
#
|
12
11
|
class Polling < Adapter
|
13
|
-
|
12
|
+
private
|
14
13
|
|
15
|
-
#
|
14
|
+
# The default delay between checking for changes.
|
16
15
|
#
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
# @see Listen::Adapter#default_latency
|
17
|
+
#
|
18
|
+
def default_latency
|
19
|
+
1.0
|
20
20
|
end
|
21
21
|
|
22
|
-
#
|
22
|
+
# The thread on which the main thread should wait
|
23
|
+
# when the adapter has been started in blocking mode.
|
23
24
|
#
|
24
|
-
# @
|
25
|
+
# @see Listen::Adapter#blocking_thread
|
25
26
|
#
|
26
|
-
def
|
27
|
-
|
28
|
-
return if @stop == false
|
29
|
-
super
|
30
|
-
end
|
31
|
-
|
32
|
-
@poll_thread = Thread.new { poll }
|
33
|
-
@poll_thread.join if blocking
|
27
|
+
def blocking_thread
|
28
|
+
poller_thread
|
34
29
|
end
|
35
30
|
|
36
|
-
#
|
31
|
+
# @see Listen::Adapter#start_worker
|
37
32
|
#
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
@poll_thread.join
|
33
|
+
# @see Listen::Adapter#start_worker
|
34
|
+
#
|
35
|
+
def start_worker
|
36
|
+
# The polling adapter has no worker! Sad panda! :'(
|
45
37
|
end
|
46
38
|
|
47
|
-
private
|
48
|
-
|
49
39
|
# Poll listener directory for file system changes.
|
50
40
|
#
|
51
|
-
|
52
|
-
|
53
|
-
|
41
|
+
# @see Listen::Adapter#poll_changed_directories
|
42
|
+
#
|
43
|
+
def poll_changed_directories
|
44
|
+
until stopped
|
45
|
+
next if paused
|
54
46
|
|
55
47
|
start = Time.now.to_f
|
56
|
-
|
57
|
-
|
58
|
-
nap_time =
|
48
|
+
callback.call(directories.dup, :recursive => true)
|
49
|
+
turnstile.signal
|
50
|
+
nap_time = latency - (Time.now.to_f - start)
|
59
51
|
sleep(nap_time) if nap_time > 0
|
60
52
|
end
|
61
53
|
rescue Interrupt
|
62
54
|
end
|
63
|
-
|
64
55
|
end
|
65
56
|
|
66
57
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'rubygems'
|
2
3
|
|
3
4
|
module Listen
|
4
5
|
module Adapters
|
@@ -6,81 +7,84 @@ module Listen
|
|
6
7
|
# Adapter implementation for Windows `wdm`.
|
7
8
|
#
|
8
9
|
class Windows < Adapter
|
9
|
-
extend DependencyManager
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
|
12
|
+
Please add the following to your Gemfile to avoid polling for changes:
|
13
|
+
require 'rbconfig'
|
14
|
+
gem 'wdm', '>= 0.1.0' if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
15
|
+
EOS
|
13
16
|
|
14
|
-
|
17
|
+
def self.target_os_regex; /mswin|mingw/i; end
|
18
|
+
def self.adapter_gem; 'wdm'; end
|
19
|
+
|
20
|
+
# Checks if the adapter is usable on target OS.
|
15
21
|
#
|
16
|
-
|
17
|
-
|
18
|
-
|
22
|
+
# @return [Boolean] whether usable or not
|
23
|
+
#
|
24
|
+
def self.usable?
|
25
|
+
super if mri? && at_least_ruby_1_9?
|
19
26
|
end
|
20
27
|
|
21
|
-
#
|
28
|
+
# Load the adapter gem
|
22
29
|
#
|
23
|
-
# @
|
30
|
+
# @return [Boolean] whether required or not
|
24
31
|
#
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
@worker_thread = Thread.new { @worker.run! }
|
32
|
-
|
33
|
-
# Wait for the worker to start. This is needed to avoid a deadlock
|
34
|
-
# when stopping immediately after starting.
|
35
|
-
sleep 0.1
|
36
|
-
|
37
|
-
@poll_thread = Thread.new { poll_changed_dirs } if @report_changes
|
38
|
-
|
39
|
-
@worker_thread.join if blocking
|
32
|
+
def self.load_dependent_adapter
|
33
|
+
super
|
34
|
+
rescue Gem::LoadError
|
35
|
+
Kernel.warn BUNDLER_DECLARE_GEM
|
40
36
|
end
|
41
37
|
|
42
|
-
|
43
|
-
#
|
44
|
-
def stop
|
45
|
-
@mutex.synchronize do
|
46
|
-
return if @stop == true
|
47
|
-
super
|
48
|
-
end
|
38
|
+
private
|
49
39
|
|
50
|
-
|
51
|
-
|
52
|
-
|
40
|
+
# Checks if Ruby engine is MRI.
|
41
|
+
#
|
42
|
+
# @return [Boolean]
|
43
|
+
#
|
44
|
+
def self.mri?
|
45
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
53
46
|
end
|
54
47
|
|
55
|
-
# Checks if
|
48
|
+
# Checks if Ruby engine is MRI.
|
56
49
|
#
|
57
|
-
# @return [Boolean]
|
50
|
+
# @return [Boolean]
|
58
51
|
#
|
59
|
-
def self.
|
60
|
-
|
61
|
-
super
|
52
|
+
def self.at_least_ruby_1_9?
|
53
|
+
Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('1.9.2')
|
62
54
|
end
|
63
55
|
|
64
|
-
private
|
65
|
-
|
66
56
|
# Initializes a WDM monitor and adds a watcher for
|
67
57
|
# each directory passed to the adapter.
|
68
58
|
#
|
69
59
|
# @return [WDM::Monitor] initialized worker
|
70
60
|
#
|
71
|
-
|
61
|
+
# @see Listen::Adapter#initialize_worker
|
62
|
+
#
|
63
|
+
def initialize_worker
|
72
64
|
callback = Proc.new do |change|
|
73
|
-
next if
|
74
|
-
|
75
|
-
|
65
|
+
next if paused
|
66
|
+
|
67
|
+
mutex.synchronize do
|
68
|
+
@changed_directories << File.dirname(change.path)
|
76
69
|
end
|
77
70
|
end
|
78
71
|
|
79
72
|
WDM::Monitor.new.tap do |worker|
|
80
|
-
|
73
|
+
directories.each { |dir| worker.watch_recursively(dir, &callback) }
|
81
74
|
end
|
82
75
|
end
|
83
76
|
|
77
|
+
# Start the worker in a new thread and sleep 0.1 second.
|
78
|
+
#
|
79
|
+
# @see Listen::Adapter#start_worker
|
80
|
+
#
|
81
|
+
def start_worker
|
82
|
+
@worker_thread = Thread.new { worker.run! }
|
83
|
+
# Wait for the worker to start. This is needed to avoid a deadlock
|
84
|
+
# when stopping immediately after starting.
|
85
|
+
sleep 0.1
|
86
|
+
end
|
87
|
+
|
84
88
|
end
|
85
89
|
|
86
90
|
end
|
@@ -11,8 +11,10 @@ module Listen
|
|
11
11
|
class DirectoryRecord
|
12
12
|
attr_reader :directory, :paths, :sha1_checksums
|
13
13
|
|
14
|
-
|
14
|
+
# The default list of directories that get ignored by the listener.
|
15
|
+
DEFAULT_IGNORED_DIRECTORIES = %w[.rbx .bundle .git .svn bundle log tmp vendor]
|
15
16
|
|
17
|
+
# The default list of files that get ignored by the listener.
|
16
18
|
DEFAULT_IGNORED_EXTENSIONS = %w[.DS_Store]
|
17
19
|
|
18
20
|
# Defines the used precision based on the type of mtime returned by the
|
@@ -56,15 +58,14 @@ module Listen
|
|
56
58
|
def initialize(directory)
|
57
59
|
raise ArgumentError, "The path '#{directory}' is not a directory!" unless File.directory?(directory)
|
58
60
|
|
59
|
-
@directory
|
60
|
-
@ignoring_patterns
|
61
|
-
@filtering_patterns = Set.new
|
62
|
-
@sha1_checksums = Hash.new
|
61
|
+
@directory, @sha1_checksums = File.expand_path(directory), Hash.new
|
62
|
+
@ignoring_patterns, @filtering_patterns = Set.new, Set.new
|
63
63
|
|
64
64
|
@ignoring_patterns.merge(DirectoryRecord.generate_default_ignoring_patterns)
|
65
65
|
end
|
66
66
|
|
67
|
-
# Returns the ignoring patterns in the record
|
67
|
+
# Returns the ignoring patterns in the record to know
|
68
|
+
# which paths should be ignored.
|
68
69
|
#
|
69
70
|
# @return [Array<Regexp>] the ignoring patterns
|
70
71
|
#
|
@@ -72,7 +73,7 @@ module Listen
|
|
72
73
|
@ignoring_patterns.to_a
|
73
74
|
end
|
74
75
|
|
75
|
-
# Returns the filtering patterns
|
76
|
+
# Returns the filtering patterns in the record to know
|
76
77
|
# which paths should be stored.
|
77
78
|
#
|
78
79
|
# @return [Array<Regexp>] the filtering patterns
|
@@ -86,10 +87,10 @@ module Listen
|
|
86
87
|
# @example Ignore some paths
|
87
88
|
# ignore %r{^ignored/path/}, /man/
|
88
89
|
#
|
89
|
-
# @param [Regexp]
|
90
|
+
# @param [Regexp] regexps a list of patterns for ignoring paths
|
90
91
|
#
|
91
92
|
def ignore(*regexps)
|
92
|
-
@ignoring_patterns.merge(regexps)
|
93
|
+
@ignoring_patterns.merge(regexps).reject! { |r| r.nil? }
|
93
94
|
end
|
94
95
|
|
95
96
|
# Replaces ignoring patterns in the record.
|
@@ -97,37 +98,37 @@ module Listen
|
|
97
98
|
# @example Ignore only these paths
|
98
99
|
# ignore! %r{^ignored/path/}, /man/
|
99
100
|
#
|
100
|
-
# @param [Regexp]
|
101
|
+
# @param [Regexp] regexps a list of patterns for ignoring paths
|
101
102
|
#
|
102
103
|
def ignore!(*regexps)
|
103
|
-
@ignoring_patterns.replace(regexps)
|
104
|
+
@ignoring_patterns.replace(regexps).reject! { |r| r.nil? }
|
104
105
|
end
|
105
106
|
|
106
|
-
# Adds filtering patterns to the
|
107
|
+
# Adds filtering patterns to the record.
|
107
108
|
#
|
108
109
|
# @example Filter some files
|
109
|
-
#
|
110
|
+
# filter /\.txt$/, /.*\.zip/
|
110
111
|
#
|
111
|
-
# @param [Regexp]
|
112
|
+
# @param [Regexp] regexps a list of patterns for filtering files
|
112
113
|
#
|
113
114
|
def filter(*regexps)
|
114
|
-
@filtering_patterns.merge(regexps)
|
115
|
+
@filtering_patterns.merge(regexps).reject! { |r| r.nil? }
|
115
116
|
end
|
116
117
|
|
117
|
-
# Replaces filtering patterns in the
|
118
|
+
# Replaces filtering patterns in the record.
|
118
119
|
#
|
119
120
|
# @example Filter only these files
|
120
|
-
#
|
121
|
+
# filter! /\.txt$/, /.*\.zip/
|
121
122
|
#
|
122
|
-
# @param [Regexp]
|
123
|
+
# @param [Regexp] regexps a list of patterns for filtering files
|
123
124
|
#
|
124
125
|
def filter!(*regexps)
|
125
|
-
@filtering_patterns.replace(regexps)
|
126
|
+
@filtering_patterns.replace(regexps).reject! { |r| r.nil? }
|
126
127
|
end
|
127
128
|
|
128
129
|
# Returns whether a path should be ignored or not.
|
129
130
|
#
|
130
|
-
# @param [String] path the path to test
|
131
|
+
# @param [String] path the path to test
|
131
132
|
#
|
132
133
|
# @return [Boolean]
|
133
134
|
#
|
@@ -138,7 +139,7 @@ module Listen
|
|
138
139
|
|
139
140
|
# Returns whether a path should be filtered or not.
|
140
141
|
#
|
141
|
-
# @param [String] path the path to test
|
142
|
+
# @param [String] path the path to test
|
142
143
|
#
|
143
144
|
# @return [Boolean]
|
144
145
|
#
|
@@ -159,9 +160,9 @@ module Listen
|
|
159
160
|
end
|
160
161
|
|
161
162
|
# Detects changes in the passed directories, updates
|
162
|
-
# the record with the new changes and returns the changes
|
163
|
+
# the record with the new changes and returns the changes.
|
163
164
|
#
|
164
|
-
# @param [Array] directories the list of directories scan for changes
|
165
|
+
# @param [Array] directories the list of directories to scan for changes
|
165
166
|
# @param [Hash] options
|
166
167
|
# @option options [Boolean] recursive scan all sub-directories recursively
|
167
168
|
# @option options [Boolean] relative_paths whether or not to use relative paths for changes
|
@@ -174,6 +175,7 @@ module Listen
|
|
174
175
|
|
175
176
|
directories.each do |directory|
|
176
177
|
next unless directory[@directory] # Path is or inside directory
|
178
|
+
|
177
179
|
detect_modifications_and_removals(directory, options)
|
178
180
|
detect_additions(directory, options)
|
179
181
|
end
|
@@ -188,9 +190,15 @@ module Listen
|
|
188
190
|
# @return [String] the relative path
|
189
191
|
#
|
190
192
|
def relative_to_base(path)
|
191
|
-
|
192
|
-
|
193
|
-
path.
|
193
|
+
path = path.dup
|
194
|
+
regexp = "\\A#{Regexp.quote directory}(#{File::SEPARATOR}|\\z)"
|
195
|
+
if path.respond_to?(:force_encoding)
|
196
|
+
path.force_encoding("BINARY")
|
197
|
+
regexp.force_encoding("BINARY")
|
198
|
+
end
|
199
|
+
if path.sub!(Regexp.new(regexp), '')
|
200
|
+
path
|
201
|
+
end
|
194
202
|
end
|
195
203
|
|
196
204
|
private
|
@@ -207,43 +215,69 @@ module Listen
|
|
207
215
|
# @option options [Boolean] relative_paths whether or not to use relative paths for changes
|
208
216
|
#
|
209
217
|
def detect_modifications_and_removals(directory, options = {})
|
210
|
-
|
218
|
+
paths[directory].each do |basename, meta_data|
|
211
219
|
path = File.join(directory, basename)
|
212
|
-
|
213
220
|
case meta_data.type
|
214
221
|
when 'Dir'
|
215
|
-
|
216
|
-
detect_modifications_and_removals(path, options) if options[:recursive]
|
217
|
-
else
|
218
|
-
detect_modifications_and_removals(path, { :recursive => true }.merge(options))
|
219
|
-
@paths[directory].delete(basename)
|
220
|
-
@paths.delete("#{directory}/#{basename}")
|
221
|
-
end
|
222
|
+
detect_modification_or_removal_for_dir(path, options)
|
222
223
|
when 'File'
|
223
|
-
|
224
|
-
|
224
|
+
detect_modification_or_removal_for_file(path, meta_data, options)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
225
228
|
|
226
|
-
|
227
|
-
# before checking the time difference
|
228
|
-
if (meta_data.mtime.to_i == new_mtime.to_i && content_modified?(path)) || meta_data.mtime < new_mtime
|
229
|
-
# Update the sha1 checksum of the file
|
230
|
-
insert_sha1_checksum(path)
|
229
|
+
def detect_modification_or_removal_for_dir(path, options)
|
231
230
|
|
232
|
-
|
233
|
-
|
234
|
-
|
231
|
+
# Directory still exists
|
232
|
+
if File.directory?(path)
|
233
|
+
detect_modifications_and_removals(path, options) if options[:recursive]
|
235
234
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
235
|
+
# Directory has been removed
|
236
|
+
else
|
237
|
+
detect_modifications_and_removals(path, options)
|
238
|
+
@paths[File.dirname(path)].delete(File.basename(path))
|
239
|
+
@paths.delete("#{File.dirname(path)}/#{File.basename(path)}")
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def detect_modification_or_removal_for_file(path, meta_data, options)
|
244
|
+
# File still exists
|
245
|
+
if File.exist?(path)
|
246
|
+
detect_modification(path, meta_data, options)
|
247
|
+
|
248
|
+
# File has been removed
|
249
|
+
else
|
250
|
+
removal_detected(path, meta_data, options)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def detect_modification(path, meta_data, options)
|
255
|
+
new_mtime = mtime_of(path)
|
256
|
+
|
257
|
+
# First check if we are in the same second (to update checksums)
|
258
|
+
# before checking the time difference
|
259
|
+
if (meta_data.mtime.to_i == new_mtime.to_i && content_modified?(path)) || meta_data.mtime < new_mtime
|
260
|
+
modification_detected(path, meta_data, new_mtime, options)
|
244
261
|
end
|
245
262
|
end
|
246
263
|
|
264
|
+
def modification_detected(path, meta_data, new_mtime, options)
|
265
|
+
# Update the sha1 checksum of the file
|
266
|
+
update_sha1_checksum(path)
|
267
|
+
|
268
|
+
# Update the meta data of the file
|
269
|
+
meta_data.mtime = new_mtime
|
270
|
+
@paths[File.dirname(path)][File.basename(path)] = meta_data
|
271
|
+
|
272
|
+
@changes[:modified] << (options[:relative_paths] ? relative_to_base(path) : path)
|
273
|
+
end
|
274
|
+
|
275
|
+
def removal_detected(path, meta_data, options)
|
276
|
+
@paths[File.dirname(path)].delete(File.basename(path))
|
277
|
+
@sha1_checksums.delete(path)
|
278
|
+
@changes[:removed] << (options[:relative_paths] ? relative_to_base(path) : path)
|
279
|
+
end
|
280
|
+
|
247
281
|
# Detects additions in a directory.
|
248
282
|
#
|
249
283
|
# @param [String] directory the path to analyze
|
@@ -283,9 +317,10 @@ module Listen
|
|
283
317
|
# @param [String] path the file path
|
284
318
|
#
|
285
319
|
def content_modified?(path)
|
320
|
+
return false unless File.ftype(path) == 'file'
|
286
321
|
@sha1_checksum = sha1_checksum(path)
|
287
|
-
if
|
288
|
-
|
322
|
+
if sha1_checksums[path] == @sha1_checksum || !sha1_checksums.key?(path)
|
323
|
+
update_sha1_checksum(path)
|
289
324
|
false
|
290
325
|
else
|
291
326
|
true
|
@@ -296,7 +331,7 @@ module Listen
|
|
296
331
|
#
|
297
332
|
# @param [String] path the SHA1-checksum path to insert in @sha1_checksums.
|
298
333
|
#
|
299
|
-
def
|
334
|
+
def update_sha1_checksum(path)
|
300
335
|
if @sha1_checksum ||= sha1_checksum(path)
|
301
336
|
@sha1_checksums[path] = @sha1_checksum
|
302
337
|
@sha1_checksum = nil
|
@@ -309,18 +344,18 @@ module Listen
|
|
309
344
|
#
|
310
345
|
def sha1_checksum(path)
|
311
346
|
Digest::SHA1.file(path).to_s
|
312
|
-
rescue
|
347
|
+
rescue
|
313
348
|
nil
|
314
349
|
end
|
315
350
|
|
316
351
|
# Traverses the base directory looking for paths that should
|
317
|
-
# be stored; thus paths that are
|
352
|
+
# be stored; thus paths that are filtered or not ignored.
|
318
353
|
#
|
319
354
|
# @yield [path] an important path
|
320
355
|
#
|
321
356
|
def important_paths
|
322
|
-
Find.find(
|
323
|
-
next if path ==
|
357
|
+
Find.find(directory) do |path|
|
358
|
+
next if path == directory
|
324
359
|
|
325
360
|
if File.directory?(path)
|
326
361
|
# Add a trailing slash to directories when checking if a directory is
|
@@ -355,7 +390,7 @@ module Listen
|
|
355
390
|
# @return [Boolean]
|
356
391
|
#
|
357
392
|
def existing_path?(path)
|
358
|
-
|
393
|
+
paths[File.dirname(path)][File.basename(path)] != nil
|
359
394
|
end
|
360
395
|
|
361
396
|
# Returns the modification time of a file based on the precision defined by the system
|