reenrb 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/Gemfile.lock +1 -1
- data/README.md +17 -17
- data/Rakefile +4 -4
- data/bin/reen +78 -13
- data/lib/reenrb/actions/delete.rb +20 -0
- data/lib/reenrb/actions/file_rename.rb +20 -0
- data/lib/reenrb/actions/nothing.rb +17 -0
- data/lib/reenrb/change.rb +119 -0
- data/lib/{changes.rb → reenrb/changes.rb} +19 -9
- data/lib/reenrb/changes_file.rb +42 -0
- data/lib/reenrb/reen.rb +40 -0
- data/lib/reenrb/version.rb +1 -1
- data/lib/reenrb.rb +4 -48
- metadata +9 -5
- data/lib/change.rb +0 -108
- data/lib/changes_file.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f607120ef687c5ace8a02b12e3fb6306abc16aa45b6f906f3e853f995df9a51
|
4
|
+
data.tar.gz: 0d4b19262e64584508daf56c0ec2024603ff1a375d134fc000774d1e9233714e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 420f2980c0e6a22ddeb7dbfc9a6abc62a9c8845f19f0902ba975c13996e87c371da9dd003b1d70fbdca9a14ebfeae36e85bd883671bb8b9eec27aa622a1e4fe1
|
7
|
+
data.tar.gz: 971cade35548d6fbde58a07ebd3707f34e95da5933e37c37d4eef1f8f108a1714b789aa873ccbcd2331a0214c330bd93a83c4b4f52f295f04130802efff0a726
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
1
8
|
## [Unreleased]
|
2
9
|
|
10
|
+
## [0.2.1] - 2022-11-05
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Simplified API for passing block to request/execute
|
15
|
+
|
3
16
|
## [0.1.0] - 2022-11-05
|
4
17
|
|
5
|
-
- Initial release
|
18
|
+
- Initial release: CLI and API working
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -14,26 +14,29 @@ Or add this line to your Ruby application's Gemfile for programmatic use:
|
|
14
14
|
gem 'reenrb'
|
15
15
|
```
|
16
16
|
|
17
|
-
And then execute:
|
18
|
-
|
19
|
-
$ bundle install
|
20
|
-
|
21
|
-
Or install it yourself as:
|
22
|
-
|
23
|
-
$ gem install reenrb
|
24
|
-
|
25
17
|
## Usage
|
26
18
|
|
27
19
|
### Command line
|
28
20
|
|
29
21
|
From command line, run `reen` with file list:
|
30
22
|
|
31
|
-
reen [
|
23
|
+
reen files [options]
|
24
|
+
|
25
|
+
where `files` are a list of files or wildcard pattern (defaults to `*`; see examples)
|
26
|
+
|
27
|
+
Options include:
|
28
|
+
|
29
|
+
- `--help` or `-h`' to see options help
|
30
|
+
- `--editor EDITOR` or `-e EDITOR`' to set the editor (defaults to $VISUAL or $EDITOR otherwise) such as `emacs` or `vi`. For Visual Studio Code use `'code -w'` to block until editor finishes.
|
31
|
+
- `--review` or `-r` request to review and confirm changes
|
32
32
|
|
33
33
|
Examples:
|
34
34
|
|
35
|
-
reen *
|
36
|
-
reen
|
35
|
+
reen # reen all files (*)
|
36
|
+
reen **/* # reen all files in all subfolders
|
37
|
+
reen myfolder/**/*.mov # reen all mov files in subfolders
|
38
|
+
reen *.md --editor vi # reen all markdown files using vi
|
39
|
+
reen --editor 'code -w' # reen all markdown files using vscode
|
37
40
|
|
38
41
|
### Ruby application
|
39
42
|
|
@@ -45,13 +48,10 @@ require 'reenrb'
|
|
45
48
|
glob = Dir.glob("*")
|
46
49
|
reen = Reenrb::Reen.new(editor: nil)
|
47
50
|
|
48
|
-
reen.execute(glob) do |
|
49
|
-
lines = File.read(tmpfile_path).split("\n")
|
50
|
-
|
51
|
+
reen.execute(glob) do |file|
|
51
52
|
# Rename LICENSE.txt -> LICENSE.md
|
52
|
-
index =
|
53
|
-
|
54
|
-
File.write(tmpfile_path, lines.join("\n"))
|
53
|
+
index = file.list.index { |l| l.include? "LICENSE.txt" }
|
54
|
+
file.list[index] = file.list[index].gsub("txt", "md")
|
55
55
|
end
|
56
56
|
```
|
57
57
|
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require "bundler/gem_tasks"
|
|
4
4
|
require "rake/testtask"
|
5
5
|
|
6
6
|
Rake::TestTask.new(:spec) do |t|
|
7
|
-
t.libs << "
|
7
|
+
t.libs << "spec"
|
8
8
|
t.libs << "lib"
|
9
9
|
t.test_files = FileList["spec/**/spec_*.rb"]
|
10
10
|
end
|
@@ -14,18 +14,18 @@ task :respec do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
namespace :example do
|
17
|
-
task :
|
17
|
+
task :helper do
|
18
18
|
require_relative "spec/fixture_helper"
|
19
19
|
end
|
20
20
|
|
21
21
|
desc "Recreates the example fixture folder"
|
22
|
-
task :recreate => :
|
22
|
+
task :recreate => :helper do
|
23
23
|
FixtureHelper.recreate_example_dir
|
24
24
|
puts "Example fixture recreated"
|
25
25
|
end
|
26
26
|
|
27
27
|
desc "Deletes the example fixture folder"
|
28
|
-
task :remove => :
|
28
|
+
task :remove => :helper do
|
29
29
|
FixtureHelper.remove_example_dirs
|
30
30
|
puts "Example fixture removed"
|
31
31
|
end
|
data/bin/reen
CHANGED
@@ -1,22 +1,87 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
EDITOR_MSG = "Editor not set in $EDITOR or $VISUAL -- please set one of those environment variables"
|
5
|
-
|
6
|
-
def exit_with_error(err)
|
7
|
-
puts err
|
8
|
-
exit(false)
|
9
|
-
end
|
10
|
-
|
11
4
|
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
12
5
|
require "reenrb"
|
6
|
+
require "optparse"
|
7
|
+
|
8
|
+
# Reen command line application
|
9
|
+
class ReenCLI
|
10
|
+
EDITOR_MSG = "Editor not set in $EDITOR or $VISUAL -- please set one of those environment variables"
|
11
|
+
Options = Struct.new(:editor, :review) do
|
12
|
+
def initialize(editor: nil, review: false)
|
13
|
+
editor ||= ENV["VISUAL"] || ENV["EDITOR"] # rubocop:disable Style/FetchEnvVar
|
14
|
+
super(editor, review)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :options, :files
|
19
|
+
|
20
|
+
def initialize(args)
|
21
|
+
@options = Options.new
|
22
|
+
optparse = OptionParser.new { |parser| setup_options(parser) }
|
23
|
+
optparse.parse!(args, into: @options)
|
24
|
+
|
25
|
+
exit_with_msg(EDITOR_MSG) unless options.editor
|
26
|
+
|
27
|
+
@files = args.empty? ? Dir.glob("*") : args
|
28
|
+
rescue OptionParser::InvalidOption => e
|
29
|
+
puts "#{e.message}\n\n"
|
30
|
+
exit_with_msg(optparse)
|
31
|
+
end
|
32
|
+
|
33
|
+
def setup_options(parser)
|
34
|
+
parser.banner = "Usage: reen files [options]"
|
35
|
+
parser.version = Reenrb::VERSION
|
13
36
|
|
14
|
-
|
15
|
-
|
16
|
-
exit_with_error(EDITOR_MSG) if editor.empty?
|
37
|
+
parser.on("-e", "--editor EDITOR", "Specify EDITOR to use")
|
38
|
+
parser.on("-r", "--review", "Require review and confirmation of changes")
|
17
39
|
|
18
|
-
|
40
|
+
parser.on("-h", "--help", "Show help for options") do
|
41
|
+
exit_with_msg(parser)
|
42
|
+
end
|
43
|
+
end
|
19
44
|
|
20
|
-
|
45
|
+
def exit_with_msg(message)
|
46
|
+
puts message
|
47
|
+
exit(false)
|
48
|
+
end
|
21
49
|
|
22
|
-
|
50
|
+
def review_changes
|
51
|
+
puts
|
52
|
+
puts @requests.change_requested.summarize
|
53
|
+
print "\nContinue? (y/n) "
|
54
|
+
confirmation = %w[y yes].include?($stdin.gets.chomp.downcase)
|
55
|
+
exit_with_msg("Nothing changed") unless confirmation
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_inputs
|
59
|
+
@files = files.empty? ? Dir.glob("*") : files
|
60
|
+
exit_with_msg(EDITOR_MSG) unless options.editor
|
61
|
+
end
|
62
|
+
|
63
|
+
def user_review?
|
64
|
+
@options.review && @requests.changes_requested?
|
65
|
+
end
|
66
|
+
|
67
|
+
def call
|
68
|
+
@requests = Reenrb::Reen.new(editor: options.editor).request(files)
|
69
|
+
review_changes if user_review?
|
70
|
+
|
71
|
+
changes = @requests
|
72
|
+
.execute_all
|
73
|
+
.change_requested
|
74
|
+
|
75
|
+
if user_review? && changes.all_executed?
|
76
|
+
puts "Changes made"
|
77
|
+
else
|
78
|
+
puts changes.summarize
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
begin
|
84
|
+
ReenCLI.new(ARGV).call
|
85
|
+
rescue Reenrb::Error => e
|
86
|
+
puts "#{e.message}\n"
|
87
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reenrb
|
4
|
+
module Actions
|
5
|
+
# Deletes a file
|
6
|
+
class FileDelete
|
7
|
+
def initialize(old_name, new_name)
|
8
|
+
@old_name = old_name
|
9
|
+
@new_name = new_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
File.delete(@old_name)
|
14
|
+
nil
|
15
|
+
rescue Errno::ENOENT
|
16
|
+
"Could not delete"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reenrb
|
4
|
+
module Actions
|
5
|
+
# Renames files
|
6
|
+
class FileRename
|
7
|
+
def initialize(old_name, new_name)
|
8
|
+
@old_name = old_name
|
9
|
+
@new_name = new_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
File.rename(@old_name, @new_name)
|
14
|
+
nil
|
15
|
+
rescue Errno::ENOENT
|
16
|
+
"No such target file or directory"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reenrb
|
4
|
+
module Actions
|
5
|
+
# Does nothing to items
|
6
|
+
class DoNothing
|
7
|
+
def initialize(old_name, new_name)
|
8
|
+
@old_name = old_name
|
9
|
+
@new_name = new_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "actions/delete"
|
4
|
+
require_relative "actions/file_rename"
|
5
|
+
require_relative "actions/nothing"
|
6
|
+
|
7
|
+
module Reenrb
|
8
|
+
# Change to an orignal file
|
9
|
+
class Change
|
10
|
+
attr_reader :original, :requested, :change, :status
|
11
|
+
|
12
|
+
module STATUS
|
13
|
+
ACCEPTED = :accepted
|
14
|
+
REJECTED = :rejected
|
15
|
+
EXECUTED = :executed
|
16
|
+
FAILED = :failed
|
17
|
+
end
|
18
|
+
|
19
|
+
module CHANGE
|
20
|
+
NONE = :none
|
21
|
+
DELETE = :delete
|
22
|
+
RENAME = :rename
|
23
|
+
end
|
24
|
+
|
25
|
+
ACTION_HANDLER = {
|
26
|
+
CHANGE::NONE => Actions::DoNothing,
|
27
|
+
CHANGE::DELETE => Actions::FileDelete,
|
28
|
+
CHANGE::RENAME => Actions::FileRename
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
CHANGES_DESC = {
|
32
|
+
CHANGE::NONE => "Nothing",
|
33
|
+
CHANGE::DELETE => "Deleting",
|
34
|
+
CHANGE::RENAME => "Renaming"
|
35
|
+
}.freeze
|
36
|
+
|
37
|
+
def initialize(original, requested)
|
38
|
+
@original = original
|
39
|
+
@requested = requested
|
40
|
+
@change = compare
|
41
|
+
consider
|
42
|
+
end
|
43
|
+
|
44
|
+
def compare
|
45
|
+
if original == requested
|
46
|
+
CHANGE::NONE
|
47
|
+
elsif requested.start_with? "-"
|
48
|
+
CHANGE::DELETE
|
49
|
+
else
|
50
|
+
CHANGE::RENAME
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def consider
|
55
|
+
if request_full_dir? && request_delete?
|
56
|
+
@status = STATUS::REJECTED
|
57
|
+
@reason = "Directories with files cannot be changed"
|
58
|
+
else
|
59
|
+
@status = STATUS::ACCEPTED
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute
|
64
|
+
return(self) if not_accepted?
|
65
|
+
|
66
|
+
@status = STATUS::EXECUTED
|
67
|
+
error = ACTION_HANDLER[@change].new(original, requested).call
|
68
|
+
|
69
|
+
if error
|
70
|
+
@status = STATUS::FAILED
|
71
|
+
@reason = error
|
72
|
+
end
|
73
|
+
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Predicates
|
78
|
+
|
79
|
+
def request_dir? = Dir.exist?(@original)
|
80
|
+
|
81
|
+
def request_nothing? = @change == CHANGE::NONE
|
82
|
+
|
83
|
+
def request_rename? = @change == CHANGE::RENAME
|
84
|
+
|
85
|
+
def request_delete? = @change == CHANGE::DELETE
|
86
|
+
|
87
|
+
def request_empty_dir? = Dir.empty?(@original)
|
88
|
+
|
89
|
+
def accepted? = @status == STATUS::ACCEPTED
|
90
|
+
|
91
|
+
def not_accepted? = !accepted?
|
92
|
+
|
93
|
+
def executed? = @status == STATUS::EXECUTED
|
94
|
+
|
95
|
+
def rejected? = @status == STATUS::REJECTED
|
96
|
+
|
97
|
+
def failed? = @status == STATUS::FAILED
|
98
|
+
|
99
|
+
def request_full_dir? = request_dir? && !request_empty_dir?
|
100
|
+
|
101
|
+
def executed_or_rejected? = %i[executed rejected].include?(@status)
|
102
|
+
|
103
|
+
# Decoration
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
file_desc =
|
107
|
+
case @change
|
108
|
+
when CHANGE::RENAME
|
109
|
+
"#{@original} -> #{@requested}"
|
110
|
+
else
|
111
|
+
@original
|
112
|
+
end
|
113
|
+
|
114
|
+
reason_desc = rejected? || failed? ? " (failed: #{@reason})" : ""
|
115
|
+
|
116
|
+
"#{CHANGES_DESC[@change]}: #{file_desc}#{reason_desc}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "change"
|
4
|
+
|
3
5
|
module Reenrb
|
4
6
|
# Change to an orignal file
|
5
7
|
class Changes
|
@@ -9,35 +11,43 @@ module Reenrb
|
|
9
11
|
@list = changes_list
|
10
12
|
end
|
11
13
|
|
12
|
-
def
|
14
|
+
def execute_all
|
13
15
|
@list.map(&:execute)
|
14
16
|
self
|
15
17
|
end
|
16
18
|
|
17
|
-
#
|
19
|
+
# Queries
|
18
20
|
|
19
21
|
def rename_requested
|
20
|
-
Changes.new(@list.select
|
22
|
+
Changes.new(@list.select(&:request_rename?))
|
21
23
|
end
|
22
24
|
|
23
25
|
def delete_requested
|
24
|
-
Changes.new(@list.select
|
26
|
+
Changes.new(@list.select(&:request_delete?))
|
25
27
|
end
|
26
28
|
|
27
29
|
def change_requested
|
28
|
-
Changes.new(@list.reject
|
30
|
+
Changes.new(@list.reject(&:request_nothing?))
|
29
31
|
end
|
30
32
|
|
31
33
|
def rejected
|
32
|
-
Changes.new(@list.select
|
34
|
+
Changes.new(@list.select(&:rejected?))
|
33
35
|
end
|
34
36
|
|
35
37
|
def accepted
|
36
|
-
Changes.new(@list.select
|
38
|
+
Changes.new(@list.select(&:accepted?))
|
37
39
|
end
|
38
40
|
|
39
41
|
def executed
|
40
|
-
Changes.new(@list.select
|
42
|
+
Changes.new(@list.select(&:executed?))
|
43
|
+
end
|
44
|
+
|
45
|
+
def failed
|
46
|
+
Changes.new(@list.select(&:failed?))
|
47
|
+
end
|
48
|
+
|
49
|
+
def any?
|
50
|
+
!@list.empty?
|
41
51
|
end
|
42
52
|
|
43
53
|
def count
|
@@ -55,7 +65,7 @@ module Reenrb
|
|
55
65
|
# Predicates
|
56
66
|
|
57
67
|
def no_changes_requested?
|
58
|
-
list.map(&:change).all?
|
68
|
+
list.map(&:change).all? Change::CHANGE::NONE
|
59
69
|
end
|
60
70
|
|
61
71
|
def changes_requested?
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tempfile"
|
4
|
+
|
5
|
+
module Reenrb
|
6
|
+
# Manages a temporary file with requested changes
|
7
|
+
class ChangesFile
|
8
|
+
INSTRUCTIONS = <<~COMMENTS
|
9
|
+
# Edit the names of any files/folders to rename or move them
|
10
|
+
# - Put a preceeding dash to delete a file or empty folder
|
11
|
+
|
12
|
+
COMMENTS
|
13
|
+
|
14
|
+
attr_accessor :list
|
15
|
+
|
16
|
+
def initialize(requested_list)
|
17
|
+
@list_file = Tempfile.new("reenrb-")
|
18
|
+
@list_file.write(INSTRUCTIONS)
|
19
|
+
@list_file.write(requested_list.join("\n"))
|
20
|
+
@list_file.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def allow_changes(editor, &block)
|
24
|
+
await_editor(editor) if editor
|
25
|
+
@list = File.read(path).split("\n").map(&:strip)
|
26
|
+
.reject { |line| line.start_with?("#") || line.empty? }
|
27
|
+
|
28
|
+
block&.call(self)
|
29
|
+
@list
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def path
|
35
|
+
@list_file.path
|
36
|
+
end
|
37
|
+
|
38
|
+
def await_editor(editor)
|
39
|
+
`#{editor} #{@list_file.path}`
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/reenrb/reen.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reenrb
|
4
|
+
# Renames pattern of files with given editor
|
5
|
+
# Examples:
|
6
|
+
# Reenrb::Reen.new(editor: "code -w").call("spec/fixtures/example/*")
|
7
|
+
# Reenrb::Reen.new(editor: nil).call("spec/fixtures/example/*") { ... }
|
8
|
+
class Reen
|
9
|
+
DEL_ERROR = "Do not remove any file/folder names (no changes made)"
|
10
|
+
|
11
|
+
attr_reader :changes
|
12
|
+
|
13
|
+
def initialize(editor: "emacs", options: {})
|
14
|
+
@editor = editor
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def request(original_list, &block)
|
19
|
+
changed_list = ChangesFile.new(original_list).allow_changes(@editor, &block)
|
20
|
+
|
21
|
+
raise(Error, DEL_ERROR) if changed_list.size != original_list.size
|
22
|
+
|
23
|
+
@changes = compare_lists(original_list, changed_list)
|
24
|
+
.then { |change_array| Changes.new(change_array) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute(original_list, &block)
|
28
|
+
@changes ||= request(original_list, &block)
|
29
|
+
@changes = @changes.execute_all
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def compare_lists(original_list, changed_list)
|
35
|
+
original_list.zip(changed_list).map do |original, revised|
|
36
|
+
Change.new(original, revised)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/reenrb/version.rb
CHANGED
data/lib/reenrb.rb
CHANGED
@@ -1,55 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "reenrb/version"
|
4
|
-
require_relative "changes_file"
|
5
|
-
require_relative "change"
|
6
|
-
require_relative "changes"
|
4
|
+
require_relative "reenrb/changes_file"
|
5
|
+
require_relative "reenrb/change"
|
6
|
+
require_relative "reenrb/changes"
|
7
|
+
require_relative "reenrb/reen"
|
7
8
|
|
8
9
|
module Reenrb
|
9
10
|
class Error < StandardError; end
|
10
|
-
|
11
|
-
# Renames pattern of files with given editor
|
12
|
-
# Examples:
|
13
|
-
# Reenrb::Reen.new(editor: "code -w").call("spec/fixtures/example/*")
|
14
|
-
# Reenrb::Reen.new(editor: nil).call("spec/fixtures/example/*") { ... }
|
15
|
-
class Reen
|
16
|
-
DEL_ERROR = "Do not delete any file/folder names"
|
17
|
-
|
18
|
-
attr_reader :changes
|
19
|
-
|
20
|
-
def initialize(editor: "emacs", options: {})
|
21
|
-
@editor = editor
|
22
|
-
@options = options
|
23
|
-
end
|
24
|
-
|
25
|
-
def request(original_list, &block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
26
|
-
changed_list = ChangesFile.new(original_list).allow_changes do |file|
|
27
|
-
file.blocking_edit(@editor) if @editor
|
28
|
-
|
29
|
-
if block
|
30
|
-
lines = File.read(file.path).split("\n")
|
31
|
-
new_lines = block.call(lines) || lines
|
32
|
-
File.write(file.path, new_lines.join("\n"))
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
raise(Error, DEL_ERROR) if changed_list.size != original_list.size
|
37
|
-
|
38
|
-
@changes = compare_lists(original_list, changed_list)
|
39
|
-
.then { |change_array| Changes.new(change_array) }
|
40
|
-
end
|
41
|
-
|
42
|
-
def execute(original_list, &block)
|
43
|
-
@changes ||= request(original_list, &block)
|
44
|
-
@changes = @changes.execute!
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def compare_lists(original_list, changed_list)
|
50
|
-
original_list.zip(changed_list).map do |original, revised|
|
51
|
-
Change.new(original, revised)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
11
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reenrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Soumya Ray
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -110,10 +110,14 @@ files:
|
|
110
110
|
- README.md
|
111
111
|
- Rakefile
|
112
112
|
- bin/reen
|
113
|
-
- lib/change.rb
|
114
|
-
- lib/changes.rb
|
115
|
-
- lib/changes_file.rb
|
116
113
|
- lib/reenrb.rb
|
114
|
+
- lib/reenrb/actions/delete.rb
|
115
|
+
- lib/reenrb/actions/file_rename.rb
|
116
|
+
- lib/reenrb/actions/nothing.rb
|
117
|
+
- lib/reenrb/change.rb
|
118
|
+
- lib/reenrb/changes.rb
|
119
|
+
- lib/reenrb/changes_file.rb
|
120
|
+
- lib/reenrb/reen.rb
|
117
121
|
- lib/reenrb/version.rb
|
118
122
|
- reenrb.gemspec
|
119
123
|
- sig/reenrb.rbs
|
data/lib/change.rb
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Reenrb
|
4
|
-
# Change to an orignal file
|
5
|
-
class Change
|
6
|
-
attr_reader :original, :requested, :change, :status
|
7
|
-
|
8
|
-
module STATUS
|
9
|
-
ACCEPTED = :accepted
|
10
|
-
REJECTED = :rejected
|
11
|
-
EXECUTED = :executed
|
12
|
-
end
|
13
|
-
|
14
|
-
module CHANGE
|
15
|
-
NONE = :none
|
16
|
-
DELETE = :delete
|
17
|
-
RENAME = :rename
|
18
|
-
end
|
19
|
-
|
20
|
-
CHANGES_DESC = {
|
21
|
-
none: "Nothing",
|
22
|
-
delete: "Deleting",
|
23
|
-
rename: "Renaming"
|
24
|
-
}.freeze
|
25
|
-
|
26
|
-
def initialize(original, requested)
|
27
|
-
@original = original
|
28
|
-
@requested = requested
|
29
|
-
@change = compare
|
30
|
-
consider
|
31
|
-
end
|
32
|
-
|
33
|
-
def compare
|
34
|
-
if original == requested
|
35
|
-
:none
|
36
|
-
elsif requested.start_with? "-"
|
37
|
-
:delete
|
38
|
-
else
|
39
|
-
:rename
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def consider
|
44
|
-
if request_full_directory? && request_delete?
|
45
|
-
@status = STATUS::REJECTED
|
46
|
-
@reason = "Directories with files cannot be changed"
|
47
|
-
else
|
48
|
-
@status = STATUS::ACCEPTED
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def execute
|
53
|
-
return(self) if executed_or_rejected?
|
54
|
-
|
55
|
-
case @change
|
56
|
-
when :rename
|
57
|
-
File.rename(@original, @requested)
|
58
|
-
when :delete
|
59
|
-
File.delete(@original)
|
60
|
-
end
|
61
|
-
|
62
|
-
@status = STATUS::EXECUTED
|
63
|
-
self
|
64
|
-
end
|
65
|
-
|
66
|
-
# Inspection
|
67
|
-
|
68
|
-
def request_directory?
|
69
|
-
Dir.exist? @original
|
70
|
-
end
|
71
|
-
|
72
|
-
def request_delete?
|
73
|
-
@change == CHANGE::DELETE
|
74
|
-
end
|
75
|
-
|
76
|
-
def request_empty_directory?
|
77
|
-
Dir.empty? @original
|
78
|
-
end
|
79
|
-
|
80
|
-
def executed?
|
81
|
-
@status == STATUS::EXECUTED
|
82
|
-
end
|
83
|
-
|
84
|
-
def request_full_directory?
|
85
|
-
request_directory? && !request_empty_directory?
|
86
|
-
end
|
87
|
-
|
88
|
-
def executed_or_rejected?
|
89
|
-
%i[executed rejected].include? @status
|
90
|
-
end
|
91
|
-
|
92
|
-
# Decoration
|
93
|
-
|
94
|
-
def to_s
|
95
|
-
file_desc =
|
96
|
-
case @change
|
97
|
-
when :rename
|
98
|
-
"#{@original} -> #{@requested}"
|
99
|
-
else
|
100
|
-
@original
|
101
|
-
end
|
102
|
-
|
103
|
-
reason_desc = @status == STATUS::REJECTED ? " (failed: #{@reason})" : ""
|
104
|
-
|
105
|
-
"#{CHANGES_DESC[@change]}: #{file_desc}#{reason_desc}"
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
data/lib/changes_file.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "tempfile"
|
4
|
-
|
5
|
-
module Reenrb
|
6
|
-
# Manages a temporary file with requested changes
|
7
|
-
class ChangesFile
|
8
|
-
def initialize(requested_list)
|
9
|
-
@list_file = Tempfile.new("reenrb-")
|
10
|
-
@list_file.write(requested_list.join("\n"))
|
11
|
-
@list_file.close
|
12
|
-
end
|
13
|
-
|
14
|
-
def path
|
15
|
-
@list_file.path
|
16
|
-
end
|
17
|
-
|
18
|
-
def allow_changes(&block)
|
19
|
-
block.call(self)
|
20
|
-
File.read(path).split("\n")
|
21
|
-
end
|
22
|
-
|
23
|
-
def blocking_edit(editor)
|
24
|
-
`#{editor} #{@list_file.path}`
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|