sass 3.1.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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