build-files 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e35654286f32140f640677be45768252c787b496
4
- data.tar.gz: 7edf8aa92f327d5ba8e96c25a8731b249b05f6e0
3
+ metadata.gz: 2e83395d241b8365a554f4f56d3c6a79019e4912
4
+ data.tar.gz: 02c1657d7907c8a9955b1d329cb343cd5946a1af
5
5
  SHA512:
6
- metadata.gz: 8c06e927233aed011f491da416cec580053dda563f9dcbce6777c341f03a290a1e7db064a9a86c91c42f51034fc9a1c10b4e85dc85f5eb619f8d05a120221653
7
- data.tar.gz: 54b64d67398fb465a1a679f91b9d381a8bbce41044e66514b7c20b9cd89ae32da0bf4c060ba8daa10ce0cf68f78d6f55de95750941860f5fd63832def8a90e55
6
+ metadata.gz: ce8ea60d34378a7bd007479e5ec7b005819d5484421adfc14bae08331eca00b5e62c6eee56ad8828d8547b4c06339c9d644ff46f94e0f77fd481e664b354cdf3
7
+ data.tar.gz: 84980ca0c4bfc21caf219355276ff44463c665b0d0e4802603d92a583ba9288513a03e9bc3877e1d6b9e2a2bd86799e8c3d5405019e2326e6324e91340ca8824
@@ -146,6 +146,12 @@ module Build
146
146
 
147
147
  attr :files
148
148
 
149
+ def freeze
150
+ self.roots
151
+
152
+ super
153
+ end
154
+
149
155
  def each
150
156
  return to_enum(:each) unless block_given?
151
157
 
@@ -24,60 +24,85 @@ require 'build/files/state'
24
24
 
25
25
  module Build
26
26
  module Files
27
+ class Handle
28
+ def initialize(monitor, files, &block)
29
+ @monitor = monitor
30
+ @state = State.new(files)
31
+ @on_changed = block
32
+ end
33
+
34
+ attr :monitor
35
+
36
+ def commit!
37
+ @state.update!
38
+ end
39
+
40
+ def directories
41
+ @state.files.roots
42
+ end
43
+
44
+ def remove!
45
+ monitor.delete(self)
46
+ end
47
+
48
+ def changed!
49
+ @on_changed.call(@state) if @state.update!
50
+ end
51
+ end
52
+
27
53
  class Monitor
28
54
  def initialize
29
55
  @directories = Hash.new { |hash, key| hash[key] = Set.new }
30
56
 
31
57
  @updated = false
58
+
59
+ @deletions = nil
32
60
  end
33
61
 
34
62
  attr :updated
35
63
 
36
64
  # Notify the monitor that files in these directories have changed.
37
65
  def update(directories, *args)
38
- directories.each do |directory|
39
- # directory = File.realpath(directory)
40
-
41
- @directories[directory].each do |handle|
42
- handle.changed!(*args)
66
+ delay_deletions do
67
+ directories.each do |directory|
68
+ # directory = File.realpath(directory)
69
+
70
+ @directories[directory].each do |handle|
71
+ handle.changed!(*args)
72
+ end
43
73
  end
44
74
  end
45
75
  end
46
-
76
+
47
77
  def roots
48
78
  @directories.keys
49
79
  end
50
-
51
- def delete(handle)
52
- handle.directories.each do |directory|
53
- @directories[directory].delete(handle)
54
80
 
55
- # Remove the entire record if there are no handles:
56
- if @directories[directory].size == 0
57
- @directories.delete(directory)
58
-
59
- @updated = true
60
- end
81
+ def delete(handle)
82
+ if @deletions
83
+ @deletions << handle
84
+ else
85
+ purge(handle)
61
86
  end
62
87
  end
63
-
88
+
64
89
  def track_changes(files, &block)
65
90
  handle = Handle.new(self, files, &block)
66
-
91
+
67
92
  add(handle)
68
93
  end
69
-
94
+
70
95
  def add(handle)
71
96
  handle.directories.each do |directory|
72
97
  @directories[directory] << handle
73
-
98
+
74
99
  # We just added the first handle:
75
100
  if @directories[directory].size == 1
76
101
  # If the handle already existed, this might trigger unnecessarily.
77
102
  @updated = true
78
103
  end
79
104
  end
80
-
105
+
81
106
  handle
82
107
  end
83
108
 
@@ -93,6 +118,33 @@ module Build
93
118
  Files.send(method_name, self, options, &block)
94
119
  end
95
120
  end
121
+
122
+ protected
123
+
124
+ def delay_deletions
125
+ @deletions = []
126
+
127
+ yield
128
+
129
+ @deletions.each do |handle|
130
+ purge(handle)
131
+ end
132
+
133
+ @deletions = nil
134
+ end
135
+
136
+ def purge(handle)
137
+ handle.directories.each do |directory|
138
+ @directories[directory].delete(handle)
139
+
140
+ # Remove the entire record if there are no handles:
141
+ if @directories[directory].size == 0
142
+ @directories.delete(directory)
143
+
144
+ @updated = true
145
+ end
146
+ end
147
+ end
96
148
  end
97
149
 
98
150
  def self.run_with_inotify(monitor, options = {}, &block)
@@ -135,7 +187,7 @@ module Build
135
187
  notifier.stop
136
188
  end
137
189
  end
138
-
190
+
139
191
  notifier.run
140
192
  end
141
193
  end
@@ -18,6 +18,10 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require_relative 'list'
22
+
23
+ require 'forwardable'
24
+
21
25
  module Build
22
26
  module Files
23
27
  # Represents a specific file on disk with a specific mtime.
@@ -41,35 +45,40 @@ module Build
41
45
  end
42
46
  end
43
47
 
44
- class State
48
+ # A stateful list of files captured at a specific time, which can then be checked for changes.
49
+ class State < Files::List
50
+ extend Forwardable
51
+
45
52
  def initialize(files)
46
53
  raise ArgumentError.new("Invalid files list: #{files}") unless Files::List === files
47
-
54
+
48
55
  @files = files
49
-
56
+
50
57
  @times = {}
51
-
58
+
52
59
  update!
53
60
  end
54
-
61
+
55
62
  attr :files
56
-
63
+
57
64
  attr :added
58
65
  attr :removed
59
66
  attr :changed
60
67
  attr :missing
61
-
68
+
62
69
  attr :times
63
-
70
+
71
+ def_delegators :@files, :each, :roots, :count
72
+
64
73
  def update!
65
74
  last_times = @times
66
75
  @times = {}
67
-
76
+
68
77
  @added = []
69
78
  @removed = []
70
79
  @changed = []
71
80
  @missing = []
72
-
81
+
73
82
  file_times = []
74
83
 
75
84
  @files.each do |path|
@@ -123,120 +132,40 @@ module Build
123
132
  !@missing.empty?
124
133
  end
125
134
 
126
- # Outputs is a list of full paths and must not include any patterns/globs.
127
- def intersects?(outputs)
128
- @files.intersects?(outputs)
129
- end
130
-
131
135
  def empty?
132
- @files.to_a.empty?
136
+ @times.empty?
133
137
  end
134
138
 
135
139
  def inspect
136
140
  "<State Added:#{@added} Removed:#{@removed} Changed:#{@changed} Missing:#{@missing}>"
137
141
  end
138
- end
139
-
140
- class IOState
141
- def initialize(inputs, outputs)
142
- @input_state = State.new(inputs)
143
- @output_state = State.new(outputs)
144
- end
145
142
 
146
- attr :input_state
147
- attr :output_state
143
+ # Are these files dirty with respect to the given inputs?
144
+ def dirty?(inputs)
145
+ self.class.dirty?(inputs, self)
146
+ end
148
147
 
149
- # Output is dirty if files are missing or if latest input is older than any output.
150
- def dirty?
151
- @dirty = []
152
-
153
- if @output_state.missing?
148
+ def self.dirty?(inputs, outputs)
149
+ if outputs.missing?
154
150
  # puts "Output file missing: #{output_state.missing.inspect}"
155
-
156
151
  return true
157
152
  end
158
153
 
159
- # If there are no inputs, we are always clean as long as outputs exist:
160
- # if @input_state.empty?
161
- # return false
162
- # end
154
+ # If there are no inputs or no outputs, we are always clean:
155
+ if inputs.empty? or outputs.empty?
156
+ return false
157
+ end
163
158
 
164
- oldest_output_time = @output_state.oldest_time
165
- newest_input_time = @input_state.newest_time
159
+ oldest_output_time = outputs.oldest_time
160
+ newest_input_time = inputs.newest_time
166
161
 
167
162
  if newest_input_time and oldest_output_time
168
- # if newest_input_time > oldest_output_time
169
- # puts "Out of date file: #{newest_input_time.inspect} > #{oldest_output_time.inspect}"
170
- # end
171
-
163
+ # We are dirty if any inputs are newer (bigger) than any outputs:
172
164
  return newest_input_time > oldest_output_time
173
165
  end
174
166
 
175
- # puts "Missing file dates: #{newest_input_time.inspect} < #{oldest_output_time.inspect}"
176
-
177
167
  return true
178
168
  end
179
-
180
- def fresh?
181
- not dirty?
182
- end
183
-
184
- def files
185
- @input_state.files + @output_state.files
186
- end
187
-
188
- def added
189
- @input_state.added + @output_state.added
190
- end
191
-
192
- def removed
193
- @input_state.removed + @output_state.removed
194
- end
195
-
196
- def changed
197
- @input_state.changed + @output_state.changed
198
- end
199
-
200
- def update!
201
- input_changed = @input_state.update!
202
- output_changed = @output_state.update!
203
-
204
- input_changed or output_changed
205
- end
206
-
207
- def intersects?(outputs)
208
- @input_state.intersects?(outputs) or @output_state.intersects?(outputs)
209
- end
210
-
211
- def inspect
212
- "<IOState Input:#{@input_state.inspect} Output:#{@output_state.inspect}>"
213
- end
214
- end
215
-
216
- class Handle
217
- def initialize(monitor, files, &block)
218
- @monitor = monitor
219
- @state = State.new(files)
220
- @on_changed = block
221
- end
222
-
223
- attr :monitor
224
-
225
- def commit!
226
- @state.update!
227
- end
228
-
229
- def directories
230
- @state.files.roots
231
- end
232
-
233
- def remove!
234
- monitor.delete(self)
235
- end
236
-
237
- def changed!
238
- @on_changed.call(@state) if @state.update!
239
- end
240
169
  end
241
170
  end
242
171
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Build
22
22
  module Files
23
- VERSION = "0.3.1"
23
+ VERSION = "0.3.2"
24
24
  end
25
25
  end
@@ -56,5 +56,13 @@ module Build::Files::StateSpec
56
56
  expect(state.removed).to be == []
57
57
  expect(state.missing).to be == []
58
58
  end
59
+
60
+ it "should be clean with empty inputs or outputs" do
61
+ empty = Build::Files::State.new(Build::Files::Paths::NONE)
62
+ something = Build::Files::State.new(files)
63
+
64
+ expect(Build::Files::State.dirty?(empty, something)).to be false
65
+ expect(Build::Files::State.dirty?(something, empty)).to be false
66
+ end
59
67
  end
60
68
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: build-files
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-18 00:00:00.000000000 Z
11
+ date: 2015-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler