Class: Blufin::Files

Inherits:
Object
  • Object
show all
Defined in:
lib/core/files.rb

Constant Summary collapse

JAVA_AUTO_GENERATED_EVERY_RUN =
'java_auto_generated_every_run'
JAVA_AUTO_GENERATED_ONCE =
'java_auto_generated_once'

Class Method Summary collapse

Class Method Details

.create_directory(path) ⇒ Object

Create a directory recursively (if it doesn’t already exist).

Returns:

  • String



388
389
390
391
392
393
394
395
396
# File 'lib/core/files.rb', line 388

def self.create_directory(path)
    begin
        path = File.expand_path(path)
        FileUtils.mkpath(path) unless path_exists(path)
        path
    rescue => e
        Blufin::Terminal::print_exception(e)
    end
end

.create_path(path) ⇒ Object

Proxy function for the above.

Returns:

  • void

Raises:

  • (RuntimeError)


400
401
402
403
# File 'lib/core/files.rb', line 400

def self.create_path(path)
    raise RuntimeError, "Expected String, instead got: #{path.class}" unless path.is_a?(String)
    create_directory(path)
end

.delete_file(path_and_file) ⇒ Object

Deletes a file (if exists)

Returns:

  • void

Raises:

  • (RuntimeError)


332
333
334
335
# File 'lib/core/files.rb', line 332

def self.delete_file(path_and_file)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    FileUtils.rm(File.expand_path(path_and_file)) if file_exists(path_and_file)
end

.extract_file_name(path_and_file, include_extension = true) ⇒ Object

Get the file name ONLY (from a full path).

Returns:

  • string

Raises:

  • (RuntimeError)


414
415
416
417
418
# File 'lib/core/files.rb', line 414

def self.extract_file_name(path_and_file, include_extension = true)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    return File.basename(File.expand_path(path_and_file)) if include_extension
    return File.basename(File.expand_path(path_and_file), '*') unless include_extension
end

.extract_path_name(path_and_file) ⇒ Object

Get the path name ONLY (from a full path).

Returns:

  • string

Raises:

  • (RuntimeError)


407
408
409
410
# File 'lib/core/files.rb', line 407

def self.extract_path_name(path_and_file)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    File.dirname(File.expand_path(path_and_file))
end

.file_exists(path_and_file) ⇒ Object

Returns TRUE or FALSE depending whether a path exists.

Returns:

  • void

Raises:

  • (RuntimeError)


346
347
348
349
# File 'lib/core/files.rb', line 346

def self.file_exists(path_and_file)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    File.exist?(File.expand_path(path_and_file))
end

.get_dirs_in_dir(path, recursive = false) ⇒ Object

Get and array of directories in a directory.

Returns:

  • Array

Raises:

  • (RuntimeError)


367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/core/files.rb', line 367

def self.get_dirs_in_dir(path, recursive = false)
    raise RuntimeError, "Expected String, instead got: #{path.class}" unless path.is_a?(String)
    path = "/#{Blufin::Strings.remove_surrounding_slashes(File.expand_path(path))}"
    raise RuntimeError, "Directory doesn't exist: #{path}" unless path_exists(path)
    dirs = Dir.glob("#{path}/**/*/")
    unless recursive
        root_dirs   = []
        root_length = path.split('/').length
        dirs.each do |dir|
            root_dirs << dir if dir.split('/').length == root_length + 1
        end
        dirs = root_dirs
    end
    dirs.map! { |value| value.gsub(/\/\z/, '') }
    dirs.uniq!
    dirs.sort!
    dirs
end

.get_files_in_dir(path, only_with_extension = nil) ⇒ Object

Get and array of files in a directory.

Returns:

  • Array

Raises:

  • (RuntimeError)


353
354
355
356
357
358
359
360
361
362
363
# File 'lib/core/files.rb', line 353

def self.get_files_in_dir(path, only_with_extension = nil)
    raise RuntimeError, "Expected String, instead got: #{path.class}" unless path.is_a?(String)
    unless only_with_extension.nil?
        raise RuntimeError, "Expected String, instead got: #{path.class}" unless only_with_extension.is_a?(String)
        raise RuntimeError, 'only_with_extension must be alphanumeric & lowercase (no periods)' unless only_with_extension =~ /[a-z0-9]/
    end
    path = "/#{Blufin::Strings.remove_surrounding_slashes(File.expand_path(path))}"
    raise RuntimeError, "Directory doesn't exist: #{path}" unless path_exists(path)
    files = Dir.glob("#{path}/**/*.#{only_with_extension.nil? ? '*' : only_with_extension}")
    files.uniq.sort
end

.is_empty(path_and_file) ⇒ Object

Returns TRUE if file is empty. Ignores spaces and new-line characters.

Returns:

  • bool

Raises:

  • (RuntimeError)


422
423
424
425
426
427
428
429
# File 'lib/core/files.rb', line 422

def self.is_empty(path_and_file)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    Blufin::Files::read_file(path_and_file).each do |line|
        line.strip!
        return false if line != ''
    end
    true
end

.is_file(path_and_file) ⇒ Object

Returns TRUE if path_and_file is a file, FALSE if it’s a directory. File does not need to exist.

Returns:

  • bool

Raises:

  • (RuntimeError)


434
435
436
437
# File 'lib/core/files.rb', line 434

def self.is_file(path_and_file)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    path_and_file =~ /\.[a-z0-9]{1,10}$/i ? true : false
end

.path_exists(path) ⇒ Object

Returns TRUE or FALSE depending whether a path exists.

Returns:

  • void

Raises:

  • (RuntimeError)


339
340
341
342
# File 'lib/core/files.rb', line 339

def self.path_exists(path)
    raise RuntimeError, "Expected String, instead got: #{path.class}" unless path.is_a?(String)
    File.directory?(File.expand_path(path))
end

.read_file(path_and_file, start_line = nil, end_line = nil) ⇒ Object

Get content of a file as an “Array of Lines”

Returns:

  • Array

Raises:

  • (RuntimeError)


312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/core/files.rb', line 312

def self.read_file(path_and_file, start_line = nil, end_line = nil)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    line_count    = 0
    path_and_file = File.expand_path(path_and_file)
    raise RuntimeError, "File not found: #{path_and_file}" unless file_exists(path_and_file)
    raise RuntimeError, "start_line (#{start_line}) cannot be bigger than end_line (#{end_line})." if (!start_line.nil? && !end_line.nil?) && (start_line > end_line)
    file_content = []
    file         = File.open(path_and_file).read
    file.gsub!(/\r\n?/, "\n")
    file.each_line do |line|
        line_count += 1
        next if !start_line.nil? && start_line > line_count
        next if !end_line.nil? && end_line < line_count
        file_content << line
    end
    file_content
end

.remove_line_from_file(path_and_file, regex, multiple_occurrences = false) ⇒ Object

Removes a line from a file. multiple_occurrences -> If set to TRUE, will remove ALL occurrences from file.

Returns:

  • void

Raises:

  • (RuntimeError)


285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/core/files.rb', line 285

def self.remove_line_from_file(path_and_file, regex, multiple_occurrences = false)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    path_and_file = File.expand_path(path_and_file)
    raise RuntimeError, "Expected Regexp to match to line, instead got: #{regex.class}" unless regex.is_a? Regexp
    raise RuntimeError, "File not found: #{path_and_file}" unless file_exists(path_and_file)
    new_lines    = []
    line_removed = false
    read_file(path_and_file).each do |line_content|
        line_content.gsub!(/\n\z/, '')
        line_content_to_match = line_content.strip
        if line_content_to_match =~ regex
            if multiple_occurrences
                next
            else
                unless line_removed
                    line_removed = true
                    next
                end
            end
        end
        new_lines << line_content
    end
    write_file(path_and_file, new_lines)
end

.write_file(path_and_file, array_of_lines, write_empty_trailing_line: false) ⇒ Object

Write an “Array of Lines” to a file – overwrites file if exists!

Returns:

  • String

Raises:

  • (RuntimeError)


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/core/files.rb', line 187

def self.write_file(path_and_file, array_of_lines, write_empty_trailing_line: false)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    path_and_file = File.expand_path(path_and_file)
    # If this comes in as a string, convert it to an Array of lines.
    if array_of_lines.is_a?(String)
        array_of_lines_new = []
        array_of_lines.split("\n").each { |line| array_of_lines_new << line.gsub("\n", '') }
        array_of_lines = array_of_lines_new
    end
    raise RuntimeError, "Expected an array of lines to write to file, instead got: #{array_of_lines.class}" unless array_of_lines.is_a? Array
    prepare_for_file_write(path_and_file)
    begin
        File.open(path_and_file, 'w') { |file|
            array_of_lines.each_with_index do |line, index|
                if index == array_of_lines.size - 1
                    file.write("#{line}") if line.to_s.strip != '' || write_empty_trailing_line
                else
                    file.write("#{line}\n")
                end
            end
            file.close
        }
    rescue Exception => e
        Blufin::Terminal::print_exception(e)
    end
    path_and_file
end

.write_file_if_changed(path_and_file, array_of_lines, write_empty_trailing_line: false) ⇒ Object

Writes a file (but only if content has changed). Returns true if file was overwritten, false if not.

Returns:

  • boolean

Raises:

  • (RuntimeError)


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/core/files.rb', line 166

def self.write_file_if_changed(path_and_file, array_of_lines, write_empty_trailing_line: false)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    raise RuntimeError, "Expected Array, instead got: #{array_of_lines.class}" unless array_of_lines.is_a?(Array)
    # If file does not exist, writes it regardless and returns TRUE.
    unless Blufin::Files::file_exists(path_and_file)
        Blufin::Files::write_file(path_and_file, array_of_lines, write_empty_trailing_line: write_empty_trailing_line)
        return true
    end
    tmp_file = "/tmp/#{Blufin::Strings::random_string(2)}.txt"
    Blufin::Files::write_file(tmp_file, array_of_lines, write_empty_trailing_line: write_empty_trailing_line)
    target_file_hash = Digest::SHA1.hexdigest Blufin::Arrays::convert_line_array_to_string(Blufin::Files::read_file(path_and_file)).gsub(/\s/, '')
    tmp_file_hash    = Digest::SHA1.hexdigest Blufin::Arrays::convert_line_array_to_string(Blufin::Files::read_file(tmp_file)).gsub(/\s/, '')
    if target_file_hash != tmp_file_hash
        FileUtils.mv(tmp_file, path_and_file)
        return true
    end
    false
end

.write_file_java(path_and_file, array_of_lines, auto_generated = JAVA_AUTO_GENERATED_EVERY_RUN, test_annotations = true) ⇒ Object

Same as write_file() but for Java files. If arrange_imports = TRUE (default) it will sort the import statements in the same order as IntelliJ would.

Returns:

  • String

Raises:

  • (RuntimeError)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/core/files.rb', line 16

def self.write_file_java(path_and_file, array_of_lines, auto_generated = JAVA_AUTO_GENERATED_EVERY_RUN, test_annotations = true)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    auto_generated_valid = [nil, JAVA_AUTO_GENERATED_EVERY_RUN, JAVA_AUTO_GENERATED_ONCE]
    raise RuntimeError, "Expected .java file, instead got: #{path_and_file}" unless path_and_file =~ /\.java\z/
    raise RuntimeError, "auto_generated must be one of the following: #{auto_generated_valid.join(', ')}" unless auto_generated.nil? || auto_generated_valid.include?(auto_generated)
    raise RuntimeError, "Expected Boolean, instead got: #{test_annotations.class}" unless !!test_annotations == test_annotations
    package               = nil
    import_statements_one = []
    import_statements_two = []
    previous_was_blank    = false
    annotations_one       = []
    annotations_two       = []
    contents              = []
    array_of_lines.each do |line|
        if contents.any?
            next if line.strip == '' && previous_was_blank
            previous_was_blank = false
            contents << line
            previous_was_blank = true if line.strip == ''
            next
        end
        if package.nil? && line.strip =~ /^package\s+[A-Za-z0-9\._-]+;(\s*#.*)?$/
            # Get package.
            raise RuntimeError, "Package has a hyphen in it \xe2\x86\x92 \x1B[38;5;184m#{line.strip}\x1B[0m. When this file's contents were generated you forgot a .gsub() statement to replace it." if line.gsub(/(\s*#.*)?$/, '').strip =~ /-/
            package = line.strip
            next
        elsif line.strip =~ /^import\s+[A-Za-z0-9\.*_-]+;$/ || line.strip =~ /^import\s+static\s+[A-Za-z0-9\.*_-]+;$/
            # Get import statements.
            raise RuntimeError, "Import has a hyphen in it \xe2\x86\x92 \x1B[38;5;184m#{line.strip}\x1B[0m. When this file's contents were generated you forgot a .gsub() statement to replace it." if line.strip =~ /-/
            import_statements_one << line.strip
            next
        elsif !contents.any? && line.strip =~ /^@(.)+$/
            annotations_one << line.strip
        elsif !contents.any? && line.strip =~ /^(public|private|protected)\s+.*(class|enum)\s+.*\{(\s*\})?$/
            contents << line.strip
            next
        end
    end
    raise RuntimeError, "Couldn't parse content for: #{path_and_file}" unless contents.any?
    # Add @AutoGenerated + @TestNotRequired stuff.
    import_statements_one << 'import org.blufin.base.annotations.AutoGenerated;' unless auto_generated.nil?
    import_statements_one << 'import org.blufin.base.annotations.TestNotRequired;' if test_annotations
    import_statements_one << 'import org.blufin.base.annotations.helper.ON;' unless auto_generated.nil?
    annotations_one.each do |annotation|
        next if %w(@TestNotRequired @AutoGenerated @AutoGenerated(ON.EVERY_RUN) @AutoGenerated(ON.CREATION_ONLY)).include?(annotation)
        annotations_two << annotation
    end
    annotations_two << '@TestNotRequired' if test_annotations
    annotations_two << ((auto_generated == JAVA_AUTO_GENERATED_EVERY_RUN) ? '@AutoGenerated(ON.EVERY_RUN)' : '@AutoGenerated(ON.CREATION_ONLY)') unless auto_generated.nil?
    annotations_two.uniq!
    import_statements_one.uniq!
    import_statements_one.sort!
    package_path = package.gsub(/package\s/, '').gsub(/;/, '').strip

    # Figure out which import statements to "keep" -- IE: Are they being used?
    import_statements_one.each do |import_statement|

        import_statement_path  = []
        import_statement_split = import_statement.gsub(/import\s/, '').gsub(/;/, '').strip.split('.')
        import_statement_split.each_with_index { |n, idx| import_statement_path << n unless idx == (import_statement_split.length - 1) }

        # Skip import if we're in the same package.
        next if import_statement_path.join('.') == package_path

        found = false
        is    = import_statement.split('.')
        is    = is[is.length - 1].gsub(';', '').strip
        if is == '*'
            import_statements_two << import_statement
            next
        end
        annotations_two.each do |annotation|
            if annotation =~ /@#{is}/ || annotation =~ /[\(\.]#{is}[\)\.]/
                found = true
                break
            end
        end
        unless found
            contents.each do |content_line|
                if content_line =~ /[<\(\s{]#{is}[\.<>,\s\(\)]/ || content_line =~ /@#{is}[\(]?/
                    found = true
                    break
                end
            end
        end
        import_statements_two << import_statement if found
    end
    import_statement_counter = {}
    import_statement_tracker = {}
    # Convert multiple imports to .*
    import_statements_two.each do |import_statement|
        is                           = import_statement.split('.')
        is                           = is.first(is.length - 1).join('.')
        import_statement_counter[is] = 0 if import_statement_counter[is].nil?
        import_statement_counter[is] += 1
        import_statement_tracker[is] = [] if import_statement_tracker[is].nil?
        import_statement_tracker[is] << import_statement
    end
    import_statements_one = []
    import_statements_two.each do |import_statement|
        is = import_statement.split('.')
        is = is.first(is.length - 1).join('.')
        if import_statement_counter[is] > 5
            import_statements_one << "#{is}.*;"
        else
            import_statements_one << import_statement
        end
    end
    import_statements_one.uniq!
    import_statements_one.sort!
    # Re-assemble the file.
    final_written_java_non = 0
    final_written_java     = 0
    final_contents         = [package, '']
    # Write NON-JAVA statements.
    import_statements_one.each do |import_statement|
        unless import_statement =~ /^import\s+java/ || import_statement =~ /^import\s+static/
            final_contents << import_statement
            final_written_java_non += 1
        end
    end
    static_statement_found = false
    # Write STATIC statements.
    import_statements_one.each do |import_statement|
        if import_statement =~ /^import\s+static/
            unless static_statement_found
                final_contents << ''
                static_statement_found = true
            end
            final_contents << import_statement
            final_written_java += 1
        end
    end
    final_contents << '' if final_written_java_non > 0
    # Write JAVA statements.
    import_statements_one.each do |import_statement|
        if import_statement =~ /^import\s+java/
            final_contents << import_statement
            final_written_java += 1
        end
    end
    final_contents << '' if final_written_java > 0
    annotations_two.each { |line| final_contents << line } if annotations_two.any?
    contents.each { |line| final_contents << line } if contents.any?
    return self.write_file(path_and_file, final_contents)
end

.write_file_string(path_and_file, content) ⇒ Object

Write a “String” to a file – overwrites file if exists!

Returns:

  • void

Raises:

  • (RuntimeError)


217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/core/files.rb', line 217

def self.write_file_string(path_and_file, content)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    path_and_file = File.expand_path(path_and_file)
    raise RuntimeError, "Expected String to write to file, instead got: #{content.class}" unless content.is_a? String
    prepare_for_file_write(path_and_file)
    begin
        File.open(path_and_file, 'w') { |file|
            file.write("#{content}")
            file.close
        }
    rescue Exception => e
        Blufin::Terminal::print_exception(e)
    end
end

.write_line_to_file(path_and_file, line, regex = nil, only_if_not_exists = true, replace = false) ⇒ Object

Writes a line to a file. regex -> If a value exists the program checks for this regex and writes on the line AFTER match(es). if nil (or not found), writes at the end of file. only_if_not_exists -> If TRUE, will add line only if it doesn’t already exist. replace -> If TRUE, will replace file rather than writing on next line after regex.

Returns:

  • void

Raises:

  • (RuntimeError)


237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/core/files.rb', line 237

def self.write_line_to_file(path_and_file, line, regex = nil, only_if_not_exists = true, replace = false)
    raise RuntimeError, "Expected String, instead got: #{path_and_file.class}" unless path_and_file.is_a?(String)
    path_and_file = File.expand_path(path_and_file)
    raise RuntimeError, "File not found: #{path_and_file}" unless file_exists(path_and_file)
    raise RuntimeError, "Expected String to write to file, instead got: #{line.class}" unless line.is_a? String
    raise RuntimeError, "Expected Regexp to match to line, instead got: #{regex.class}" unless regex.nil? || regex.is_a?(Regexp)
    raise RuntimeError, 'Cannot set replace to TRUE when regex is nil.' if regex.nil? && replace
    raise RuntimeError, 'only_if_not_exists must be set to FALSE when replace is TRUE.' if only_if_not_exists && replace
    new_lines = []
    if only_if_not_exists
        line_found = false
        read_file(path_and_file).each do |line_content|
            line_content.gsub!(/\n\z/, '')
            line_found = true if line_content == line
        end
        return if line_found
    end
    if !regex.nil? && !replace
        regex_found   = false
        line_inserted = false
        read_file(path_and_file).each do |line_content|
            line_content.gsub!(/\n\z/, '')
            regex_found = true if line_content =~ regex
            if regex_found && line_content !~ regex && !line_inserted
                new_lines << line
                line_inserted = true
            end
            new_lines << line_content
        end
        new_lines << line unless line_inserted
    elsif !regex.nil? && replace
        regex_found = false
        read_file(path_and_file).each do |line_content|
            line_content.gsub!(/\n\z/, '')
            if !regex_found && line_content =~ regex
                regex_found = true
                new_lines << line
            else
                new_lines << line_content
            end
        end
    end
    write_file(path_and_file, new_lines)
end