claiss 1.1.5 → 1.1.7
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 +4 -4
- data/README.md +24 -2
- data/lib/claiss/version.rb +1 -1
- data/lib/claiss.rb +321 -86
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c3930d146c451e61732c1decad285dcedf966e9203d96c7b9566fda7fc0f927
|
4
|
+
data.tar.gz: 2194f5f1e491c3e332da0d8097f113636db9deee184b09d59b9771acf6a037bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd0230b487e14d9993b1963705bb3206091924d63cdff8f714fb1dfe9bc4d19db9390ade541d0916b99c77165dc7d6c6a7337b34ed4c30cf805abe9e952b495b
|
7
|
+
data.tar.gz: e5302f23687c15e282067bd6666810538874dae561b3db2446df01bc22a502de730e2bbc0f79c373fad04cc67c8779b2476e5fcf7b6ab9de57c937a39a68e5eb
|
data/README.md
CHANGED
@@ -59,6 +59,7 @@ $ claiss refactor <project_path> <json_file> [options]
|
|
59
59
|
**Options:**
|
60
60
|
- `--destination, -d`: Specify output directory (default: in-place)
|
61
61
|
- `--dry-run`: Preview changes without modifying files
|
62
|
+
- `--debug`: Show detailed debug messages during processing
|
62
63
|
|
63
64
|
**Example:**
|
64
65
|
```bash
|
@@ -201,14 +202,35 @@ $ claiss refactor <caminho_do_projeto> <arquivo_json> [opções]
|
|
201
202
|
**Opções:**
|
202
203
|
- `--destination, -d`: Especifica o diretório de saída (padrão: no local)
|
203
204
|
- `--dry-run`: Visualiza as alterações sem modificar os arquivos
|
205
|
+
- `--debug`: Mostra mensagens detalhadas de depuração durante o processamento
|
204
206
|
|
205
|
-
**
|
207
|
+
**Exemplos:**
|
206
208
|
```bash
|
207
|
-
|
209
|
+
# Modo normal
|
210
|
+
$ claiss refactor meu_projeto ~/.claiss/regras.json
|
211
|
+
|
212
|
+
# Especificando diretório de destino
|
213
|
+
$ claiss refactor meu_projeto ~/.claiss/regras.json --destination pasta_destino
|
214
|
+
|
215
|
+
# Modo debug (mostra mensagens detalhadas)
|
216
|
+
$ claiss refactor meu_projeto ~/.claiss/regras.json --debug
|
217
|
+
|
218
|
+
# Todas as opções juntas
|
219
|
+
$ claiss refactor meu_projeto ~/.claiss/regras.json --destination pasta_destino --debug
|
208
220
|
```
|
209
221
|
|
210
222
|
#### Formato do Dicionário JSON
|
211
223
|
|
224
|
+
O arquivo de regras pode estar em qualquer local, mas se estiver no diretório `~/.claiss/`, você pode usar apenas o nome do arquivo. Exemplo:
|
225
|
+
|
226
|
+
```bash
|
227
|
+
# Se o arquivo estiver em ~/.claiss/regras.json
|
228
|
+
$ claiss refactor meu_projeto regras.json
|
229
|
+
|
230
|
+
# Ou especifique o caminho completo
|
231
|
+
$ claiss refactor meu_projeto /caminho/completo/para/regras.json
|
232
|
+
```
|
233
|
+
|
212
234
|
Crie um arquivo JSON com pares chave-valor para as substituições:
|
213
235
|
|
214
236
|
```json
|
data/lib/claiss/version.rb
CHANGED
data/lib/claiss.rb
CHANGED
@@ -1,23 +1,24 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
1
|
+
require 'dry/cli'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'json'
|
4
|
+
require 'logger'
|
5
|
+
require 'parallel'
|
6
|
+
require 'ruby-progressbar'
|
7
|
+
require 'pathname'
|
7
8
|
|
8
9
|
# CLAISS module provides CLI commands for refactoring and managing Ruby projects
|
9
10
|
module CLAISS
|
10
|
-
IGNORED_DIRECTORIES = [
|
11
|
-
DEFAULT_JSON_DIR = File.join(Dir.home,
|
11
|
+
IGNORED_DIRECTORIES = ['.git/', 'node_modules/']
|
12
|
+
DEFAULT_JSON_DIR = File.join(Dir.home, '.claiss')
|
12
13
|
|
13
14
|
# Initialize logger
|
14
15
|
LOGGER = Logger.new(STDOUT)
|
15
|
-
LOGGER.level = Logger::INFO
|
16
|
+
LOGGER.level = Logger::INFO # Set to Logger::DEBUG for more verbose output
|
16
17
|
|
17
18
|
class Error < StandardError; end
|
18
19
|
|
19
20
|
class Version < Dry::CLI::Command
|
20
|
-
desc
|
21
|
+
desc 'Print version'
|
21
22
|
|
22
23
|
def call(*)
|
23
24
|
puts "CLAISS version #{CLAISS::VERSION}"
|
@@ -25,88 +26,193 @@ module CLAISS
|
|
25
26
|
end
|
26
27
|
|
27
28
|
class Refactor < Dry::CLI::Command
|
28
|
-
desc
|
29
|
+
desc 'Refactor files and filenames'
|
29
30
|
|
30
|
-
argument :path, type: :string, required: true, desc:
|
31
|
-
argument :rules, type: :string, required: true, desc:
|
32
|
-
option :destination, type: :string, desc:
|
31
|
+
argument :path, type: :string, required: true, desc: 'Path to the directory to refactor'
|
32
|
+
argument :rules, type: :string, required: true, desc: 'Name of the rules file (without .json extension)'
|
33
|
+
option :destination, type: :string, desc: 'Destination path for refactored files'
|
33
34
|
|
34
35
|
example [
|
35
|
-
|
36
|
-
|
36
|
+
'path/to/project rules_file',
|
37
|
+
'path/to/project rules_file --destination path/to/output',
|
38
|
+
'path/to/project rules_file --debug',
|
39
|
+
'path/to/project rules_file --destination path/to/output --debug'
|
37
40
|
]
|
38
41
|
|
39
|
-
def call(path:, rules:, destination: nil, **)
|
42
|
+
def call(path:, rules:, destination: nil, debug: false, **)
|
43
|
+
@debug = debug
|
40
44
|
origin_path = File.expand_path(path)
|
41
45
|
destination_path = destination ? File.expand_path(destination) : nil
|
42
46
|
|
43
|
-
|
47
|
+
# Primeiro tenta carregar o arquivo como está (pode ser caminho completo)
|
48
|
+
json_file = rules.end_with?('.json') ? rules : "#{rules}.json"
|
49
|
+
|
50
|
+
debug_puts("\n[DEBUG] Iniciando refatoração")
|
51
|
+
debug_puts("[DEBUG] Origem: #{origin_path}")
|
52
|
+
debug_puts("[DEBUG] Destino: #{destination_path || 'Não especificado'}")
|
53
|
+
debug_puts("[DEBUG] Arquivo de regras: #{json_file}")
|
54
|
+
|
55
|
+
# Tenta carregar o dicionário
|
44
56
|
dict = load_dictionary(json_file)
|
45
|
-
|
57
|
+
|
58
|
+
if dict.empty?
|
59
|
+
puts "[ERRO] O dicionário de substituição está vazio. Nenhuma alteração será feita."
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
63
|
+
debug_puts("[DEBUG] Dicionário carregado com #{dict.size} entradas")
|
64
|
+
|
65
|
+
# Obtém a lista de arquivos para processar
|
46
66
|
files = get_files_to_process(origin_path)
|
67
|
+
debug_puts("[DEBUG] Encontrados #{files.size} arquivos para processar")
|
68
|
+
|
69
|
+
# Processa os arquivos
|
47
70
|
process_files_in_parallel(files, dict, origin_path, destination_path)
|
48
71
|
|
49
|
-
|
50
|
-
|
72
|
+
puts "\n[SUCESSO] Processamento concluído!"
|
73
|
+
puts "Arquivos processados: #{files.size}"
|
51
74
|
target_path = destination_path || origin_path
|
52
75
|
remove_empty_directories(target_path)
|
53
76
|
|
54
|
-
puts "Done! Files have been refactored#{destination_path ?
|
77
|
+
puts "Done! Files have been refactored#{destination_path ? ' to the destination' : ' in place'}."
|
55
78
|
end
|
56
79
|
|
57
80
|
private
|
58
81
|
|
59
82
|
def get_files_to_process(origin_path)
|
60
|
-
Dir.glob(File.join(origin_path,
|
61
|
-
File.directory?(file_name)
|
83
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH).reject do |file_name|
|
84
|
+
next true if File.directory?(file_name)
|
85
|
+
next true if IGNORED_DIRECTORIES.any? { |dir| file_name.include?(dir) }
|
86
|
+
|
87
|
+
false
|
62
88
|
end
|
63
89
|
end
|
64
90
|
|
91
|
+
def is_binary_file?(file_name)
|
92
|
+
binary_extensions = ['.pdf', '.png', '.jpg', '.jpeg', '.svg', '.ico', '.gif', '.zip', '.gz', '.tar', '.bin',
|
93
|
+
'.exe', '.dll', '.so', '.dylib']
|
94
|
+
binary_extensions.any? { |ext| file_name.downcase.end_with?(ext) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def debug_puts(message)
|
98
|
+
puts message if @debug
|
99
|
+
end
|
100
|
+
|
65
101
|
def process_files_in_parallel(files, dict, origin_path, destination_path)
|
102
|
+
debug_puts("Diretório de trabalho atual: #{Dir.pwd}")
|
103
|
+
puts "\n=== Iniciando processamento de #{files.size} arquivos em paralelo ==="
|
104
|
+
puts "Dicionário de substituição: #{dict.inspect}"
|
105
|
+
puts "Diretório de origem: #{origin_path}"
|
106
|
+
puts "Diretório de destino: #{destination_path || 'Mesmo que origem'}"
|
107
|
+
|
108
|
+
# Log do diretório de trabalho atual
|
109
|
+
puts "Diretório de trabalho atual: #{Dir.pwd}"
|
110
|
+
|
66
111
|
progress_bar = ProgressBar.create(
|
67
112
|
total: files.size,
|
68
113
|
format: "%a %b\u{15E7}%i %p%% %t",
|
69
|
-
progress_mark:
|
70
|
-
remainder_mark: "\u{FF65}"
|
114
|
+
progress_mark: ' ',
|
115
|
+
remainder_mark: "\u{FF65}"
|
71
116
|
)
|
72
117
|
|
73
|
-
Parallel.each(files, in_threads: Parallel.processor_count) do |file_name|
|
74
|
-
|
118
|
+
Parallel.each(files, in_threads: [2, Parallel.processor_count].min) do |file_name|
|
119
|
+
next if File.directory?(file_name)
|
120
|
+
|
121
|
+
debug_puts("\n=== Processando arquivo: #{file_name} ===")
|
122
|
+
debug_puts("Arquivo existe? #{File.exist?(file_name) ? 'Sim' : 'NÃO'}")
|
123
|
+
|
124
|
+
begin
|
125
|
+
if is_binary_file?(file_name)
|
126
|
+
debug_puts("[BINÁRIO] Iniciando processamento de: #{file_name}")
|
127
|
+
process_binary_file_rename(file_name, dict, origin_path, destination_path)
|
128
|
+
debug_puts("[BINÁRIO] Finalizado: #{file_name}")
|
129
|
+
else
|
130
|
+
debug_puts("[TEXTO] Iniciando processamento de: #{file_name}")
|
131
|
+
refactor_file(file_name, dict, origin_path, destination_path)
|
132
|
+
debug_puts("[TEXTO] Finalizado: #{file_name}")
|
133
|
+
end
|
134
|
+
rescue StandardError => e
|
135
|
+
puts "[ERRO] Erro ao processar #{file_name}: #{e.message}"
|
136
|
+
puts e.backtrace.join("\n")
|
137
|
+
LOGGER.error("Error processing #{file_name}: #{e.message}")
|
138
|
+
LOGGER.error(e.backtrace.join("\n")) if LOGGER.debug?
|
139
|
+
end
|
140
|
+
|
75
141
|
progress_bar.increment
|
76
142
|
end
|
143
|
+
|
144
|
+
puts "\n=== Processamento concluído ==="
|
77
145
|
end
|
78
146
|
|
79
147
|
def load_dictionary(json_file)
|
148
|
+
debug_puts("\n[DEBUG] 1. Iniciando load_dictionary")
|
149
|
+
debug_puts("[DEBUG] 2. Diretório de trabalho atual: #{Dir.pwd}")
|
150
|
+
debug_puts("[DEBUG] 3. Arquivo JSON informado: #{json_file.inspect}")
|
151
|
+
|
80
152
|
if json_file
|
81
153
|
# Procura o arquivo no diretório atual e no ~/.claiss
|
82
154
|
possible_paths = [
|
83
155
|
json_file,
|
84
156
|
File.expand_path(json_file),
|
85
|
-
File.
|
86
|
-
|
157
|
+
File.join(Dir.home, '.claiss', json_file),
|
158
|
+
File.expand_path("~/.claiss/#{json_file}")
|
159
|
+
].uniq
|
160
|
+
|
161
|
+
debug_puts("[DEBUG] 4. Procurando arquivo nos seguintes locais:")
|
162
|
+
possible_paths.each_with_index do |path, i|
|
163
|
+
exists = File.exist?(path) ? 'EXISTE' : 'não encontrado'
|
164
|
+
debug_puts(" #{i+1}. #{path} (#{exists})")
|
165
|
+
if exists == 'EXISTE'
|
166
|
+
debug_puts(" Permissões: #{File.stat(path).mode.to_s(8)}")
|
167
|
+
debug_puts(" Tamanho: #{File.size(path)} bytes")
|
168
|
+
end
|
169
|
+
end
|
87
170
|
|
88
171
|
found_file = possible_paths.find { |path| File.exist?(path) }
|
89
172
|
|
90
173
|
if found_file
|
174
|
+
debug_puts("[DEBUG] 5. Arquivo encontrado em: #{found_file}")
|
91
175
|
begin
|
92
|
-
|
93
|
-
|
176
|
+
debug_puts("[DEBUG] 6. Lendo conteúdo do arquivo...")
|
177
|
+
file_content = File.read(found_file)
|
178
|
+
debug_puts("[DEBUG] 7. Tamanho do conteúdo: #{file_content.length} bytes")
|
179
|
+
debug_puts("[DEBUG] 8. Conteúdo do arquivo (início):\n#{file_content[0..200].inspect}...")
|
180
|
+
|
181
|
+
dict = JSON.parse(file_content)
|
182
|
+
debug_puts("[DEBUG] 9. Dicionário carregado com sucesso!")
|
183
|
+
debug_puts("[DEBUG] 10. Número de entradas: #{dict.size}")
|
184
|
+
debug_puts("[DEBUG] 11. Primeiras 3 entradas: #{dict.first(3).to_h.inspect}") if dict.any?
|
185
|
+
|
94
186
|
return dict
|
95
187
|
rescue JSON::ParserError => e
|
96
|
-
puts "
|
188
|
+
puts "[ERRO] O arquivo JSON não está no formato correto. Erro: #{e.message}"
|
189
|
+
debug_puts("[ERRO] Linha do erro: #{e.backtrace.find { |l| l.include?('parse') }}")
|
190
|
+
rescue StandardError => e
|
191
|
+
puts "[ERRO] Erro ao ler o arquivo: #{e.class}: #{e.message}"
|
192
|
+
debug_puts("[ERRO] Backtrace: #{e.backtrace.join("\n")}")
|
97
193
|
end
|
98
194
|
else
|
99
|
-
puts
|
195
|
+
puts '[ERRO] Não consegui encontrar o arquivo em nenhum dos locais:'
|
100
196
|
possible_paths.each { |path| puts " - #{path}" }
|
197
|
+
|
198
|
+
# Verifica se o diretório .claiss existe
|
199
|
+
claiss_dir = File.join(Dir.home, '.claiss')
|
200
|
+
if Dir.exist?(claiss_dir)
|
201
|
+
debug_puts("[DEBUG] Conteúdo do diretório #{claiss_dir}:")
|
202
|
+
Dir.entries(claiss_dir).each { |f| debug_puts(" - #{f}") }
|
203
|
+
else
|
204
|
+
debug_puts("[DEBUG] Diretório #{claiss_dir} não existe!")
|
205
|
+
end
|
101
206
|
end
|
102
207
|
|
103
|
-
puts
|
208
|
+
puts '[AVISO] Vamos usar o dicionário interativo em vez disso, tá bem?'
|
104
209
|
interactive_dictionary
|
105
210
|
else
|
211
|
+
debug_puts('[DEBUG] Nenhum arquivo JSON informado, usando dicionário interativo')
|
106
212
|
interactive_dictionary
|
107
213
|
end
|
108
214
|
end
|
109
|
-
|
215
|
+
|
110
216
|
def interactive_dictionary
|
111
217
|
# Em ambiente de teste, retorna um dicionário de substituição padrão
|
112
218
|
if ENV['RACK_ENV'] == 'test' || ENV['RAILS_ENV'] == 'test' || caller.any? { |line| line.include?('rspec') }
|
@@ -118,49 +224,54 @@ module CLAISS
|
|
118
224
|
}
|
119
225
|
else
|
120
226
|
# Implementação interativa real iria aqui
|
121
|
-
puts
|
227
|
+
puts 'Modo interativo não implementado. Retornando dicionário vazio.'
|
122
228
|
{}
|
123
229
|
end
|
124
230
|
end
|
125
231
|
|
126
232
|
def process_files(origin_path, dict, destination_path)
|
127
|
-
Dir.glob(File.join(origin_path,
|
233
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH) do |file_name|
|
128
234
|
next if File.directory?(file_name)
|
129
235
|
next if IGNORED_DIRECTORIES.any? { |dir| file_name.include?(dir) }
|
236
|
+
|
130
237
|
refactor_file(file_name, dict, origin_path, destination_path)
|
131
238
|
end
|
132
239
|
end
|
133
240
|
|
134
241
|
def refactor_file(file_name, dict, origin_path, destination_path)
|
242
|
+
if is_binary_file?(file_name)
|
243
|
+
LOGGER.info("Processing binary file (renaming only): ...#{File.basename(file_name)}")
|
244
|
+
process_binary_file_rename(file_name, dict, origin_path, destination_path)
|
245
|
+
return
|
246
|
+
end
|
247
|
+
|
135
248
|
begin
|
136
249
|
# First, try to read the file as UTF-8
|
137
|
-
text = File.read(file_name, encoding:
|
250
|
+
text = File.read(file_name, encoding: 'UTF-8')
|
138
251
|
rescue Encoding::InvalidByteSequenceError
|
139
252
|
# If UTF-8 reading fails, fall back to binary reading and force UTF-8 encoding
|
140
253
|
# This approach helps handle files with mixed or unknown encodings
|
141
254
|
truncated_file_name = File.basename(file_name)
|
142
255
|
LOGGER.warn("Invalid UTF-8 byte sequence in ...#{truncated_file_name}. Falling back to binary reading.")
|
143
|
-
text = File.read(file_name, encoding:
|
144
|
-
text.force_encoding(
|
256
|
+
text = File.read(file_name, encoding: 'BINARY')
|
257
|
+
text.force_encoding('UTF-8')
|
145
258
|
# Replace any invalid or undefined characters with empty string
|
146
|
-
text.encode!(
|
259
|
+
text.encode!('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
147
260
|
end
|
148
261
|
|
149
262
|
text_changed = false
|
150
263
|
dict.each do |search, replace|
|
151
|
-
if text.gsub!(/#{Regexp.escape(search)}/, replace)
|
152
|
-
text_changed = true
|
153
|
-
end
|
264
|
+
text_changed = true if text.gsub!(/#{Regexp.escape(search)}/, replace)
|
154
265
|
end
|
155
266
|
|
156
267
|
relative_path = Pathname.new(file_name).relative_path_from(Pathname.new(origin_path))
|
157
268
|
new_relative_path = replace_in_path(relative_path.to_s, dict)
|
158
269
|
|
159
|
-
if destination_path
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
270
|
+
new_file_name = if destination_path
|
271
|
+
File.join(destination_path, new_relative_path)
|
272
|
+
else
|
273
|
+
File.join(origin_path, new_relative_path)
|
274
|
+
end
|
164
275
|
|
165
276
|
new_dir = File.dirname(new_file_name)
|
166
277
|
FileUtils.mkdir_p(new_dir) unless File.directory?(new_dir)
|
@@ -170,69 +281,193 @@ module CLAISS
|
|
170
281
|
if destination_path || new_file_name != file_name
|
171
282
|
truncated_old = "...#{File.basename(file_name)}"
|
172
283
|
truncated_new = "...#{File.basename(new_file_name)}"
|
173
|
-
LOGGER.info("File #{destination_path ?
|
284
|
+
LOGGER.info("File #{destination_path ? 'copied' : 'renamed'} from #{truncated_old} to #{truncated_new}")
|
174
285
|
else
|
175
286
|
truncated_file = "...#{File.basename(file_name)}"
|
176
287
|
LOGGER.info("File contents updated: #{truncated_file}")
|
177
288
|
end
|
178
289
|
File.delete(file_name) if !destination_path && new_file_name != file_name
|
179
290
|
end
|
180
|
-
rescue => e
|
291
|
+
rescue StandardError => e
|
181
292
|
truncated_file = "...#{File.basename(file_name)}"
|
182
293
|
LOGGER.error("Error processing file #{truncated_file}: #{e.message}")
|
183
294
|
LOGGER.debug(e.backtrace.join("\n"))
|
184
295
|
end
|
185
296
|
|
297
|
+
def process_binary_file_rename(file_name, dict, origin_path, destination_path)
|
298
|
+
debug_puts("[DEBUG] Iniciando process_binary_file_rename")
|
299
|
+
debug_puts("[DEBUG] Arquivo: #{file_name}")
|
300
|
+
debug_puts("[DEBUG] Origem: #{origin_path}")
|
301
|
+
debug_puts("[DEBUG] Destino: #{destination_path || 'Não especificado'}")
|
302
|
+
debug_puts("[DEBUG] Dicionário: #{dict.inspect}")
|
303
|
+
|
304
|
+
begin
|
305
|
+
relative_path = Pathname.new(file_name).relative_path_from(Pathname.new(origin_path)).to_s
|
306
|
+
|
307
|
+
debug_puts("[DEBUG] Caminho relativo: #{relative_path}")
|
308
|
+
|
309
|
+
new_relative_path = replace_in_path(relative_path, dict)
|
310
|
+
debug_puts("[DEBUG] Novo caminho relativo: #{new_relative_path}")
|
311
|
+
|
312
|
+
new_file_name = if destination_path
|
313
|
+
File.join(destination_path, new_relative_path)
|
314
|
+
else
|
315
|
+
File.join(origin_path, new_relative_path)
|
316
|
+
end
|
317
|
+
|
318
|
+
debug_puts("[DEBUG] Novo caminho completo: #{new_file_name}")
|
319
|
+
|
320
|
+
if new_file_name != file_name
|
321
|
+
new_dir = File.dirname(new_file_name)
|
322
|
+
|
323
|
+
debug_puts("[DEBUG] Criando diretório: #{new_dir}")
|
324
|
+
|
325
|
+
unless File.directory?(new_dir)
|
326
|
+
debug_puts("[DEBUG] Diretório não existe, criando: #{new_dir}")
|
327
|
+
FileUtils.mkdir_p(new_dir)
|
328
|
+
end
|
329
|
+
|
330
|
+
debug_puts("[DEBUG] Movendo arquivo de:\n #{file_name}\n para:\n #{new_file_name}")
|
331
|
+
|
332
|
+
FileUtils.mv(file_name, new_file_name, force: true)
|
333
|
+
|
334
|
+
unless destination_path
|
335
|
+
debug_puts("[DEBUG] Removendo arquivo original: #{file_name}")
|
336
|
+
File.delete(file_name) if File.exist?(file_name)
|
337
|
+
end
|
338
|
+
|
339
|
+
debug_puts("[DEBUG] Arquivo movido com sucesso!")
|
340
|
+
else
|
341
|
+
debug_puts("[DEBUG] Nenhuma alteração necessária para o arquivo")
|
342
|
+
end
|
343
|
+
|
344
|
+
true
|
345
|
+
rescue StandardError => e
|
346
|
+
puts "[ERRO] Erro ao processar arquivo binário #{file_name}: #{e.message}"
|
347
|
+
debug_puts("[ERRO] Backtrace: #{e.backtrace.join("\n")}")
|
348
|
+
false
|
349
|
+
ensure
|
350
|
+
debug_puts("[DEBUG] Finalizando process_binary_file_rename\n")
|
351
|
+
puts "[ERRO CRÍTICO] Erro ao processar #{file_name}: #{e.message}"
|
352
|
+
puts e.backtrace.join("\n")
|
353
|
+
LOGGER.error("Error renaming binary file #{file_name}: #{e.message}")
|
354
|
+
LOGGER.error(e.backtrace.join("\n")) if LOGGER.debug?
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
186
358
|
def replace_in_path(path, dict)
|
187
|
-
|
188
|
-
|
359
|
+
debug_puts("[DEBUG] Iniciando replace_in_path")
|
360
|
+
debug_puts("[DEBUG] Caminho original: #{path}")
|
361
|
+
debug_puts("[DEBUG] Dicionário: #{dict.inspect}")
|
362
|
+
|
363
|
+
return path if dict.empty? || path.nil? || path.empty?
|
364
|
+
|
365
|
+
# Ordena as chaves por tamanho (maior primeiro) para evitar substituições parciais
|
366
|
+
sorted_dict = dict.sort_by { |k, _| -k.size }.to_h
|
367
|
+
debug_puts("[DEBUG] Dicionário ordenado: #{sorted_dict.inspect}")
|
368
|
+
|
369
|
+
new_path = path.dup
|
370
|
+
|
371
|
+
sorted_dict.each do |search, replace|
|
372
|
+
debug_puts("[DEBUG] Buscando: '#{search}'")
|
373
|
+
debug_puts("[DEBUG] Substituindo por: '#{replace}'")
|
374
|
+
debug_puts("[DEBUG] Caminho atual: #{new_path}")
|
375
|
+
|
376
|
+
# Verifica se o caminho contém o termo de busca
|
377
|
+
if new_path.include?(search)
|
378
|
+
# Separa diretório e nome do arquivo
|
379
|
+
dirname = File.dirname(new_path)
|
380
|
+
basename = File.basename(new_path)
|
381
|
+
debug_puts("[DEBUG] Diretório: #{dirname}")
|
382
|
+
debug_puts("[DEBUG] Nome do arquivo: #{basename}")
|
383
|
+
|
384
|
+
# Aplica a substituição apenas no nome do arquivo
|
385
|
+
new_basename = basename.gsub(search, replace)
|
386
|
+
debug_puts("[DEBUG] Novo nome do arquivo: #{new_basename}")
|
387
|
+
|
388
|
+
# Reconstrói o caminho
|
389
|
+
if dirname == '.'
|
390
|
+
new_path = new_basename
|
391
|
+
else
|
392
|
+
new_path = File.join(dirname, new_basename)
|
393
|
+
end
|
394
|
+
|
395
|
+
# Se o diretório também precisar ser substituído
|
396
|
+
dirname_parts = dirname.split(File::SEPARATOR)
|
397
|
+
new_dirname_parts = dirname_parts.map do |part|
|
398
|
+
if part.include?(search)
|
399
|
+
new_part = part.gsub(search, replace)
|
400
|
+
debug_puts("[DEBUG] Substituição no diretório: '#{part}' -> '#{new_part}'")
|
401
|
+
new_part
|
402
|
+
else
|
403
|
+
part
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
# Reconstrói o caminho completo com o diretório atualizado
|
408
|
+
new_path = File.join(*new_dirname_parts, File.basename(new_path))
|
409
|
+
|
410
|
+
debug_puts("[DEBUG] Caminho após substituição: #{new_path}")
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
if new_path != path
|
415
|
+
debug_puts("[DEBUG] Caminho alterado de:\n #{path}\n para:\n #{new_path}")
|
416
|
+
else
|
417
|
+
debug_puts("[DEBUG] Nenhuma substituição foi feita no caminho")
|
189
418
|
end
|
419
|
+
|
420
|
+
debug_puts("[DEBUG] Finalizando replace_in_path\n")
|
421
|
+
new_path
|
190
422
|
end
|
191
423
|
|
192
424
|
def remove_empty_directories(origin_path)
|
193
|
-
Dir.glob(File.join(origin_path,
|
425
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH).reverse_each do |dir_name|
|
194
426
|
next unless File.directory?(dir_name)
|
195
|
-
next if dir_name.include?(
|
196
|
-
next if
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
427
|
+
next if dir_name.include?('.git/') || dir_name.include?('node_modules/') # Ignorar pastas .git e node_modules
|
428
|
+
next if ['.', '..'].include?(dir_name) # Ignorar diretórios especiais . e ..
|
429
|
+
|
430
|
+
next unless (Dir.entries(dir_name) - %w[. ..]).empty?
|
431
|
+
|
432
|
+
begin
|
433
|
+
Dir.rmdir(dir_name)
|
434
|
+
puts "Diretório vazio removido: #{dir_name}"
|
435
|
+
rescue Errno::ENOTEMPTY, Errno::EINVAL => e
|
436
|
+
puts "Não foi possível remover o diretório: #{dir_name}. Erro: #{e.message}"
|
204
437
|
end
|
205
438
|
end
|
206
439
|
end
|
207
440
|
end
|
208
441
|
|
209
442
|
class FixRubyPermissions < Dry::CLI::Command
|
210
|
-
desc
|
443
|
+
desc 'Fix permissions for a Ruby project'
|
211
444
|
|
212
|
-
argument :path, type: :string, required: false, default:
|
445
|
+
argument :path, type: :string, required: false, default: '.',
|
446
|
+
desc: 'Path to the Ruby project (default: current directory)'
|
213
447
|
|
214
448
|
example [
|
215
|
-
|
216
|
-
|
449
|
+
'',
|
450
|
+
'path/to/ruby/project'
|
217
451
|
]
|
218
452
|
|
219
|
-
def call(path:
|
453
|
+
def call(path: '.', **)
|
220
454
|
path = File.expand_path(path)
|
221
455
|
|
222
|
-
Dir.glob(File.join(path,
|
223
|
-
next if
|
224
|
-
next if item.include?(
|
456
|
+
Dir.glob(File.join(path, '**', '*'), File::FNM_DOTMATCH) do |item|
|
457
|
+
next if ['.', '..'].include?(item)
|
458
|
+
next if item.include?('node_modules/') # Ignore node_modules folder
|
459
|
+
|
225
460
|
if File.directory?(item)
|
226
|
-
File.chmod(
|
461
|
+
File.chmod(0o755, item)
|
227
462
|
else
|
228
463
|
fix_file_permissions(item)
|
229
464
|
end
|
230
465
|
end
|
231
466
|
|
232
|
-
executable_files = [
|
467
|
+
executable_files = %w[bundle rails rake spring]
|
233
468
|
executable_files.each do |file|
|
234
|
-
file_path = File.join(path,
|
235
|
-
File.chmod(
|
469
|
+
file_path = File.join(path, 'bin', file)
|
470
|
+
File.chmod(0o755, file_path) if File.exist?(file_path)
|
236
471
|
end
|
237
472
|
|
238
473
|
puts "Permissions fixed for Ruby project at #{path}"
|
@@ -243,28 +478,28 @@ module CLAISS
|
|
243
478
|
def fix_file_permissions(file)
|
244
479
|
# Não altera permissões de arquivos em diretórios restritos
|
245
480
|
return if file.include?('/restricted/')
|
246
|
-
|
481
|
+
|
247
482
|
# Obtém o modo atual do arquivo
|
248
|
-
current_mode = File.stat(file).mode &
|
249
|
-
|
483
|
+
current_mode = File.stat(file).mode & 0o777
|
484
|
+
|
250
485
|
# Se o arquivo já tem permissões restritivas (menos de 0600), não altera
|
251
486
|
return if current_mode < 0o600
|
252
|
-
|
487
|
+
|
253
488
|
# Define as permissões apropriadas
|
254
|
-
new_mode = if File.extname(file) ==
|
489
|
+
new_mode = if File.extname(file) == '.rb' || file.include?('/bin/')
|
255
490
|
0o755 # Executável
|
256
491
|
else
|
257
492
|
0o644 # Apenas leitura/escrita para o dono
|
258
493
|
end
|
259
|
-
|
494
|
+
|
260
495
|
# Aplica as permissões apenas se forem diferentes
|
261
496
|
File.chmod(new_mode, file) if new_mode != current_mode
|
262
|
-
rescue => e
|
497
|
+
rescue StandardError => e
|
263
498
|
LOGGER.warn("Could not change permissions for #{file}: #{e.message}")
|
264
499
|
end
|
265
500
|
end
|
266
501
|
end
|
267
502
|
|
268
|
-
require_relative
|
269
|
-
require_relative
|
270
|
-
require_relative
|
503
|
+
require_relative 'claiss/version'
|
504
|
+
require_relative 'claiss/commands'
|
505
|
+
require_relative 'claiss/cli'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: claiss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Júlio Papel
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-06-01 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: dry-cli
|