sass 3.1.0 → 3.3.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 +7 -0
- data/CONTRIBUTING +1 -1
- data/MIT-LICENSE +2 -2
- data/README.md +29 -17
- data/Rakefile +43 -9
- data/VERSION +1 -1
- data/VERSION_DATE +1 -0
- data/VERSION_NAME +1 -1
- data/bin/sass +6 -1
- data/bin/sass-convert +6 -1
- data/bin/scss +6 -1
- data/ext/mkrf_conf.rb +27 -0
- data/lib/sass/cache_stores/base.rb +7 -3
- data/lib/sass/cache_stores/chain.rb +3 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -7
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +2 -1
- data/lib/sass/css.rb +168 -53
- data/lib/sass/engine.rb +502 -174
- data/lib/sass/environment.rb +151 -111
- data/lib/sass/error.rb +7 -7
- data/lib/sass/exec.rb +176 -60
- data/lib/sass/features.rb +40 -0
- data/lib/sass/importers/base.rb +46 -7
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +113 -30
- data/lib/sass/importers.rb +1 -0
- data/lib/sass/logger/base.rb +30 -0
- data/lib/sass/logger/log_level.rb +45 -0
- data/lib/sass/logger.rb +12 -0
- data/lib/sass/media.rb +213 -0
- data/lib/sass/plugin/compiler.rb +194 -104
- data/lib/sass/plugin/configuration.rb +18 -25
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +37 -11
- data/lib/sass/plugin.rb +10 -13
- data/lib/sass/railtie.rb +2 -1
- data/lib/sass/repl.rb +5 -6
- data/lib/sass/script/css_lexer.rb +8 -4
- data/lib/sass/script/css_parser.rb +5 -2
- data/lib/sass/script/functions.rb +1547 -618
- data/lib/sass/script/lexer.rb +122 -72
- data/lib/sass/script/parser.rb +304 -135
- data/lib/sass/script/tree/funcall.rb +306 -0
- data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
- data/lib/sass/script/tree/list_literal.rb +77 -0
- data/lib/sass/script/tree/literal.rb +45 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
- data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
- data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
- data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +15 -0
- data/lib/sass/script/value/arg_list.rb +36 -0
- data/lib/sass/script/value/base.rb +238 -0
- data/lib/sass/script/value/bool.rb +40 -0
- data/lib/sass/script/{color.rb → value/color.rb} +256 -74
- data/lib/sass/script/value/deprecated_false.rb +55 -0
- data/lib/sass/script/value/helpers.rb +155 -0
- data/lib/sass/script/value/list.rb +128 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +49 -0
- data/lib/sass/script/{number.rb → value/number.rb} +115 -62
- data/lib/sass/script/{string.rb → value/string.rb} +9 -11
- data/lib/sass/script/value.rb +12 -0
- data/lib/sass/script.rb +35 -9
- data/lib/sass/scss/css_parser.rb +2 -12
- data/lib/sass/scss/parser.rb +657 -230
- data/lib/sass/scss/rx.rb +17 -12
- data/lib/sass/scss/static_parser.rb +37 -6
- data/lib/sass/scss.rb +0 -1
- data/lib/sass/selector/abstract_sequence.rb +35 -3
- data/lib/sass/selector/comma_sequence.rb +29 -14
- data/lib/sass/selector/sequence.rb +371 -74
- data/lib/sass/selector/simple.rb +28 -13
- data/lib/sass/selector/simple_sequence.rb +163 -36
- data/lib/sass/selector.rb +138 -36
- data/lib/sass/shared.rb +3 -5
- data/lib/sass/source/map.rb +196 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +126 -0
- data/lib/sass/supports.rb +228 -0
- data/lib/sass/tree/at_root_node.rb +82 -0
- data/lib/sass/tree/comment_node.rb +34 -29
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +60 -0
- data/lib/sass/tree/debug_node.rb +3 -3
- data/lib/sass/tree/directive_node.rb +33 -3
- data/lib/sass/tree/each_node.rb +9 -9
- data/lib/sass/tree/extend_node.rb +20 -6
- data/lib/sass/tree/for_node.rb +6 -6
- data/lib/sass/tree/function_node.rb +12 -4
- data/lib/sass/tree/if_node.rb +2 -15
- data/lib/sass/tree/import_node.rb +11 -5
- data/lib/sass/tree/media_node.rb +27 -11
- data/lib/sass/tree/mixin_def_node.rb +15 -4
- data/lib/sass/tree/mixin_node.rb +27 -7
- data/lib/sass/tree/node.rb +69 -35
- data/lib/sass/tree/prop_node.rb +47 -31
- data/lib/sass/tree/return_node.rb +4 -3
- data/lib/sass/tree/root_node.rb +20 -4
- data/lib/sass/tree/rule_node.rb +37 -26
- data/lib/sass/tree/supports_node.rb +38 -0
- data/lib/sass/tree/trace_node.rb +33 -0
- data/lib/sass/tree/variable_node.rb +10 -4
- data/lib/sass/tree/visitors/base.rb +5 -8
- data/lib/sass/tree/visitors/check_nesting.rb +67 -52
- data/lib/sass/tree/visitors/convert.rb +134 -53
- data/lib/sass/tree/visitors/cssize.rb +245 -51
- data/lib/sass/tree/visitors/deep_copy.rb +102 -0
- data/lib/sass/tree/visitors/extend.rb +68 -0
- data/lib/sass/tree/visitors/perform.rb +331 -105
- data/lib/sass/tree/visitors/set_options.rb +125 -0
- data/lib/sass/tree/visitors/to_css.rb +259 -95
- data/lib/sass/tree/warn_node.rb +3 -3
- data/lib/sass/tree/while_node.rb +3 -3
- data/lib/sass/util/cross_platform_random.rb +19 -0
- data/lib/sass/util/multibyte_string_scanner.rb +157 -0
- data/lib/sass/util/normalized_map.rb +130 -0
- data/lib/sass/util/ordered_hash.rb +192 -0
- data/lib/sass/util/subset_map.rb +11 -2
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +565 -39
- data/lib/sass/version.rb +27 -15
- data/lib/sass.rb +39 -4
- data/test/sass/cache_test.rb +15 -0
- data/test/sass/compiler_test.rb +223 -0
- data/test/sass/conversion_test.rb +901 -107
- data/test/sass/css2sass_test.rb +94 -0
- data/test/sass/engine_test.rb +1059 -164
- data/test/sass/exec_test.rb +86 -0
- data/test/sass/extend_test.rb +933 -837
- data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
- data/test/sass/functions_test.rb +995 -136
- data/test/sass/importer_test.rb +338 -18
- data/test/sass/logger_test.rb +58 -0
- data/test/sass/more_results/more_import.css +2 -2
- data/test/sass/plugin_test.rb +114 -30
- data/test/sass/results/cached_import_option.css +3 -0
- data/test/sass/results/filename_fn.css +3 -0
- data/test/sass/results/import.css +2 -2
- data/test/sass/results/import_charset.css +1 -0
- data/test/sass/results/import_charset_1_8.css +1 -0
- data/test/sass/results/import_charset_ibm866.css +1 -0
- data/test/sass/results/import_content.css +1 -0
- data/test/sass/results/script.css +1 -1
- data/test/sass/results/scss_import.css +2 -2
- data/test/sass/results/units.css +2 -2
- data/test/sass/script_conversion_test.rb +43 -1
- data/test/sass/script_test.rb +380 -36
- data/test/sass/scss/css_test.rb +257 -75
- data/test/sass/scss/scss_test.rb +2322 -110
- data/test/sass/source_map_test.rb +887 -0
- data/test/sass/templates/_cached_import_option_partial.scss +1 -0
- data/test/sass/templates/_double_import_loop2.sass +1 -0
- data/test/sass/templates/_filename_fn_import.scss +11 -0
- data/test/sass/templates/_imported_content.sass +3 -0
- data/test/sass/templates/_same_name_different_partiality.scss +1 -0
- data/test/sass/templates/bork5.sass +3 -0
- data/test/sass/templates/cached_import_option.scss +3 -0
- data/test/sass/templates/double_import_loop1.sass +1 -0
- data/test/sass/templates/filename_fn.scss +18 -0
- data/test/sass/templates/import_charset.sass +2 -0
- data/test/sass/templates/import_charset_1_8.sass +2 -0
- data/test/sass/templates/import_charset_ibm866.sass +2 -0
- data/test/sass/templates/import_content.sass +4 -0
- data/test/sass/templates/same_name_different_ext.sass +2 -0
- data/test/sass/templates/same_name_different_ext.scss +1 -0
- data/test/sass/templates/same_name_different_partiality.scss +1 -0
- data/test/sass/templates/single_import_loop.sass +1 -0
- data/test/sass/templates/subdir/import_up1.scss +1 -0
- data/test/sass/templates/subdir/import_up2.scss +1 -0
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
- data/test/sass/util/normalized_map_test.rb +51 -0
- data/test/sass/util_test.rb +183 -0
- data/test/sass/value_helpers_test.rb +181 -0
- data/test/test_helper.rb +45 -5
- data/vendor/listen/CHANGELOG.md +228 -0
- data/vendor/listen/CONTRIBUTING.md +38 -0
- data/vendor/listen/Gemfile +30 -0
- data/vendor/listen/Guardfile +8 -0
- data/vendor/{fssm → listen}/LICENSE +1 -1
- data/vendor/listen/README.md +315 -0
- data/vendor/listen/Rakefile +47 -0
- data/vendor/listen/Vagrantfile +96 -0
- data/vendor/listen/lib/listen/adapter.rb +214 -0
- data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
- data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
- data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
- data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
- data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
- data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
- data/vendor/listen/lib/listen/directory_record.rb +371 -0
- data/vendor/listen/lib/listen/listener.rb +225 -0
- data/vendor/listen/lib/listen/multi_listener.rb +143 -0
- data/vendor/listen/lib/listen/turnstile.rb +28 -0
- data/vendor/listen/lib/listen/version.rb +3 -0
- data/vendor/listen/lib/listen.rb +40 -0
- data/vendor/listen/listen.gemspec +22 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
- data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
- data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
- data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
- data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
- data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
- data/vendor/listen/spec/listen/listener_spec.rb +169 -0
- data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
- data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
- data/vendor/listen/spec/listen_spec.rb +73 -0
- data/vendor/listen/spec/spec_helper.rb +21 -0
- data/vendor/listen/spec/support/adapter_helper.rb +629 -0
- data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
- data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
- data/vendor/listen/spec/support/listeners_helper.rb +156 -0
- data/vendor/listen/spec/support/platform_helper.rb +15 -0
- metadata +344 -271
- data/lib/sass/less.rb +0 -382
- data/lib/sass/script/bool.rb +0 -18
- data/lib/sass/script/funcall.rb +0 -162
- data/lib/sass/script/list.rb +0 -76
- data/lib/sass/script/literal.rb +0 -245
- data/lib/sass/script/variable.rb +0 -54
- data/lib/sass/scss/sass_parser.rb +0 -11
- data/test/sass/less_conversion_test.rb +0 -653
- data/vendor/fssm/README.markdown +0 -55
- data/vendor/fssm/Rakefile +0 -59
- data/vendor/fssm/VERSION.yml +0 -5
- data/vendor/fssm/example.rb +0 -9
- data/vendor/fssm/fssm.gemspec +0 -77
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
- data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
- data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
- data/vendor/fssm/lib/fssm/monitor.rb +0 -26
- data/vendor/fssm/lib/fssm/path.rb +0 -91
- data/vendor/fssm/lib/fssm/pathname.rb +0 -502
- data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
- data/vendor/fssm/lib/fssm/state/file.rb +0 -24
- data/vendor/fssm/lib/fssm/support.rb +0 -63
- data/vendor/fssm/lib/fssm/tree.rb +0 -176
- data/vendor/fssm/lib/fssm.rb +0 -33
- data/vendor/fssm/profile/prof-cache.rb +0 -40
- data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
- data/vendor/fssm/profile/prof-pathname.rb +0 -68
- data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
- data/vendor/fssm/profile/prof.html +0 -2379
- data/vendor/fssm/spec/path_spec.rb +0 -75
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -0,0 +1,112 @@
|
|
1
|
+
module Listen
|
2
|
+
module Adapters
|
3
|
+
|
4
|
+
# Listener implementation for BSD's `kqueue`.
|
5
|
+
#
|
6
|
+
class BSD < Adapter
|
7
|
+
extend DependencyManager
|
8
|
+
|
9
|
+
# Declare the adapter's dependencies
|
10
|
+
dependency 'rb-kqueue', '~> 0.2'
|
11
|
+
|
12
|
+
# Watched kqueue events
|
13
|
+
#
|
14
|
+
# @see http://www.freebsd.org/cgi/man.cgi?query=kqueue
|
15
|
+
# @see https://github.com/nex3/rb-kqueue/blob/master/lib/rb-kqueue/queue.rb
|
16
|
+
#
|
17
|
+
EVENTS = [ :delete, :write, :extend, :attrib, :link, :rename, :revoke ]
|
18
|
+
|
19
|
+
# Initializes the Adapter. See {Listen::Adapter#initialize} for
|
20
|
+
# more info.
|
21
|
+
#
|
22
|
+
def initialize(directories, options = {}, &callback)
|
23
|
+
super
|
24
|
+
@kqueue = init_kqueue
|
25
|
+
end
|
26
|
+
|
27
|
+
# Starts the adapter.
|
28
|
+
#
|
29
|
+
# @param [Boolean] blocking whether or not to block the current thread after starting
|
30
|
+
#
|
31
|
+
def start(blocking = true)
|
32
|
+
@mutex.synchronize do
|
33
|
+
return if @stop == false
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
@kqueue_thread = Thread.new do
|
38
|
+
until @stop
|
39
|
+
@kqueue.poll
|
40
|
+
sleep(@latency)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@poll_thread = Thread.new { poll_changed_dirs } if @report_changes
|
44
|
+
|
45
|
+
@kqueue_thread.join if blocking
|
46
|
+
end
|
47
|
+
|
48
|
+
# Stops the adapter.
|
49
|
+
#
|
50
|
+
def stop
|
51
|
+
@mutex.synchronize do
|
52
|
+
return if @stop == true
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
@kqueue.stop
|
57
|
+
Thread.kill(@kqueue_thread) if @kqueue_thread
|
58
|
+
@poll_thread.join if @poll_thread
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks if the adapter is usable on the current OS.
|
62
|
+
#
|
63
|
+
# @return [Boolean] whether usable or not
|
64
|
+
#
|
65
|
+
def self.usable?
|
66
|
+
return false unless RbConfig::CONFIG['target_os'] =~ /freebsd/i
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Initializes a kqueue Queue and adds a watcher for each files in
|
73
|
+
# the directories passed to the adapter.
|
74
|
+
#
|
75
|
+
# @return [INotify::Notifier] initialized kqueue
|
76
|
+
#
|
77
|
+
def init_kqueue
|
78
|
+
require 'find'
|
79
|
+
|
80
|
+
callback = lambda do |event|
|
81
|
+
path = event.watcher.path
|
82
|
+
@mutex.synchronize do
|
83
|
+
# kqueue watches everything, but Listen only needs the
|
84
|
+
# directory where stuffs happens.
|
85
|
+
@changed_dirs << (File.directory?(path) ? path : File.dirname(path))
|
86
|
+
|
87
|
+
# If it is a directory, and it has a write flag, it means a
|
88
|
+
# file has been added so find out which and deal with it.
|
89
|
+
# No need to check for removed file, kqueue will forget them
|
90
|
+
# when the vfs does..
|
91
|
+
if File.directory?(path) && !(event.flags & [:write]).empty?
|
92
|
+
queue = event.watcher.queue
|
93
|
+
Find.find(path) do |file|
|
94
|
+
unless queue.watchers.detect {|k,v| v.path == file.to_s}
|
95
|
+
queue.watch_file(file, *EVENTS, &callback)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
KQueue::Queue.new.tap do |queue|
|
103
|
+
@directories.each do |directory|
|
104
|
+
Find.find(directory) do |path|
|
105
|
+
queue.watch_file(path, *EVENTS, &callback)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Listen
|
2
|
+
module Adapters
|
3
|
+
|
4
|
+
# Adapter implementation for Mac OS X `FSEvents`.
|
5
|
+
#
|
6
|
+
class Darwin < Adapter
|
7
|
+
extend DependencyManager
|
8
|
+
|
9
|
+
# Declare the adapter's dependencies
|
10
|
+
dependency 'rb-fsevent', '~> 0.9'
|
11
|
+
|
12
|
+
LAST_SEPARATOR_REGEX = /\/$/
|
13
|
+
|
14
|
+
# Initializes the Adapter. See {Listen::Adapter#initialize} for more info.
|
15
|
+
#
|
16
|
+
def initialize(directories, options = {}, &callback)
|
17
|
+
super
|
18
|
+
@worker = init_worker
|
19
|
+
end
|
20
|
+
|
21
|
+
# Starts the adapter.
|
22
|
+
#
|
23
|
+
# @param [Boolean] blocking whether or not to block the current thread after starting
|
24
|
+
#
|
25
|
+
def start(blocking = true)
|
26
|
+
@mutex.synchronize do
|
27
|
+
return if @stop == false
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
@worker_thread = Thread.new { @worker.run }
|
32
|
+
|
33
|
+
# The FSEvent worker needs sometime to startup. Turnstiles can't
|
34
|
+
# be used to wait for it as it runs in a loop.
|
35
|
+
# TODO: Find a better way to block until the worker starts.
|
36
|
+
sleep 0.1
|
37
|
+
|
38
|
+
@poll_thread = Thread.new { poll_changed_dirs } if @report_changes
|
39
|
+
@worker_thread.join if blocking
|
40
|
+
end
|
41
|
+
|
42
|
+
# Stops the adapter.
|
43
|
+
#
|
44
|
+
def stop
|
45
|
+
@mutex.synchronize do
|
46
|
+
return if @stop == true
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
@worker.stop
|
51
|
+
@worker_thread.join if @worker_thread
|
52
|
+
@poll_thread.join if @poll_thread
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks if the adapter is usable on the current OS.
|
56
|
+
#
|
57
|
+
# @return [Boolean] whether usable or not
|
58
|
+
#
|
59
|
+
def self.usable?
|
60
|
+
return false unless RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Initializes a FSEvent worker and adds a watcher for
|
67
|
+
# each directory passed to the adapter.
|
68
|
+
#
|
69
|
+
# @return [FSEvent] initialized worker
|
70
|
+
#
|
71
|
+
def init_worker
|
72
|
+
FSEvent.new.tap do |worker|
|
73
|
+
worker.watch(@directories.dup, :latency => @latency) do |changes|
|
74
|
+
next if @paused
|
75
|
+
@mutex.synchronize do
|
76
|
+
changes.each { |path| @changed_dirs << path.sub(LAST_SEPARATOR_REGEX, '') }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Listen
|
2
|
+
module Adapters
|
3
|
+
|
4
|
+
# Listener implementation for Linux `inotify`.
|
5
|
+
#
|
6
|
+
class Linux < Adapter
|
7
|
+
extend DependencyManager
|
8
|
+
|
9
|
+
# Declare the adapter's dependencies
|
10
|
+
dependency 'rb-inotify', '~> 0.9'
|
11
|
+
|
12
|
+
# Watched inotify events
|
13
|
+
#
|
14
|
+
# @see http://www.tin.org/bin/man.cgi?section=7&topic=inotify
|
15
|
+
# @see https://github.com/nex3/rb-inotify/blob/master/lib/rb-inotify/notifier.rb#L99-L177
|
16
|
+
#
|
17
|
+
EVENTS = %w[recursive attrib create delete move close_write]
|
18
|
+
|
19
|
+
# The message to show when the limit of inotify watchers is not enough
|
20
|
+
#
|
21
|
+
INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '')
|
22
|
+
Listen error: unable to monitor directories for changes.
|
23
|
+
|
24
|
+
Please head to https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers
|
25
|
+
for information on how to solve this issue.
|
26
|
+
EOS
|
27
|
+
|
28
|
+
# Initializes the Adapter. See {Listen::Adapter#initialize} for more info.
|
29
|
+
#
|
30
|
+
def initialize(directories, options = {}, &callback)
|
31
|
+
super
|
32
|
+
@worker = init_worker
|
33
|
+
rescue Errno::ENOSPC
|
34
|
+
abort(INOTIFY_LIMIT_MESSAGE)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Starts the adapter.
|
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
|
76
|
+
|
77
|
+
# Initializes a INotify worker and adds a watcher for
|
78
|
+
# each directory passed to the adapter.
|
79
|
+
#
|
80
|
+
# @return [INotify::Notifier] initialized worker
|
81
|
+
#
|
82
|
+
def init_worker
|
83
|
+
callback = lambda do |event|
|
84
|
+
if @paused || (
|
85
|
+
# Event on root directory
|
86
|
+
event.name == ""
|
87
|
+
) || (
|
88
|
+
# INotify reports changes to files inside directories as events
|
89
|
+
# on the directories themselves too.
|
90
|
+
#
|
91
|
+
# @see http://linux.die.net/man/7/inotify
|
92
|
+
event.flags.include?(:isdir) and event.flags & [:close, :modify] != []
|
93
|
+
)
|
94
|
+
# Skip all of these!
|
95
|
+
next
|
96
|
+
end
|
97
|
+
|
98
|
+
@mutex.synchronize do
|
99
|
+
@changed_dirs << File.dirname(event.absolute_name)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
INotify::Notifier.new.tap do |worker|
|
104
|
+
@directories.each do |directory|
|
105
|
+
worker.watch(directory, *EVENTS.map(&:to_sym), &callback)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Listen
|
2
|
+
module Adapters
|
3
|
+
|
4
|
+
# The default delay between checking for changes.
|
5
|
+
DEFAULT_POLLING_LATENCY = 1.0
|
6
|
+
|
7
|
+
# Polling Adapter that works cross-platform and
|
8
|
+
# has no dependencies. This is the adapter that
|
9
|
+
# uses the most CPU processing power and has higher
|
10
|
+
# file IO that the other implementations.
|
11
|
+
#
|
12
|
+
class Polling < Adapter
|
13
|
+
extend DependencyManager
|
14
|
+
|
15
|
+
# Initialize the Adapter. See {Listen::Adapter#initialize} for more info.
|
16
|
+
#
|
17
|
+
def initialize(directories, options = {}, &callback)
|
18
|
+
@latency ||= DEFAULT_POLLING_LATENCY
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
# Start the adapter.
|
23
|
+
#
|
24
|
+
# @param [Boolean] blocking whether or not to block the current thread after starting
|
25
|
+
#
|
26
|
+
def start(blocking = true)
|
27
|
+
@mutex.synchronize do
|
28
|
+
return if @stop == false
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
@poll_thread = Thread.new { poll }
|
33
|
+
@poll_thread.join if blocking
|
34
|
+
end
|
35
|
+
|
36
|
+
# Stop the adapter.
|
37
|
+
#
|
38
|
+
def stop
|
39
|
+
@mutex.synchronize do
|
40
|
+
return if @stop == true
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
@poll_thread.join
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Poll listener directory for file system changes.
|
50
|
+
#
|
51
|
+
def poll
|
52
|
+
until @stop
|
53
|
+
next if @paused
|
54
|
+
|
55
|
+
start = Time.now.to_f
|
56
|
+
@callback.call(@directories.dup, :recursive => true)
|
57
|
+
@turnstile.signal
|
58
|
+
nap_time = @latency - (Time.now.to_f - start)
|
59
|
+
sleep(nap_time) if nap_time > 0
|
60
|
+
end
|
61
|
+
rescue Interrupt
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Listen
|
4
|
+
module Adapters
|
5
|
+
|
6
|
+
# Adapter implementation for Windows `wdm`.
|
7
|
+
#
|
8
|
+
class Windows < Adapter
|
9
|
+
extend DependencyManager
|
10
|
+
|
11
|
+
# Declare the adapter's dependencies
|
12
|
+
dependency 'wdm', '~> 0.1'
|
13
|
+
|
14
|
+
# Initializes the Adapter. See {Listen::Adapter#initialize} for more info.
|
15
|
+
#
|
16
|
+
def initialize(directories, options = {}, &callback)
|
17
|
+
super
|
18
|
+
@worker = init_worker
|
19
|
+
end
|
20
|
+
|
21
|
+
# Starts the adapter.
|
22
|
+
#
|
23
|
+
# @param [Boolean] blocking whether or not to block the current thread after starting
|
24
|
+
#
|
25
|
+
def start(blocking = true)
|
26
|
+
@mutex.synchronize do
|
27
|
+
return if @stop == false
|
28
|
+
super
|
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
|
40
|
+
end
|
41
|
+
|
42
|
+
# Stops the adapter.
|
43
|
+
#
|
44
|
+
def stop
|
45
|
+
@mutex.synchronize do
|
46
|
+
return if @stop == true
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
@worker.stop
|
51
|
+
@worker_thread.join if @worker_thread
|
52
|
+
@poll_thread.join if @poll_thread
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks if the adapter is usable on the current OS.
|
56
|
+
#
|
57
|
+
# @return [Boolean] whether usable or not
|
58
|
+
#
|
59
|
+
def self.usable?
|
60
|
+
return false unless RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Initializes a WDM monitor and adds a watcher for
|
67
|
+
# each directory passed to the adapter.
|
68
|
+
#
|
69
|
+
# @return [WDM::Monitor] initialized worker
|
70
|
+
#
|
71
|
+
def init_worker
|
72
|
+
callback = Proc.new do |change|
|
73
|
+
next if @paused
|
74
|
+
@mutex.synchronize do
|
75
|
+
@changed_dirs << File.dirname(change.path)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
WDM::Monitor.new.tap do |worker|
|
80
|
+
@directories.each { |d| worker.watch_recursively(d, &callback) }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Listen
|
4
|
+
|
5
|
+
# The dependency-manager offers a simple DSL which allows
|
6
|
+
# classes to declare their gem dependencies and load them when
|
7
|
+
# needed.
|
8
|
+
# It raises a user-friendly exception when the dependencies
|
9
|
+
# can't be loaded which has the install command in the message.
|
10
|
+
#
|
11
|
+
module DependencyManager
|
12
|
+
|
13
|
+
GEM_LOAD_MESSAGE = <<-EOS.gsub(/^ {6}/, '')
|
14
|
+
Missing dependency '%s' (version '%s')!
|
15
|
+
EOS
|
16
|
+
|
17
|
+
GEM_INSTALL_COMMAND = <<-EOS.gsub(/^ {6}/, '')
|
18
|
+
Please run the following to satisfy the dependency:
|
19
|
+
gem install --version '%s' %s
|
20
|
+
EOS
|
21
|
+
|
22
|
+
BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
|
23
|
+
Please add the following to your Gemfile to satisfy the dependency:
|
24
|
+
gem '%s', '%s'
|
25
|
+
EOS
|
26
|
+
|
27
|
+
Dependency = Struct.new(:name, :version)
|
28
|
+
|
29
|
+
# The error raised when a dependency can't be loaded.
|
30
|
+
class Error < StandardError; end
|
31
|
+
|
32
|
+
# A list of all loaded dependencies in the dependency manager.
|
33
|
+
@_loaded_dependencies = Set.new
|
34
|
+
|
35
|
+
# class methods
|
36
|
+
class << self
|
37
|
+
|
38
|
+
# Initializes the extended class.
|
39
|
+
#
|
40
|
+
# @param [Class] the class for which some dependencies must be managed
|
41
|
+
#
|
42
|
+
def extended(base)
|
43
|
+
base.class_eval do
|
44
|
+
@_dependencies = Set.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Adds a loaded dependency to a list so that it doesn't have
|
49
|
+
# to be loaded again by another classes.
|
50
|
+
#
|
51
|
+
# @param [Dependency] dependency
|
52
|
+
#
|
53
|
+
def add_loaded(dependency)
|
54
|
+
@_loaded_dependencies << dependency
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns whether the dependency is alread loaded or not.
|
58
|
+
#
|
59
|
+
# @param [Dependency] dependency
|
60
|
+
# @return [Boolean]
|
61
|
+
#
|
62
|
+
def already_loaded?(dependency)
|
63
|
+
@_loaded_dependencies.include?(dependency)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Clears the list of loaded dependencies.
|
67
|
+
#
|
68
|
+
def clear_loaded
|
69
|
+
@_loaded_dependencies.clear
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Registers a new dependency.
|
74
|
+
#
|
75
|
+
# @param [String] name the name of the gem
|
76
|
+
# @param [String] version the version of the gem
|
77
|
+
#
|
78
|
+
def dependency(name, version)
|
79
|
+
@_dependencies << Dependency.new(name, version)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Loads the registered dependencies.
|
83
|
+
#
|
84
|
+
# @raise DependencyManager::Error if the dependency can't be loaded.
|
85
|
+
#
|
86
|
+
def load_depenencies
|
87
|
+
@_dependencies.each do |dependency|
|
88
|
+
begin
|
89
|
+
next if DependencyManager.already_loaded?(dependency)
|
90
|
+
gem(dependency.name, dependency.version)
|
91
|
+
require(dependency.name)
|
92
|
+
DependencyManager.add_loaded(dependency)
|
93
|
+
@_dependencies.delete(dependency)
|
94
|
+
rescue Gem::LoadError
|
95
|
+
args = [dependency.name, dependency.version]
|
96
|
+
command = if running_under_bundler?
|
97
|
+
BUNDLER_DECLARE_GEM % args
|
98
|
+
else
|
99
|
+
GEM_INSTALL_COMMAND % args.reverse
|
100
|
+
end
|
101
|
+
message = GEM_LOAD_MESSAGE % args
|
102
|
+
|
103
|
+
raise Error.new(message + command)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns whether all the dependencies has been loaded or not.
|
109
|
+
#
|
110
|
+
# @return [Boolean]
|
111
|
+
#
|
112
|
+
def dependencies_loaded?
|
113
|
+
@_dependencies.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
# Returns whether we are running under bundler or not
|
119
|
+
#
|
120
|
+
# @return [Boolean]
|
121
|
+
#
|
122
|
+
def running_under_bundler?
|
123
|
+
!!(File.exists?('Gemfile') && ENV['BUNDLE_GEMFILE'])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|