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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -2
  3. data/lib/claiss/version.rb +1 -1
  4. data/lib/claiss.rb +321 -86
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7395901310b440af99e2a2c8398d2c419165457c3938c7f8d5cc394a49d416b
4
- data.tar.gz: 0cde7e9f094e6d3a873d15f886a1625e115b568c91b1f94ca947aad0cf5764f4
3
+ metadata.gz: 3c3930d146c451e61732c1decad285dcedf966e9203d96c7b9566fda7fc0f927
4
+ data.tar.gz: 2194f5f1e491c3e332da0d8097f113636db9deee184b09d59b9771acf6a037bb
5
5
  SHA512:
6
- metadata.gz: 1fad084036805ad9c71f3484c50d83e096c479c50575b3efb9d4ee0154fe40147830501f7749b94eaf19b25bb328f7a5cb6e773185338e0e9d94e775de552a93
7
- data.tar.gz: 0fba7b018af22b864ea3c19b920de07a9ccc71f94948953ae872d595778b2c0e2ac8d7237dca6ba59e9bcc081b4ae2ca29bff68c7fa157a9329e806751832ef0
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
- **Exemplo:**
207
+ **Exemplos:**
206
208
  ```bash
207
- $ claiss refactor meu_projeto regras.json --dry-run
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
@@ -1,3 +1,3 @@
1
1
  module CLAISS
2
- VERSION = '1.1.5'
2
+ VERSION = '1.1.7'
3
3
  end
data/lib/claiss.rb CHANGED
@@ -1,23 +1,24 @@
1
- require "dry/cli"
2
- require "fileutils"
3
- require "json"
4
- require "logger"
5
- require "parallel"
6
- require "ruby-progressbar"
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 = [".git/", "node_modules/"]
11
- DEFAULT_JSON_DIR = File.join(Dir.home, ".claiss")
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 # Set to Logger::DEBUG for more verbose output
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 "Print version"
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 "Refactor files and filenames"
29
+ desc 'Refactor files and filenames'
29
30
 
30
- argument :path, type: :string, required: true, desc: "Path to the directory to refactor"
31
- argument :rules, type: :string, required: true, desc: "Name of the rules file (without .json extension)"
32
- option :destination, type: :string, desc: "Destination path for refactored files"
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
- "path/to/project autokeras",
36
- "path/to/project autokeras --destination path/to/output",
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
- json_file = File.join(DEFAULT_JSON_DIR, "#{rules}.json")
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
- # Se estivermos copiando para um destino, verifica o diretório de destino
50
- # Caso contrário, verifica o diretório de origem
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 ? " to the destination" : " in place"}."
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, "**", "*"), File::FNM_DOTMATCH).reject do |file_name|
61
- File.directory?(file_name) || IGNORED_DIRECTORIES.any? { |dir| file_name.include?(dir) }
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
- refactor_file(file_name, dict, origin_path, destination_path)
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.expand_path("~/.claiss/#{json_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
- dict = JSON.parse(File.read(found_file))
93
- puts "Oba! Encontrei o arquivo em: #{found_file}"
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 "Ops! O arquivo JSON não está no formato correto. Erro: #{e.message}"
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 "Hmm, não consegui encontrar o arquivo. Procurei nesses lugares:"
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 "Vamos usar o dicionário interativo em vez disso, tá bom?"
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 "Modo interativo não implementado. Retornando dicionário vazio."
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, "**", "*"), File::FNM_DOTMATCH) do |file_name|
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: "UTF-8")
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: "BINARY")
144
- text.force_encoding("UTF-8")
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!("UTF-8", invalid: :replace, undef: :replace, replace: "")
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
- new_file_name = File.join(destination_path, new_relative_path)
161
- else
162
- new_file_name = File.join(origin_path, new_relative_path)
163
- end
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 ? "copied" : "renamed"} from #{truncated_old} to #{truncated_new}")
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
- dict.reduce(path) do |s, (search, replace)|
188
- s.gsub(/#{Regexp.escape(search)}/, replace)
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, "**", "*"), File::FNM_DOTMATCH).reverse_each do |dir_name|
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?(".git/") || dir_name.include?("node_modules/") # Ignorar pastas .git e node_modules
196
- next if dir_name == "." || dir_name == ".." # Ignorar diretórios especiais . e ..
197
- if (Dir.entries(dir_name) - %w[. ..]).empty?
198
- begin
199
- Dir.rmdir(dir_name)
200
- puts "Diretório vazio removido: #{dir_name}"
201
- rescue Errno::ENOTEMPTY, Errno::EINVAL => e
202
- puts "Não foi possível remover o diretório: #{dir_name}. Erro: #{e.message}"
203
- end
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 "Fix permissions for a Ruby project"
443
+ desc 'Fix permissions for a Ruby project'
211
444
 
212
- argument :path, type: :string, required: false, default: ".", desc: "Path to the Ruby project (default: current directory)"
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
- "path/to/ruby/project",
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, "**", "*"), File::FNM_DOTMATCH) do |item|
223
- next if item == "." || item == ".."
224
- next if item.include?("node_modules/") # Ignore node_modules folder
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(0755, item)
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 = ["bundle", "rails", "rake", "spring"]
467
+ executable_files = %w[bundle rails rake spring]
233
468
  executable_files.each do |file|
234
- file_path = File.join(path, "bin", file)
235
- File.chmod(0755, file_path) if File.exist?(file_path)
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 & 0777
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) == ".rb" || file.include?("/bin/")
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 "claiss/version"
269
- require_relative "claiss/commands"
270
- require_relative "claiss/cli"
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.5
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-05-31 00:00:00.000000000 Z
10
+ date: 2025-06-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: dry-cli