build-files 1.4.3 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
data/build-files.gemspec DELETED
@@ -1,28 +0,0 @@
1
-
2
- require_relative 'lib/build/files/version'
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "build-files"
6
- spec.version = Build::Files::VERSION
7
- spec.authors = ["Samuel Williams"]
8
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
- # spec.description = %q{}
10
- spec.summary = %q{Build::Files is a set of idiomatic classes for dealing with paths and monitoring directories.}
11
- spec.homepage = ""
12
- spec.license = "MIT"
13
-
14
- spec.files = `git ls-files`.split($/)
15
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
- spec.require_paths = ["lib"]
18
-
19
- spec.required_ruby_version = '>= 2.0'
20
-
21
- spec.add_dependency "rb-inotify"
22
- spec.add_dependency "rb-fsevent"
23
-
24
- spec.add_development_dependency "covered"
25
- spec.add_development_dependency "bundler"
26
- spec.add_development_dependency "rspec", "~> 3.4"
27
- spec.add_development_dependency "rake"
28
- end
@@ -1,59 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative 'state'
22
-
23
- module Build
24
- module Files
25
- class Handle
26
- def initialize(monitor, files, &block)
27
- @monitor = monitor
28
- @state = State.new(files)
29
- @block = block
30
- end
31
-
32
- attr :monitor
33
-
34
- def commit!
35
- @state.update!
36
- end
37
-
38
- def directories
39
- @state.files.roots
40
- end
41
-
42
- def remove!
43
- @monitor.delete(self)
44
- end
45
-
46
- # Inform the handle that it might have been modified.
47
- def changed!
48
- # If @state.update! did not find any changes, don't invoke the callback:
49
- if @state.update!
50
- @block.call(@state)
51
- end
52
- end
53
-
54
- def to_s
55
- "\#<#{self.class} @state=#{@state} @block=#{@block}>"
56
- end
57
- end
58
- end
59
- end
@@ -1,43 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- module Build
22
- module Files
23
- module Monitor
24
- case RUBY_PLATFORM
25
- when /linux/i
26
- require_relative 'monitor/inotify'
27
- Native = INotify
28
- Default = Native
29
- when /darwin/i
30
- require_relative 'monitor/fsevent'
31
- Native = FSEvent
32
- Default = Native
33
- else
34
- require_relative 'monitor/polling'
35
- Default = Polling
36
- end
37
-
38
- def self.new(*args)
39
- Default.new(*args)
40
- end
41
- end
42
- end
43
- end
@@ -1,55 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative 'polling'
22
-
23
- require 'rb-fsevent'
24
-
25
- module Build
26
- module Files
27
- module Monitor
28
- class FSEvent < Polling
29
- def run(**options, &block)
30
- notifier = ::FSEvent.new
31
-
32
- catch(:interrupt) do
33
- while true
34
- notifier.watch self.roots do |directories|
35
- directories.collect! do |directory|
36
- File.expand_path(directory)
37
- end
38
-
39
- self.update(directories)
40
-
41
- yield
42
-
43
- if self.updated
44
- notifier.stop
45
- end
46
- end
47
-
48
- notifier.run
49
- end
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,53 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative 'polling'
22
-
23
- require 'rb-inotify'
24
-
25
- module Build
26
- module Files
27
- module Monitor
28
- class INotify < Polling
29
- def run(**options, &block)
30
- notifier = ::INotify::Notifier.new
31
-
32
- catch(:interrupt) do
33
- while true
34
- self.roots.each do |root|
35
- notifier.watch root, :create, :modify, :attrib, :delete do |event|
36
- self.update([root])
37
-
38
- yield
39
-
40
- if self.updated
41
- notifier.stop
42
- end
43
- end
44
- end
45
-
46
- notifier.run
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,145 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'set'
22
- require 'logger'
23
-
24
- require_relative '../handle'
25
-
26
- module Build
27
- module Files
28
- module Monitor
29
- class Polling
30
- def initialize(logger: nil)
31
- @directories = Hash.new do |hash, key|
32
- hash[key] = Set.new
33
- end
34
-
35
- @updated = false
36
-
37
- @deletions = nil
38
-
39
- @logger = logger || Logger.new(nil)
40
- end
41
-
42
- attr :updated
43
-
44
- # Notify the monitor that files in these directories have changed.
45
- def update(directories, *args)
46
- @logger.debug{"Update: #{directories} #{args.inspect}"}
47
-
48
- delay_deletions do
49
- directories.each do |directory|
50
- @logger.debug{"Directory: #{directory}"}
51
-
52
- @directories[directory].each do |handle|
53
- @logger.debug{"Handle changed: #{handle.inspect}"}
54
-
55
- # Changes here may not actually require an update to the handle:
56
- handle.changed!(*args)
57
- end
58
- end
59
- end
60
- end
61
-
62
- def roots
63
- @directories.keys
64
- end
65
-
66
- def delete(handle)
67
- if @deletions
68
- @logger.debug{"Delayed delete handle: #{handle}"}
69
- @deletions << handle
70
- else
71
- purge(handle)
72
- end
73
- end
74
-
75
- def track_changes(files, &block)
76
- handle = Handle.new(self, files, &block)
77
-
78
- add(handle)
79
- end
80
-
81
- def add(handle)
82
- @logger.debug{"Adding handle: #{handle}"}
83
-
84
- handle.directories.each do |directory|
85
- # We want the full path as a plain string:
86
- directory = directory.to_s
87
-
88
- @directories[directory] << handle
89
-
90
- # We just added the first handle:
91
- if @directories[directory].size == 1
92
- # If the handle already existed, this might trigger unnecessarily.
93
- @updated = true
94
- end
95
- end
96
-
97
- handle
98
- end
99
-
100
- def run(**options, &block)
101
- catch(:interrupt) do
102
- while true
103
- monitor.update(monitor.roots)
104
-
105
- yield
106
-
107
- sleep(options[:latency] || 1.0)
108
- end
109
- end
110
- end
111
-
112
- protected
113
-
114
- def delay_deletions
115
- @deletions = []
116
-
117
- yield
118
-
119
- @deletions.each do |handle|
120
- purge(handle)
121
- end
122
-
123
- @deletions = nil
124
- end
125
-
126
- def purge(handle)
127
- @logger.debug{"Purge handle: #{handle}"}
128
-
129
- handle.directories.each do |directory|
130
- directory = directory.to_s
131
-
132
- @directories[directory].delete(handle)
133
-
134
- # Remove the entire record if there are no handles:
135
- if @directories[directory].size == 0
136
- @directories.delete(directory)
137
-
138
- @updated = true
139
- end
140
- end
141
- end
142
- end
143
- end
144
- end
145
- end
@@ -1,172 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require_relative 'list'
22
-
23
- require 'forwardable'
24
-
25
- module Build
26
- module Files
27
- # Represents a specific file on disk with a specific mtime.
28
- class FileTime
29
- include Comparable
30
-
31
- def initialize(path, time)
32
- @path = path
33
- @time = time
34
- end
35
-
36
- attr :path
37
- attr :time
38
-
39
- def <=> other
40
- @time <=> other.time
41
- end
42
-
43
- def inspect
44
- "<FileTime #{@path.inspect} #{@time.inspect}>"
45
- end
46
- end
47
-
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
-
52
- def initialize(files)
53
- raise ArgumentError.new("Invalid files list: #{files}") unless Files::List === files
54
-
55
- @files = files
56
-
57
- @times = {}
58
-
59
- update!
60
- end
61
-
62
- attr :files
63
-
64
- attr :added
65
- attr :removed
66
- attr :changed
67
- attr :missing
68
-
69
- attr :times
70
-
71
- def_delegators :@files, :each, :roots, :count
72
-
73
- def update!
74
- last_times = @times
75
- @times = {}
76
-
77
- @added = []
78
- @removed = []
79
- @changed = []
80
- @missing = []
81
-
82
- file_times = []
83
-
84
- @files.each do |path|
85
- # When processing the same path twice (perhaps by accident), we should skip it otherwise it might cause issues when being deleted from last_times multuple times.
86
- next if @times.include? path
87
-
88
- if File.exist?(path)
89
- modified_time = File.mtime(path)
90
-
91
- if last_time = last_times.delete(path)
92
- # Path was valid last update:
93
- if modified_time != last_time
94
- @changed << path
95
-
96
- # puts "Changed: #{path}"
97
- end
98
- else
99
- # Path didn't exist before:
100
- @added << path
101
-
102
- # puts "Added: #{path}"
103
- end
104
-
105
- @times[path] = modified_time
106
-
107
- unless File.directory?(path)
108
- file_times << FileTime.new(path, modified_time)
109
- end
110
- else
111
- @missing << path
112
-
113
- # puts "Missing: #{path}"
114
- end
115
- end
116
-
117
- @removed = last_times.keys
118
- # puts "Removed: #{@removed.inspect}" if @removed.size > 0
119
-
120
- @oldest_time = file_times.min
121
- @newest_time = file_times.max
122
-
123
- return @added.size > 0 || @changed.size > 0 || @removed.size > 0 || @missing.size > 0
124
- end
125
-
126
- attr :oldest_time
127
- attr :newest_time
128
-
129
- def missing?
130
- !@missing.empty?
131
- end
132
-
133
- def empty?
134
- @times.empty?
135
- end
136
-
137
- def inspect
138
- "<State Added:#{@added} Removed:#{@removed} Changed:#{@changed} Missing:#{@missing}>"
139
- end
140
-
141
- # Are these (output) files dirty with respect to the given inputs?
142
- def dirty?(inputs)
143
- if self.missing?
144
- return true
145
- end
146
-
147
- # If there are no inputs or no outputs, we are always clean:
148
- if inputs.empty? or self.empty?
149
- return false
150
- end
151
-
152
- oldest_output_time = self.oldest_time
153
- newest_input_time = inputs.newest_time
154
-
155
- if newest_input_time and oldest_output_time
156
- # We are dirty if any inputs are newer (bigger) than any outputs:
157
- if newest_input_time > oldest_output_time
158
- return true
159
- else
160
- return false
161
- end
162
- end
163
-
164
- return true
165
- end
166
-
167
- def self.dirty?(inputs, outputs)
168
- outputs.dirty?(inputs)
169
- end
170
- end
171
- end
172
- end