sass 3.7.4 → 4.0.0.alpha.1

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.
Files changed (257) hide show
  1. checksums.yaml +13 -5
  2. data/.yardopts +1 -1
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/CONTRIBUTING.md +1 -146
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +25 -39
  7. data/Rakefile +274 -0
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/lib/sass.rb +3 -3
  11. data/lib/sass/cache_stores/filesystem.rb +2 -2
  12. data/lib/sass/cache_stores/memory.rb +5 -4
  13. data/lib/sass/callbacks.rb +2 -2
  14. data/lib/sass/css.rb +12 -12
  15. data/lib/sass/engine.rb +44 -62
  16. data/lib/sass/environment.rb +7 -35
  17. data/lib/sass/error.rb +14 -14
  18. data/lib/sass/exec/base.rb +14 -3
  19. data/lib/sass/exec/sass_convert.rb +6 -20
  20. data/lib/sass/exec/sass_scss.rb +29 -5
  21. data/lib/sass/features.rb +2 -3
  22. data/lib/sass/importers/filesystem.rb +6 -11
  23. data/lib/sass/logger.rb +3 -8
  24. data/lib/sass/logger/base.rb +2 -19
  25. data/lib/sass/plugin.rb +2 -3
  26. data/lib/sass/plugin/compiler.rb +67 -48
  27. data/lib/sass/plugin/configuration.rb +3 -3
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/rack.rb +3 -3
  30. data/lib/sass/plugin/staleness_checker.rb +3 -3
  31. data/lib/sass/railtie.rb +1 -1
  32. data/lib/sass/script.rb +3 -3
  33. data/lib/sass/script/css_parser.rb +15 -5
  34. data/lib/sass/script/functions.rb +121 -337
  35. data/lib/sass/script/lexer.rb +36 -102
  36. data/lib/sass/script/parser.rb +153 -529
  37. data/lib/sass/script/tree/funcall.rb +34 -42
  38. data/lib/sass/script/tree/interpolation.rb +26 -171
  39. data/lib/sass/script/tree/list_literal.rb +8 -23
  40. data/lib/sass/script/tree/map_literal.rb +2 -2
  41. data/lib/sass/script/tree/node.rb +3 -3
  42. data/lib/sass/script/tree/operation.rb +16 -43
  43. data/lib/sass/script/tree/string_interpolation.rb +43 -64
  44. data/lib/sass/script/tree/variable.rb +1 -1
  45. data/lib/sass/script/value.rb +0 -2
  46. data/lib/sass/script/value/arg_list.rb +1 -1
  47. data/lib/sass/script/value/base.rb +9 -27
  48. data/lib/sass/script/value/color.rb +18 -26
  49. data/lib/sass/script/value/helpers.rb +18 -44
  50. data/lib/sass/script/value/list.rb +14 -35
  51. data/lib/sass/script/value/map.rb +2 -2
  52. data/lib/sass/script/value/number.rb +16 -26
  53. data/lib/sass/script/value/string.rb +1 -30
  54. data/lib/sass/scss.rb +2 -0
  55. data/lib/sass/scss/css_parser.rb +3 -7
  56. data/lib/sass/scss/parser.rb +78 -196
  57. data/lib/sass/scss/rx.rb +14 -7
  58. data/lib/sass/scss/script_lexer.rb +15 -0
  59. data/lib/sass/scss/script_parser.rb +25 -0
  60. data/lib/sass/scss/static_parser.rb +55 -38
  61. data/lib/sass/selector.rb +10 -7
  62. data/lib/sass/selector/abstract_sequence.rb +12 -15
  63. data/lib/sass/selector/comma_sequence.rb +6 -24
  64. data/lib/sass/selector/pseudo.rb +6 -19
  65. data/lib/sass/selector/sequence.rb +16 -14
  66. data/lib/sass/selector/simple.rb +7 -9
  67. data/lib/sass/selector/simple_sequence.rb +12 -16
  68. data/lib/sass/shared.rb +1 -1
  69. data/lib/sass/source/map.rb +9 -7
  70. data/lib/sass/source/position.rb +4 -4
  71. data/lib/sass/stack.rb +3 -23
  72. data/lib/sass/tree/charset_node.rb +1 -1
  73. data/lib/sass/tree/comment_node.rb +1 -1
  74. data/lib/sass/tree/function_node.rb +3 -2
  75. data/lib/sass/tree/node.rb +3 -5
  76. data/lib/sass/tree/prop_node.rb +58 -49
  77. data/lib/sass/tree/rule_node.rb +8 -15
  78. data/lib/sass/tree/visitors/check_nesting.rb +23 -19
  79. data/lib/sass/tree/visitors/convert.rb +13 -15
  80. data/lib/sass/tree/visitors/cssize.rb +15 -4
  81. data/lib/sass/tree/visitors/deep_copy.rb +2 -2
  82. data/lib/sass/tree/visitors/extend.rb +14 -10
  83. data/lib/sass/tree/visitors/perform.rb +18 -29
  84. data/lib/sass/tree/visitors/set_options.rb +2 -2
  85. data/lib/sass/tree/visitors/to_css.rb +47 -77
  86. data/lib/sass/util.rb +311 -98
  87. data/lib/sass/util/cross_platform_random.rb +19 -0
  88. data/lib/sass/util/multibyte_string_scanner.rb +133 -127
  89. data/lib/sass/util/normalized_map.rb +8 -1
  90. data/lib/sass/util/ordered_hash.rb +192 -0
  91. data/lib/sass/version.rb +6 -2
  92. data/test/sass/cache_test.rb +131 -0
  93. data/test/sass/callbacks_test.rb +61 -0
  94. data/test/sass/compiler_test.rb +236 -0
  95. data/test/sass/conversion_test.rb +2171 -0
  96. data/test/sass/css2sass_test.rb +526 -0
  97. data/test/sass/data/hsl-rgb.txt +319 -0
  98. data/test/sass/encoding_test.rb +219 -0
  99. data/test/sass/engine_test.rb +3400 -0
  100. data/test/sass/exec_test.rb +86 -0
  101. data/test/sass/extend_test.rb +1719 -0
  102. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  103. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  104. data/test/sass/functions_test.rb +1984 -0
  105. data/test/sass/importer_test.rb +421 -0
  106. data/test/sass/logger_test.rb +58 -0
  107. data/test/sass/mock_importer.rb +49 -0
  108. data/test/sass/more_results/more1.css +9 -0
  109. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  110. data/test/sass/more_results/more_import.css +29 -0
  111. data/test/sass/more_templates/_more_partial.sass +2 -0
  112. data/test/sass/more_templates/more1.sass +23 -0
  113. data/test/sass/more_templates/more_import.sass +11 -0
  114. data/test/sass/plugin_test.rb +556 -0
  115. data/test/sass/results/alt.css +4 -0
  116. data/test/sass/results/basic.css +9 -0
  117. data/test/sass/results/cached_import_option.css +3 -0
  118. data/test/sass/results/compact.css +5 -0
  119. data/test/sass/results/complex.css +86 -0
  120. data/test/sass/results/compressed.css +1 -0
  121. data/test/sass/results/expanded.css +19 -0
  122. data/test/sass/results/filename_fn.css +3 -0
  123. data/test/sass/results/if.css +3 -0
  124. data/test/sass/results/import.css +31 -0
  125. data/test/sass/results/import_charset.css +5 -0
  126. data/test/sass/results/import_charset_1_8.css +5 -0
  127. data/test/sass/results/import_charset_ibm866.css +5 -0
  128. data/test/sass/results/import_content.css +1 -0
  129. data/test/sass/results/line_numbers.css +49 -0
  130. data/test/sass/results/mixins.css +95 -0
  131. data/test/sass/results/multiline.css +24 -0
  132. data/test/sass/results/nested.css +22 -0
  133. data/test/sass/results/options.css +1 -0
  134. data/test/sass/results/parent_ref.css +13 -0
  135. data/test/sass/results/script.css +16 -0
  136. data/test/sass/results/scss_import.css +31 -0
  137. data/test/sass/results/scss_importee.css +2 -0
  138. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/test/sass/results/subdir/subdir.css +3 -0
  140. data/test/sass/results/units.css +11 -0
  141. data/test/sass/results/warn.css +0 -0
  142. data/test/sass/results/warn_imported.css +0 -0
  143. data/test/sass/script_conversion_test.rb +306 -0
  144. data/test/sass/script_test.rb +1206 -0
  145. data/test/sass/scss/css_test.rb +1281 -0
  146. data/test/sass/scss/rx_test.rb +160 -0
  147. data/test/sass/scss/scss_test.rb +4147 -0
  148. data/test/sass/scss/test_helper.rb +37 -0
  149. data/test/sass/source_map_test.rb +1055 -0
  150. data/test/sass/superselector_test.rb +210 -0
  151. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  152. data/test/sass/templates/_double_import_loop2.sass +1 -0
  153. data/test/sass/templates/_filename_fn_import.scss +11 -0
  154. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  155. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  156. data/test/sass/templates/_imported_content.sass +3 -0
  157. data/test/sass/templates/_partial.sass +2 -0
  158. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork1.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/bork3.sass +2 -0
  164. data/test/sass/templates/bork4.sass +2 -0
  165. data/test/sass/templates/bork5.sass +3 -0
  166. data/test/sass/templates/cached_import_option.scss +3 -0
  167. data/test/sass/templates/compact.sass +17 -0
  168. data/test/sass/templates/complex.sass +305 -0
  169. data/test/sass/templates/compressed.sass +15 -0
  170. data/test/sass/templates/double_import_loop1.sass +1 -0
  171. data/test/sass/templates/expanded.sass +17 -0
  172. data/test/sass/templates/filename_fn.scss +18 -0
  173. data/test/sass/templates/if.sass +11 -0
  174. data/test/sass/templates/import.sass +12 -0
  175. data/test/sass/templates/import_charset.sass +9 -0
  176. data/test/sass/templates/import_charset_1_8.sass +6 -0
  177. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  178. data/test/sass/templates/import_content.sass +4 -0
  179. data/test/sass/templates/importee.less +2 -0
  180. data/test/sass/templates/importee.sass +19 -0
  181. data/test/sass/templates/line_numbers.sass +13 -0
  182. data/test/sass/templates/mixin_bork.sass +5 -0
  183. data/test/sass/templates/mixins.sass +76 -0
  184. data/test/sass/templates/multiline.sass +20 -0
  185. data/test/sass/templates/nested.sass +25 -0
  186. data/test/sass/templates/nested_bork1.sass +2 -0
  187. data/test/sass/templates/nested_bork2.sass +2 -0
  188. data/test/sass/templates/nested_bork3.sass +2 -0
  189. data/test/sass/templates/nested_bork4.sass +2 -0
  190. data/test/sass/templates/nested_import.sass +2 -0
  191. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  192. data/test/sass/templates/options.sass +2 -0
  193. data/test/sass/templates/parent_ref.sass +25 -0
  194. data/test/sass/templates/same_name_different_ext.sass +2 -0
  195. data/test/sass/templates/same_name_different_ext.scss +1 -0
  196. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  197. data/test/sass/templates/script.sass +101 -0
  198. data/test/sass/templates/scss_import.scss +12 -0
  199. data/test/sass/templates/scss_importee.scss +1 -0
  200. data/test/sass/templates/single_import_loop.sass +1 -0
  201. data/test/sass/templates/subdir/import_up1.scss +1 -0
  202. data/test/sass/templates/subdir/import_up2.scss +1 -0
  203. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  204. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  205. data/test/sass/templates/subdir/subdir.sass +6 -0
  206. data/test/sass/templates/units.sass +11 -0
  207. data/test/sass/templates/warn.sass +3 -0
  208. data/test/sass/templates/warn_imported.sass +4 -0
  209. data/test/sass/test_helper.rb +8 -0
  210. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  211. data/test/sass/util/normalized_map_test.rb +51 -0
  212. data/test/sass/util/subset_map_test.rb +91 -0
  213. data/test/sass/util_test.rb +438 -0
  214. data/test/sass/value_helpers_test.rb +179 -0
  215. data/test/test_helper.rb +110 -0
  216. data/vendor/listen/CHANGELOG.md +1 -0
  217. data/vendor/listen/CONTRIBUTING.md +38 -0
  218. data/vendor/listen/Gemfile +20 -0
  219. data/vendor/listen/Guardfile +8 -0
  220. data/vendor/listen/LICENSE +20 -0
  221. data/vendor/listen/README.md +349 -0
  222. data/vendor/listen/Rakefile +5 -0
  223. data/vendor/listen/Vagrantfile +96 -0
  224. data/vendor/listen/lib/listen.rb +54 -0
  225. data/vendor/listen/lib/listen/adapter.rb +327 -0
  226. data/vendor/listen/lib/listen/adapters/bsd.rb +75 -0
  227. data/vendor/listen/lib/listen/adapters/darwin.rb +48 -0
  228. data/vendor/listen/lib/listen/adapters/linux.rb +81 -0
  229. data/vendor/listen/lib/listen/adapters/polling.rb +58 -0
  230. data/vendor/listen/lib/listen/adapters/windows.rb +91 -0
  231. data/vendor/listen/lib/listen/directory_record.rb +406 -0
  232. data/vendor/listen/lib/listen/listener.rb +323 -0
  233. data/vendor/listen/lib/listen/turnstile.rb +32 -0
  234. data/vendor/listen/lib/listen/version.rb +3 -0
  235. data/vendor/listen/listen.gemspec +28 -0
  236. data/vendor/listen/spec/listen/adapter_spec.rb +149 -0
  237. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  238. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  239. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  240. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  241. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  242. data/vendor/listen/spec/listen/directory_record_spec.rb +1250 -0
  243. data/vendor/listen/spec/listen/listener_spec.rb +258 -0
  244. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  245. data/vendor/listen/spec/listen_spec.rb +67 -0
  246. data/vendor/listen/spec/spec_helper.rb +25 -0
  247. data/vendor/listen/spec/support/adapter_helper.rb +666 -0
  248. data/vendor/listen/spec/support/directory_record_helper.rb +57 -0
  249. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  250. data/vendor/listen/spec/support/listeners_helper.rb +179 -0
  251. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  252. metadata +217 -76
  253. data/extra/sass-spec-ref.sh +0 -40
  254. data/lib/sass/deprecation.rb +0 -55
  255. data/lib/sass/logger/delayed.rb +0 -50
  256. data/lib/sass/script/value/callable.rb +0 -25
  257. data/lib/sass/script/value/function.rb +0 -19
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -0,0 +1,96 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ Vagrant::Config.run do |config|
5
+ # All Vagrant configuration is done here. The most common configuration
6
+ # options are documented and commented below. For a complete reference,
7
+ # please see the online documentation at vagrantup.com.
8
+
9
+ # Every Vagrant virtual environment requires a box to build off of.
10
+ config.vm.box = "lucid32"
11
+
12
+ # The url from where the 'config.vm.box' box will be fetched if it
13
+ # doesn't already exist on the user's system.
14
+ # config.vm.box_url = "http://domain.com/path/to/above.box"
15
+
16
+ # Boot with a GUI so you can see the screen. (Default is headless)
17
+ # config.vm.boot_mode = :gui
18
+
19
+ # Assign this VM to a host-only network IP, allowing you to access it
20
+ # via the IP. Host-only networks can talk to the host machine as well as
21
+ # any other machines on the same network, but cannot be accessed (through this
22
+ # network interface) by any external networks.
23
+ # config.vm.network :hostonly, "33.33.33.10"
24
+
25
+ # Assign this VM to a bridged network, allowing you to connect directly to a
26
+ # network using the host's network device. This makes the VM appear as another
27
+ # physical device on your network.
28
+ # config.vm.network :bridged
29
+
30
+ # Forward a port from the guest to the host, which allows for outside
31
+ # computers to access the VM, whereas host only networking does not.
32
+ # config.vm.forward_port 80, 8080
33
+
34
+ # Share an additional folder to the guest VM. The first argument is
35
+ # an identifier, the second is the path on the guest to mount the
36
+ # folder, and the third is the path on the host to the actual folder.
37
+ # config.vm.share_folder "v-data", "/vagrant_data", "../data"
38
+
39
+ # Enable provisioning with Puppet stand alone. Puppet manifests
40
+ # are contained in a directory path relative to this Vagrantfile.
41
+ # You will need to create the manifests directory and a manifest in
42
+ # the file lucid32.pp in the manifests_path directory.
43
+ #
44
+ # An example Puppet manifest to provision the message of the day:
45
+ #
46
+ # # group { "puppet":
47
+ # # ensure => "present",
48
+ # # }
49
+ # #
50
+ # # File { owner => 0, group => 0, mode => 0644 }
51
+ # #
52
+ # # file { '/etc/motd':
53
+ # # content => "Welcome to your Vagrant-built virtual machine!
54
+ # # Managed by Puppet.\n"
55
+ # # }
56
+ #
57
+ # config.vm.provision :puppet do |puppet|
58
+ # puppet.manifests_path = "manifests"
59
+ # puppet.manifest_file = "lucid32.pp"
60
+ # end
61
+
62
+ # Enable provisioning with chef solo, specifying a cookbooks path (relative
63
+ # to this Vagrantfile), and adding some recipes and/or roles.
64
+ #
65
+ # config.vm.provision :chef_solo do |chef|
66
+ # chef.cookbooks_path = "cookbooks"
67
+ # chef.add_recipe "mysql"
68
+ # chef.add_role "web"
69
+ #
70
+ # # You may also specify custom JSON attributes:
71
+ # chef.json = { :mysql_password => "foo" }
72
+ # end
73
+
74
+ # Enable provisioning with chef server, specifying the chef server URL,
75
+ # and the path to the validation key (relative to this Vagrantfile).
76
+ #
77
+ # The Opscode Platform uses HTTPS. Substitute your organization for
78
+ # ORGNAME in the URL and validation key.
79
+ #
80
+ # If you have your own Chef Server, use the appropriate URL, which may be
81
+ # HTTP instead of HTTPS depending on your configuration. Also change the
82
+ # validation key to validation.pem.
83
+ #
84
+ # config.vm.provision :chef_client do |chef|
85
+ # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
86
+ # chef.validation_key_path = "ORGNAME-validator.pem"
87
+ # end
88
+ #
89
+ # If you're using the Opscode platform, your validator client is
90
+ # ORGNAME-validator, replacing ORGNAME with your organization name.
91
+ #
92
+ # IF you have your own Chef Server, the default validation client name is
93
+ # chef-validator, unless you changed the configuration.
94
+ #
95
+ # chef.validation_client_name = "ORGNAME-validator"
96
+ end
@@ -0,0 +1,54 @@
1
+ require 'listen/turnstile'
2
+ require 'listen/listener'
3
+ require 'listen/directory_record'
4
+ require 'listen/adapter'
5
+
6
+ module Listen
7
+
8
+ module Adapters
9
+ Adapter::ADAPTERS.each do |adapter|
10
+ require "listen/adapters/#{adapter.downcase}"
11
+ end
12
+ end
13
+
14
+ # Listens to file system modifications on a either single directory or multiple directories.
15
+ # When calling this method, the current thread is not blocked.
16
+ #
17
+ # @param (see Listen::Listener#new)
18
+ #
19
+ # @yield [modified, added, removed] the changed files
20
+ # @yieldparam [Array<String>] modified the list of modified files
21
+ # @yieldparam [Array<String>] added the list of added files
22
+ # @yieldparam [Array<String>] removed the list of removed files
23
+ #
24
+ # @return [Listen::Listener] the file listener if no block given
25
+ #
26
+ def self.to(*args, &block)
27
+ listener = _init_listener(*args, &block)
28
+
29
+ block ? listener.start : listener
30
+ end
31
+
32
+ # Listens to file system modifications on a either single directory or multiple directories.
33
+ # When calling this method, the current thread is blocked.
34
+ #
35
+ # @param (see Listen::Listener#new)
36
+ #
37
+ # @yield [modified, added, removed] the changed files
38
+ # @yieldparam [Array<String>] modified the list of modified files
39
+ # @yieldparam [Array<String>] added the list of added files
40
+ # @yieldparam [Array<String>] removed the list of removed files
41
+ #
42
+ # @since 1.0.0
43
+ #
44
+ def self.to!(*args, &block)
45
+ _init_listener(*args, &block).start!
46
+ end
47
+
48
+ # @private
49
+ #
50
+ def self._init_listener(*args, &block)
51
+ Listener.new(*args, &block)
52
+ end
53
+
54
+ end
@@ -0,0 +1,327 @@
1
+ require 'rbconfig'
2
+ require 'thread'
3
+ require 'set'
4
+ require 'fileutils'
5
+
6
+ module Listen
7
+ class Adapter
8
+ attr_accessor :directories, :callback, :stopped, :paused,
9
+ :mutex, :changed_directories, :turnstile, :latency,
10
+ :worker, :worker_thread, :poller_thread
11
+
12
+ # The list of existing optimized adapters.
13
+ OPTIMIZED_ADAPTERS = %w[Darwin Linux BSD Windows]
14
+
15
+ # The list of existing fallback adapters.
16
+ FALLBACK_ADAPTERS = %w[Polling]
17
+
18
+ # The list of all existing adapters.
19
+ ADAPTERS = OPTIMIZED_ADAPTERS + FALLBACK_ADAPTERS
20
+
21
+ # The default delay between checking for changes.
22
+ DEFAULT_LATENCY = 0.25
23
+
24
+ # The default warning message when falling back to polling adapter.
25
+ POLLING_FALLBACK_MESSAGE = <<-EOS.gsub(/^\s*/, '')
26
+ Listen will be polling for changes. Learn more at https://github.com/guard/listen#polling-fallback.
27
+ EOS
28
+
29
+ # Selects the appropriate adapter implementation for the
30
+ # current OS and initializes it.
31
+ #
32
+ # @param [String, Array<String>] directories the directories to watch
33
+ # @param [Hash] options the adapter options
34
+ # @option options [Boolean] force_polling to force polling or not
35
+ # @option options [String, Boolean] polling_fallback_message to change polling fallback message or remove it
36
+ # @option options [Float] latency the delay between checking for changes in seconds
37
+ #
38
+ # @yield [changed_directories, options] callback the callback called when a change happens
39
+ # @yieldparam [Array<String>] changed_directories the changed directories
40
+ # @yieldparam [Hash] options callback options (like recursive: true)
41
+ #
42
+ # @return [Listen::Adapter] the chosen adapter
43
+ #
44
+ def self.select_and_initialize(directories, options = {}, &callback)
45
+ forced_adapter_class = options.delete(:force_adapter)
46
+ force_polling = options.delete(:force_polling)
47
+
48
+ if forced_adapter_class
49
+ forced_adapter_class.load_dependent_adapter
50
+ return forced_adapter_class.new(directories, options, &callback)
51
+ end
52
+
53
+ return Adapters::Polling.new(directories, options, &callback) if force_polling
54
+
55
+ OPTIMIZED_ADAPTERS.each do |adapter|
56
+ namespaced_adapter = Adapters.const_get(adapter)
57
+ if namespaced_adapter.send(:usable_and_works?, directories, options)
58
+ return namespaced_adapter.new(directories, options, &callback)
59
+ end
60
+ end
61
+
62
+ self.warn_polling_fallback(options)
63
+ Adapters::Polling.new(directories, options, &callback)
64
+ end
65
+
66
+ # Initializes the adapter.
67
+ #
68
+ # @param [String, Array<String>] directories the directories to watch
69
+ # @param [Hash] options the adapter options
70
+ # @option options [Float] latency the delay between checking for changes in seconds
71
+ #
72
+ # @yield [changed_directories, options] callback Callback called when a change happens
73
+ # @yieldparam [Array<String>] changed_directories the changed directories
74
+ # @yieldparam [Hash] options callback options (like recursive: true)
75
+ #
76
+ # @return [Listen::Adapter] the adapter
77
+ #
78
+ def initialize(directories, options = {}, &callback)
79
+ @directories = Array(directories)
80
+ @callback = callback
81
+ @stopped = true
82
+ @paused = false
83
+ @mutex = Mutex.new
84
+ @changed_directories = Set.new
85
+ @turnstile = Turnstile.new
86
+ @latency = options.fetch(:latency, default_latency)
87
+ @worker = initialize_worker
88
+ end
89
+
90
+ # Starts the adapter and don't block the current thread.
91
+ #
92
+ # @param [Boolean] blocking whether or not to block the current thread after starting
93
+ #
94
+ def start
95
+ mutex.synchronize do
96
+ return unless stopped
97
+ @stopped = false
98
+ end
99
+
100
+ start_worker
101
+ start_poller
102
+ end
103
+
104
+ # Starts the adapter and block the current thread.
105
+ #
106
+ # @since 1.0.0
107
+ #
108
+ def start!
109
+ start
110
+ blocking_thread.join
111
+ end
112
+
113
+ # Stops the adapter.
114
+ #
115
+ def stop
116
+ mutex.synchronize do
117
+ return if stopped
118
+ @stopped = true
119
+ turnstile.signal # ensure no thread is blocked
120
+ end
121
+
122
+ worker.stop if worker
123
+ Thread.kill(worker_thread) if worker_thread
124
+ if poller_thread
125
+ poller_thread.kill
126
+ poller_thread.join
127
+ end
128
+ end
129
+
130
+ # Pauses the adapter.
131
+ #
132
+ def pause
133
+ @paused = true
134
+ end
135
+
136
+ # Unpauses the adapter.
137
+ #
138
+ def unpause
139
+ @paused = false
140
+ end
141
+
142
+ # Returns whether the adapter is started or not.
143
+ #
144
+ # @return [Boolean] whether the adapter is started or not
145
+ #
146
+ def started?
147
+ !stopped
148
+ end
149
+
150
+ # Returns whether the adapter is paused or not.
151
+ #
152
+ # @return [Boolean] whether the adapter is paused or not
153
+ #
154
+ def paused?
155
+ paused
156
+ end
157
+
158
+ # Blocks the main thread until the poll thread
159
+ # runs the callback.
160
+ #
161
+ def wait_for_callback
162
+ turnstile.wait unless paused
163
+ end
164
+
165
+ # Blocks the main thread until N changes are
166
+ # detected.
167
+ #
168
+ def wait_for_changes(threshold = 0)
169
+ changes = 0
170
+
171
+ loop do
172
+ mutex.synchronize { changes = changed_directories.size }
173
+
174
+ return if paused || stopped
175
+ return if changes >= threshold
176
+
177
+ sleep(latency)
178
+ end
179
+ end
180
+
181
+ # Checks if the adapter is usable and works on the current OS.
182
+ #
183
+ # @param [String, Array<String>] directories the directories to watch
184
+ # @param [Hash] options the adapter options
185
+ # @option options [Float] latency the delay between checking for changes in seconds
186
+ #
187
+ # @return [Boolean] whether the adapter is usable and work or not
188
+ #
189
+ def self.usable_and_works?(directories, options = {})
190
+ usable? && Array(directories).all? { |d| works?(d, options) }
191
+ end
192
+
193
+ # Checks if the adapter is usable on target OS.
194
+ #
195
+ # @return [Boolean] whether usable or not
196
+ #
197
+ def self.usable?
198
+ load_dependent_adapter if RbConfig::CONFIG['target_os'] =~ target_os_regex
199
+ end
200
+
201
+ # Load the adapter gem
202
+ #
203
+ # @return [Boolean] whether loaded or not
204
+ #
205
+ def self.load_dependent_adapter
206
+ return true if @loaded
207
+ require adapter_gem
208
+ return @loaded = true
209
+ rescue LoadError
210
+ false
211
+ end
212
+
213
+ # Runs a tests to determine if the adapter can actually pick up
214
+ # changes in a given directory and returns the result.
215
+ #
216
+ # @note This test takes some time depending on the adapter latency.
217
+ #
218
+ # @param [String, Pathname] directory the directory to watch
219
+ # @param [Hash] options the adapter options
220
+ # @option options [Float] latency the delay between checking for changes in seconds
221
+ #
222
+ # @return [Boolean] whether the adapter works or not
223
+ #
224
+ def self.works?(directory, options = {})
225
+ work = false
226
+ test_file = "#{directory}/.listen_test"
227
+ callback = lambda { |*| work = true }
228
+ adapter = self.new(directory, options, &callback)
229
+ adapter.start
230
+
231
+ FileUtils.touch(test_file)
232
+
233
+ t = Thread.new { sleep(adapter.latency * 5); adapter.stop }
234
+
235
+ adapter.wait_for_callback
236
+ work
237
+ ensure
238
+ Thread.kill(t) if t
239
+ FileUtils.rm(test_file, :force => true)
240
+ adapter.stop if adapter && adapter.started?
241
+ end
242
+
243
+ # Runs the callback and passes it the changes if there are any.
244
+ #
245
+ def report_changes
246
+ changed_dirs = nil
247
+
248
+ mutex.synchronize do
249
+ return if @changed_directories.empty?
250
+ changed_dirs = @changed_directories.to_a
251
+ @changed_directories.clear
252
+ end
253
+
254
+ callback.call(changed_dirs, {})
255
+ turnstile.signal
256
+ end
257
+
258
+ private
259
+
260
+ # The default delay between checking for changes.
261
+ #
262
+ # @note This method can be overriden on a per-adapter basis.
263
+ #
264
+ def default_latency
265
+ DEFAULT_LATENCY
266
+ end
267
+
268
+ # The thread on which the main thread should wait
269
+ # when the adapter has been started in blocking mode.
270
+ #
271
+ # @note This method can be overriden on a per-adapter basis.
272
+ #
273
+ def blocking_thread
274
+ worker_thread
275
+ end
276
+
277
+ # Initialize the adpater' specific worker.
278
+ #
279
+ # @note Each adapter must override this method
280
+ # to initialize its own @worker.
281
+ #
282
+ def initialize_worker
283
+ nil
284
+ end
285
+
286
+ # Should start the worker in a new thread.
287
+ #
288
+ # @note Each adapter must override this method
289
+ # to start its worker on a new @worker_thread thread.
290
+ #
291
+ def start_worker
292
+ raise NotImplementedError, "#{self.class} cannot respond to: #{__method__}"
293
+ end
294
+
295
+ # This method starts a new thread which poll for changes detected by
296
+ # the adapter and report them back to the user.
297
+ #
298
+ def start_poller
299
+ @poller_thread = Thread.new { poll_changed_directories }
300
+ end
301
+
302
+ # Warn of polling fallback unless the :polling_fallback_message
303
+ # has been set to false.
304
+ #
305
+ # @param [String] warning an existing warning message
306
+ # @param [Hash] options the adapter options
307
+ # @option options [Boolean] polling_fallback_message to change polling fallback message or remove it
308
+ #
309
+ def self.warn_polling_fallback(options)
310
+ return if options[:polling_fallback_message] == false
311
+
312
+ warning = options[:polling_fallback_message] || POLLING_FALLBACK_MESSAGE
313
+ Kernel.warn "[Listen warning]:\n#{warning.gsub(/^(.*)/, ' \1')}"
314
+ end
315
+
316
+ # Polls changed directories and reports them back when there are changes.
317
+ #
318
+ # @note This method can be overriden on a per-adapter basis.
319
+ #
320
+ def poll_changed_directories
321
+ until stopped
322
+ sleep(latency)
323
+ report_changes
324
+ end
325
+ end
326
+ end
327
+ end