typingpool 0.7.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.
- data/Rakefile +23 -0
- data/bin/tp-assign +240 -0
- data/bin/tp-collect +50 -0
- data/bin/tp-config +114 -0
- data/bin/tp-finish +101 -0
- data/bin/tp-make +169 -0
- data/bin/tp-review +175 -0
- data/lib/typingpool/amazon.rb +732 -0
- data/lib/typingpool/app.rb +634 -0
- data/lib/typingpool/config.rb +344 -0
- data/lib/typingpool/error.rb +22 -0
- data/lib/typingpool/filer.rb +396 -0
- data/lib/typingpool/project.rb +593 -0
- data/lib/typingpool/template.rb +175 -0
- data/lib/typingpool/templates/assignment/amazon-init.js +38 -0
- data/lib/typingpool/templates/assignment/interview/nameless.html.erb +13 -0
- data/lib/typingpool/templates/assignment/interview/noisy.html.erb +12 -0
- data/lib/typingpool/templates/assignment/interview/partials/voices.html.erb +10 -0
- data/lib/typingpool/templates/assignment/interview/phone.html.erb +12 -0
- data/lib/typingpool/templates/assignment/interview.html.erb +11 -0
- data/lib/typingpool/templates/assignment/main.css +20 -0
- data/lib/typingpool/templates/assignment/partials/entry.html.erb +19 -0
- data/lib/typingpool/templates/assignment/partials/footer.html.erb +3 -0
- data/lib/typingpool/templates/assignment/partials/header.html.erb +11 -0
- data/lib/typingpool/templates/assignment/partials/labeling-example.html.erb +4 -0
- data/lib/typingpool/templates/assignment/partials/labeling.html.erb +5 -0
- data/lib/typingpool/templates/assignment/partials/length-description.html.erb +6 -0
- data/lib/typingpool/templates/assignment/partials/voices.html.erb +10 -0
- data/lib/typingpool/templates/assignment/speech.html.erb +11 -0
- data/lib/typingpool/templates/config.yml +21 -0
- data/lib/typingpool/templates/project/audio/chunks/.empty_directory +0 -0
- data/lib/typingpool/templates/project/audio/originals/.empty_directory +0 -0
- data/lib/typingpool/templates/project/data/.empty_directory +0 -0
- data/lib/typingpool/templates/project/etc/ About these files - read me.txt +8 -0
- data/lib/typingpool/templates/project/etc/audio-compat.js +25 -0
- data/lib/typingpool/templates/project/etc/player/audio-player.js +4 -0
- data/lib/typingpool/templates/project/etc/player/license.txt +19 -0
- data/lib/typingpool/templates/project/etc/player/player.swf +0 -0
- data/lib/typingpool/templates/project/etc/transcript.css +49 -0
- data/lib/typingpool/templates/transcript.html.erb +23 -0
- data/lib/typingpool/test/fixtures/amazon-question-html.html +95 -0
- data/lib/typingpool/test/fixtures/amazon-question-url.txt +1 -0
- data/lib/typingpool/test/fixtures/audio/mp3/interview.1.mp3 +0 -0
- data/lib/typingpool/test/fixtures/audio/mp3/interview.2.mp3 +0 -0
- data/lib/typingpool/test/fixtures/audio/wma/VN620007.WMA +0 -0
- data/lib/typingpool/test/fixtures/audio/wma/VN620052.WMA +0 -0
- data/lib/typingpool/test/fixtures/config-1 +20 -0
- data/lib/typingpool/test/fixtures/config-2 +25 -0
- data/lib/typingpool/test/fixtures/not_yaml.txt +4 -0
- data/lib/typingpool/test/fixtures/template-2.html.erb +10 -0
- data/lib/typingpool/test/fixtures/template-3.html.erb +22 -0
- data/lib/typingpool/test/fixtures/template.html.erb +10 -0
- data/lib/typingpool/test/fixtures/tp_collect_id.txt +1 -0
- data/lib/typingpool/test/fixtures/tp_collect_sandbox-assignment.csv +8 -0
- data/lib/typingpool/test/fixtures/tp_review_id.txt +1 -0
- data/lib/typingpool/test/fixtures/tp_review_sandbox-assignment.csv +8 -0
- data/lib/typingpool/test/fixtures/transcript-chunks.csv +226 -0
- data/lib/typingpool/test/fixtures/utf8_transcript.txt +7 -0
- data/lib/typingpool/test/fixtures/vcr/tp-collect-1.yml +2712 -0
- data/lib/typingpool/test/fixtures/vcr/tp-collect-2.yml +2718 -0
- data/lib/typingpool/test/fixtures/vcr/tp-collect-3.yml +2768 -0
- data/lib/typingpool/test/fixtures/vcr/tp-review-1.yml +570 -0
- data/lib/typingpool/test/fixtures/vcr/tp-review-2.yml +351 -0
- data/lib/typingpool/test.rb +418 -0
- data/lib/typingpool/transcript.rb +181 -0
- data/lib/typingpool/utility.rb +272 -0
- data/lib/typingpool.rb +500 -0
- data/test/make_amazon_question_fixture.rb +24 -0
- data/test/make_tp_collect_fixture_1.rb +26 -0
- data/test/make_tp_collect_fixture_2.rb +16 -0
- data/test/make_tp_collect_fixture_3.rb +15 -0
- data/test/make_tp_collect_fixture_4.rb +17 -0
- data/test/make_tp_review_fixture_1.rb +26 -0
- data/test/make_tp_review_fixture_2.rb +30 -0
- data/test/make_transcript_chunks_fixture.rb +53 -0
- data/test/test_integration_script_1_tp_config.rb +108 -0
- data/test/test_integration_script_2_tp_make.rb +119 -0
- data/test/test_integration_script_3_tp_assign.rb +152 -0
- data/test/test_integration_script_4_tp_review.rb +72 -0
- data/test/test_integration_script_5_tp_collect.rb +44 -0
- data/test/test_integration_script_6_tp_finish.rb +123 -0
- data/test/test_unit_amazon.rb +153 -0
- data/test/test_unit_config.rb +94 -0
- data/test/test_unit_filer.rb +202 -0
- data/test/test_unit_project.rb +168 -0
- data/test/test_unit_project_local.rb +68 -0
- data/test/test_unit_project_remote.rb +157 -0
- data/test/test_unit_template.rb +111 -0
- data/test/test_unit_transcript.rb +77 -0
- metadata +234 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
module Typingpool
|
|
2
|
+
|
|
3
|
+
#Convenience wrapper for basic file operations. Base class for
|
|
4
|
+
#wrappers for specialized file types (audio, CSV) and for file
|
|
5
|
+
#collections.
|
|
6
|
+
class Filer
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
include Utility::Castable
|
|
9
|
+
include Comparable
|
|
10
|
+
|
|
11
|
+
#Fully-expanded path to file
|
|
12
|
+
attr_reader :path
|
|
13
|
+
|
|
14
|
+
#Constructor.
|
|
15
|
+
# ==== Params
|
|
16
|
+
#[path] Fully expanded path to file.
|
|
17
|
+
#[encoding] Optional. Encoding for all text operations on the
|
|
18
|
+
# file. Should be compatiable with :encoding arg to
|
|
19
|
+
# IO.read. Default is 'UTF-8'.
|
|
20
|
+
def initialize(path, encoding='UTF-8')
|
|
21
|
+
@path = path
|
|
22
|
+
@encoding = encoding
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def <=>(other)
|
|
26
|
+
path <=> other.path
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#Returns contents of file or nil if the file does not exist.
|
|
30
|
+
def read
|
|
31
|
+
if File.exists? @path
|
|
32
|
+
IO.read(@path, :encoding => @encoding)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#Write data to the file.
|
|
37
|
+
def write(data, mode='w')
|
|
38
|
+
File.open(@path, mode, :encoding => @encoding) do |out|
|
|
39
|
+
out << data
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
#Moves the underlying file AND updates the @path of the Filer instance.
|
|
44
|
+
def mv!(to)
|
|
45
|
+
FileUtils.mv(@path, to)
|
|
46
|
+
if File.directory? to
|
|
47
|
+
to = File.join(to, File.basename(path))
|
|
48
|
+
end
|
|
49
|
+
@path = to
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#Returns the underlying file as an IO stream. Convenient for
|
|
53
|
+
#Project::Remote#put.
|
|
54
|
+
def to_stream(mode='r')
|
|
55
|
+
File.new(@path, mode, :encoding => @encoding)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
#Filer objects always stringify to their path. We might change
|
|
59
|
+
#this later such that to_str gives the path but to_s gives the
|
|
60
|
+
#content of the file as text.
|
|
61
|
+
def to_s
|
|
62
|
+
@path
|
|
63
|
+
end
|
|
64
|
+
alias :to_str :to_s
|
|
65
|
+
|
|
66
|
+
#Returns the parent dir of the underlying file as a Filer::Dir
|
|
67
|
+
#instance.
|
|
68
|
+
def dir
|
|
69
|
+
Filer::Dir.new(File.dirname(@path))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#Cast this file into a new Filer subtype, e.g. Filer::Audio.
|
|
73
|
+
# ==== Params
|
|
74
|
+
# [sym] Symbol corresponding to Filer subclass to cast into. For
|
|
75
|
+
# example, passing :audio will cast into a Filer::Audio.
|
|
76
|
+
# ==== Returns
|
|
77
|
+
# Instance of new Filer subclass
|
|
78
|
+
def as(sym)
|
|
79
|
+
#super calls into Utility::Castable mixin
|
|
80
|
+
super(sym, @path)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
#Convenience wrapper for CSV files. Makes them Enumerable, so you
|
|
85
|
+
#can iterate through rows with each, map, select, etc. You can
|
|
86
|
+
#also modify in place with each!. See Filer base class for other
|
|
87
|
+
#methods.
|
|
88
|
+
class CSV < Filer
|
|
89
|
+
include Enumerable
|
|
90
|
+
require 'csv'
|
|
91
|
+
|
|
92
|
+
#Reads into an array of hashes, with hash keys determined by the
|
|
93
|
+
#first row of the CSV file. Parsing rules are the default for
|
|
94
|
+
#CSV.parse.
|
|
95
|
+
def read
|
|
96
|
+
raw = super or return []
|
|
97
|
+
rows = ::CSV.parse(raw.to_s)
|
|
98
|
+
headers = rows.shift or raise Error::File, "No CSV at #{@path}"
|
|
99
|
+
rows.map{|row| Utility.array_to_hash(row, headers) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
#Takes array of hashes followed by optional list of keys (by
|
|
103
|
+
#default keys are determined by looking at all the
|
|
104
|
+
#hashes). Lines are written per the defaults of
|
|
105
|
+
#CSV.generate_line.
|
|
106
|
+
def write(hashes, headers=hashes.map{|h| h.keys}.flatten.uniq)
|
|
107
|
+
super(
|
|
108
|
+
::CSV.generate_line(headers, :encoding => @encoding) +
|
|
109
|
+
hashes.map do |hash|
|
|
110
|
+
::CSV.generate_line(headers.map{|header| hash[header] }, :encoding => @encoding)
|
|
111
|
+
end.join
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#Takes an array of arrays, corresponding to the rows, and a list
|
|
116
|
+
#of headers/keys to write at the top.
|
|
117
|
+
def write_arrays(arrays, headers)
|
|
118
|
+
write(arrays.map{|array| Utility.array_to_hash(array, headers) }, headers)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
#Enumerate through the rows, with each row represented by a
|
|
122
|
+
#hash.
|
|
123
|
+
def each
|
|
124
|
+
read.each do |row|
|
|
125
|
+
yield row
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
#Same as each, but any changes to the rows will be written back
|
|
130
|
+
#out to the underlying CSV file.
|
|
131
|
+
def each!
|
|
132
|
+
#each_with_index doesn't return the array, so we have to use each
|
|
133
|
+
write(each{|hash| yield(hash) })
|
|
134
|
+
end
|
|
135
|
+
end #CSV
|
|
136
|
+
|
|
137
|
+
#Convenience wrapper for audio files.You can convert to mp3s,
|
|
138
|
+
#split into multiple files, and dynamically read the bitrate.
|
|
139
|
+
class Audio < Filer
|
|
140
|
+
require 'open3'
|
|
141
|
+
|
|
142
|
+
#Does the file have a '.mp3' extension?
|
|
143
|
+
def mp3?
|
|
144
|
+
File.extname(@path).downcase.eql?('.mp3')
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
#Convert to mp3 via ffmpeg.
|
|
148
|
+
# ==== Params
|
|
149
|
+
# [dest] Filer object corresponding to the path the mp3 version
|
|
150
|
+
# should end up at.
|
|
151
|
+
# [bitrate] If passed, bitrate should be an integer
|
|
152
|
+
# corresponding to kb/s. If not, we use the bitrate
|
|
153
|
+
# from the current file or, if that can't be read,
|
|
154
|
+
# default to 192kbps. Does not check if the file is
|
|
155
|
+
# already an mp3. Returns a new Filer::Audio
|
|
156
|
+
# representing the new mp3 file.
|
|
157
|
+
# ==== Returns
|
|
158
|
+
# Filer::Audio containing the new mp3.
|
|
159
|
+
def to_mp3(dest=self.dir.file("#{File.basename(@path, '.*') }.mp3"), bitrate=nil)
|
|
160
|
+
bitrate ||= self.bitrate || 192
|
|
161
|
+
Utility.system_quietly('ffmpeg', '-i', @path, '-acodec', 'libmp3lame', '-ab', "#{bitrate}k", '-ac', '2', dest)
|
|
162
|
+
File.exists?(dest) or raise Error::Shell, "Could not found output from `ffmpeg` on #{path}"
|
|
163
|
+
self.class.new(dest.path)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
#Reads the bitrate of the audio file via ffmpeg. Returns an
|
|
167
|
+
#integer corresponding to kb/s, or nil if the bitrate could not
|
|
168
|
+
#be determined.
|
|
169
|
+
def bitrate
|
|
170
|
+
out, err, status = Open3.capture3('ffmpeg', '-i', @path)
|
|
171
|
+
bitrate = err.match(/(\d+) kb\/s/)
|
|
172
|
+
return bitrate ? bitrate[1].to_i : nil
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
#Splits an mp3 into smaller files.
|
|
176
|
+
# ==== Params
|
|
177
|
+
# [interval_in_min_dot_seconds] Split the file into chunks this
|
|
178
|
+
# large. The interval should be of the format
|
|
179
|
+
# minute.seconds, for example 2 minutes 15 seconds
|
|
180
|
+
# would be written as "2.15". For further details on
|
|
181
|
+
# interval format, consult the documentation for
|
|
182
|
+
# mp3split, a command-line unix utility.
|
|
183
|
+
# [basename] Name the new chunks using this base. Default is the
|
|
184
|
+
# basename of the original file.
|
|
185
|
+
# [dest] Destination directory for the new chunks as a
|
|
186
|
+
# Filer::Dir. Default is the same directory as the
|
|
187
|
+
# original file.
|
|
188
|
+
# ==== Returns
|
|
189
|
+
# Filer::Files containing the new files.
|
|
190
|
+
def split(interval_in_min_dot_seconds, basename=File.basename(path, '.*'), dest=dir)
|
|
191
|
+
#We have to cd into the wrapfile directory and do everything
|
|
192
|
+
#there because old/packaged versions of mp3splt were
|
|
193
|
+
#retarded at handling absolute directory paths
|
|
194
|
+
::Dir.chdir(dir.path) do
|
|
195
|
+
Utility.system_quietly('mp3splt', '-t', interval_in_min_dot_seconds, '-o', "#{basename}.@m.@s", File.basename(path))
|
|
196
|
+
end
|
|
197
|
+
files = Filer::Files::Audio.new(dir.select{|file| File.basename(file.path).match(/^#{Regexp.escape(basename) }\.\d+\.\d+\.mp3$/) })
|
|
198
|
+
if files.to_a.empty?
|
|
199
|
+
raise Error::Shell, "Could not find output from `mp3splt` on #{path}"
|
|
200
|
+
end
|
|
201
|
+
if dest.path != dir.path
|
|
202
|
+
files.mv!(dest)
|
|
203
|
+
end
|
|
204
|
+
files.sort
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
#Extracts from the filename the offset time of the chunk
|
|
208
|
+
#relative to the original from which it was split. Format is
|
|
209
|
+
#minute.seconds. Suitable for use on files created by 'split'
|
|
210
|
+
#method.
|
|
211
|
+
def offset
|
|
212
|
+
match = File.basename(@path).match(/\d+\.\d\d\b/)
|
|
213
|
+
return match[0] if match
|
|
214
|
+
end
|
|
215
|
+
end #Audio
|
|
216
|
+
|
|
217
|
+
#Handler for collection of Filer instances. Makes them enumerable,
|
|
218
|
+
#Allows easy re-casting to Filer::Files subclasses,
|
|
219
|
+
#and provides various other convenience methods.
|
|
220
|
+
class Files
|
|
221
|
+
include Enumerable
|
|
222
|
+
include Utility::Castable
|
|
223
|
+
require 'fileutils'
|
|
224
|
+
|
|
225
|
+
#Array of Filer instances included in the collection
|
|
226
|
+
attr_reader :files
|
|
227
|
+
|
|
228
|
+
#Constructor. Takes array of Filer instances.
|
|
229
|
+
def initialize(files)
|
|
230
|
+
@files = files
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
#Enumerate through Filer instances.
|
|
234
|
+
def each
|
|
235
|
+
files.each do |file|
|
|
236
|
+
yield file
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
#Cast this collection into a new Filer::Files subtype,
|
|
241
|
+
#e.g. Filer::Files::Audio.
|
|
242
|
+
# ==== Params
|
|
243
|
+
# [sym] Symbol corresponding to Filer::Files subclass to cast
|
|
244
|
+
# into. For example, passing :audio will cast into a
|
|
245
|
+
# Filer::Files::Audio.
|
|
246
|
+
# ==== Returns
|
|
247
|
+
# Instance of new Filer::Files subclass
|
|
248
|
+
def as(sym)
|
|
249
|
+
#super calls into Utility::Castable mixin
|
|
250
|
+
super(sym, files)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
#Returns array of IO streams created by calling to_stream on
|
|
254
|
+
#each Filer instance in the collection.
|
|
255
|
+
def to_streams
|
|
256
|
+
self.map{|file| file.to_stream }
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
#Calls mv! on each Filer instance in the collection. See
|
|
260
|
+
#documentation for Filer#mv! for definition of "to" param and
|
|
261
|
+
#for return value.
|
|
262
|
+
def mv!(to)
|
|
263
|
+
files.map{|file| file.mv! to }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
#Handler for collection of Filer::Audio instances. Does
|
|
267
|
+
#everything Filer::Files does, plus can batch convert to mp3 an
|
|
268
|
+
#can merge the Filer::Audio instances into a single audio file,
|
|
269
|
+
#provided they are in mp3 format.
|
|
270
|
+
class Audio < Files
|
|
271
|
+
|
|
272
|
+
#Constructor. Takes an array of Filer or Filer subclass instances.
|
|
273
|
+
def initialize(files)
|
|
274
|
+
@files = files.map{|file| self.file(file.path) }
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def file(path)
|
|
278
|
+
Filer::Audio.new(path)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
#Batch convert Filer::Audio instances to mp3 format.
|
|
282
|
+
# ==== Params
|
|
283
|
+
# [dest_dir] Filer::Dir instance corresponding to directory
|
|
284
|
+
# into which mp3 file versions will be created.
|
|
285
|
+
# [bitrate] See documentation for Filer::Audio#bitrate.
|
|
286
|
+
# ==== Returns
|
|
287
|
+
# Filer::Files::Audio instance corresponding to new mp3
|
|
288
|
+
# versions of the original files or, in the case where the
|
|
289
|
+
# original file was already in mp3 format, corresponding to
|
|
290
|
+
# the original files themselves.
|
|
291
|
+
def to_mp3(dest_dir, bitrate=nil)
|
|
292
|
+
mp3s = self.map do |file|
|
|
293
|
+
if file.mp3?
|
|
294
|
+
file
|
|
295
|
+
else
|
|
296
|
+
yield(file) if block_given?
|
|
297
|
+
file.to_mp3(dest_dir.file("#{File.basename(file.path, '.*') }.mp3"), bitrate)
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
self.class.new(mp3s)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
#Merge Filer::Audio instances into a single new file, provided
|
|
304
|
+
#they are all in mp3 format.
|
|
305
|
+
# ==== Params
|
|
306
|
+
#[into_file] Filer or Filer subclass instance corresponding to
|
|
307
|
+
#the location of the new, merged file that should be created.
|
|
308
|
+
# ==== Returns
|
|
309
|
+
# Filer::Audio instance corresponding to the new, merged file.
|
|
310
|
+
def merge(into_file)
|
|
311
|
+
raise Error::Argument, "No files to merge" if self.to_a.empty?
|
|
312
|
+
if self.count > 1
|
|
313
|
+
Utility.system_quietly('mp3wrap', into_file, *self.to_a)
|
|
314
|
+
written = File.join(into_file.dir, "#{File.basename(into_file.path, '.*') }_MP3WRAP.mp3")
|
|
315
|
+
FileUtils.mv(written, into_file)
|
|
316
|
+
else
|
|
317
|
+
FileUtils.cp(self.first, into_file)
|
|
318
|
+
end
|
|
319
|
+
self.file(into_file.path)
|
|
320
|
+
end
|
|
321
|
+
end #Audio
|
|
322
|
+
end #Files
|
|
323
|
+
|
|
324
|
+
#Convenience wrapper for basic directory operations and for
|
|
325
|
+
#casting files to specific filer types (CSV, Audio).
|
|
326
|
+
class Dir < Files
|
|
327
|
+
|
|
328
|
+
#Full expanded path to the dir
|
|
329
|
+
attr_reader :path
|
|
330
|
+
|
|
331
|
+
#Constructor. Takes full expanded path to the dir. Does NOT
|
|
332
|
+
#create dir in the filesystem.
|
|
333
|
+
def initialize(path)
|
|
334
|
+
@path = path
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
class << self
|
|
338
|
+
|
|
339
|
+
#Constructor. Takes full expanded path to the dir and creates
|
|
340
|
+
#the dir in the filesystem. Returns new Filer::Dir.
|
|
341
|
+
def create(path)
|
|
342
|
+
FileUtils.mkdir(path)
|
|
343
|
+
new(path)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
#Constructor. Takes directory name and full expanded path of
|
|
347
|
+
#the parent directory. If the so-named directory exists within
|
|
348
|
+
#the parent directory, returns it. If not, returns nil.
|
|
349
|
+
def named(name, in_dir)
|
|
350
|
+
path = File.join(in_dir, name)
|
|
351
|
+
if File.exists?(path) && File.directory?(path)
|
|
352
|
+
new(path)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end #class << self
|
|
356
|
+
|
|
357
|
+
#Filer::Dir isntances stringify to their path.
|
|
358
|
+
def to_s
|
|
359
|
+
@path
|
|
360
|
+
end
|
|
361
|
+
alias :to_str :to_s
|
|
362
|
+
|
|
363
|
+
#Takes an aribtrary number of path elements relative to the
|
|
364
|
+
#Filer::Dir instance. So a file in the subdir path/to/file.txt
|
|
365
|
+
#would be referenced via file('path', 'to', 'file.txt'). Returns
|
|
366
|
+
#a new Filer instance wrapping the referenced file. Does not
|
|
367
|
+
#guarantee that the referenced file exists.
|
|
368
|
+
def file(*relative_path)
|
|
369
|
+
Filer.new(file_path(*relative_path))
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
#Returns the files in the Filer::Dir directory as Filer
|
|
373
|
+
#instances. Excludes files whose names start with a dot.
|
|
374
|
+
def files
|
|
375
|
+
::Dir.entries(@path).select{|entry| File.file? file_path(entry) }.reject{|entry| entry.match(/^\./) }.map{|entry| self.file(entry) }
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
#Takes relative path elements as params just like the file
|
|
379
|
+
#method. Returns a new Filer::Dir instance wrapping the
|
|
380
|
+
#referenced subdir.
|
|
381
|
+
def subdir(*relative_path)
|
|
382
|
+
Dir.new(file_path(*relative_path))
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
#OS X specific. Opens the dir in the Finder via the 'open' command.
|
|
386
|
+
def finder_open
|
|
387
|
+
system('open', @path)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def file_path(*relative_path)
|
|
391
|
+
File.join(@path, *relative_path)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
end #Dir
|
|
395
|
+
end #Filer
|
|
396
|
+
end #Typingpool
|