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.
Files changed (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -0,0 +1,629 @@
1
+ # Adapter watch
2
+ #
3
+ # @param [Listen::Listener] listener the adapter listener
4
+ # @param [String] path the path to watch
5
+ #
6
+ def watch(listener, expected_changes, *paths)
7
+ callback = lambda { |changed_dirs, options| @called = true; listener.on_change(changed_dirs) }
8
+ @adapter = Listen::Adapter.select_and_initialize(paths, { :report_changes => false, :latency => test_latency }, &callback)
9
+
10
+ forced_stop = false
11
+ prevent_deadlock = Proc.new { sleep(10); puts "Forcing stop"; @adapter.stop; forced_stop = true }
12
+
13
+ @adapter.start(false)
14
+
15
+ yield
16
+
17
+ t = Thread.new(&prevent_deadlock)
18
+ @adapter.wait_for_changes(expected_changes)
19
+
20
+ unless forced_stop
21
+ Thread.kill(t)
22
+ @adapter.report_changes
23
+ end
24
+ ensure
25
+ unless forced_stop
26
+ Thread.kill(t) if t
27
+ @adapter.stop
28
+ end
29
+ end
30
+
31
+ shared_examples_for 'a filesystem adapter' do
32
+ subject { described_class.new(File.dirname(__FILE__), &Proc.new {}) }
33
+
34
+ describe '#start' do
35
+ after { subject.stop }
36
+
37
+ context 'with the blocking param set to true' do
38
+ it 'blocks the current thread after starting the workers' do
39
+ @called = false
40
+ t = Thread.new { subject.start(true); @called = true }
41
+ sleep(test_latency * 3)
42
+ Thread.kill(t) if t
43
+ @called.should be_false
44
+ end
45
+ end
46
+
47
+ context 'with the blocking param set to false' do
48
+ it 'does not block the current thread after starting the workers' do
49
+ @called = false
50
+ t = Thread.new { subject.start(false); @called = true }
51
+ sleep(test_latency * 3)
52
+ Thread.kill(t) if t
53
+ @called.should be_true
54
+ end
55
+ end
56
+ end
57
+
58
+ describe '#started?' do
59
+ context 'with a new adapter' do
60
+ it 'returns false' do
61
+ subject.should_not be_started
62
+ end
63
+ end
64
+
65
+ context 'with a stopped adapter' do
66
+ before { subject.start(false); subject.stop }
67
+
68
+ it 'returns false' do
69
+ subject.should_not be_started
70
+ end
71
+ end
72
+
73
+ context 'with a started adapter' do
74
+ before { subject.start(false) }
75
+ after { subject.stop }
76
+
77
+ it 'returns true' do
78
+ subject.should be_started
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ shared_examples_for 'an adapter that call properly listener#on_change' do |*args|
85
+ options = (args.first && args.first.is_a?(Hash)) ? args.first : {}
86
+ let(:listener) { mock(Listen::Listener) }
87
+ before { described_class.stub(:works?) { true } }
88
+
89
+ context 'single file operations' do
90
+ context 'when a file is created' do
91
+ it 'detects the added file' do
92
+ fixtures do |path|
93
+ listener.should_receive(:on_change).once.with do |array|
94
+ array.should include(path)
95
+ end
96
+
97
+ watch(listener, 1, path) do
98
+ touch 'new_file.rb'
99
+ end
100
+ end
101
+ end
102
+
103
+ context 'given a symlink', :unless => windows? do
104
+ it 'detects the added file' do
105
+ fixtures do |path|
106
+ listener.should_receive(:on_change).once.with do |array|
107
+ array.should include(path)
108
+ end
109
+
110
+ touch 'new_file.rb'
111
+
112
+ watch(listener, 1, path) do
113
+ ln_s 'new_file.rb', 'new_file_symlink.rb'
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ context 'given a new created directory' do
120
+ it 'detects the added file' do
121
+ fixtures do |path|
122
+ listener.should_receive(:on_change).once.with do |array|
123
+ array.should include(path, "#{path}/a_directory")
124
+ end
125
+
126
+ watch(listener, 2, path) do
127
+ mkdir 'a_directory'
128
+ # Needed for INotify, because of :recursive rb-inotify custom flag?
129
+ sleep 0.05
130
+ touch 'a_directory/new_file.rb'
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ context 'given an existing directory' do
137
+ it 'detects the added file' do
138
+ fixtures do |path|
139
+ listener.should_receive(:on_change).once.with do |array|
140
+ array.should include("#{path}/a_directory")
141
+ end
142
+
143
+ mkdir 'a_directory'
144
+
145
+ watch(listener, 1, path) do
146
+ touch 'a_directory/new_file.rb'
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ context 'given a directory with subdirectories' do
153
+ it 'detects the added file' do
154
+ fixtures do |path|
155
+ listener.should_receive(:on_change).once.with do |array|
156
+ array.should include("#{path}/a_directory/subdirectory")
157
+ end
158
+
159
+ mkdir_p 'a_directory/subdirectory'
160
+
161
+ watch(listener, 1, path) do
162
+ touch 'a_directory/subdirectory/new_file.rb'
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ context 'when a file is modified' do
170
+ it 'detects the modified file' do
171
+ fixtures do |path|
172
+ listener.should_receive(:on_change).once.with do |array|
173
+ array.should include(path)
174
+ end
175
+
176
+ touch 'existing_file.txt'
177
+
178
+ watch(listener, 1, path) do
179
+ touch 'existing_file.txt'
180
+ end
181
+ end
182
+ end
183
+
184
+ context 'given a symlink', :unless => windows? do
185
+ it 'detects the modified file' do
186
+ fixtures do |path|
187
+ listener.should_receive(:on_change).once.with do |array|
188
+ array.should include(path)
189
+ end
190
+
191
+ touch 'existing_file.rb'
192
+ ln_s 'existing_file.rb', 'existing_file_symlink.rb'
193
+
194
+ watch(listener, 1, path) do
195
+ touch 'existing_file.rb'
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ context 'given a hidden file' do
202
+ it 'detects the modified file' do
203
+ fixtures do |path|
204
+ listener.should_receive(:on_change).once.with do |array|
205
+ array.should include(path)
206
+ end
207
+
208
+ touch '.hidden'
209
+
210
+ watch(listener, 1, path) do
211
+ touch '.hidden'
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ context 'given a file mode change', :unless => windows? do
218
+ it 'does not detect the mode change' do
219
+ fixtures do |path|
220
+ listener.should_receive(:on_change).once.with do |array|
221
+ array.should include(path)
222
+ end
223
+
224
+ touch 'run.rb'
225
+
226
+ watch(listener, 1, path) do
227
+ chmod 0777, 'run.rb'
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ context 'given an existing directory' do
234
+ it 'detects the modified file' do
235
+ fixtures do |path|
236
+ listener.should_receive(:on_change).once.with do |array|
237
+ array.should include("#{path}/a_directory")
238
+ end
239
+
240
+ mkdir 'a_directory'
241
+ touch 'a_directory/existing_file.txt'
242
+
243
+ watch(listener, 1, path) do
244
+ touch 'a_directory/existing_file.txt'
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ context 'given a directory with subdirectories' do
251
+ it 'detects the modified file' do
252
+ fixtures do |path|
253
+ listener.should_receive(:on_change).once.with do |array|
254
+ array.should include("#{path}/a_directory/subdirectory")
255
+ end
256
+
257
+ mkdir_p 'a_directory/subdirectory'
258
+ touch 'a_directory/subdirectory/existing_file.txt'
259
+
260
+ watch(listener, 1, path) do
261
+ touch 'a_directory/subdirectory/new_file.rb'
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ context 'when a file is moved' do
269
+ it 'detects the file move' do
270
+ fixtures do |path|
271
+ listener.should_receive(:on_change).once.with do |array|
272
+ array.should include(path)
273
+ end
274
+
275
+ touch 'move_me.txt'
276
+
277
+ watch(listener, 1, path) do
278
+ mv 'move_me.txt', 'new_name.txt'
279
+ end
280
+ end
281
+ end
282
+
283
+ context 'given a symlink', :unless => windows? do
284
+ it 'detects the file move' do
285
+ fixtures do |path|
286
+ listener.should_receive(:on_change).once.with do |array|
287
+ array.should include(path)
288
+ end
289
+
290
+ touch 'move_me.rb'
291
+ ln_s 'move_me.rb', 'move_me_symlink.rb'
292
+
293
+ watch(listener, 1, path) do
294
+ mv 'move_me_symlink.rb', 'new_symlink.rb'
295
+ end
296
+ end
297
+ end
298
+ end
299
+
300
+ context 'given an existing directory' do
301
+ it 'detects the file move into the directory' do
302
+ fixtures do |path|
303
+ listener.should_receive(:on_change).once.with do |array|
304
+ array.should include(path, "#{path}/a_directory")
305
+ end
306
+
307
+ mkdir 'a_directory'
308
+ touch 'move_me.txt'
309
+
310
+ watch(listener, 2, path) do
311
+ mv 'move_me.txt', 'a_directory/move_me.txt'
312
+ end
313
+ end
314
+ end
315
+
316
+ it 'detects a file move out of the directory' do
317
+ fixtures do |path|
318
+ listener.should_receive(:on_change).once.with do |array|
319
+ array.should include(path, "#{path}/a_directory")
320
+ end
321
+
322
+ mkdir 'a_directory'
323
+ touch 'a_directory/move_me.txt'
324
+
325
+ watch(listener, 2, path) do
326
+ mv 'a_directory/move_me.txt', 'i_am_here.txt'
327
+ end
328
+ end
329
+ end
330
+
331
+ it 'detects a file move between two directories' do
332
+ fixtures do |path|
333
+ listener.should_receive(:on_change).once.with do |array|
334
+ array.should include("#{path}/from_directory", "#{path}/to_directory")
335
+ end
336
+
337
+ mkdir 'from_directory'
338
+ touch 'from_directory/move_me.txt'
339
+ mkdir 'to_directory'
340
+
341
+ watch(listener, 2, path) do
342
+ mv 'from_directory/move_me.txt', 'to_directory/move_me.txt'
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ context 'given a directory with subdirectories' do
349
+ it 'detects files movements between subdirectories' do
350
+ fixtures do |path|
351
+ listener.should_receive(:on_change).once.with do |array|
352
+ array.should include("#{path}/a_directory/subdirectory", "#{path}/b_directory/subdirectory")
353
+ end
354
+
355
+ mkdir_p 'a_directory/subdirectory'
356
+ mkdir_p 'b_directory/subdirectory'
357
+ touch 'a_directory/subdirectory/move_me.txt'
358
+
359
+ watch(listener, 2, path) do
360
+ mv 'a_directory/subdirectory/move_me.txt', 'b_directory/subdirectory'
361
+ end
362
+ end
363
+ end
364
+ end
365
+ end
366
+
367
+ context 'when a file is deleted' do
368
+ it 'detects the file removal' do
369
+ fixtures do |path|
370
+ listener.should_receive(:on_change).once.with do |array|
371
+ array.should include(path)
372
+ end
373
+
374
+ touch 'unnecessary.txt'
375
+
376
+ watch(listener, 1, path) do
377
+ rm 'unnecessary.txt'
378
+ end
379
+ end
380
+ end
381
+
382
+ context 'given a symlink', :unless => windows? do
383
+ it 'detects the file removal' do
384
+ fixtures do |path|
385
+ listener.should_receive(:on_change).once.with do |array|
386
+ array.should include(path)
387
+ end
388
+
389
+ touch 'unnecessary.rb'
390
+ ln_s 'unnecessary.rb', 'unnecessary_symlink.rb'
391
+
392
+ watch(listener, 1, path) do
393
+ rm 'unnecessary_symlink.rb'
394
+ end
395
+ end
396
+ end
397
+ end
398
+
399
+ context 'given an existing directory' do
400
+ it 'detects the file removal' do
401
+ fixtures do |path|
402
+ listener.should_receive(:on_change).once.with do |array|
403
+ array.should include("#{path}/a_directory")
404
+ end
405
+
406
+ mkdir 'a_directory'
407
+ touch 'a_directory/do_not_use.rb'
408
+
409
+ watch(listener, 1, path) do
410
+ rm 'a_directory/do_not_use.rb'
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ context 'given a directory with subdirectories' do
417
+ it 'detects the file removal' do
418
+ fixtures do |path|
419
+ listener.should_receive(:on_change).once.with do |array|
420
+ array.should include("#{path}/a_directory/subdirectory")
421
+ end
422
+
423
+ mkdir_p 'a_directory/subdirectory'
424
+ touch 'a_directory/subdirectory/do_not_use.rb'
425
+
426
+ watch(listener, 1, path) do
427
+ rm 'a_directory/subdirectory/do_not_use.rb'
428
+ end
429
+ end
430
+ end
431
+ end
432
+ end
433
+ end
434
+
435
+ context 'multiple file operations' do
436
+ it 'detects the added files' do
437
+ fixtures do |path|
438
+ listener.should_receive(:on_change).once.with do |array|
439
+ array.should include(path, "#{path}/a_directory")
440
+ end
441
+
442
+ watch(listener, 2, path) do
443
+ touch 'a_file.rb'
444
+ touch 'b_file.rb'
445
+ mkdir 'a_directory'
446
+ # Needed for INotify, because of :recursive rb-inotify custom flag?
447
+ # Also needed for the osx adapter
448
+ sleep 0.05
449
+ touch 'a_directory/a_file.rb'
450
+ touch 'a_directory/b_file.rb'
451
+ end
452
+ end
453
+ end
454
+
455
+ it 'detects the modified files' do
456
+ fixtures do |path|
457
+ listener.should_receive(:on_change).once.with do |array|
458
+ array.should include(path, "#{path}/a_directory")
459
+ end
460
+
461
+ touch 'a_file.rb'
462
+ touch 'b_file.rb'
463
+ mkdir 'a_directory'
464
+ touch 'a_directory/a_file.rb'
465
+ touch 'a_directory/b_file.rb'
466
+
467
+ watch(listener, 2, path) do
468
+ touch 'b_file.rb'
469
+ touch 'a_directory/a_file.rb'
470
+ end
471
+ end
472
+ end
473
+
474
+ it 'detects the removed files' do
475
+ fixtures do |path|
476
+ listener.should_receive(:on_change).once.with do |array|
477
+ array.should include(path, "#{path}/a_directory")
478
+ end
479
+
480
+ touch 'a_file.rb'
481
+ touch 'b_file.rb'
482
+ mkdir 'a_directory'
483
+ touch 'a_directory/a_file.rb'
484
+ touch 'a_directory/b_file.rb'
485
+
486
+ watch(listener, 2, path) do
487
+ rm 'b_file.rb'
488
+ rm 'a_directory/a_file.rb'
489
+ end
490
+ end
491
+ end
492
+ end
493
+
494
+ context 'single directory operations' do
495
+ it 'detects a moved directory' do
496
+ fixtures do |path|
497
+ listener.should_receive(:on_change).once.with do |array|
498
+ array.should include(path)
499
+ end
500
+
501
+ mkdir 'a_directory'
502
+ touch 'a_directory/a_file.rb'
503
+ touch 'a_directory/b_file.rb'
504
+
505
+ watch(listener, 1, path) do
506
+ mv 'a_directory', 'renamed'
507
+ end
508
+ end
509
+ end
510
+
511
+ it 'detects a removed directory' do
512
+ fixtures do |path|
513
+ listener.should_receive(:on_change).once.with do |array|
514
+ array.should include(path, "#{path}/a_directory")
515
+ end
516
+
517
+ mkdir 'a_directory'
518
+ touch 'a_directory/a_file.rb'
519
+ touch 'a_directory/b_file.rb'
520
+
521
+ watch(listener, 2, path) do
522
+ rm_rf 'a_directory'
523
+ end
524
+ end
525
+ end
526
+ end
527
+
528
+ context "paused adapter" do
529
+ context 'when a file is created' do
530
+ it "doesn't detects the added file" do
531
+ fixtures do |path|
532
+ watch(listener, 1, path) do # The expected changes param is set to one!
533
+ @adapter.paused = true
534
+ touch 'new_file.rb'
535
+ end
536
+ @called.should be_nil
537
+ end
538
+ end
539
+ end
540
+ end
541
+
542
+ context "when multiple directories are listened to" do
543
+ context 'when files are added to one of multiple directories' do
544
+ it 'detects added files' do
545
+ fixtures(2) do |path1, path2|
546
+ listener.should_receive(:on_change).once.with do |array|
547
+ array.should include(path2)
548
+ end
549
+
550
+ watch(listener, 1, path1, path2) do
551
+ touch "#{path2}/new_file.rb"
552
+ end
553
+ end
554
+ end
555
+ end
556
+
557
+ context 'when files are added to multiple directories' do
558
+ it 'detects added files' do
559
+ fixtures(2) do |path1, path2|
560
+ listener.should_receive(:on_change).once.with do |array|
561
+ array.should include(path1, path2)
562
+ end
563
+
564
+ watch(listener, 2, path1, path2) do
565
+ touch "#{path1}/new_file.rb"
566
+ touch "#{path2}/new_file.rb"
567
+ end
568
+ end
569
+ end
570
+ end
571
+
572
+ context 'given a new and an existing directory on multiple directories' do
573
+ it 'detects the added file' do
574
+ fixtures(2) do |path1, path2|
575
+ listener.should_receive(:on_change).once.with do |array|
576
+ array.should include(path2, "#{path1}/a_directory", "#{path2}/b_directory")
577
+ end
578
+
579
+ mkdir "#{path1}/a_directory"
580
+
581
+ watch(listener, 3, path1, path2) do
582
+ mkdir "#{path2}/b_directory"
583
+ # Needed for INotify
584
+ sleep 0.05
585
+ touch "#{path1}/a_directory/new_file.rb"
586
+ touch "#{path2}/b_directory/new_file.rb"
587
+ end
588
+ end
589
+ end
590
+ end
591
+
592
+ context 'when a file is moved between the multiple watched directories' do
593
+ it 'detects the movements of the file' do
594
+ fixtures(3) do |path1, path2, path3|
595
+ listener.should_receive(:on_change).once.with do |array|
596
+ array.should include("#{path1}/from_directory", path2, "#{path3}/to_directory")
597
+ end
598
+
599
+ mkdir "#{path1}/from_directory"
600
+ touch "#{path1}/from_directory/move_me.txt"
601
+ mkdir "#{path3}/to_directory"
602
+
603
+ watch(listener, 3, path1, path2, path3) do
604
+ mv "#{path1}/from_directory/move_me.txt", "#{path2}/move_me.txt"
605
+ mv "#{path2}/move_me.txt", "#{path3}/to_directory/move_me.txt"
606
+ end
607
+ end
608
+ end
609
+ end
610
+
611
+ context 'when files are deleted from the multiple watched directories' do
612
+ it 'detects the files removal' do
613
+ fixtures(2) do |path1, path2|
614
+ listener.should_receive(:on_change).once.with do |array|
615
+ array.should include(path1, path2)
616
+ end
617
+
618
+ touch "#{path1}/unnecessary.txt"
619
+ touch "#{path2}/unnecessary.txt"
620
+
621
+ watch(listener, 2, path1, path2) do
622
+ rm "#{path1}/unnecessary.txt"
623
+ rm "#{path2}/unnecessary.txt"
624
+ end
625
+ end
626
+ end
627
+ end
628
+ end
629
+ end
@@ -0,0 +1,55 @@
1
+ # Prepares a record for the test and fetches changes
2
+ # afterwards.
3
+ #
4
+ # @param [String] root_path the path to watch
5
+ # @param [Hash] options
6
+ # @option options [Array<string>] :paths optional paths fetch changes for
7
+ # @option options [Boolean] :use_last_record allow the use of an already
8
+ # created record, handy for ordered tests.
9
+ #
10
+ # @return [Array, Array, Array] the changes
11
+ #
12
+ def changes(root_path, options = {})
13
+ unless @record || options[:use_last_record]
14
+ @record = Listen::DirectoryRecord.new(root_path)
15
+ @record.filter(options.delete(:filter)) if options[:filter]
16
+ @record.ignore(options.delete(:ignore)) if options[:ignore]
17
+
18
+ # Build the record after adding the filtering and ignoring patterns
19
+ @record.build
20
+ end
21
+
22
+ yield if block_given?
23
+
24
+ paths = options.delete(:paths) || [root_path]
25
+ options[:recursive] = true if options[:recursive].nil?
26
+
27
+ changes = @record.fetch_changes(paths, {:relative_paths => true}.merge(options))
28
+
29
+ [changes[:modified], changes[:added], changes[:removed]]
30
+ end
31
+
32
+ # Generates a small time difference before performing a time sensitive
33
+ # task (like comparing mtimes of files).
34
+ #
35
+ # @note Modification time for files only includes the milliseconds on Linux with MRI > 1.9.2,
36
+ # that's why we generate a difference that's greater than 1 second.
37
+ #
38
+ def small_time_difference
39
+ t = Time.now
40
+ diff = t.to_f - t.to_i
41
+
42
+ sleep( 1.5 - (diff < 0.5 ? diff : 0.4) )
43
+ end
44
+
45
+ # Ensures that the test runs at almost the same second at which
46
+ # changes are being checked.
47
+ #
48
+ def ensure_same_second
49
+ t = Time.now
50
+ diff = t.to_f - t.to_i
51
+
52
+ if diff > 0.1 # We are not at the beginning of a second
53
+ sleep 1.1 - diff # 1.1 is used instead of 1 to account for the processing time (estimated at 0.1 sec)
54
+ end
55
+ end
@@ -0,0 +1,29 @@
1
+ require 'tmpdir'
2
+
3
+ include FileUtils
4
+
5
+ # Prepares temporary fixture-directories and
6
+ # cleans them afterwards.
7
+ #
8
+ # @param [Fixnum] number_of_directories the number of fixture-directories to make
9
+ #
10
+ # @yield [path1, path2, ...] the empty fixture-directories
11
+ # @yieldparam [String] path the path to a fixture directory
12
+ #
13
+ def fixtures(number_of_directories = 1)
14
+ current_pwd = pwd
15
+ paths = 1.upto(number_of_directories).map do
16
+ File.expand_path(File.join(pwd, "spec/.fixtures/#{Time.now.to_f.to_s.sub('.', '') + rand(9999).to_s}"))
17
+ end
18
+
19
+ # Create the dirs
20
+ paths.each { |p| mkdir_p(p) }
21
+
22
+ cd(paths.first) if number_of_directories == 1
23
+
24
+ yield(*paths)
25
+
26
+ ensure
27
+ cd current_pwd
28
+ paths.map { |p| rm_rf(p) if File.exists?(p) }
29
+ end