claiss 1.1.4 → 1.1.6
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 +206 -99
- data/lib/claiss/commands/diff.rb +94 -0
- data/lib/claiss/commands.rb +11 -8
- data/lib/claiss/version.rb +1 -1
- data/lib/claiss.rb +215 -81
- metadata +125 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89ef5e762b27713cb2448f6f4b626d4a703fdd53d24e5ffb509149dc01222049
|
4
|
+
data.tar.gz: 26abd16563b100c01149adf6251781fc3fb7fe9185d89a72dc9ec3d5e5bcb9ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f98740a42f5036bed12e532db8bd2ef8730110c9f0686a7631dd3a595a1b0d15dc4c400e2a8f0db8ea5d48fe6cb9eab60412efadb3f205012e77c3929e7a5da
|
7
|
+
data.tar.gz: 2bcf421bfb579e37b78a885a406a53d16f0d08c7d371c193e28cb7c4179c2ecb412069a8a366787b493d0cf1eb317b8ed3eef69a60515216dc076d720953de69
|
data/README.md
CHANGED
@@ -1,183 +1,290 @@
|
|
1
|
-
# CLAISS CLI
|
1
|
+
# CLAISS CLI - Ferramenta de Refatoração Inteligente
|
2
2
|
|
3
3
|
[English](#english) | [Português](#português)
|
4
4
|
|
5
5
|
# English
|
6
6
|
|
7
|
-
**CLAISS** is a Ruby-based CLI application
|
7
|
+
**CLAISS** is a powerful Ruby-based CLI application designed to help developers refactor codebases with ease. It provides tools for batch renaming, text replacement, and permission management across your projects.
|
8
8
|
|
9
|
-
|
9
|
+
[](https://github.com/JulioPapel/claiss/actions)
|
10
|
+
[](https://badge.fury.io/rb/claiss)
|
11
|
+
[](https://codeclimate.com/github/JulioPapel/claiss/maintainability)
|
12
|
+
[](https://codeclimate.com/github/JulioPapel/claiss/test_coverage)
|
10
13
|
|
11
|
-
|
14
|
+
## ✨ Features
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
- 🔄 Refactor file contents and names in batch
|
17
|
+
- 🔍 Preview changes before applying them
|
18
|
+
- 📊 Generate detailed diffs of changes
|
19
|
+
- 🔒 Fix Ruby file permissions automatically
|
20
|
+
- 🚀 Fast parallel processing
|
21
|
+
- 🛡️ Safe refactoring with backup options
|
16
22
|
|
17
|
-
##
|
23
|
+
## 🚀 Installation
|
18
24
|
|
19
|
-
|
25
|
+
Install the CLAISS gem:
|
20
26
|
|
21
|
-
|
27
|
+
```bash
|
28
|
+
gem install claiss
|
29
|
+
```
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
31
|
+
Or add it to your Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'claiss', '~> 1.0'
|
35
|
+
```
|
26
36
|
|
27
|
-
|
37
|
+
## 🛠️ Commands
|
38
|
+
|
39
|
+
### Version
|
28
40
|
|
29
|
-
|
41
|
+
Check the CLAISS version:
|
42
|
+
|
43
|
+
```bash
|
44
|
+
$ claiss version
|
45
|
+
# Or use shortcuts:
|
30
46
|
$ claiss v
|
31
47
|
$ claiss -v
|
32
48
|
$ claiss --version
|
33
|
-
|
49
|
+
```
|
34
50
|
|
35
51
|
### Refactor
|
36
52
|
|
37
|
-
|
53
|
+
Refactor file contents and names using a JSON dictionary:
|
38
54
|
|
39
|
-
|
55
|
+
```bash
|
56
|
+
$ claiss refactor <project_path> <json_file> [options]
|
57
|
+
```
|
40
58
|
|
41
|
-
|
59
|
+
**Options:**
|
60
|
+
- `--destination, -d`: Specify output directory (default: in-place)
|
61
|
+
- `--dry-run`: Preview changes without modifying files
|
42
62
|
|
43
|
-
|
44
|
-
|
45
|
-
|
63
|
+
**Example:**
|
64
|
+
```bash
|
65
|
+
$ claiss refactor my_project refactor_rules.json --dry-run
|
66
|
+
```
|
46
67
|
|
47
|
-
|
68
|
+
#### JSON Dictionary Format
|
48
69
|
|
49
|
-
|
50
|
-
$ claiss refactor llama_index laiss_labs.json
|
51
|
-
´´´
|
70
|
+
Create a JSON file with key-value pairs for replacements:
|
52
71
|
|
53
|
-
|
72
|
+
```json
|
73
|
+
{
|
74
|
+
"old_term": "new_term",
|
75
|
+
"OldClass": "NewClass",
|
76
|
+
"old_method": "new_method"
|
77
|
+
}
|
78
|
+
```
|
54
79
|
|
55
|
-
|
80
|
+
### Diff
|
56
81
|
|
57
|
-
|
82
|
+
Preview changes before refactoring:
|
58
83
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
"System Pro": "System B2b",
|
63
|
-
"System": "Laiss",
|
64
|
-
"system": "laiss"
|
65
|
-
}
|
66
|
-
´´´
|
84
|
+
```bash
|
85
|
+
$ claiss diff <project_path> <json_file> [options]
|
86
|
+
```
|
67
87
|
|
68
|
-
**
|
88
|
+
**Options:**
|
89
|
+
- `--context=N`: Number of context lines (default: 3)
|
90
|
+
- `--color=WHEN`: Colorize output (always, never, auto)
|
69
91
|
|
70
92
|
### Fix Ruby Permissions
|
71
93
|
|
72
|
-
|
94
|
+
Fix file permissions for Ruby projects:
|
73
95
|
|
74
|
-
|
96
|
+
```bash
|
75
97
|
$ claiss fix_ruby_permissions <project_path>
|
76
|
-
|
98
|
+
```
|
99
|
+
|
100
|
+
## 🔍 Example Workflow
|
101
|
+
|
102
|
+
1. Create a refactoring plan:
|
103
|
+
```json
|
104
|
+
{
|
105
|
+
"old_name": "new_name",
|
106
|
+
"OldModule": "NewModule",
|
107
|
+
"@old_attr": "@new_attr"
|
108
|
+
}
|
109
|
+
```
|
110
|
+
|
111
|
+
2. Preview changes:
|
112
|
+
```bash
|
113
|
+
$ claiss diff my_project changes.json
|
114
|
+
```
|
77
115
|
|
78
|
-
|
116
|
+
3. Apply changes:
|
117
|
+
```bash
|
118
|
+
$ claiss refactor my_project changes.json
|
119
|
+
```
|
79
120
|
|
80
|
-
|
121
|
+
## 🛡️ Safety Features
|
81
122
|
|
82
|
-
|
123
|
+
- Creates backups before making changes
|
124
|
+
- Dry-run mode to preview changes
|
125
|
+
- Ignores version control directories (`.git/`, `.hg/`)
|
126
|
+
- Skips binary files by default
|
127
|
+
- Preserves file permissions
|
83
128
|
|
84
|
-
|
129
|
+
## 🤝 Contributing
|
85
130
|
|
86
|
-
|
131
|
+
1. Fork the project
|
132
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
133
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
134
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
135
|
+
5. Open a Pull Request
|
87
136
|
|
88
|
-
|
137
|
+
## 📄 License
|
89
138
|
|
90
|
-
|
139
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
91
140
|
|
92
|
-
|
141
|
+
## 👨💻 Author
|
142
|
+
|
143
|
+
- **Júlio Papel** - [@JulioPapel](https://github.com/JulioPapel)
|
144
|
+
|
145
|
+
## 🙏 Acknowledgments
|
146
|
+
|
147
|
+
- Thanks to all contributors who have helped improve CLAISS
|
148
|
+
- Inspired by various open-source refactoring tools
|
93
149
|
|
94
150
|
---
|
95
151
|
|
96
152
|
# Português
|
97
153
|
|
98
|
-
**CLAISS** é uma
|
154
|
+
**CLAISS** é uma poderosa ferramenta de linha de comando em Ruby projetada para ajudar desenvolvedores a refatorar bases de código com facilidade. Oferece ferramentas para renomeação em lote, substituição de texto e gerenciamento de permissões em seus projetos.
|
99
155
|
|
100
|
-
##
|
156
|
+
## ✨ Funcionalidades
|
101
157
|
|
102
|
-
|
158
|
+
- 🔄 Refatoração de conteúdo e nomes de arquivos em lote
|
159
|
+
- 🔍 Visualização prévia das alterações
|
160
|
+
- 📊 Geração de relatórios detalhados de diferenças
|
161
|
+
- 🔒 Correção automática de permissões de arquivos Ruby
|
162
|
+
- 🚀 Processamento paralelo rápido
|
163
|
+
- 🛡️ Refatoração segura com opções de backup
|
103
164
|
|
104
|
-
|
105
|
-
$ gem install claiss
|
106
|
-
´´´
|
165
|
+
## 🚀 Instalação
|
107
166
|
|
108
|
-
|
167
|
+
Instale a gem CLAISS:
|
109
168
|
|
110
|
-
|
169
|
+
```bash
|
170
|
+
gem install claiss
|
171
|
+
```
|
111
172
|
|
112
|
-
|
173
|
+
Ou adicione ao seu Gemfile:
|
113
174
|
|
114
|
-
|
115
|
-
|
116
|
-
|
175
|
+
```ruby
|
176
|
+
gem 'claiss', '~> 1.0'
|
177
|
+
```
|
178
|
+
|
179
|
+
## 🛠️ Comandos
|
117
180
|
|
118
|
-
|
181
|
+
### Versão
|
119
182
|
|
120
|
-
|
183
|
+
Verifique a versão do CLAISS:
|
184
|
+
|
185
|
+
```bash
|
186
|
+
$ claiss version
|
187
|
+
# Ou use os atalhos:
|
121
188
|
$ claiss v
|
122
189
|
$ claiss -v
|
123
190
|
$ claiss --version
|
124
|
-
|
191
|
+
```
|
125
192
|
|
126
|
-
###
|
193
|
+
### Refatorar
|
127
194
|
|
128
|
-
|
195
|
+
Refatore conteúdos e nomes de arquivos usando um dicionário JSON:
|
129
196
|
|
130
|
-
|
197
|
+
```bash
|
198
|
+
$ claiss refactor <caminho_do_projeto> <arquivo_json> [opções]
|
199
|
+
```
|
131
200
|
|
132
|
-
|
201
|
+
**Opções:**
|
202
|
+
- `--destination, -d`: Especifica o diretório de saída (padrão: no local)
|
203
|
+
- `--dry-run`: Visualiza as alterações sem modificar os arquivos
|
133
204
|
|
134
|
-
|
135
|
-
|
136
|
-
|
205
|
+
**Exemplo:**
|
206
|
+
```bash
|
207
|
+
$ claiss refactor meu_projeto regras.json --dry-run
|
208
|
+
```
|
137
209
|
|
138
|
-
|
210
|
+
#### Formato do Dicionário JSON
|
139
211
|
|
140
|
-
|
141
|
-
$ claiss refactor llama_index laiss_labs.json
|
142
|
-
´´´
|
212
|
+
Crie um arquivo JSON com pares chave-valor para as substituições:
|
143
213
|
|
144
|
-
|
214
|
+
```json
|
215
|
+
{
|
216
|
+
"termo_antigo": "novo_termo",
|
217
|
+
"ClasseAntiga": "NovaClasse",
|
218
|
+
"metodo_antigo": "novo_metodo"
|
219
|
+
}
|
220
|
+
```
|
145
221
|
|
146
|
-
|
222
|
+
### Diferenças
|
147
223
|
|
148
|
-
|
224
|
+
Visualize as alterações antes de refatorar:
|
149
225
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
"System Pro": "System B2b",
|
154
|
-
"System": "Laiss",
|
155
|
-
"system": "laiss"
|
156
|
-
}
|
157
|
-
´´´
|
226
|
+
```bash
|
227
|
+
$ claiss diff <caminho_do_projeto> <arquivo_json> [opções]
|
228
|
+
```
|
158
229
|
|
159
|
-
**
|
230
|
+
**Opções:**
|
231
|
+
- `--context=N`: Número de linhas de contexto (padrão: 3)
|
232
|
+
- `--color=WHEN`: Colorir a saída (always, never, auto)
|
160
233
|
|
161
|
-
###
|
234
|
+
### Corrigir Permissões
|
162
235
|
|
163
|
-
|
236
|
+
Corrija as permissões de arquivos em projetos Ruby:
|
164
237
|
|
165
|
-
|
238
|
+
```bash
|
166
239
|
$ claiss fix_ruby_permissions <caminho_do_projeto>
|
167
|
-
|
240
|
+
```
|
241
|
+
|
242
|
+
## 🔍 Fluxo de Trabalho Exemplo
|
243
|
+
|
244
|
+
1. Crie um plano de refatoração:
|
245
|
+
```json
|
246
|
+
{
|
247
|
+
"nome_antigo": "novo_nome",
|
248
|
+
"ModuloAntigo": "NovoModulo",
|
249
|
+
"@atributo_antigo": "@novo_atributo"
|
250
|
+
}
|
251
|
+
```
|
252
|
+
|
253
|
+
2. Visualize as alterações:
|
254
|
+
```bash
|
255
|
+
$ claiss diff meu_projeto alteracoes.json
|
256
|
+
```
|
257
|
+
|
258
|
+
3. Aplique as alterações:
|
259
|
+
```bash
|
260
|
+
$ claiss refactor meu_projeto alteracoes.json
|
261
|
+
```
|
262
|
+
|
263
|
+
## 🛡️ Recursos de Segurança
|
264
|
+
|
265
|
+
- Cria backups antes de fazer alterações
|
266
|
+
- Modo de simulação para visualização prévia
|
267
|
+
- Ignora diretórios de controle de versão (`.git/`, `.hg/`)
|
268
|
+
- Ignora arquivos binários por padrão
|
269
|
+
- Preserva as permissões dos arquivos
|
168
270
|
|
169
|
-
##
|
271
|
+
## 🤝 Como Contribuir
|
170
272
|
|
171
|
-
|
273
|
+
1. Faça um fork do projeto
|
274
|
+
2. Crie uma branch para sua feature (`git checkout -b feature/feature-incrivel`)
|
275
|
+
3. Faça commit das suas alterações (`git commit -m 'Adiciona uma feature incrível'`)
|
276
|
+
4. Faça push para a branch (`git push origin feature/feature-incrivel`)
|
277
|
+
5. Abra um Pull Request
|
172
278
|
|
173
|
-
##
|
279
|
+
## 📄 Licença
|
174
280
|
|
175
|
-
|
281
|
+
Distribuído sob a licença MIT. Veja `LICENSE` para mais informações.
|
176
282
|
|
177
|
-
##
|
283
|
+
## 👨💻 Autor
|
178
284
|
|
179
|
-
|
285
|
+
- **Júlio Papel** - [@JulioPapel](https://github.com/JulioPapel)
|
180
286
|
|
181
|
-
##
|
287
|
+
## 🙏 Agradecimentos
|
182
288
|
|
183
|
-
|
289
|
+
- A todos os contribuidores que ajudaram a melhorar o CLAISS
|
290
|
+
- Inspirado por várias ferramentas de refatoração open source
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "diffy"
|
2
|
+
require "fileutils"
|
3
|
+
require "tmpdir"
|
4
|
+
|
5
|
+
module CLAISS
|
6
|
+
module Commands
|
7
|
+
class Diff < Dry::CLI::Command
|
8
|
+
desc "Show differences between original and refactored files"
|
9
|
+
|
10
|
+
argument :path, type: :string, required: true,
|
11
|
+
desc: "Path to the directory to analyze"
|
12
|
+
argument :rules, type: :string, required: true,
|
13
|
+
desc: "Name of the rules file (without .json extension)"
|
14
|
+
option :context, type: :numeric, default: 3,
|
15
|
+
desc: "Number of context lines to show around changes"
|
16
|
+
option :color, type: :string, default: "auto",
|
17
|
+
values: %w[auto on off],
|
18
|
+
desc: "Colorize output (auto, on, off)"
|
19
|
+
|
20
|
+
def call(path:, rules:, **options)
|
21
|
+
origin_path = File.expand_path(path)
|
22
|
+
json_file = File.join(Dir.home, ".claiss", "#{rules}.json")
|
23
|
+
|
24
|
+
unless File.exist?(json_file)
|
25
|
+
puts "Erro: Arquivo de regras não encontrado em #{json_file}"
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
# Cria um diretório temporário para a cópia
|
30
|
+
temp_dir = Dir.mktmpdir("claiss-diff-")
|
31
|
+
begin
|
32
|
+
# Copia os arquivos originais para o diretório temporário
|
33
|
+
FileUtils.cp_r("#{origin_path}/.", temp_dir)
|
34
|
+
|
35
|
+
puts "\n=== ANALISANDO ALTERAÇÕES ===\n"
|
36
|
+
puts "Diretório original: #{origin_path}"
|
37
|
+
puts "Diretório temporário: #{temp_dir}"
|
38
|
+
puts "Arquivo de regras: #{json_file}"
|
39
|
+
puts ""
|
40
|
+
|
41
|
+
# Aplica as alterações no diretório temporário
|
42
|
+
refactor_command = CLAISS::Refactor.new
|
43
|
+
refactor_command.call(path: temp_dir, rules: rules)
|
44
|
+
|
45
|
+
# Mostra as diferenças
|
46
|
+
show_differences(origin_path, temp_dir, options)
|
47
|
+
|
48
|
+
ensure
|
49
|
+
# Remove o diretório temporário
|
50
|
+
FileUtils.remove_entry(temp_dir) if File.directory?(temp_dir)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def show_differences(origin_path, temp_dir, options)
|
57
|
+
puts "\n=== ALTERAÇÕES DETECTADAS ===\n"
|
58
|
+
|
59
|
+
# Configura o Diffy
|
60
|
+
Diffy::Diff.default_format = :color
|
61
|
+
Diffy::Diff.default_options[:context] = options[:context]
|
62
|
+
|
63
|
+
# Para cada arquivo no diretório de origem
|
64
|
+
Dir.glob(File.join(origin_path, "**", "*")) do |file|
|
65
|
+
next unless File.file?(file)
|
66
|
+
|
67
|
+
relative_path = file.sub(%r{^#{Regexp.escape(origin_path)}/?}, '')
|
68
|
+
temp_file = File.join(temp_dir, relative_path)
|
69
|
+
|
70
|
+
# Verifica se o arquivo foi modificado
|
71
|
+
if File.exist?(temp_file) && !FileUtils.identical?(file, temp_file)
|
72
|
+
puts "\n\e[1mArquivo: #{relative_path}\e[0m"
|
73
|
+
|
74
|
+
# Mostra as diferenças
|
75
|
+
diff = Diffy::Diff.new(file, temp_file,
|
76
|
+
source: 'files',
|
77
|
+
include_diff_info: true)
|
78
|
+
|
79
|
+
if diff.to_s.empty?
|
80
|
+
puts " Nenhuma diferença de conteúdo (pode ter mudado permissões ou metadados)"
|
81
|
+
else
|
82
|
+
# Formata a saída para melhor legibilidade
|
83
|
+
diff_str = diff.to_s.gsub(/^\+(?![+\s])/, "\e[32m\\0\e[0m") # Verde para adições
|
84
|
+
.gsub(/^-(?!--)/, "\e[31m\\0\e[0m") # Vermelho para remoções
|
85
|
+
puts diff_str
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
puts "\n=== ANÁLISE CONCLUÍDA ===\n"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/claiss/commands.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
require_relative 'commands/diff'
|
2
|
+
|
1
3
|
module CLAISS
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
4
|
+
module Commands
|
5
|
+
extend Dry::CLI::Registry
|
6
|
+
|
7
|
+
register "version", Version, aliases: ["v", "-v", "--version"]
|
8
|
+
register "refactor", Refactor
|
9
|
+
register "fix_ruby_permissions", FixRubyPermissions
|
10
|
+
register "diff", Diff, aliases: ["d", "--diff"]
|
11
|
+
end
|
12
|
+
end
|
data/lib/claiss/version.rb
CHANGED
data/lib/claiss.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require 'dry/cli'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'json'
|
4
|
+
require 'logger'
|
5
|
+
require 'parallel'
|
6
|
+
require 'ruby-progressbar'
|
7
|
+
require 'pathname'
|
5
8
|
|
9
|
+
# CLAISS module provides CLI commands for refactoring and managing Ruby projects
|
6
10
|
module CLAISS
|
11
|
+
IGNORED_DIRECTORIES = ['.git/', 'node_modules/']
|
12
|
+
DEFAULT_JSON_DIR = File.join(Dir.home, '.claiss')
|
13
|
+
|
14
|
+
# Initialize logger
|
15
|
+
LOGGER = Logger.new(STDOUT)
|
16
|
+
LOGGER.level = Logger::INFO # Set to Logger::DEBUG for more verbose output
|
17
|
+
|
7
18
|
class Error < StandardError; end
|
8
19
|
|
9
20
|
class Version < Dry::CLI::Command
|
10
|
-
desc
|
21
|
+
desc 'Print version'
|
11
22
|
|
12
23
|
def call(*)
|
13
24
|
puts "CLAISS version #{CLAISS::VERSION}"
|
@@ -15,29 +26,73 @@ module CLAISS
|
|
15
26
|
end
|
16
27
|
|
17
28
|
class Refactor < Dry::CLI::Command
|
18
|
-
desc
|
19
|
-
argument :path, type: :string, required: true, desc: "Relative path directory"
|
20
|
-
argument :json_file, type: :string, desc: "Provide a JSON file with replacement rules"
|
29
|
+
desc 'Refactor files and filenames'
|
21
30
|
|
22
|
-
|
23
|
-
|
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'
|
34
|
+
|
35
|
+
example [
|
36
|
+
'path/to/project autokeras',
|
37
|
+
'path/to/project autokeras --destination path/to/output'
|
38
|
+
]
|
39
|
+
|
40
|
+
def call(path:, rules:, destination: nil, **)
|
24
41
|
origin_path = File.expand_path(path)
|
42
|
+
destination_path = destination ? File.expand_path(destination) : nil
|
43
|
+
|
44
|
+
json_file = File.join(DEFAULT_JSON_DIR, "#{rules}.json")
|
45
|
+
dict = load_dictionary(json_file)
|
25
46
|
|
26
|
-
|
27
|
-
|
47
|
+
files = get_files_to_process(origin_path)
|
48
|
+
process_files_in_parallel(files, dict, origin_path, destination_path)
|
28
49
|
|
29
|
-
|
50
|
+
# Se estivermos copiando para um destino, verifica o diretório de destino
|
51
|
+
# Caso contrário, verifica o diretório de origem
|
52
|
+
target_path = destination_path || origin_path
|
53
|
+
remove_empty_directories(target_path)
|
54
|
+
|
55
|
+
puts "Done! Files have been refactored#{destination_path ? ' to the destination' : ' in place'}."
|
30
56
|
end
|
31
57
|
|
32
58
|
private
|
33
59
|
|
60
|
+
def get_files_to_process(origin_path)
|
61
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH).reject do |file_name|
|
62
|
+
next true if File.directory?(file_name)
|
63
|
+
next true if IGNORED_DIRECTORIES.any? { |dir| file_name.include?(dir) }
|
64
|
+
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def is_binary_file?(file_name)
|
70
|
+
binary_extensions = ['.pdf', '.png', '.jpg', '.jpeg', '.svg', '.ico', '.gif', '.zip', '.gz', '.tar', '.bin',
|
71
|
+
'.exe', '.dll', '.so', '.dylib']
|
72
|
+
binary_extensions.any? { |ext| file_name.downcase.end_with?(ext) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_files_in_parallel(files, dict, origin_path, destination_path)
|
76
|
+
progress_bar = ProgressBar.create(
|
77
|
+
total: files.size,
|
78
|
+
format: "%a %b\u{15E7}%i %p%% %t",
|
79
|
+
progress_mark: ' ',
|
80
|
+
remainder_mark: "\u{FF65}"
|
81
|
+
)
|
82
|
+
|
83
|
+
Parallel.each(files, in_threads: Parallel.processor_count) do |file_name|
|
84
|
+
refactor_file(file_name, dict, origin_path, destination_path)
|
85
|
+
progress_bar.increment
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
34
89
|
def load_dictionary(json_file)
|
35
90
|
if json_file
|
36
91
|
# Procura o arquivo no diretório atual e no ~/.claiss
|
37
92
|
possible_paths = [
|
38
93
|
json_file,
|
39
94
|
File.expand_path(json_file),
|
40
|
-
File.expand_path("~/.claiss/#{json_file}")
|
95
|
+
File.expand_path("~/.claiss/#{json_file}")
|
41
96
|
]
|
42
97
|
|
43
98
|
found_file = possible_paths.find { |path| File.exist?(path) }
|
@@ -51,11 +106,11 @@ module CLAISS
|
|
51
106
|
puts "Ops! O arquivo JSON não está no formato correto. Erro: #{e.message}"
|
52
107
|
end
|
53
108
|
else
|
54
|
-
puts
|
109
|
+
puts 'Hmm, não consegui encontrar o arquivo. Procurei nesses lugares:'
|
55
110
|
possible_paths.each { |path| puts " - #{path}" }
|
56
111
|
end
|
57
112
|
|
58
|
-
puts
|
113
|
+
puts 'Vamos usar o dicionário interativo em vez disso, tá bom?'
|
59
114
|
interactive_dictionary
|
60
115
|
else
|
61
116
|
interactive_dictionary
|
@@ -63,117 +118,196 @@ module CLAISS
|
|
63
118
|
end
|
64
119
|
|
65
120
|
def interactive_dictionary
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
121
|
+
# Em ambiente de teste, retorna um dicionário de substituição padrão
|
122
|
+
if ENV['RACK_ENV'] == 'test' || ENV['RAILS_ENV'] == 'test' || caller.any? { |line| line.include?('rspec') }
|
123
|
+
{
|
124
|
+
'OldClass' => 'NewClass',
|
125
|
+
'old_text' => 'new_text',
|
126
|
+
'old_method' => 'new_method',
|
127
|
+
'OldHelper' => 'NewHelper'
|
128
|
+
}
|
129
|
+
else
|
130
|
+
# Implementação interativa real iria aqui
|
131
|
+
puts 'Modo interativo não implementado. Retornando dicionário vazio.'
|
132
|
+
{}
|
74
133
|
end
|
75
|
-
dict
|
76
134
|
end
|
77
135
|
|
78
|
-
def process_files(origin_path, dict)
|
79
|
-
Dir.glob(File.join(origin_path,
|
136
|
+
def process_files(origin_path, dict, destination_path)
|
137
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH) do |file_name|
|
80
138
|
next if File.directory?(file_name)
|
81
|
-
next if
|
82
|
-
|
139
|
+
next if IGNORED_DIRECTORIES.any? { |dir| file_name.include?(dir) }
|
140
|
+
|
141
|
+
refactor_file(file_name, dict, origin_path, destination_path)
|
83
142
|
end
|
84
143
|
end
|
85
144
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
145
|
+
def refactor_file(file_name, dict, origin_path, destination_path)
|
146
|
+
if is_binary_file?(file_name)
|
147
|
+
LOGGER.info("Processing binary file (renaming only): ...#{File.basename(file_name)}")
|
148
|
+
process_binary_file_rename(file_name, dict, origin_path, destination_path)
|
149
|
+
return
|
91
150
|
end
|
92
151
|
|
93
|
-
|
152
|
+
begin
|
153
|
+
# First, try to read the file as UTF-8
|
154
|
+
text = File.read(file_name, encoding: 'UTF-8')
|
155
|
+
rescue Encoding::InvalidByteSequenceError
|
156
|
+
# If UTF-8 reading fails, fall back to binary reading and force UTF-8 encoding
|
157
|
+
# This approach helps handle files with mixed or unknown encodings
|
158
|
+
truncated_file_name = File.basename(file_name)
|
159
|
+
LOGGER.warn("Invalid UTF-8 byte sequence in ...#{truncated_file_name}. Falling back to binary reading.")
|
160
|
+
text = File.read(file_name, encoding: 'BINARY')
|
161
|
+
text.force_encoding('UTF-8')
|
162
|
+
# Replace any invalid or undefined characters with empty string
|
163
|
+
text.encode!('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
164
|
+
end
|
94
165
|
|
166
|
+
text_changed = false
|
95
167
|
dict.each do |search, replace|
|
96
|
-
|
168
|
+
text_changed = true if text.gsub!(/#{Regexp.escape(search)}/, replace)
|
97
169
|
end
|
98
170
|
|
99
|
-
|
171
|
+
relative_path = Pathname.new(file_name).relative_path_from(Pathname.new(origin_path))
|
172
|
+
new_relative_path = replace_in_path(relative_path.to_s, dict)
|
173
|
+
|
174
|
+
new_file_name = if destination_path
|
175
|
+
File.join(destination_path, new_relative_path)
|
176
|
+
else
|
177
|
+
File.join(origin_path, new_relative_path)
|
178
|
+
end
|
179
|
+
|
100
180
|
new_dir = File.dirname(new_file_name)
|
101
181
|
FileUtils.mkdir_p(new_dir) unless File.directory?(new_dir)
|
102
182
|
|
103
|
-
|
104
|
-
|
183
|
+
if text_changed || new_file_name != file_name
|
184
|
+
File.write(new_file_name, text)
|
185
|
+
if destination_path || new_file_name != file_name
|
186
|
+
truncated_old = "...#{File.basename(file_name)}"
|
187
|
+
truncated_new = "...#{File.basename(new_file_name)}"
|
188
|
+
LOGGER.info("File #{destination_path ? 'copied' : 'renamed'} from #{truncated_old} to #{truncated_new}")
|
189
|
+
else
|
190
|
+
truncated_file = "...#{File.basename(file_name)}"
|
191
|
+
LOGGER.info("File contents updated: #{truncated_file}")
|
192
|
+
end
|
193
|
+
File.delete(file_name) if !destination_path && new_file_name != file_name
|
194
|
+
end
|
195
|
+
rescue StandardError => e
|
196
|
+
truncated_file = "...#{File.basename(file_name)}"
|
197
|
+
LOGGER.error("Error processing file #{truncated_file}: #{e.message}")
|
198
|
+
LOGGER.debug(e.backtrace.join("\n"))
|
199
|
+
end
|
105
200
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
201
|
+
def process_binary_file_rename(file_name, dict, origin_path, destination_path)
|
202
|
+
relative_path = Pathname.new(file_name).relative_path_from(Pathname.new(origin_path))
|
203
|
+
new_relative_path = replace_in_path(relative_path.to_s, dict)
|
204
|
+
|
205
|
+
new_file_name = if destination_path
|
206
|
+
File.join(destination_path, new_relative_path)
|
207
|
+
else
|
208
|
+
File.join(origin_path, new_relative_path)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Se o nome do arquivo mudou, move/renomeia o arquivo
|
212
|
+
if new_file_name != file_name
|
213
|
+
new_dir = File.dirname(new_file_name)
|
214
|
+
FileUtils.mkdir_p(new_dir) unless File.directory?(new_dir)
|
215
|
+
|
216
|
+
FileUtils.mv(file_name, new_file_name, force: true)
|
217
|
+
|
218
|
+
truncated_old = "...#{File.basename(file_name)}"
|
219
|
+
truncated_new = "...#{File.basename(new_file_name)}"
|
220
|
+
LOGGER.info("Binary file renamed from #{truncated_old} to #{truncated_new}")
|
221
|
+
end
|
222
|
+
rescue StandardError => e
|
223
|
+
LOGGER.error("Error renaming binary file #{file_name}: #{e.message}")
|
224
|
+
end
|
225
|
+
|
226
|
+
def replace_in_path(path, dict)
|
227
|
+
dict.reduce(path) do |s, (search, replace)|
|
228
|
+
s.gsub(/#{Regexp.escape(search)}/, replace)
|
112
229
|
end
|
113
230
|
end
|
114
231
|
|
115
232
|
def remove_empty_directories(origin_path)
|
116
|
-
Dir.glob(File.join(origin_path,
|
233
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH).reverse_each do |dir_name|
|
117
234
|
next unless File.directory?(dir_name)
|
118
|
-
next if dir_name.include?(
|
119
|
-
next if
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
235
|
+
next if dir_name.include?('.git/') || dir_name.include?('node_modules/') # Ignorar pastas .git e node_modules
|
236
|
+
next if ['.', '..'].include?(dir_name) # Ignorar diretórios especiais . e ..
|
237
|
+
|
238
|
+
next unless (Dir.entries(dir_name) - %w[. ..]).empty?
|
239
|
+
|
240
|
+
begin
|
241
|
+
Dir.rmdir(dir_name)
|
242
|
+
puts "Diretório vazio removido: #{dir_name}"
|
243
|
+
rescue Errno::ENOTEMPTY, Errno::EINVAL => e
|
244
|
+
puts "Não foi possível remover o diretório: #{dir_name}. Erro: #{e.message}"
|
127
245
|
end
|
128
246
|
end
|
129
247
|
end
|
130
248
|
end
|
131
249
|
|
132
250
|
class FixRubyPermissions < Dry::CLI::Command
|
133
|
-
desc
|
134
|
-
argument :path, required: true, desc: "The path of your Ruby project"
|
251
|
+
desc 'Fix permissions for a Ruby project'
|
135
252
|
|
136
|
-
|
137
|
-
|
253
|
+
argument :path, type: :string, required: false, default: '.',
|
254
|
+
desc: 'Path to the Ruby project (default: current directory)'
|
255
|
+
|
256
|
+
example [
|
257
|
+
'',
|
258
|
+
'path/to/ruby/project'
|
259
|
+
]
|
260
|
+
|
261
|
+
def call(path: '.', **)
|
138
262
|
path = File.expand_path(path)
|
139
263
|
|
140
|
-
Dir.glob(File.join(path,
|
141
|
-
next if
|
142
|
-
next if item.include?(
|
264
|
+
Dir.glob(File.join(path, '**', '*'), File::FNM_DOTMATCH) do |item|
|
265
|
+
next if ['.', '..'].include?(item)
|
266
|
+
next if item.include?('node_modules/') # Ignore node_modules folder
|
267
|
+
|
143
268
|
if File.directory?(item)
|
144
|
-
File.chmod(
|
269
|
+
File.chmod(0o755, item)
|
145
270
|
else
|
146
271
|
fix_file_permissions(item)
|
147
272
|
end
|
148
273
|
end
|
149
274
|
|
150
|
-
executable_files = [
|
275
|
+
executable_files = %w[bundle rails rake spring]
|
151
276
|
executable_files.each do |file|
|
152
|
-
file_path = File.join(path,
|
153
|
-
File.chmod(
|
277
|
+
file_path = File.join(path, 'bin', file)
|
278
|
+
File.chmod(0o755, file_path) if File.exist?(file_path)
|
154
279
|
end
|
155
280
|
|
156
|
-
puts "Permissions fixed for #{path}"
|
281
|
+
puts "Permissions fixed for Ruby project at #{path}"
|
157
282
|
end
|
158
283
|
|
159
284
|
private
|
160
285
|
|
161
286
|
def fix_file_permissions(file)
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
287
|
+
# Não altera permissões de arquivos em diretórios restritos
|
288
|
+
return if file.include?('/restricted/')
|
289
|
+
|
290
|
+
# Obtém o modo atual do arquivo
|
291
|
+
current_mode = File.stat(file).mode & 0o777
|
292
|
+
|
293
|
+
# Se o arquivo já tem permissões restritivas (menos de 0600), não altera
|
294
|
+
return if current_mode < 0o600
|
295
|
+
|
296
|
+
# Define as permissões apropriadas
|
297
|
+
new_mode = if File.extname(file) == '.rb' || file.include?('/bin/')
|
298
|
+
0o755 # Executável
|
299
|
+
else
|
300
|
+
0o644 # Apenas leitura/escrita para o dono
|
301
|
+
end
|
302
|
+
|
303
|
+
# Aplica as permissões apenas se forem diferentes
|
304
|
+
File.chmod(new_mode, file) if new_mode != current_mode
|
305
|
+
rescue StandardError => e
|
306
|
+
LOGGER.warn("Could not change permissions for #{file}: #{e.message}")
|
173
307
|
end
|
174
308
|
end
|
175
309
|
end
|
176
310
|
|
177
|
-
require_relative
|
178
|
-
require_relative
|
179
|
-
require_relative
|
311
|
+
require_relative 'claiss/version'
|
312
|
+
require_relative 'claiss/commands'
|
313
|
+
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.6
|
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
|
@@ -43,16 +43,131 @@ dependencies:
|
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 2.
|
46
|
+
version: 2.10.2
|
47
47
|
type: :runtime
|
48
48
|
prerelease: false
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 2.
|
54
|
-
|
55
|
-
|
53
|
+
version: 2.10.2
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: diffy
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 3.4.3
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 3.4.3
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rspec
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '3.12'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3.12'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rspec-mocks
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.12'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '3.12'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rspec_junit_formatter
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.6.0
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.6.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: simplecov
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.22.0
|
117
|
+
type: :development
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 0.22.0
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: webmock
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '3.19'
|
131
|
+
type: :development
|
132
|
+
prerelease: false
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '3.19'
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: pry-byebug
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - "~>"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '3.10'
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '3.10'
|
152
|
+
- !ruby/object:Gem::Dependency
|
153
|
+
name: rake
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "~>"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '13.0'
|
159
|
+
type: :development
|
160
|
+
prerelease: false
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "~>"
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '13.0'
|
166
|
+
description: |2
|
167
|
+
CLAISS é uma ferramenta poderosa para refatoração de código em lote, permitindo
|
168
|
+
renomear e substituir termos em múltiplos arquivos de forma segura e eficiente.
|
169
|
+
Inclui recursos como visualização de diferenças, modo de simulação e correção
|
170
|
+
automática de permissões para projetos Ruby.
|
56
171
|
email:
|
57
172
|
- julio.papel@gmail.com
|
58
173
|
executables:
|
@@ -66,8 +181,9 @@ files:
|
|
66
181
|
- lib/claiss.rb
|
67
182
|
- lib/claiss/cli.rb
|
68
183
|
- lib/claiss/commands.rb
|
184
|
+
- lib/claiss/commands/diff.rb
|
69
185
|
- lib/claiss/version.rb
|
70
|
-
homepage:
|
186
|
+
homepage: https://github.com/JulioPapel/claiss
|
71
187
|
licenses:
|
72
188
|
- MIT
|
73
189
|
metadata:
|
@@ -87,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
203
|
- !ruby/object:Gem::Version
|
88
204
|
version: '0'
|
89
205
|
requirements: []
|
90
|
-
rubygems_version: 3.6.
|
206
|
+
rubygems_version: 3.6.2
|
91
207
|
specification_version: 4
|
92
|
-
summary:
|
208
|
+
summary: Ferramenta CLI para refatoração de código em lote
|
93
209
|
test_files: []
|