jeanny 0.8
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.
- data/README +8 -0
- data/lib/jeanny/engine.rb +341 -0
- data/lib/jeanny/extend.rb +179 -0
- data/lib/jeanny/sugar.rb +234 -0
- data/lib/jeanny.rb +20 -0
- metadata +60 -0
data/README
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
|
2
|
+
module Jeanny
|
3
|
+
|
4
|
+
# Класс который выполнят всю основную работу.
|
5
|
+
# Парсит и заменяет классы, сохраняет и сравнивает их.
|
6
|
+
class Engine
|
7
|
+
|
8
|
+
attr_reader :classes
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@classes = Dictionary.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Метод ищет имена классов, в переданном ему тексте
|
15
|
+
def analyze file_meat
|
16
|
+
|
17
|
+
fail TypeError, "передан неверный аргумент (Jeanny::Engine.analyze)" if file_meat.empty?
|
18
|
+
|
19
|
+
# Удаляем все экспрешены и удаляем все что в простых и фигурных скобках
|
20
|
+
file_meat = file_meat.remove_expressions.gsub(/\{.*?\}/m , '{}').gsub(/\(.*?\)/, '()')
|
21
|
+
|
22
|
+
short_words = generate_short_words
|
23
|
+
|
24
|
+
# Находим имена классов
|
25
|
+
file_meat.gsub(/\.([^\.,\{\} :#\[\]\*\n\s\/]+)/) do |match|
|
26
|
+
# Если найденная строка соответствует маске и класс еще не был добавлен — добавляем его
|
27
|
+
@classes[$1] = short_words.shift if match =~ /^\.([a-z]-.+)$/ and not(@classes.include? $1 )
|
28
|
+
end
|
29
|
+
|
30
|
+
fail JeannyClassesNotFound, "похоже, что в анализируемом файле нет классов подходящих по условию" if @classes.empty?
|
31
|
+
|
32
|
+
# @classes.sort!
|
33
|
+
@classes
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# Метод сравниваеи найденные классы с переданными в аргументе saved_classes
|
38
|
+
# и возвращает имена элементво которых нет в saved_classes
|
39
|
+
def compare_with saved_classes
|
40
|
+
|
41
|
+
return if saved_classes.nil? or saved_classes.empty?
|
42
|
+
|
43
|
+
saved_classes = Dictionary.new saved_classes
|
44
|
+
|
45
|
+
# находим новые классы
|
46
|
+
new_classes = ((saved_classes.keys | @classes.keys) - saved_classes.keys)
|
47
|
+
|
48
|
+
@classes = saved_classes
|
49
|
+
|
50
|
+
# генерируем короткие имена и удаляем из них уже используемые
|
51
|
+
short_words = generate_short_words - saved_classes.values
|
52
|
+
new_classes.each do |class_name|
|
53
|
+
@classes[class_name] = short_words.shift
|
54
|
+
end
|
55
|
+
|
56
|
+
# @classes.sort!
|
57
|
+
|
58
|
+
new_classes
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
# Метод для замены классов
|
63
|
+
def replace data, type
|
64
|
+
|
65
|
+
fail "Тип блока не понятный" unless [:js, :css, :html, :plain].include? type
|
66
|
+
fail "nil Ololo" if data.nil?
|
67
|
+
|
68
|
+
code = case type
|
69
|
+
when :js then JSCode
|
70
|
+
when :css then CSSCode
|
71
|
+
when :html then HTMLCode
|
72
|
+
when :plain then PlainCode
|
73
|
+
end
|
74
|
+
|
75
|
+
@classes.sort!
|
76
|
+
|
77
|
+
code.new(data).replace @classes
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Метод генерирует и возращает массив коротких имен.
|
84
|
+
# По умолчанию генерируется 38471 имя. Если надо больше, добавить — легко
|
85
|
+
def generate_short_words again = false
|
86
|
+
|
87
|
+
short_words = []
|
88
|
+
|
89
|
+
%w(a aa a0 a_ a- aaa a00 a0a aa0 aa_ a_a aa- a-a a0_ a0- a_0 a-0).each do |name|
|
90
|
+
max = name.length + 1
|
91
|
+
while name.length < max
|
92
|
+
short_words << name
|
93
|
+
name = name.next
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
short_words
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
# Класс-попытка реализовать что нибудь похожее на упорядоченный хэш
|
104
|
+
class Dictionary
|
105
|
+
|
106
|
+
include Enumerable
|
107
|
+
|
108
|
+
attr_reader :keys, :values
|
109
|
+
|
110
|
+
def initialize hash = { }
|
111
|
+
|
112
|
+
@keys = [ ]
|
113
|
+
@values = [ ]
|
114
|
+
|
115
|
+
hash.each_pair { |key, val| append key, val } if hash.kind_of? Hash
|
116
|
+
hash.each { |item| append item[0], item[1] } if hash.kind_of? Array
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
def [](key)
|
121
|
+
if include? key
|
122
|
+
@values[@keys.index(key)]
|
123
|
+
else
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def []=(key, value)
|
129
|
+
if include? key
|
130
|
+
@values[@keys.index(key)] = value
|
131
|
+
else
|
132
|
+
append key, value
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def append key, value
|
137
|
+
@keys << key
|
138
|
+
@values << value
|
139
|
+
end
|
140
|
+
|
141
|
+
def include? class_name
|
142
|
+
@keys.include? class_name
|
143
|
+
end
|
144
|
+
|
145
|
+
alias :has_key? include?
|
146
|
+
|
147
|
+
def empty?
|
148
|
+
@keys.empty?
|
149
|
+
end
|
150
|
+
|
151
|
+
def each
|
152
|
+
@keys.length.times do |i|
|
153
|
+
yield @keys[i], @values[i]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def sort!
|
158
|
+
@keys.map { |x| [x, @values[@keys.index(x)]] }.sort_by { |x| x[0].length }.reverse.each_with_index do |x, i|
|
159
|
+
@keys[i] = x[0]
|
160
|
+
@values[i] = x[1]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def select_keys_if &block
|
165
|
+
array = []
|
166
|
+
@keys.length.times do |i|
|
167
|
+
need_append = yield @keys[i], @values[i]
|
168
|
+
array << @keys[i] if need_append
|
169
|
+
end
|
170
|
+
array
|
171
|
+
end
|
172
|
+
|
173
|
+
def length
|
174
|
+
@keys.length
|
175
|
+
end
|
176
|
+
|
177
|
+
def last
|
178
|
+
unless @keys.empty?
|
179
|
+
[@keys.last, @values.last]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_s
|
184
|
+
each do |key, val|
|
185
|
+
puts key.ljust(40) + val
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_a
|
190
|
+
@keys.map { |x| [x, @values[@keys.index(x)]] }
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
class Code
|
196
|
+
|
197
|
+
attr_reader :code
|
198
|
+
|
199
|
+
def initialize code
|
200
|
+
@code = code
|
201
|
+
end
|
202
|
+
|
203
|
+
def replace classes
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
class JSCode < Code
|
210
|
+
|
211
|
+
def replace classes
|
212
|
+
|
213
|
+
# Находим все строки и регулярные выражения
|
214
|
+
@code.gsub(/(("|'|\/)((\\\2|.)*?)\2)/m) do |string|
|
215
|
+
|
216
|
+
string_before, string_after = $3, $3
|
217
|
+
|
218
|
+
# Проходимся по всем классам
|
219
|
+
classes.each do |full_name, short_name|
|
220
|
+
|
221
|
+
# И заменяем старый класс, на новый
|
222
|
+
string_after = string_after.gsub(full_name, short_name)
|
223
|
+
end
|
224
|
+
|
225
|
+
string.gsub(string_before, string_after.gsub(/(\\+)("|')/, "\\1\\1\\2"))
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
class CSSCode < Code
|
234
|
+
|
235
|
+
def replace classes
|
236
|
+
|
237
|
+
# Вырезаем экспрешены
|
238
|
+
expression_list = []
|
239
|
+
@code.replace_expressions! do |expression|
|
240
|
+
# и заменяем в них классы как в js
|
241
|
+
expression_list << JSCode.new(expression).replace(classes)
|
242
|
+
"_ololo_#{expression_list.length}_ololo_"
|
243
|
+
end
|
244
|
+
|
245
|
+
# Вставляем экспрешены с замененными классами обратно
|
246
|
+
expression_list.each_with_index do |expression, index|
|
247
|
+
@code.gsub! /_ololo_#{index + 1}_ololo_/, expression
|
248
|
+
end
|
249
|
+
|
250
|
+
@code.gsub!(/\[class\^=(.*?)\]/) do |class_name|
|
251
|
+
if classes.include? $1
|
252
|
+
class_name.gsub $1, classes[$1]
|
253
|
+
else
|
254
|
+
class_name
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Случайная строка
|
259
|
+
unique_string = Time.now.object_id.to_s
|
260
|
+
|
261
|
+
# Проходимся по классам
|
262
|
+
classes.each do |full_name, short_name|
|
263
|
+
|
264
|
+
# Заменяем старое имя класса на новое, плюс случайное число,
|
265
|
+
# чтобы знать что этот класс мы уже изменяли
|
266
|
+
# TODO: Может это нахрен не надо?
|
267
|
+
@code = @code.gsub(/\.#{full_name}(?=[^-\w])/, ".#{unique_string}#{short_name}")
|
268
|
+
end
|
269
|
+
|
270
|
+
# После замены имен классов, случайное число уже не нужно,
|
271
|
+
# так что удаляем его, и возвращаем css с замененными значениями
|
272
|
+
@code.gsub(unique_string, '')
|
273
|
+
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
class HTMLCode < Code
|
279
|
+
|
280
|
+
def replace classes
|
281
|
+
|
282
|
+
# Заменяем классы во встроенных стилях
|
283
|
+
@code.gsub!(/<style[^>]*?>(.*?)<\s*\/\s*style\s*>/mi) do |style|
|
284
|
+
style.gsub($1, CSSCode.new($1).replace(classes))
|
285
|
+
end
|
286
|
+
|
287
|
+
# Заменяем классы во встроенных скриптах
|
288
|
+
@code.gsub!(/<script[^>]*?>(.*?)<\s*\/\s*script\s*>/mi) do |script|
|
289
|
+
script.gsub($1, JSCode.new($1).replace(classes))
|
290
|
+
end
|
291
|
+
|
292
|
+
# Находим аттрибуты с именем "class"
|
293
|
+
# TODO: Надо находить не просто "class=blablabl", а искать
|
294
|
+
# именно теги с аттрибутом "class"
|
295
|
+
@code.gsub!(/class\s*=\s*('|")(.*?)\1/) do |match|
|
296
|
+
|
297
|
+
# берем то что в кавычках и разбиваем по пробелам
|
298
|
+
match = $2.split(' ')
|
299
|
+
|
300
|
+
# проходимся по получившемуся массиву
|
301
|
+
match.map! do |class_name|
|
302
|
+
|
303
|
+
# удаляем проблелы по бокам
|
304
|
+
class_name = class_name.strip
|
305
|
+
|
306
|
+
# и если в нашем списке замены есть такой класс заменяем на новое значение
|
307
|
+
if classes.has_key? class_name
|
308
|
+
classes[class_name]
|
309
|
+
else
|
310
|
+
class_name
|
311
|
+
end
|
312
|
+
# elsif class_name.eql? 'g-js'
|
313
|
+
# class_name
|
314
|
+
# end
|
315
|
+
|
316
|
+
end.delete_if { |class_name| class_name.nil? or class_name.empty? }
|
317
|
+
|
318
|
+
unless match.empty?
|
319
|
+
"class=\"#{match.join(' ')}\""
|
320
|
+
else
|
321
|
+
''
|
322
|
+
# puts match
|
323
|
+
# match
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
# Находим тэги с аттрибутами в которых может быть js
|
329
|
+
@code.gsub(/<[^>]*?(onload|onunload|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onfocus|onblur|onkeypress|onkeydown|onkeyup|onsubmit|onreset|onselect|onchange)\s*=\s*("|')((\\\2|.)*?)\2[^>]*?>/mi) do |tag|
|
330
|
+
tag.gsub($3, JSCode.new($3.gsub(/\\-/ , '-')).replace(classes))
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
|
337
|
+
class PlainCode < Code
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
# Проверка наличия метода
|
4
|
+
def jeanny_extension(method)
|
5
|
+
if method_defined?(method)
|
6
|
+
$stderr.puts "WARNING: Possible conflict with jeanny extension: #{self}##{method} already exists"
|
7
|
+
else
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class File
|
15
|
+
|
16
|
+
jeanny_extension('open_file') do
|
17
|
+
# Метод для чтения файла
|
18
|
+
def self.open_file file
|
19
|
+
# Если файл существует
|
20
|
+
if exists?(expand_path(file))
|
21
|
+
# Открываем, читаем, объединяем, возвращаем содержимое
|
22
|
+
# open(expand_path(file), 'r').readlines.join
|
23
|
+
open(expand_path(file), 'r').read
|
24
|
+
else
|
25
|
+
# Возвращаем пустую строку
|
26
|
+
raise Jeanny::JeannyFileNotFound, "Файл не найден: #{expand_path(file)}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
jeanny_extension('open_file') do
|
32
|
+
# Метод для сохранения файла
|
33
|
+
def self.save_file file, data, prefix = ''
|
34
|
+
# Если префикс не пустой, добавляем его к имени файла
|
35
|
+
file = "#{dirname(expand_path(file))}/#{prefix}#{basename(file)}" unless prefix.empty?
|
36
|
+
# Открываем файл
|
37
|
+
open(file, 'w') do |file|
|
38
|
+
# Помещаем данные
|
39
|
+
file.puts data
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
jeanny_extension('list') do
|
45
|
+
def self.list path
|
46
|
+
|
47
|
+
file_list = []
|
48
|
+
file_path = [path].flatten.map do |item|
|
49
|
+
expand_path item
|
50
|
+
end
|
51
|
+
|
52
|
+
file_path.each do |file|
|
53
|
+
|
54
|
+
list_item = Dir[file]
|
55
|
+
file_list << list_item
|
56
|
+
|
57
|
+
if block_given?
|
58
|
+
unless list_item.empty?
|
59
|
+
list_item.each { |x| yield x, 0 }
|
60
|
+
else
|
61
|
+
yield file, 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
file_list.flatten
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class String
|
75
|
+
|
76
|
+
jeanny_extension('colorize') do
|
77
|
+
# Функция для подсвечивания строки с помошью ANSI кодов...
|
78
|
+
def colorize(color_code)
|
79
|
+
unless PLATFORM =~ /win32/
|
80
|
+
"#{color_code}#{self}\e[0m"
|
81
|
+
else
|
82
|
+
self
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
jeanny_extension('red') do
|
88
|
+
# ... красным
|
89
|
+
def red; colorize("\e[31m"); end
|
90
|
+
end
|
91
|
+
|
92
|
+
jeanny_extension('green') do
|
93
|
+
# ... зеленым цветом
|
94
|
+
def green; colorize("\e[32m"); end
|
95
|
+
end
|
96
|
+
|
97
|
+
jeanny_extension('yellow') do
|
98
|
+
# ... и желтым
|
99
|
+
def yellow; colorize("\e[33m"); end
|
100
|
+
end
|
101
|
+
|
102
|
+
jeanny_extension('each_expression') do
|
103
|
+
def each_expression &block
|
104
|
+
|
105
|
+
expression_list = []
|
106
|
+
code, index, length = self.dup, 0, self.length
|
107
|
+
|
108
|
+
while code[index, length].include? 'expression(' do
|
109
|
+
brake = 0
|
110
|
+
start = code[index, length].index 'expression('
|
111
|
+
block = code[index + start, length]
|
112
|
+
|
113
|
+
block.length.times do |i|
|
114
|
+
char = block[i, 1]
|
115
|
+
if char =~ /\(|\)/
|
116
|
+
brake = brake + 1 if char.eql? '('
|
117
|
+
brake = brake - 1 if char.eql? ')'
|
118
|
+
|
119
|
+
if brake.zero?
|
120
|
+
brake = block[0, i + 1]
|
121
|
+
break
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
index = index + start + brake.length
|
127
|
+
expression_list << brake
|
128
|
+
|
129
|
+
yield brake if block_given?
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
expression_list
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
jeanny_extension 'replace_expressions' do
|
139
|
+
def replace_expressions replace_string = '', &block
|
140
|
+
code = self.dup
|
141
|
+
self.each_expression do |expression|
|
142
|
+
if block_given?
|
143
|
+
edoc = yield expression
|
144
|
+
code.gsub!(expression, edoc)
|
145
|
+
else
|
146
|
+
code.gsub!(expression, replace_string)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
code
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
jeanny_extension 'replace_expressions!' do
|
155
|
+
def replace_expressions! replace_string = '', &block
|
156
|
+
replace replace_expressions(replace_string, &block)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
jeanny_extension 'remove_expressions' do
|
161
|
+
def remove_expressions
|
162
|
+
replace_expressions ''
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
jeanny_extension 'remove_expressions!' do
|
167
|
+
def remove_expressions!
|
168
|
+
replace replace_expressions ''
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
module Jeanny
|
175
|
+
|
176
|
+
%w(FileNotFound CompareFileFormatError ClassesNotFound).each { |error| eval "class Jeanny#{error} < RuntimeError; end" }
|
177
|
+
# %w(CompareFileNotFound SaveError).each { |error| eval "class Jeanny#{error} < SystemCallError; end" }
|
178
|
+
|
179
|
+
end
|
data/lib/jeanny/sugar.rb
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Jeanny
|
4
|
+
|
5
|
+
module Sugar
|
6
|
+
|
7
|
+
# Singleton класс который взаимодействует c Jeanny
|
8
|
+
class BridgeToJeanny
|
9
|
+
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
# refresh
|
14
|
+
end
|
15
|
+
|
16
|
+
def analyze(path, args = {})
|
17
|
+
|
18
|
+
refresh
|
19
|
+
|
20
|
+
begin
|
21
|
+
|
22
|
+
file_list = File.list path
|
23
|
+
|
24
|
+
fail JeannyFileNotFound, "файлы для анализа не найдены (Jeanny::Engine.analyze)" if file_list.empty?
|
25
|
+
|
26
|
+
file_meat = ''
|
27
|
+
file_list.each do |file|
|
28
|
+
file_meat = file_meat + File.open_file(file)
|
29
|
+
end
|
30
|
+
|
31
|
+
if @engine.analyze file_meat
|
32
|
+
@canbe[:save], @canbe[:process] = true, true
|
33
|
+
@canbe[:analyze] = false
|
34
|
+
end
|
35
|
+
|
36
|
+
rescue StandardError => e
|
37
|
+
$stderr.puts "Ошибка: ".red + e.message
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
@compare_file = (args[:compare_with] or '')
|
42
|
+
|
43
|
+
# Завершаем метод если сравнивать ни с чем не надо
|
44
|
+
return true if @compare_file.empty?
|
45
|
+
|
46
|
+
# Если файл не найден, спрашиваем у юзернейма, как быть дальше,
|
47
|
+
# продолжать без сравнения или прекратить работу.
|
48
|
+
unless File.exists? @compare_file
|
49
|
+
puts "Файл с сохраненными классами не найден. Продолжаем без сравнения.".yellow
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
53
|
+
saved_classes = []
|
54
|
+
|
55
|
+
begin
|
56
|
+
# Открываем файл
|
57
|
+
raw_file = File.open_file @compare_file
|
58
|
+
raw_data = raw_file.split "\n"
|
59
|
+
|
60
|
+
# ... и читаем структиуру
|
61
|
+
raw_data.map do |line|
|
62
|
+
line.split(':').map do |hash|
|
63
|
+
hash.strip
|
64
|
+
end
|
65
|
+
end.each_with_index do |item, index|
|
66
|
+
if item.length != 2 or item[1].empty?
|
67
|
+
fail JeannyCompareFileFormatError, "Какая то ерунда с одим (а может больше) классом. Можно пропустить, но хрен его знает что дальше будет…\n" + "файл: #{file}, строка: #{index}\n#{raw_data[index]}".red
|
68
|
+
else
|
69
|
+
saved_classes << [item[0], item[1]]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
rescue Exception => e
|
73
|
+
$stderr.puts e.message
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
|
77
|
+
# Сравниваем
|
78
|
+
new_classes = @engine.compare_with saved_classes
|
79
|
+
|
80
|
+
unless new_classes.nil? or new_classes.empty?
|
81
|
+
puts 'Новые классы: '
|
82
|
+
new_classes.each do |class_name|
|
83
|
+
puts " #{class_name.ljust(40, '.')}#{@engine.classes[class_name].green}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
true
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
def save file = ''
|
92
|
+
|
93
|
+
fail RuntimeError, 'на данном этапе нельзя вызывать сохранение классов' unless canbe[:save]
|
94
|
+
|
95
|
+
file = file.empty? ? @compare_file : file
|
96
|
+
|
97
|
+
File.open(File.expand_path(file), 'w') do |f|
|
98
|
+
@engine.classes.each do |key, val|
|
99
|
+
f.puts "#{key}: #{val.rjust(40 - key.length)}"
|
100
|
+
end
|
101
|
+
end unless @compare_file.empty? and file.empty?
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
def save_to file
|
106
|
+
save file
|
107
|
+
end
|
108
|
+
|
109
|
+
def group type, args = {}, &block
|
110
|
+
|
111
|
+
begin
|
112
|
+
|
113
|
+
fail "We can`t process here..." unless @canbe[:process]
|
114
|
+
fail "Блоки process не должны быть рекурсивными" if @process_block_start
|
115
|
+
fail "Не передан блок" unless block_given?
|
116
|
+
|
117
|
+
@canbe[:process] = false
|
118
|
+
@canbe[:replace] = true
|
119
|
+
|
120
|
+
@process_block_start = true
|
121
|
+
@process_block = []
|
122
|
+
|
123
|
+
yield block
|
124
|
+
|
125
|
+
@process_block_start = false
|
126
|
+
|
127
|
+
@canbe[:replace] = false
|
128
|
+
@canbe[:process] = true
|
129
|
+
|
130
|
+
rescue Exception => e
|
131
|
+
$stderr.puts "Ошибка: ".red + e.message
|
132
|
+
exit 1
|
133
|
+
end
|
134
|
+
|
135
|
+
@process_block.each do |replace|
|
136
|
+
File.list replace[:in] do |file, status|
|
137
|
+
|
138
|
+
# next unless status.zero?
|
139
|
+
unless status.zero?
|
140
|
+
puts file.red
|
141
|
+
next
|
142
|
+
end
|
143
|
+
|
144
|
+
exclude = false
|
145
|
+
replace[:ex].each do |exclude_rule|
|
146
|
+
if exclude_rule.kind_of? Regexp
|
147
|
+
exclude = file =~ exclude_rule
|
148
|
+
else
|
149
|
+
exclude = file.include? exclude_rule
|
150
|
+
end
|
151
|
+
break if exclude
|
152
|
+
end
|
153
|
+
|
154
|
+
# next if exclude
|
155
|
+
if exclude
|
156
|
+
puts file.yellow
|
157
|
+
next
|
158
|
+
end
|
159
|
+
|
160
|
+
begin
|
161
|
+
data = File.open_file file
|
162
|
+
data = @engine.replace data, type
|
163
|
+
|
164
|
+
File.save_file file, data
|
165
|
+
|
166
|
+
puts file.green
|
167
|
+
rescue Exception => e
|
168
|
+
puts e.message + "\n#{$@}"
|
169
|
+
exit 1
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
def replace args = { }
|
178
|
+
|
179
|
+
fail "We can`t replace here..." unless @canbe[:replace]
|
180
|
+
|
181
|
+
struct = { :in => [], :ex => [], :prefix => '' }
|
182
|
+
|
183
|
+
struct[:in] = ([args[:in]] | [args[:include]]).flatten.delete_if { |item| item.nil? or item.empty? }
|
184
|
+
struct[:ex] = ([args[:ex]] | [args[:exclude]]).flatten.delete_if { |item| item.nil? or item.empty? }
|
185
|
+
|
186
|
+
struct[:prefix] = args[:prefix] unless args[:prefix].nil?
|
187
|
+
|
188
|
+
@process_block << struct if struct[:in].length > 0
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
attr_reader :analyze_file, :compare_file
|
195
|
+
attr_reader :engine
|
196
|
+
attr_reader :canbe
|
197
|
+
|
198
|
+
attr_reader :process_block_start
|
199
|
+
attr_reader :process_block
|
200
|
+
|
201
|
+
def refresh
|
202
|
+
|
203
|
+
@engine = Engine.new
|
204
|
+
@file_list, @compare_file = '', ''
|
205
|
+
@canbe = { :analyze => true, :save => false, :process => false, :replace => false, :stat => false, :make => false }
|
206
|
+
|
207
|
+
@process_block_start = false
|
208
|
+
@process_block = []
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
def answer question, yes, no
|
213
|
+
|
214
|
+
action, answers = '', [yes, no].flatten
|
215
|
+
until answers.include? action
|
216
|
+
print "#{question} "
|
217
|
+
action = gets.chomp!
|
218
|
+
end
|
219
|
+
|
220
|
+
[yes].flatten.include? action
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
# Тут перехватываем все несуществующие методы.
|
227
|
+
# И те которые используются в DSL отправляем куда надо.
|
228
|
+
def method_missing(method, *args, &block)
|
229
|
+
BridgeToJeanny.instance.send(method, *args, &block) if BridgeToJeanny.instance.respond_to?(method)
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
data/lib/jeanny.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# jeanny.rb
|
4
|
+
# jeanny
|
5
|
+
#
|
6
|
+
# Created by seriously drunken on 2009-09-16.
|
7
|
+
# Copyright 2009 Yandex. All rights reserved.
|
8
|
+
#
|
9
|
+
|
10
|
+
$:.unshift File.dirname(__FILE__) unless $:.include? File.dirname(__FILE__)
|
11
|
+
|
12
|
+
module Jeanny
|
13
|
+
|
14
|
+
JEANNY_VERSION = '0.8'
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'jeanny/extend'
|
19
|
+
require 'jeanny/engine'
|
20
|
+
require 'jeanny/sugar'
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jeanny
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.8"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- gfranco
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-28 00:00:00 +03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: hello@gfranco.ru
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- lib/jeanny.rb
|
27
|
+
- lib/jeanny/engine.rb
|
28
|
+
- lib/jeanny/sugar.rb
|
29
|
+
- lib/jeanny/extend.rb
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://github.com/gfranco/jeanny
|
32
|
+
licenses: []
|
33
|
+
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options:
|
36
|
+
- --inline-source
|
37
|
+
- --charset=UTF-8
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
requirements: []
|
53
|
+
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.3.5
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: Lib for obfuscation css class names
|
59
|
+
test_files: []
|
60
|
+
|