claiss 1.1.5 → 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/lib/claiss/version.rb +1 -1
- data/lib/claiss.rb +117 -74
- 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: 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/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,15 +26,15 @@ 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 autokeras',
|
37
|
+
'path/to/project autokeras --destination path/to/output'
|
37
38
|
]
|
38
39
|
|
39
40
|
def call(path:, rules:, destination: nil, **)
|
@@ -45,29 +46,38 @@ module CLAISS
|
|
45
46
|
|
46
47
|
files = get_files_to_process(origin_path)
|
47
48
|
process_files_in_parallel(files, dict, origin_path, destination_path)
|
48
|
-
|
49
|
+
|
49
50
|
# Se estivermos copiando para um destino, verifica o diretório de destino
|
50
51
|
# Caso contrário, verifica o diretório de origem
|
51
52
|
target_path = destination_path || origin_path
|
52
53
|
remove_empty_directories(target_path)
|
53
54
|
|
54
|
-
puts "Done! Files have been refactored#{destination_path ?
|
55
|
+
puts "Done! Files have been refactored#{destination_path ? ' to the destination' : ' in place'}."
|
55
56
|
end
|
56
57
|
|
57
58
|
private
|
58
59
|
|
59
60
|
def get_files_to_process(origin_path)
|
60
|
-
Dir.glob(File.join(origin_path,
|
61
|
-
File.directory?(file_name)
|
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
|
62
66
|
end
|
63
67
|
end
|
64
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
|
+
|
65
75
|
def process_files_in_parallel(files, dict, origin_path, destination_path)
|
66
76
|
progress_bar = ProgressBar.create(
|
67
77
|
total: files.size,
|
68
78
|
format: "%a %b\u{15E7}%i %p%% %t",
|
69
|
-
progress_mark:
|
70
|
-
remainder_mark: "\u{FF65}"
|
79
|
+
progress_mark: ' ',
|
80
|
+
remainder_mark: "\u{FF65}"
|
71
81
|
)
|
72
82
|
|
73
83
|
Parallel.each(files, in_threads: Parallel.processor_count) do |file_name|
|
@@ -82,7 +92,7 @@ module CLAISS
|
|
82
92
|
possible_paths = [
|
83
93
|
json_file,
|
84
94
|
File.expand_path(json_file),
|
85
|
-
File.expand_path("~/.claiss/#{json_file}")
|
95
|
+
File.expand_path("~/.claiss/#{json_file}")
|
86
96
|
]
|
87
97
|
|
88
98
|
found_file = possible_paths.find { |path| File.exist?(path) }
|
@@ -96,17 +106,17 @@ module CLAISS
|
|
96
106
|
puts "Ops! O arquivo JSON não está no formato correto. Erro: #{e.message}"
|
97
107
|
end
|
98
108
|
else
|
99
|
-
puts
|
109
|
+
puts 'Hmm, não consegui encontrar o arquivo. Procurei nesses lugares:'
|
100
110
|
possible_paths.each { |path| puts " - #{path}" }
|
101
111
|
end
|
102
112
|
|
103
|
-
puts
|
113
|
+
puts 'Vamos usar o dicionário interativo em vez disso, tá bom?'
|
104
114
|
interactive_dictionary
|
105
115
|
else
|
106
116
|
interactive_dictionary
|
107
117
|
end
|
108
118
|
end
|
109
|
-
|
119
|
+
|
110
120
|
def interactive_dictionary
|
111
121
|
# Em ambiente de teste, retorna um dicionário de substituição padrão
|
112
122
|
if ENV['RACK_ENV'] == 'test' || ENV['RAILS_ENV'] == 'test' || caller.any? { |line| line.include?('rspec') }
|
@@ -118,49 +128,54 @@ module CLAISS
|
|
118
128
|
}
|
119
129
|
else
|
120
130
|
# Implementação interativa real iria aqui
|
121
|
-
puts
|
131
|
+
puts 'Modo interativo não implementado. Retornando dicionário vazio.'
|
122
132
|
{}
|
123
133
|
end
|
124
134
|
end
|
125
135
|
|
126
136
|
def process_files(origin_path, dict, destination_path)
|
127
|
-
Dir.glob(File.join(origin_path,
|
137
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH) do |file_name|
|
128
138
|
next if File.directory?(file_name)
|
129
139
|
next if IGNORED_DIRECTORIES.any? { |dir| file_name.include?(dir) }
|
140
|
+
|
130
141
|
refactor_file(file_name, dict, origin_path, destination_path)
|
131
142
|
end
|
132
143
|
end
|
133
144
|
|
134
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
|
150
|
+
end
|
151
|
+
|
135
152
|
begin
|
136
153
|
# First, try to read the file as UTF-8
|
137
|
-
text = File.read(file_name, encoding:
|
154
|
+
text = File.read(file_name, encoding: 'UTF-8')
|
138
155
|
rescue Encoding::InvalidByteSequenceError
|
139
156
|
# If UTF-8 reading fails, fall back to binary reading and force UTF-8 encoding
|
140
157
|
# This approach helps handle files with mixed or unknown encodings
|
141
158
|
truncated_file_name = File.basename(file_name)
|
142
159
|
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(
|
160
|
+
text = File.read(file_name, encoding: 'BINARY')
|
161
|
+
text.force_encoding('UTF-8')
|
145
162
|
# Replace any invalid or undefined characters with empty string
|
146
|
-
text.encode!(
|
163
|
+
text.encode!('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
147
164
|
end
|
148
165
|
|
149
166
|
text_changed = false
|
150
167
|
dict.each do |search, replace|
|
151
|
-
if text.gsub!(/#{Regexp.escape(search)}/, replace)
|
152
|
-
text_changed = true
|
153
|
-
end
|
168
|
+
text_changed = true if text.gsub!(/#{Regexp.escape(search)}/, replace)
|
154
169
|
end
|
155
170
|
|
156
171
|
relative_path = Pathname.new(file_name).relative_path_from(Pathname.new(origin_path))
|
157
172
|
new_relative_path = replace_in_path(relative_path.to_s, dict)
|
158
173
|
|
159
|
-
if destination_path
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
164
179
|
|
165
180
|
new_dir = File.dirname(new_file_name)
|
166
181
|
FileUtils.mkdir_p(new_dir) unless File.directory?(new_dir)
|
@@ -170,19 +185,44 @@ module CLAISS
|
|
170
185
|
if destination_path || new_file_name != file_name
|
171
186
|
truncated_old = "...#{File.basename(file_name)}"
|
172
187
|
truncated_new = "...#{File.basename(new_file_name)}"
|
173
|
-
LOGGER.info("File #{destination_path ?
|
188
|
+
LOGGER.info("File #{destination_path ? 'copied' : 'renamed'} from #{truncated_old} to #{truncated_new}")
|
174
189
|
else
|
175
190
|
truncated_file = "...#{File.basename(file_name)}"
|
176
191
|
LOGGER.info("File contents updated: #{truncated_file}")
|
177
192
|
end
|
178
193
|
File.delete(file_name) if !destination_path && new_file_name != file_name
|
179
194
|
end
|
180
|
-
rescue => e
|
195
|
+
rescue StandardError => e
|
181
196
|
truncated_file = "...#{File.basename(file_name)}"
|
182
197
|
LOGGER.error("Error processing file #{truncated_file}: #{e.message}")
|
183
198
|
LOGGER.debug(e.backtrace.join("\n"))
|
184
199
|
end
|
185
200
|
|
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
|
+
|
186
226
|
def replace_in_path(path, dict)
|
187
227
|
dict.reduce(path) do |s, (search, replace)|
|
188
228
|
s.gsub(/#{Regexp.escape(search)}/, replace)
|
@@ -190,49 +230,52 @@ module CLAISS
|
|
190
230
|
end
|
191
231
|
|
192
232
|
def remove_empty_directories(origin_path)
|
193
|
-
Dir.glob(File.join(origin_path,
|
233
|
+
Dir.glob(File.join(origin_path, '**', '*'), File::FNM_DOTMATCH).reverse_each do |dir_name|
|
194
234
|
next unless File.directory?(dir_name)
|
195
|
-
next if dir_name.include?(
|
196
|
-
next if
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
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}"
|
204
245
|
end
|
205
246
|
end
|
206
247
|
end
|
207
248
|
end
|
208
249
|
|
209
250
|
class FixRubyPermissions < Dry::CLI::Command
|
210
|
-
desc
|
251
|
+
desc 'Fix permissions for a Ruby project'
|
211
252
|
|
212
|
-
argument :path, type: :string, required: false, default:
|
253
|
+
argument :path, type: :string, required: false, default: '.',
|
254
|
+
desc: 'Path to the Ruby project (default: current directory)'
|
213
255
|
|
214
256
|
example [
|
215
|
-
|
216
|
-
|
257
|
+
'',
|
258
|
+
'path/to/ruby/project'
|
217
259
|
]
|
218
260
|
|
219
|
-
def call(path:
|
261
|
+
def call(path: '.', **)
|
220
262
|
path = File.expand_path(path)
|
221
263
|
|
222
|
-
Dir.glob(File.join(path,
|
223
|
-
next if
|
224
|
-
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
|
+
|
225
268
|
if File.directory?(item)
|
226
|
-
File.chmod(
|
269
|
+
File.chmod(0o755, item)
|
227
270
|
else
|
228
271
|
fix_file_permissions(item)
|
229
272
|
end
|
230
273
|
end
|
231
274
|
|
232
|
-
executable_files = [
|
275
|
+
executable_files = %w[bundle rails rake spring]
|
233
276
|
executable_files.each do |file|
|
234
|
-
file_path = File.join(path,
|
235
|
-
File.chmod(
|
277
|
+
file_path = File.join(path, 'bin', file)
|
278
|
+
File.chmod(0o755, file_path) if File.exist?(file_path)
|
236
279
|
end
|
237
280
|
|
238
281
|
puts "Permissions fixed for Ruby project at #{path}"
|
@@ -243,28 +286,28 @@ module CLAISS
|
|
243
286
|
def fix_file_permissions(file)
|
244
287
|
# Não altera permissões de arquivos em diretórios restritos
|
245
288
|
return if file.include?('/restricted/')
|
246
|
-
|
289
|
+
|
247
290
|
# Obtém o modo atual do arquivo
|
248
|
-
current_mode = File.stat(file).mode &
|
249
|
-
|
291
|
+
current_mode = File.stat(file).mode & 0o777
|
292
|
+
|
250
293
|
# Se o arquivo já tem permissões restritivas (menos de 0600), não altera
|
251
294
|
return if current_mode < 0o600
|
252
|
-
|
295
|
+
|
253
296
|
# Define as permissões apropriadas
|
254
|
-
new_mode = if File.extname(file) ==
|
297
|
+
new_mode = if File.extname(file) == '.rb' || file.include?('/bin/')
|
255
298
|
0o755 # Executável
|
256
299
|
else
|
257
300
|
0o644 # Apenas leitura/escrita para o dono
|
258
301
|
end
|
259
|
-
|
302
|
+
|
260
303
|
# Aplica as permissões apenas se forem diferentes
|
261
304
|
File.chmod(new_mode, file) if new_mode != current_mode
|
262
|
-
rescue => e
|
305
|
+
rescue StandardError => e
|
263
306
|
LOGGER.warn("Could not change permissions for #{file}: #{e.message}")
|
264
307
|
end
|
265
308
|
end
|
266
309
|
end
|
267
310
|
|
268
|
-
require_relative
|
269
|
-
require_relative
|
270
|
-
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
|