build-files 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b40717a33b523317133659681b1ce50c021ac00
4
- data.tar.gz: 61a65ca8c6d12d04a8a72bde0aac31557249e687
3
+ metadata.gz: dea84d68b0394c651e2593a20dc08b7f7631e4b3
4
+ data.tar.gz: 40cf1afa1b7bbfb740b73383529137d8810456c6
5
5
  SHA512:
6
- metadata.gz: e692d35c69a5f5896d8902ad3c4960cf8c3aefa4d9f4c06329c36ada616dd4f4d2a2f3f77cbcdec47ce67a0fc0312746451739d7426b5dc50006233a62af0714
7
- data.tar.gz: ad4623c1d5a3ca483d0f06630b96e715c4da232c9d376918d7a4b1b52cf54ddffd8184eb73c995fa334b84e317e1640e4a842137386873427df1e8be6b0f87b2
6
+ metadata.gz: c7a42fa79f16d31f4376b9dd86ca98ed794e14955b100cb844caead310f2f84c5d0b99e5d0060df9f0d1d7361f8e5d61e46a428adcbda4af4fe96e90c4c05d9c
7
+ data.tar.gz: 87603e85d029f54e8613348034c6846f3ef9a6ef19699fd3680de8b44ff7410d926abe8ef1a45b52de6ff258b329ebcc745c2ef7abc3de1b098fc8edb643f5b7
data/Rakefile CHANGED
@@ -1,9 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
- require "rake/testtask"
2
+ require "rspec/core/rake_task"
3
3
 
4
- Rake::TestTask.new do |t|
5
- t.libs << 'test'
6
- end
4
+ RSpec::Core::RakeTask.new(:spec)
7
5
 
8
- desc "Run tests"
9
- task :default => :test
6
+ task :default => :spec
data/build-files.gemspec CHANGED
@@ -19,6 +19,6 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "minitest", "~> 5.3.2"
22
+ spec.add_development_dependency "rspec", "~> 3.0.0.rc1"
23
23
  spec.add_development_dependency "rake"
24
24
  end
data/lib/build/files.rb CHANGED
@@ -18,9 +18,11 @@
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 'files/paths'
21
+ require_relative 'files/list'
22
22
  require_relative 'files/glob'
23
23
  require_relative 'files/directory'
24
24
 
25
25
  require_relative 'files/state'
26
26
  require_relative 'files/monitor'
27
+
28
+ require_relative 'files/filesystem'
@@ -18,7 +18,7 @@
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 'paths'
21
+ require_relative 'list'
22
22
 
23
23
  module Build
24
24
  module Files
@@ -0,0 +1,38 @@
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
+ # Represents a file path with an absolute root and a relative offset:
24
+ class Path
25
+ def exist?
26
+ File.exist? self
27
+ end
28
+
29
+ def directory?
30
+ File.directory? self
31
+ end
32
+
33
+ def mtime
34
+ File.mtime self
35
+ end
36
+ end
37
+ end
38
+ end
@@ -18,7 +18,7 @@
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 'paths'
21
+ require_relative 'list'
22
22
 
23
23
  module Build
24
24
  module Files
@@ -18,128 +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 'set'
21
+ require_relative 'path'
22
22
 
23
23
  module Build
24
24
  module Files
25
- # Represents a file path with an absolute root and a relative offset:
26
- class Path
27
- def self.relative_path(root, full_path)
28
- relative_offset = root.length
29
-
30
- # Deal with the case where the root may or may not end with the path separator:
31
- relative_offset += 1 unless root.end_with?(File::SEPARATOR)
32
-
33
- return full_path.slice(relative_offset..-1)
34
- end
35
-
36
- # Both paths must be full absolute paths, and path must have root as an prefix.
37
- def initialize(full_path, root = nil)
38
- # This is the object identity:
39
- @full_path = full_path
40
-
41
- if root
42
- @root = root
43
- @relative_path = nil
44
- else
45
- # Effectively dirname and basename:
46
- @root, @relative_path = File.split(full_path)
47
- end
48
- end
49
-
50
- attr :root
51
-
52
- def to_str
53
- @full_path
54
- end
55
-
56
- def to_path
57
- @full_path
58
- end
59
-
60
- def length
61
- @full_path.length
62
- end
63
-
64
- def parts
65
- @parts ||= @full_path.split(File::SEPARATOR)
66
- end
67
-
68
- def relative_path
69
- @relative_path ||= Path.relative_path(@root, @full_path)
70
- end
71
-
72
- def relative_parts
73
- basename, _, filename = self.relative_path.rpartition(File::SEPARATOR)
74
-
75
- return basename, filename
76
- end
77
-
78
- def +(extension)
79
- self.class.new(@full_path + extension, @root)
80
- end
81
-
82
- def rebase(root)
83
- self.class.new(File.join(root, relative_path), root)
84
- end
85
-
86
- def with(root: @root, extension: nil)
87
- self.class.new(File.join(root, extension ? relative_path + extension : relative_path), root)
88
- end
89
-
90
- def self.join(root, relative_path)
91
- self.new(File.join(root, relative_path), root)
92
- end
93
-
94
- def shortest_path(working_directory = Dir.pwd)
95
- if start_with? working_directory
96
- Path.new(working_directory, @full_path)
97
- else
98
- self
99
- end
100
- end
101
-
102
- def to_s
103
- @full_path
104
- end
105
-
106
- def inspect
107
- "#{@root.inspect}/#{relative_path.inspect}"
108
- end
109
-
110
- def hash
111
- @full_path.hash
112
- end
113
-
114
- def eql?(other)
115
- @full_path.eql?(other.to_s)
116
- end
117
-
118
- def ==(other)
119
- self.to_s == other.to_s
120
- end
121
-
122
- def for_reading
123
- [@full_path, File::RDONLY]
124
- end
125
-
126
- def for_writing
127
- [@full_path, File::CREAT|File::TRUNC|File::WRONLY]
128
- end
129
-
130
- def for_appending
131
- [@full_path, File::CREAT|File::APPEND|File::WRONLY]
132
- end
133
- end
134
-
135
- def self.Path(*args)
136
- if Path === args[0]
137
- args[0]
138
- else
139
- Path.new(*args)
140
- end
141
- end
142
-
143
25
  # A list of paths, where #each yields instances of Path.
144
26
  class List
145
27
  include Enumerable
@@ -308,7 +190,7 @@ module Build
308
190
  "<Composite #{@files.inspect}>"
309
191
  end
310
192
  end
311
-
312
- NONE = Composite.new([]).freeze
193
+
194
+ List::NONE = Composite.new([]).freeze
313
195
  end
314
196
  end
@@ -0,0 +1,171 @@
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
+ # Represents a file path with an absolute root and a relative offset:
24
+ class Path
25
+ # Returns the length of the prefix which is shared by two strings.
26
+ def self.prefix_length(a, b)
27
+ [a.size, b.size].min.times{|i| return i if a[i] != b[i]}
28
+ end
29
+
30
+ # Returns a list of components for a path, either represented as a Path instance or a String.
31
+ def self.components(path)
32
+ if Path === path
33
+ path.components
34
+ else
35
+ path.split(File::SEPARATOR)
36
+ end
37
+ end
38
+
39
+ # Return the shortest relative path to get to path from root:
40
+ def self.shortest_path(path, root)
41
+ path_components = Path.components(path)
42
+ root_components = Path.components(root)
43
+
44
+ # Find the common prefix:
45
+ i = prefix_length(path_components, root_components)
46
+
47
+ # The difference between the root path and the required path, taking into account the common prefix:
48
+ up = root_components.size - i
49
+
50
+ return File.join([".."] * up + path_components[i..-1])
51
+ end
52
+
53
+ def self.relative_path(root, full_path)
54
+ relative_offset = root.length
55
+
56
+ # Deal with the case where the root may or may not end with the path separator:
57
+ relative_offset += 1 unless root.end_with?(File::SEPARATOR)
58
+
59
+ return full_path.slice(relative_offset..-1)
60
+ end
61
+
62
+ # Both paths must be full absolute paths, and path must have root as an prefix.
63
+ def initialize(full_path, root = nil, relative_path = nil)
64
+ # This is the object identity:
65
+ @full_path = full_path
66
+
67
+ if root
68
+ @root = root
69
+ @relative_path = relative_path
70
+ else
71
+ # Effectively dirname and basename:
72
+ @root, _, @relative_path = full_path.rpartition(File::SEPARATOR)
73
+ end
74
+ end
75
+
76
+
77
+ def components
78
+ @components ||= @full_path.split(File::SEPARATOR)
79
+ end
80
+
81
+ # Ensure the path has an absolute root if it doesn't already:
82
+ def to_absolute(root)
83
+ if @root == "."
84
+ self.rebase(root)
85
+ else
86
+ self
87
+ end
88
+ end
89
+
90
+ attr :root
91
+
92
+ def to_str
93
+ @full_path
94
+ end
95
+
96
+ def to_path
97
+ @full_path
98
+ end
99
+
100
+ def length
101
+ @full_path.length
102
+ end
103
+
104
+ def parts
105
+ @parts ||= @full_path.split(File::SEPARATOR)
106
+ end
107
+
108
+ def relative_path
109
+ @relative_path ||= Path.relative_path(@root.to_s, @full_path)
110
+ end
111
+
112
+ def relative_parts
113
+ basename, _, filename = self.relative_path.rpartition(File::SEPARATOR)
114
+
115
+ return basename, filename
116
+ end
117
+
118
+ def +(extension)
119
+ self.class.new(@full_path + extension, @root)
120
+ end
121
+
122
+ def rebase(root)
123
+ self.class.new(File.join(root, relative_path), root)
124
+ end
125
+
126
+ def with(root: @root, extension: nil)
127
+ self.class.new(File.join(root, extension ? relative_path + extension : relative_path), root)
128
+ end
129
+
130
+ def self.join(root, relative_path)
131
+ self.new(File.join(root, relative_path), root)
132
+ end
133
+
134
+ def shortest_path(root)
135
+ self.class.shortest_path(self, root)
136
+ end
137
+
138
+ def to_s
139
+ @full_path
140
+ end
141
+
142
+ def inspect
143
+ "#{@root.inspect}/#{relative_path.inspect}"
144
+ end
145
+
146
+ def hash
147
+ @full_path.hash
148
+ end
149
+
150
+ def eql?(other)
151
+ @full_path.eql?(other.to_s)
152
+ end
153
+
154
+ def ==(other)
155
+ self.to_s == other.to_s
156
+ end
157
+
158
+ def for_reading
159
+ [@full_path, File::RDONLY]
160
+ end
161
+
162
+ def for_writing
163
+ [@full_path, File::CREAT|File::TRUNC|File::WRONLY]
164
+ end
165
+
166
+ def for_appending
167
+ [@full_path, File::CREAT|File::APPEND|File::WRONLY]
168
+ end
169
+ end
170
+ end
171
+ end
@@ -35,6 +35,10 @@ module Build
35
35
  def <=> other
36
36
  @time <=> other.time
37
37
  end
38
+
39
+ def inspect
40
+ "<FileTime #{@path.inspect} #{@time.inspect}>"
41
+ end
38
42
  end
39
43
 
40
44
  class State
@@ -97,7 +101,7 @@ module Build
97
101
  # puts "Missing: #{path}"
98
102
  end
99
103
  end
100
-
104
+
101
105
  @removed = last_times.keys
102
106
 
103
107
  @oldest_time = file_times.min
@@ -105,24 +109,28 @@ module Build
105
109
 
106
110
  return @added.size > 0 || @changed.size > 0 || @removed.size > 0 || @missing.size > 0
107
111
  end
108
-
112
+
109
113
  attr :oldest_time
110
114
  attr :newest_time
111
-
115
+
112
116
  attr :missing
113
-
117
+
114
118
  def missing?
115
119
  !@missing.empty?
116
120
  end
117
-
121
+
118
122
  # Outputs is a list of full paths and must not include any patterns/globs.
119
123
  def intersects?(outputs)
120
124
  @files.intersects?(outputs)
121
125
  end
122
-
126
+
123
127
  def empty?
124
128
  @files.to_a.empty?
125
129
  end
130
+
131
+ def inspect
132
+ "<State Added:#{@added} Removed:#{@removed} Changed:#{@changed} Missing:#{@missing}>"
133
+ end
126
134
  end
127
135
 
128
136
  class IOState
@@ -130,69 +138,75 @@ module Build
130
138
  @input_state = State.new(inputs)
131
139
  @output_state = State.new(outputs)
132
140
  end
133
-
141
+
134
142
  attr :input_state
135
143
  attr :output_state
136
-
144
+
137
145
  # Output is dirty if files are missing or if latest input is older than any output.
138
146
  def dirty?
147
+ @dirty = []
148
+
139
149
  if @output_state.missing?
140
150
  # puts "Output file missing: #{output_state.missing.inspect}"
141
-
151
+
142
152
  return true
143
153
  end
144
-
154
+
145
155
  # If there are no inputs, we are always clean as long as outputs exist:
146
156
  # if @input_state.empty?
147
157
  # return false
148
158
  # end
149
-
159
+
150
160
  oldest_output_time = @output_state.oldest_time
151
161
  newest_input_time = @input_state.newest_time
152
-
162
+
153
163
  if newest_input_time and oldest_output_time
154
164
  # if newest_input_time > oldest_output_time
155
165
  # puts "Out of date file: #{newest_input_time.inspect} > #{oldest_output_time.inspect}"
156
166
  # end
157
-
167
+
158
168
  return newest_input_time > oldest_output_time
159
169
  end
160
-
170
+
161
171
  # puts "Missing file dates: #{newest_input_time.inspect} < #{oldest_output_time.inspect}"
162
-
172
+
163
173
  return true
164
174
  end
165
-
175
+
166
176
  def fresh?
167
177
  not dirty?
168
178
  end
169
-
179
+
170
180
  def files
171
181
  @input_state.files + @output_state.files
172
182
  end
173
-
183
+
174
184
  def added
175
185
  @input_state.added + @output_state.added
176
186
  end
177
-
187
+
178
188
  def removed
179
189
  @input_state.removed + @output_state.removed
180
190
  end
181
-
191
+
182
192
  def changed
183
193
  @input_state.changed + @output_state.changed
184
194
  end
185
-
195
+
186
196
  def update!
187
197
  input_changed = @input_state.update!
188
198
  output_changed = @output_state.update!
189
199
 
190
200
  input_changed or output_changed
191
201
  end
192
-
202
+
193
203
  def intersects?(outputs)
194
204
  @input_state.intersects?(outputs) or @output_state.intersects?(outputs)
195
205
  end
206
+
207
+ def inspect
208
+ "<IOState Input:#{@input_state.inspect} Output:#{@output_state.inspect}>"
209
+ end
196
210
  end
197
211
 
198
212
  class Handle
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Build
22
22
  module Files
23
- VERSION = "0.1.0"
23
+ VERSION = "0.2.0"
24
24
  end
25
25
  end
@@ -0,0 +1,118 @@
1
+ # Copyright, 2012, 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 'build/files'
22
+ require 'build/files/list'
23
+ require 'build/files/glob'
24
+
25
+ module Build::Files::ListSpec
26
+ include Build::Files
27
+
28
+ describe Build::Files::Paths do
29
+ let(:path) {Path.new("/foo/bar/baz", "/foo")}
30
+
31
+ it "maps paths with a new extension" do
32
+ paths = Paths.new([
33
+ Path.join('/foo/bar', 'alice'),
34
+ Path.join('/foo/bar', 'bob'),
35
+ Path.join('/foo/bar', 'charles'),
36
+ path
37
+ ])
38
+
39
+ expect(paths).to include(path)
40
+
41
+ expect(paths).to be_intersects(paths)
42
+ expect(paths).to_not be_intersects(Paths::NONE)
43
+
44
+ mapped_paths = paths.map {|path| path + ".o"}
45
+
46
+ expect(mapped_paths).to be_kind_of(Paths)
47
+ expect(mapped_paths.roots).to be == paths.roots
48
+ end
49
+
50
+ it "globs multiple files" do
51
+ glob = Glob.new(__dir__, '*.rb')
52
+
53
+ expect(glob.count).to be > 1
54
+
55
+ mapped_paths = glob.map {|path| path + ".txt"}
56
+
57
+ expect(glob.roots).to be == mapped_paths.roots
58
+ end
59
+
60
+ it "should intersect one file in the glob" do
61
+ # Glob all test files:
62
+ glob = Glob.new(__dir__, "*.rb")
63
+
64
+ expect(glob.count).to be > 0
65
+
66
+ # Should include this file:
67
+ expect(glob).to include(__FILE__)
68
+
69
+ # Glob should intersect self:
70
+ expect(glob).to be_intersects(glob)
71
+ end
72
+
73
+ it "should include composites" do
74
+ lib = File.join(__dir__, "../lib")
75
+
76
+ test_glob = Glob.new(__dir__, "*.rb")
77
+ lib_glob = Glob.new(lib, "*.rb")
78
+
79
+ both = test_glob + lib_glob
80
+
81
+ # List#roots is the generic accessor for Lists
82
+ expect(both.roots).to include test_glob.root
83
+
84
+ # The composite should include both:
85
+ expect(both).to include(__FILE__)
86
+ end
87
+
88
+ it "should have path with correct root" do
89
+ test_glob = Glob.new(__dir__, "*.rb")
90
+
91
+ expect(test_glob.first).to be_kind_of Path
92
+
93
+ expect(test_glob.first.root).to be == __dir__
94
+ end
95
+
96
+ it "maps paths with new extension" do
97
+ glob = Glob.new(__dir__, "*.rb")
98
+
99
+ paths = glob.map {|path| path + ".txt"}
100
+
101
+ expect(paths.first).to be == (glob.first + ".txt")
102
+ end
103
+
104
+ it "should define an empty set of files" do
105
+ expect(Paths::NONE).to be_kind_of List
106
+
107
+ expect(Paths::NONE.count).to be 0
108
+ end
109
+
110
+ it "can be used as key in hash" do
111
+ cache = {}
112
+
113
+ cache[Paths.new(path)] = true
114
+
115
+ expect(cache).to include(Paths.new(path))
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,100 @@
1
+ # Copyright, 2012, 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 'build/files'
22
+ require 'build/files/path'
23
+
24
+ module Build::Files::PathSpec
25
+ include Build::Files
26
+
27
+ describe Build::Files::Path do
28
+ let(:path) {Path.new("/foo/bar/baz", "/foo")}
29
+
30
+ it "should convert to string" do
31
+ # The to_str method should return the full path (i.e. the same as to_s):
32
+ expect(path.to_s).to be == path.to_str
33
+
34
+ # Check the equality operator:
35
+ expect(path).to be == path.dup
36
+
37
+ # The length should be reported correctly:
38
+ expect(path.length).to be == path.to_s.length
39
+
40
+ # Check the return types:
41
+ expect(path).to be_kind_of Path
42
+ expect(path.root).to be_kind_of String
43
+ expect(path.relative_path).to be_kind_of String
44
+ end
45
+
46
+ it "should consist of parts" do
47
+ expect(path.parts).to be == ["", "foo", "bar", "baz"]
48
+
49
+ expect(path.root).to be == "/foo"
50
+
51
+ expect(path.relative_path).to be == "bar/baz"
52
+
53
+ expect(path.relative_parts).to be == ["bar", "baz"]
54
+ end
55
+
56
+ it "should have a new extension" do
57
+ renamed_path = path.with(root: '/tmp', extension: '.txt')
58
+
59
+ expect(renamed_path.root).to be == '/tmp'
60
+
61
+ expect(renamed_path.relative_path).to be == 'bar/baz.txt'
62
+
63
+ object_path = path + ".o"
64
+
65
+ expect(object_path.root).to be == "/foo"
66
+ expect(object_path.relative_path).to be == "bar/baz.o"
67
+ end
68
+
69
+ it "should give the shortest path for outer paths" do
70
+ spec_path = Path.new(__FILE__)
71
+ source_path = Path.new(File.expand_path("../../../lib/build/files/list.rb"))
72
+
73
+ input = Path.new("/a/b/c/file.cpp")
74
+ output = Path.new("/a/b/c/d/e/")
75
+
76
+ expect(input.root).to be == "/a/b/c"
77
+ expect(output.root).to be == "/a/b/c/d/e"
78
+
79
+ short = input.shortest_path(output)
80
+
81
+ expect(short).to be == "../../file.cpp"
82
+
83
+ expect(File.expand_path(short, output)).to be == input
84
+ end
85
+
86
+ it "should give the shortest path for inner paths" do
87
+ input = Path.new("/a/b/c/file.cpp")
88
+ output = Path.new("/a/")
89
+
90
+ expect(input.root).to be == "/a/b/c"
91
+ expect(output.root).to be == "/a"
92
+
93
+ short = input.shortest_path(output)
94
+
95
+ expect(short).to be == "b/c/file.cpp"
96
+
97
+ expect(File.expand_path(short, output)).to be == input
98
+ end
99
+ end
100
+ end
@@ -18,44 +18,43 @@
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 'minitest/autorun'
22
-
23
21
  require 'build/files'
24
22
 
25
- class TestState < MiniTest::Test
26
- include Build::Files
27
-
28
- def setup
29
- @files = Glob.new(__dir__, "*.rb")
30
- end
31
-
32
- def test_basic_update
33
- state = State.new(@files)
34
-
35
- refute state.update!, "Files not changed"
23
+ module Build::Files::StateSpec
24
+ describe Build::Files::State do
25
+ let(:files) {Build::Files::Glob.new(__dir__, "*.rb")}
36
26
 
37
- assert_equal [], state.changed
38
- assert_equal [], state.added
39
- assert_equal [], state.removed
40
- assert_equal [], state.missing
41
- end
42
-
43
- def test_missing
44
- files = @files.to_paths.rebase(File.join(__dir__, 'program'))
45
- state = State.new(files)
46
-
47
- assert state.update!, "Files missing"
48
- refute_empty state.missing
49
- end
50
-
51
- def test_duplicates
52
- state = State.new(@files + @files)
27
+ it "should have no changes initially" do
28
+ state = Build::Files::State.new(files)
29
+
30
+ expect(state.update!).to be false
31
+
32
+ expect(state.changed).to be == []
33
+ expect(state.added).to be == []
34
+ expect(state.removed).to be == []
35
+ expect(state.missing).to be == []
36
+ end
53
37
 
54
- refute state.update!, "Files not changed"
38
+ it "should report missing files" do
39
+ rebased_files = files.to_paths.rebase(File.join(__dir__, 'foo'))
40
+ state = Build::Files::State.new(rebased_files)
41
+
42
+ # Some changes were detected:
43
+ expect(state.update!).to be true
44
+
45
+ # Some files are missing:
46
+ expect(state.missing).to_not be_empty
47
+ end
55
48
 
56
- assert_equal [], state.changed
57
- assert_equal [], state.added
58
- assert_equal [], state.removed
59
- assert_equal [], state.missing
49
+ it "should not be confused by duplicates" do
50
+ state = Build::Files::State.new(files + files)
51
+
52
+ expect(state.update!).to be false
53
+
54
+ expect(state.changed).to be == []
55
+ expect(state.added).to be == []
56
+ expect(state.removed).to be == []
57
+ expect(state.missing).to be == []
58
+ end
60
59
  end
61
60
  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.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-18 00:00:00.000000000 Z
11
+ date: 2014-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
- name: minitest
28
+ name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 5.3.2
33
+ version: 3.0.0.rc1
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 5.3.2
40
+ version: 3.0.0.rc1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,14 +66,16 @@ files:
66
66
  - build-files.gemspec
67
67
  - lib/build/files.rb
68
68
  - lib/build/files/directory.rb
69
+ - lib/build/files/filesystem.rb
69
70
  - lib/build/files/glob.rb
71
+ - lib/build/files/list.rb
70
72
  - lib/build/files/monitor.rb
71
- - lib/build/files/paths.rb
73
+ - lib/build/files/path.rb
72
74
  - lib/build/files/state.rb
73
75
  - lib/build/files/version.rb
74
- - test/test_files.rb
75
- - test/test_paths.rb
76
- - test/test_state.rb
76
+ - spec/build/files/list_spec.rb
77
+ - spec/build/files/path_spec.rb
78
+ - spec/build/files/state_spec.rb
77
79
  homepage: ''
78
80
  licenses:
79
81
  - MIT
@@ -100,6 +102,6 @@ specification_version: 4
100
102
  summary: Build::Files is a set of idiomatic classes for dealing with paths and monitoring
101
103
  directories.
102
104
  test_files:
103
- - test/test_files.rb
104
- - test/test_paths.rb
105
- - test/test_state.rb
105
+ - spec/build/files/list_spec.rb
106
+ - spec/build/files/path_spec.rb
107
+ - spec/build/files/state_spec.rb
data/test/test_files.rb DELETED
@@ -1,75 +0,0 @@
1
- # Copyright, 2012, 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 'minitest/autorun'
22
-
23
- require 'build/files'
24
-
25
- class TestFiles < MiniTest::Test
26
- include Build::Files
27
-
28
- def test_inclusion
29
- # Glob all test files:
30
- glob = Glob.new(__dir__, "*.rb")
31
-
32
- assert glob.count > 0
33
-
34
- # Should include this file:
35
- assert_includes glob, __FILE__
36
-
37
- # Glob should intersect self:
38
- assert glob.intersects?(glob)
39
- end
40
-
41
- def test_composites
42
- lib = File.join(__dir__, "../lib")
43
-
44
- test_glob = Glob.new(__dir__, "*.rb")
45
- lib_glob = Glob.new(lib, "*.rb")
46
-
47
- both = test_glob + lib_glob
48
-
49
- # List#roots is the generic accessor for Lists
50
- assert both.roots.include? test_glob.root
51
-
52
- # The composite should include both:
53
- assert both.include?(__FILE__)
54
- end
55
-
56
- def test_roots
57
- test_glob = Glob.new(__dir__, "*.rb")
58
-
59
- assert_kind_of Path, test_glob.first
60
-
61
- assert_equal __dir__, test_glob.first.root
62
- end
63
-
64
- def test_renaming
65
- glob = Glob.new(__dir__, "*.rb")
66
-
67
- paths = glob.map {|path| path + ".txt"}
68
-
69
- assert_equal(paths.first, glob.first + ".txt")
70
- end
71
-
72
- def test_none
73
- assert_equal 0, NONE.count
74
- end
75
- end
data/test/test_paths.rb DELETED
@@ -1,111 +0,0 @@
1
- # Copyright, 2012, 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 'minitest/autorun'
22
-
23
- require 'build/files/paths'
24
- require 'build/files/glob'
25
-
26
- class TestPaths < MiniTest::Test
27
- include Build::Files
28
-
29
- def setup
30
- @path = Path.new("/foo/bar/baz", "/foo")
31
- end
32
-
33
- def test_path_conversions
34
- # The to_str method should return the full path (i.e. the same as to_s):
35
- assert_equal @path.to_s, @path.to_str
36
-
37
- # Checkt the equality operator:
38
- assert_equal @path, @path.dup
39
-
40
- # The length should be reported correctly:
41
- assert_equal @path.length, @path.to_s.length
42
- end
43
-
44
- def test_path_parts
45
- assert_equal ["", "foo", "bar", "baz"], @path.parts
46
-
47
- assert_equal "/foo", @path.root
48
-
49
- assert_equal "bar/baz", @path.relative_path
50
-
51
- assert_equal ["bar", "baz"], @path.relative_parts
52
- end
53
-
54
- def test_path_with
55
- path = @path.with(root: '/tmp', extension: '.txt')
56
-
57
- assert_equal '/tmp', path.root
58
-
59
- assert_equal 'bar/baz.txt', path.relative_path
60
- end
61
-
62
- def test_path_class
63
- assert_instance_of Path, @path
64
- assert_instance_of String, @path.root
65
- assert_instance_of String, @path.relative_path
66
- end
67
-
68
- def test_path_manipulation
69
- object_path = @path + ".o"
70
-
71
- assert_equal "/foo", object_path.root
72
- assert_equal "bar/baz.o", object_path.relative_path
73
- end
74
-
75
- def test_paths
76
- paths = Paths.new([
77
- Path.join('/foo/bar', 'alice'),
78
- Path.join('/foo/bar', 'bob'),
79
- Path.join('/foo/bar', 'charles'),
80
- @path
81
- ])
82
-
83
- assert_includes paths, @path
84
-
85
- assert paths.intersects?(paths)
86
- refute paths.intersects?(NONE)
87
-
88
- mapped_paths = paths.map {|path| path + ".o"}
89
-
90
- assert_instance_of Paths, mapped_paths
91
- assert_equal paths.roots, mapped_paths.roots
92
- end
93
-
94
- def test_glob
95
- glob = Glob.new(__dir__, '*.rb')
96
-
97
- assert glob.count > 0, "Found some files"
98
-
99
- mapped_paths = glob.map {|path| path + ".txt"}
100
-
101
- assert_equal glob.roots, mapped_paths.roots
102
- end
103
-
104
- def test_hashing
105
- cache = {}
106
-
107
- cache[Paths.new(@path)] = true
108
-
109
- assert cache[Paths.new(@path)]
110
- end
111
- end