rutils 0.0.3
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/CHANGELOG +5 -0
- data/README +104 -0
- data/TODO +26 -0
- data/bin/gilensize +8 -0
- data/lib/datetime/datetime.rb +61 -0
- data/lib/gilenson/gilenson.rb +280 -0
- data/lib/gilenson/gilenson_port.rb +332 -0
- data/lib/integration/blue_cloth_override.rb +8 -0
- data/lib/integration/integration.rb +24 -0
- data/lib/integration/rails_date_helper_override.rb +66 -0
- data/lib/integration/red_cloth_override.rb +17 -0
- data/lib/pluralizer/pluralizer.rb +208 -0
- data/lib/rutils.rb +14 -0
- data/lib/transliteration/transliteration.rb +173 -0
- data/test/run_tests.rb +4 -0
- data/test/t_datetime.rb +10 -0
- data/test/t_gilenson.rb +42 -0
- data/test/t_integration.rb +43 -0
- data/test/t_pluralize.rb +60 -0
- data/test/t_transliteration.rb +25 -0
- data/test/t_typografica.rb +57 -0
- metadata +73 -0
@@ -0,0 +1,332 @@
|
|
1
|
+
module RuTils
|
2
|
+
|
3
|
+
module GilensonMixin
|
4
|
+
# Форматирует строку с помощью Gilensize
|
5
|
+
def gilensize(*args)
|
6
|
+
RuTils::Gilenson.new(self, *args).to_html
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Это - прямой порт Тыпографицы от pixelapes.
|
11
|
+
# Настройки можно регулировать через методы, т.е.
|
12
|
+
#
|
13
|
+
# typ = Typografica.new('Эти "так называемые" великие деятели')
|
14
|
+
# typ.html = false => "false"
|
15
|
+
# typ.dash = true => "true"
|
16
|
+
# typ.to_html => 'Эти «так называемые» великие деятели'
|
17
|
+
class Gilenson
|
18
|
+
def initialize(text, *args)
|
19
|
+
@_text = text
|
20
|
+
@skip_tags = true;
|
21
|
+
@p_prefix = "<p class=typo>";
|
22
|
+
@p_postfix = "</p>";
|
23
|
+
@a_soft = true;
|
24
|
+
@indent_a = "images/z.gif width=25 height=1 border=0 alt=\'\' align=top />" # <->
|
25
|
+
@indent_b = "images/z.gif width=50 height=1 border=0 alt=\'\' align=top />" # <-->
|
26
|
+
@fixed_size = 80 # максимальная ширина
|
27
|
+
@ignore = /notypo/ # regex, который игнорируется. Этим надо воспользоваться для обработки pre и code
|
28
|
+
|
29
|
+
@de_nobr = true;
|
30
|
+
|
31
|
+
@phonemasks = [[ /([0-9]{4})\-([0-9]{2})\-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/,
|
32
|
+
/([0-9]{4})\-([0-9]{2})\-([0-9]{2})/,
|
33
|
+
/(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
|
34
|
+
/(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
|
35
|
+
/(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})/,
|
36
|
+
/(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{3})/,
|
37
|
+
/([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
|
38
|
+
/([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
|
39
|
+
/([0-9]{1})\-([0-9]{2})\-([0-9]{2})/,
|
40
|
+
/([0-9]{2})\-([0-9]{3})/,
|
41
|
+
/([0-9]+)\-([0-9]+)/,
|
42
|
+
],[
|
43
|
+
'<nobr>\1–\2–\3 \4:\5:\6</nobr>',
|
44
|
+
'<nobr>\1–\2–\3</nobr>',
|
45
|
+
'<nobr>\1 \2–\3–\4</nobr>',
|
46
|
+
'<nobr>\1 \2–\3–\4</nobr>',
|
47
|
+
'<nobr>\1 \2–\3</nobr>',
|
48
|
+
'<nobr>\1 \2–\3</nobr>',
|
49
|
+
'<nobr>\1–\2–\3</nobr>',
|
50
|
+
'<nobr>\1–\2–\3</nobr>',
|
51
|
+
'<nobr>\1–\2–\3</nobr>',
|
52
|
+
'<nobr>\1–\2</nobr>',
|
53
|
+
'<nobr>\1–\2</nobr>'
|
54
|
+
]]
|
55
|
+
|
56
|
+
@glueleft = ['рис.', 'табл.', 'см.', 'им.', 'ул.', 'пер.', 'кв.', 'офис', 'оф.', 'г.']
|
57
|
+
@glueright = ['руб.', 'коп.', 'у.е.', 'мин.']
|
58
|
+
|
59
|
+
@settings = {
|
60
|
+
"inches" => true, # преобразовывать дюймы в "
|
61
|
+
"laquo" => true, # кавычки-ёлочки
|
62
|
+
"farlaquo" => false, # кавычки-ёлочки для фара (знаки "больше-меньше")
|
63
|
+
"quotes" => true, # кавычки-английские лапки
|
64
|
+
"dash" => true, # короткое тире (150)
|
65
|
+
"emdash" => true, # длинное тире двумя минусами (151)
|
66
|
+
"(c)" => true,
|
67
|
+
"(r)" => true,
|
68
|
+
"(tm)" => true,
|
69
|
+
"(p)" => true,
|
70
|
+
"+-" => true, # спецсимволы, какие - понятно
|
71
|
+
"degrees" => true, # знак градуса
|
72
|
+
"<-->" => true, # отступы $Indent*
|
73
|
+
"dashglue" => true, "wordglue" => true, # приклеивание предлогов и дефисов
|
74
|
+
"spacing" => true, # запятые и пробелы, перестановка
|
75
|
+
"phones" => true, # обработка телефонов
|
76
|
+
"fixed" => false, # подгон под фиксированную ширину
|
77
|
+
"html" => false # запрет тагов html
|
78
|
+
}
|
79
|
+
# irrelevant - indentation with images
|
80
|
+
@indent_a = "<!--indent-->"
|
81
|
+
@indent_b = "<!--indent-->"
|
82
|
+
|
83
|
+
@mark_tag = "\xF0\xF0\xF0\xF0" # Подстановочные маркеры тегов
|
84
|
+
@mark_ignored = "\201" # Подстановочные маркеры неизменяемых групп
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# Proxy unknown method calls as setting switches. Methods with = will set settings, methods without - fetch them
|
89
|
+
def method_missing(meth, *args) #:nodoc:
|
90
|
+
setting = meth.to_s.gsub(/=$/, '')
|
91
|
+
super(meth, *args) unless @settings.has_key?(setting) #this will pop the exception if we have no such setting
|
92
|
+
|
93
|
+
return @settings[meth.to_s] if setting == meth.to_s
|
94
|
+
return (@settings[meth.to_s] = args[0])
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def to_html(no_paragraph = false)
|
99
|
+
|
100
|
+
text = @_text
|
101
|
+
|
102
|
+
# -2. игнорируем ещё регексп
|
103
|
+
ignored = []
|
104
|
+
|
105
|
+
|
106
|
+
text.scan(@ignore) do |result|
|
107
|
+
ignored << result
|
108
|
+
end
|
109
|
+
|
110
|
+
text.gsub!(@ignore, @mark_ignored) # маркер игнора
|
111
|
+
|
112
|
+
# -1. запрет тагов html
|
113
|
+
text.gsub!(/&/, '&') if @settings["html"]
|
114
|
+
|
115
|
+
|
116
|
+
# 0. Вырезаем таги
|
117
|
+
# проблема на самом деле в том, на что похожи таги.
|
118
|
+
# вариант 1, простой (закрывающий таг) </abcz>
|
119
|
+
# вариант 2, простой (просто таг) <abcz>
|
120
|
+
# вариант 3, посложней <abcz href="abcz">
|
121
|
+
# вариант 4, простой (просто таг) <abcz />
|
122
|
+
# вариант 5, вакка \xA2\xA2...== нафиг нафиг
|
123
|
+
# самый сложный вариант - это когда в параметре тага встречается вдруг символ ">"
|
124
|
+
# вот он: <abcz href="abcz>">
|
125
|
+
# как работает вырезание? введём спецсимвол. Да, да, спецсимвол.
|
126
|
+
# нам он ещё вопьётся =)
|
127
|
+
# заменим все таги на спец.символ, запоминая одновременно их в массив.
|
128
|
+
# и будем верить, что спец.символы в дикой природе не встречаются.
|
129
|
+
|
130
|
+
tags = []
|
131
|
+
if (@skip_tags)
|
132
|
+
# re = /<\/?[a-z0-9]+("+ # имя тага
|
133
|
+
# "\s+("+ # повторяющая конструкция: хотя бы один разделитель и тельце
|
134
|
+
# "[a-z]+("+ # атрибут из букв, за которым может стоять знак равенства и потом
|
135
|
+
# "=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+))"+ #
|
136
|
+
# ")?"+
|
137
|
+
# ")?"+
|
138
|
+
# ")*\/?>|\xA2\xA2[^\n]*?==/i;
|
139
|
+
|
140
|
+
# re = /<\/?[a-z0-9]+(\s+([a-z]+(=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+)))?)?)*\/?>|\xA2\xA2[^\n]*?==/ui
|
141
|
+
|
142
|
+
re = /(<\/?[a-z0-9]+(\s+([a-z]+(=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+)))?)?)*\/?>)/ui
|
143
|
+
|
144
|
+
# по-хорошему атрибуты тоже нужно типографить. Или не нужно? бугага...
|
145
|
+
|
146
|
+
tags = text.scan(re).map{|tag| tag[0] }
|
147
|
+
# match = "<" + match if @settings["html"]
|
148
|
+
text.gsub!(re, @mark_tag) #маркер тега, мы используем Invalid UTF-sequence для него
|
149
|
+
|
150
|
+
# puts "matched #{tags.size} tags"
|
151
|
+
end
|
152
|
+
|
153
|
+
# 1. Запятые и пробелы
|
154
|
+
if @settings["spacing"]
|
155
|
+
text.gsub!( /(\s*)([,]*)/sui, '\2\1');
|
156
|
+
text.gsub!( /(\s*)([\.?!]*)(\s*[ЁА-ЯA-Z])/su, '\2\1\3');
|
157
|
+
end
|
158
|
+
|
159
|
+
# 2. Разбиение на строки длиной не более ХХ символов
|
160
|
+
# --- для ваки не портировано ---
|
161
|
+
# --- для ваки не портировано ---
|
162
|
+
|
163
|
+
# 3. Спецсимволы
|
164
|
+
# 0. дюймы с цифрами
|
165
|
+
text.gsub!(/\s([0-9]{1,2}([\.,][0-9]{1,2})?)\"/ui, ' \1"') if @settings["inches"]
|
166
|
+
|
167
|
+
# 1. лапки
|
168
|
+
if (@settings["quotes"])
|
169
|
+
text.gsub!( /\"\"/ui, """")
|
170
|
+
text.gsub!( /\"\.\"/ui, ""."")
|
171
|
+
_text = '""';
|
172
|
+
while _text != text do
|
173
|
+
_text = text
|
174
|
+
text.gsub!( /(^|\s|\201|\xF0\xF0\xF0\xF0|>)\"([0-9A-Za-z\'\!\s\.\?\,\-\&\;\:\_\xF0\xF0\xF0\xF0\201]+(\"|”))/ui, '\1“\2')
|
175
|
+
#this doesnt work in-place. somehow.
|
176
|
+
text = text.gsub( /(\&\#147\;([A-Za-z0-9\'\!\s\.\?\,\-\&\;\:\xF0\xF0\xF0\xF0\201\_]*).*[A-Za-z0-9][\xF0\xF0\xF0\xF0\201\?\.\!\,]*)\"/ui, '\1”')
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# 2. ёлочки
|
181
|
+
if @settings["laquo"]
|
182
|
+
text.gsub!( /\"\"/ui, """");
|
183
|
+
text.gsub!( /(^|\s|\201|\xF0\xF0\xF0\xF0|>|\()\"((\201|\xF0\xF0\xF0\xF0)*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, "\\1«\\2");
|
184
|
+
# nb: wacko only regexp follows:
|
185
|
+
text.gsub!( /(^|\s|\201|\xF0\xF0\xF0\xF0|>|\()\"((\201|\xF0\xF0\xF0\xF0|\/ |\/|\!)*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, "\\1«\\2")
|
186
|
+
_text = "\"\"";
|
187
|
+
while (_text != text) do
|
188
|
+
_text = text;
|
189
|
+
text.gsub!( /(\«\;([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/](\201|\xF0\xF0\xF0\xF0)*)\"/sui, "\\1»")
|
190
|
+
# nb: wacko only regexps follows:
|
191
|
+
text.gsub!( /(\«\;([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/](\201|\xF0\xF0\xF0\xF0)*\?(\201|\xF0\xF0\xF0\xF0)*)\"/sui, "\\1»")
|
192
|
+
text.gsub!( /(\«\;([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/](\201|\xF0\xF0\xF0\xF0|\/|\!)*)\"/sui, "\\1»")
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
# 2b. одновременно ёлочки и лапки
|
198
|
+
if (@settings["quotes"] && (@settings["laquo"] or @settings["farlaquo"]))
|
199
|
+
text.gsub!(/(\&\#147\;(([A-Za-z0-9'!\.?,\-&;:]|\s|\xF0\xF0\xF0\xF0|\201)*)«(.*)»)»/ui,"\\1”");
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
# 3. тире
|
204
|
+
if (@settings["dash"])
|
205
|
+
text.gsub!( /(\s|;)\-(\s)/ui, "\\1–\\2")
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
# 3a. тире длинное
|
210
|
+
if (@settings["emdash"])
|
211
|
+
text.gsub!( /(\s|;)\-\-(\s)/ui, "\\1—\\2")
|
212
|
+
# 4. (с)
|
213
|
+
text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, "©") if @settings["(c)"]
|
214
|
+
# 4a. (r)
|
215
|
+
text.gsub!( /\(r\)/ui, "<sup>®</sup>") if @settings["(r)"]
|
216
|
+
|
217
|
+
# 4b. (tm)
|
218
|
+
text.gsub!( /\(tm\)|\(тм\)/ui, "™") if @settings["(tm)"]
|
219
|
+
# 4c. (p)
|
220
|
+
text.gsub!( /\(p\)/ui, "§") if @settings["(p)"]
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
# 5. +/-
|
225
|
+
text.gsub!(/[^+]\+\-/ui, "±") if @settings["+-"]
|
226
|
+
|
227
|
+
|
228
|
+
# 5a. 12^C
|
229
|
+
if @settings["degrees"]
|
230
|
+
text.gsub!( /-([0-9])+\^([FCС])/, "–\\1°\\2")
|
231
|
+
text.gsub!( /\+([0-9])+\^([FCС])/, "+\\1°\\2")
|
232
|
+
text.gsub!( /\^([FCС])/, "°\\1")
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
# 6. телефоны
|
237
|
+
if @settings["phones"]
|
238
|
+
@phonemasks[0].each_with_index do |v, i|
|
239
|
+
text.gsub!(v, @phonemasks[1][i])
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
# 7. Короткие слова и
|
245
|
+
if (@settings["wordglue"])
|
246
|
+
|
247
|
+
text = " " + text + " ";
|
248
|
+
_text = " " + text + " ";
|
249
|
+
until _text == text
|
250
|
+
_text = text
|
251
|
+
text.gsub!( /(\s+)([a-zа-яА-Я]{1,2})(\s+)([^\\s$])/ui, '\1\2 \4')
|
252
|
+
text.gsub!( /(\s+)([a-zа-яА-Я]{3})(\s+)([^\\s$])/ui, '\1\2 \4')
|
253
|
+
end
|
254
|
+
|
255
|
+
for i in @glueleft
|
256
|
+
text.gsub!( /(\s)(#{i})(\s+)/sui, '\1\2 ')
|
257
|
+
end
|
258
|
+
|
259
|
+
for i in @glueright
|
260
|
+
text.gsub!( /(\s)(#{i})(\s+)/sui, ' \2\3')
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
|
266
|
+
# 8. Склейка ласт. Тьфу! дефисов.
|
267
|
+
text.gsub!( /([a-zа-яА-Я0-9]+(\-[a-zа-яА-Я0-9]+)+)/ui, '<nobr>\1</nobr>') if @settings["dashglue"]
|
268
|
+
|
269
|
+
|
270
|
+
# 9. Макросы
|
271
|
+
|
272
|
+
|
273
|
+
|
274
|
+
# 10. Переводы строк
|
275
|
+
# --- для ваки не портировано ---
|
276
|
+
# --- для ваки не портировано ---
|
277
|
+
|
278
|
+
|
279
|
+
# БЕСКОНЕЧНОСТЬ. Вставляем таги обратно.
|
280
|
+
# if (@skip_tags)
|
281
|
+
# text = text.split("\xF0\xF0\xF0\xF0").join
|
282
|
+
#
|
283
|
+
|
284
|
+
tags.each do |tag|
|
285
|
+
text.sub!(@mark_tag, tag)
|
286
|
+
end
|
287
|
+
|
288
|
+
# i = 0
|
289
|
+
# text.gsub!(@mark_tag) {
|
290
|
+
# i + 1
|
291
|
+
# tags[i-1]
|
292
|
+
# }
|
293
|
+
|
294
|
+
# text = text.split("\xF0\xF0\xF0\xF0")
|
295
|
+
#puts "reinserted #{i} tags"
|
296
|
+
#
|
297
|
+
# end
|
298
|
+
|
299
|
+
|
300
|
+
#ext.gsub!("a", '')
|
301
|
+
# raise "Text still has tag markers!" if text.include?("a")
|
302
|
+
|
303
|
+
# БЕСКОНЕЧНОСТЬ-2. вставляем ещё сигнорированный регексп
|
304
|
+
#
|
305
|
+
# if @ignore
|
306
|
+
# ignored.each { | tag | text.sub!(@mark_ignored, tag) }
|
307
|
+
# end
|
308
|
+
|
309
|
+
# raise "Text still has ignored markers!" if text.include?("\201")
|
310
|
+
|
311
|
+
# БОНУС: прокручивание ссылок через A(...)
|
312
|
+
# --- для ваки не портировано ---
|
313
|
+
# --- для ваки не портировано ---
|
314
|
+
|
315
|
+
# фуф, закончили.
|
316
|
+
# text.gsub!(/<nobr>/, "<span class=\"nobr\">").gsub(/<\/nobr>/, "</span>") if (@de_nobr)
|
317
|
+
|
318
|
+
# text.gsub!(/<nobr>/, "<span class=\"nobr\">").gsub(/<\/nobr>/, "</span>") if (@de_nobr)
|
319
|
+
|
320
|
+
text.gsub(/(\s)+$/, "").gsub(/^(\s)+/, "")
|
321
|
+
|
322
|
+
end
|
323
|
+
|
324
|
+
private
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
end #end RuTils
|
329
|
+
|
330
|
+
class String
|
331
|
+
include RuTils::GilensonMixin
|
332
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
load File.dirname(__FILE__) + '/blue_cloth_override.rb'
|
2
|
+
load File.dirname(__FILE__) + '/red_cloth_override.rb'
|
3
|
+
load File.dirname(__FILE__) + '/rails_date_helper_override.rb'
|
4
|
+
|
5
|
+
module RuTils
|
6
|
+
@@overrides = true
|
7
|
+
|
8
|
+
# Метод позволяет проверить, включена ли перегрузка функций других модулей.
|
9
|
+
# Попутно он спрашивает модуль Locale (если таковой имеется) является ли русский
|
10
|
+
# текущим языком, и если является, включает перегрузку функций имплицитно.
|
11
|
+
# Модуль Locale можно скачать и скомпилировать а можно получить как часть Multilingual Rails.
|
12
|
+
def self.overrides_enabled?
|
13
|
+
if defined?(Locale) and Locale.respond_to?(:current)
|
14
|
+
return true if Locale.current.split('_').first == 'ru'
|
15
|
+
end
|
16
|
+
@@overrides ? true : false
|
17
|
+
end
|
18
|
+
|
19
|
+
# Включает или выключает перегрузки других модулей. Полезно, например, в случае когда нужно рендерить страницу
|
20
|
+
# сайта на нескольких языках и нужно отключить русское оформление текста для других языков.
|
21
|
+
def self.overrides= (new_override_flag)
|
22
|
+
@@overrides = (new_override_flag ? true : false)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
if defined?(ActionView``)
|
2
|
+
module ActionView #:nodoc:
|
3
|
+
module Helpers #:nodoc:
|
4
|
+
module DateHelper #:nodoc:
|
5
|
+
|
6
|
+
# Reports the approximate distance in time between two Time objects or integers.
|
7
|
+
# For example, if the distance is 47 minutes, it'll return
|
8
|
+
# "about 1 hour". See the source for the complete wording list.
|
9
|
+
#
|
10
|
+
# Integers are interpreted as seconds. So,
|
11
|
+
# <tt>distance_of_time_in_words(50)</tt> returns "less than a minute".
|
12
|
+
#
|
13
|
+
# Set <tt>include_seconds</tt> to true if you want more detailed approximations if distance < 1 minute
|
14
|
+
|
15
|
+
alias :distance_of_time_in_words :stock_distance_of_time_in_words
|
16
|
+
def distance_of_time_in_words(*args)
|
17
|
+
RuTils::overrides_enabled? ? RuTils::DateTime::distance_of_time_in_words(*args) : stock_distance_of_time_in_words
|
18
|
+
end
|
19
|
+
|
20
|
+
# Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
|
21
|
+
def time_ago_in_words(from_time, include_seconds = false)
|
22
|
+
distance_of_time_in_words(from_time, Time.now, include_seconds)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a select tag with options for each of the months January through December with the current month selected.
|
26
|
+
# The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are used as values
|
27
|
+
# (what's submitted to the server). It's also possible to use month numbers for the presentation instead of names --
|
28
|
+
# set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you want both numbers and names,
|
29
|
+
# set the <tt>:add_month_numbers</tt> key in +options+ to true. Examples:
|
30
|
+
#
|
31
|
+
# select_month(Date.today) # Will use keys like "January", "March"
|
32
|
+
# select_month(Date.today, :use_month_numbers => true) # Will use keys like "1", "3"
|
33
|
+
# select_month(Date.today, :add_month_numbers => true) # Will use keys like "1 - January", "3 - March"
|
34
|
+
#
|
35
|
+
# Override the field name using the <tt>:field_name</tt> option, 'month' by default.
|
36
|
+
#
|
37
|
+
# If you would prefer to show month names as abbreviations, set the
|
38
|
+
# <tt>:use_short_month</tt> key in +options+ to true.
|
39
|
+
def select_month(date, options = {})
|
40
|
+
month_options = []
|
41
|
+
if RuTils::overrides_enabled?
|
42
|
+
month_names = options[:use_short_month] ? RuTils::DateTime::ABBR_MONTHNAMES : RuTils::DateTime::MONTHNAMES
|
43
|
+
else
|
44
|
+
month_names = options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES
|
45
|
+
end
|
46
|
+
1.upto(12) do |month_number|
|
47
|
+
month_name = if options[:use_month_numbers]
|
48
|
+
month_number
|
49
|
+
elsif options[:add_month_numbers]
|
50
|
+
month_number.to_s + ' - ' + month_names[month_number]
|
51
|
+
else
|
52
|
+
month_names[month_number]
|
53
|
+
end
|
54
|
+
|
55
|
+
month_options << ((date && (date.kind_of?(Fixnum) ? date : date.month) == month_number) ?
|
56
|
+
%(<option value="#{month_number}" selected="selected">#{month_name}</option>\n) :
|
57
|
+
%(<option value="#{month_number}">#{month_name}</option>\n)
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
select_html(options[:field_name] || 'month', month_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end #endif
|
@@ -0,0 +1,17 @@
|
|
1
|
+
if defined?(RedCloth)
|
2
|
+
# RuTils выполняет перегрузку Textile Glyphs в RedCloth, перенося форматирование спецсимволов на Gilenson.
|
3
|
+
class RedCloth < String #:nodoc:
|
4
|
+
# Этот метод в RedCloth эскейпит слишком много HTML, нам ничего не оставляет :-)
|
5
|
+
def htmlesc(text, mode=0) #:nodoc:
|
6
|
+
text
|
7
|
+
end
|
8
|
+
|
9
|
+
# А этот метод обрабатывает Textile Glyphs - ту самую типографицу.
|
10
|
+
# Вместо того чтобы влезать в таблицы мы просто заменим Textile Glyphs - и все будут рады.
|
11
|
+
alias_method :stock_pgl, :pgl
|
12
|
+
def pgl(text) #:nodoc:
|
13
|
+
# RuTils::overrides_enabled? ? text.replace(RuTils::Gilenson.new(text).to_html) : stock_pgl(text)
|
14
|
+
text.replace(RuTils::Gilenson.new(text).to_html)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module RuTils
|
2
|
+
module Pluralization
|
3
|
+
# Выбирает нужный падеж существительного в зависимости от числа
|
4
|
+
def self.choose_plural(amount, *variants)
|
5
|
+
variant = (amount%10==1 && amount%100!=11 ? 1 : amount%10>=2 && amount%10<=4 && (amount%100<10 || amount%100>=20) ? 2 : 3)
|
6
|
+
variants[variant-1]
|
7
|
+
end
|
8
|
+
|
9
|
+
# Выполняет преобразование числа из цифрого вида в символьное
|
10
|
+
# amount - числительное
|
11
|
+
# gender = 1 - мужской, = 2 - женский, = 3 - средний
|
12
|
+
# one_item - именительный падеж единственного числа (= 1)
|
13
|
+
# two_items - родительный падеж единственного числа (= 2-4)
|
14
|
+
# five_items - родительный падеж множественного числа ( = 5-10)
|
15
|
+
def self.sum_string(amount, gender, one_item='', two_items='', five_items='')
|
16
|
+
into = ''
|
17
|
+
tmp_val ||= 0
|
18
|
+
|
19
|
+
return "ноль " + five_items if amount == 0
|
20
|
+
|
21
|
+
tmp_val = amount
|
22
|
+
|
23
|
+
# единицы
|
24
|
+
into, tmp_val = sum_string_fn(into, tmp_val, gender, one_item, two_items, five_items)
|
25
|
+
|
26
|
+
return into if tmp_val == 0
|
27
|
+
|
28
|
+
# тысячи
|
29
|
+
into, tmp_val = sum_string_fn(into, tmp_val, 2, "тысяча", "тысячи", "тысяч")
|
30
|
+
|
31
|
+
return into if tmp_val == 0
|
32
|
+
|
33
|
+
# миллионы
|
34
|
+
into, tmp_val = sum_string_fn(into, tmp_val, 1, "миллион", "миллиона", "миллионов")
|
35
|
+
|
36
|
+
return into if tmp_val == 0
|
37
|
+
|
38
|
+
# миллиардов
|
39
|
+
into, tmp_val = sum_string_fn(into, tmp_val, 1, "миллиард", "миллиарда", "миллиардов")
|
40
|
+
return into
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def self.sum_string_fn(into, tmp_val, gender, one_item='', two_items='', five_items='')
|
45
|
+
rest, rest1, end_word, ones, tens, hundreds = [nil]*6
|
46
|
+
#
|
47
|
+
rest = tmp_val % 1000
|
48
|
+
tmp_val = tmp_val / 1000
|
49
|
+
if rest == 0
|
50
|
+
# последние три знака нулевые
|
51
|
+
into = five_items + " " if into == ""
|
52
|
+
return [into, tmp_val]
|
53
|
+
end
|
54
|
+
#
|
55
|
+
# начинаем подсчет с Rest
|
56
|
+
end_word = five_items
|
57
|
+
# сотни
|
58
|
+
case rest / 100
|
59
|
+
when 0 then hundreds = ""
|
60
|
+
when 1 then hundreds = "сто "
|
61
|
+
when 2 then hundreds = "двести "
|
62
|
+
when 3 then hundreds = "триста "
|
63
|
+
when 4 then hundreds = "четыреста "
|
64
|
+
when 5 then hundreds = "пятьсот "
|
65
|
+
when 6 then hundreds = "шестьсот "
|
66
|
+
when 7 then hundreds = "семьсот "
|
67
|
+
when 8 then hundreds = "восемьсот "
|
68
|
+
when 9 then hundreds = "девятьсот "
|
69
|
+
end
|
70
|
+
|
71
|
+
# десятки
|
72
|
+
rest = rest % 100
|
73
|
+
rest1 = rest / 10
|
74
|
+
ones = ""
|
75
|
+
case rest1
|
76
|
+
when 0 then tens = ""
|
77
|
+
when 1 # особый случай
|
78
|
+
case rest
|
79
|
+
when 10 then tens = "десять "
|
80
|
+
when 11 then tens = "одиннадцать "
|
81
|
+
when 12 then tens = "двенадцать "
|
82
|
+
when 13 then tens = "тринадцать "
|
83
|
+
when 14 then tens = "четырнадцать "
|
84
|
+
when 15 then tens = "пятнадцать "
|
85
|
+
when 16 then tens = "шестнадцать "
|
86
|
+
when 17 then tens = "семнадцать "
|
87
|
+
when 18 then tens = "восемнадцать "
|
88
|
+
when 19 then tens = "девятнадцать "
|
89
|
+
end
|
90
|
+
when 2: tens = "двадцать "
|
91
|
+
when 3: tens = "тридцать "
|
92
|
+
when 4: tens = "сорок "
|
93
|
+
when 5: tens = "пятьдесят "
|
94
|
+
when 6: tens = "шестьдесят "
|
95
|
+
when 7: tens = "семьдесят "
|
96
|
+
when 8: tens = "восемьдесят "
|
97
|
+
when 9: tens = "девяносто "
|
98
|
+
end
|
99
|
+
#
|
100
|
+
if rest1 < 1 or rest1 > 1 # единицы
|
101
|
+
case rest % 10
|
102
|
+
when 1
|
103
|
+
case gender
|
104
|
+
when 1
|
105
|
+
ones = "один "
|
106
|
+
when 2
|
107
|
+
ones = "одна "
|
108
|
+
when 3
|
109
|
+
ones = "одно "
|
110
|
+
end
|
111
|
+
end_word = one_item
|
112
|
+
when 2
|
113
|
+
if gender == 2
|
114
|
+
ones = "две "
|
115
|
+
else
|
116
|
+
ones = "два "
|
117
|
+
end
|
118
|
+
end_word = two_items
|
119
|
+
when 3
|
120
|
+
ones = "три " if end_word = two_items
|
121
|
+
when 4
|
122
|
+
ones = "четыре " if end_word = two_items
|
123
|
+
when 5
|
124
|
+
ones = "пять "
|
125
|
+
when 6
|
126
|
+
ones = "шесть "
|
127
|
+
when 7
|
128
|
+
ones = "семь "
|
129
|
+
when 8
|
130
|
+
ones = "восемь "
|
131
|
+
when 9
|
132
|
+
ones = "девять "
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# сборка строки
|
137
|
+
return [(hundreds + tens + ones + end_word + " " + into).strip, tmp_val]
|
138
|
+
end
|
139
|
+
|
140
|
+
# Реализует вывод прописью любого объекта, реализующего Float
|
141
|
+
module FloatFormatting
|
142
|
+
|
143
|
+
# Выдает сумму прописью с учетом дробной доли. Дробная доля округляется до миллионной, или (если
|
144
|
+
# дробная доля оканчивается на нули) до ближайшей доли ( 500 тысячных округляется до 5 десятых).
|
145
|
+
# Дополнительный аргумент - род существительного (1 - мужской, 2- женский, 3-средний)
|
146
|
+
def propisju(gender = 2)
|
147
|
+
raise "NaN propisju eto ne propis!" if self.nan?
|
148
|
+
|
149
|
+
st = RuTils::Pluralization::sum_string(self.to_i, gender, "целая", "целых", "целых")
|
150
|
+
it = []
|
151
|
+
|
152
|
+
rmdr = self.to_s.match(/\.(\d+)/)[1]
|
153
|
+
|
154
|
+
signs = rmdr.to_s.size- 1
|
155
|
+
|
156
|
+
it << ["десятая", "десятых"]
|
157
|
+
it << ["сотая", "сотых"]
|
158
|
+
it << ["тысячная", "тысячных"]
|
159
|
+
it << ["десятитысячная", "десятитысячных"]
|
160
|
+
it << ["стотысячная", "стотысячных"]
|
161
|
+
it << ["миллионная", "милллионных"]
|
162
|
+
# it << ["десятимиллионная", "десятимилллионных", "десятимиллионных"]
|
163
|
+
# it << ["стомиллионная", "стомилллионных", "стомиллионных"]
|
164
|
+
# it << ["миллиардная", "миллиардных", "миллиардных"]
|
165
|
+
# it << ["десятимиллиардная", "десятимиллиардных", "десятимиллиардных"]
|
166
|
+
# it << ["стомиллиардная", "стомиллиардных", "стомиллиардных"]
|
167
|
+
# it << ["триллионная", "триллионных", "триллионных"]
|
168
|
+
|
169
|
+
while it[signs].nil?
|
170
|
+
rmdr = (rmdr/10).round
|
171
|
+
signs = rmdr.to_s.size- 1
|
172
|
+
end
|
173
|
+
|
174
|
+
suf1, suf2, suf3 = it[signs][0], it[signs][1], it[signs][2]
|
175
|
+
st + " " + RuTils::Pluralization::sum_string(rmdr.to_i, 2, suf1, suf2, suf2)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Реализует вывод прописью любого объекта, реализующего Numeric
|
180
|
+
module NumericFormatting
|
181
|
+
# Выбирает корректный вариант числительного в зависимости от рода и числа и оформляет сумму прописью
|
182
|
+
# 234.propisju => "двести сорок три"
|
183
|
+
# 221.propisju(2) => "двести двадцать одна"
|
184
|
+
def propisju(gender = 1)
|
185
|
+
RuTils::Pluralization::sum_string(self, gender, "")
|
186
|
+
end
|
187
|
+
|
188
|
+
def propisju_items(gender=1, *forms)
|
189
|
+
RuTils::Pluralization::sum_string(self, gender, "") + " " + RuTils::Pluralization::choose_plural(self, *forms)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Выбирает корректный вариант числительного в зависимости от рода и числа. Например:
|
193
|
+
# * 4.items("колесо", "колеса", "колес") => "колеса"
|
194
|
+
def items(one_item, two_items, five_items)
|
195
|
+
RuTils::Pluralization::choose_plural(self, one_item, two_items, five_items)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Numeric
|
202
|
+
include RuTils::Pluralization::NumericFormatting
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
class Float
|
207
|
+
include RuTils::Pluralization::FloatFormatting
|
208
|
+
end
|
data/lib/rutils.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$KCODE = 'u'
|
2
|
+
require 'jcode'
|
3
|
+
|
4
|
+
# Главный контейнер модуля
|
5
|
+
module RuTils
|
6
|
+
VERSION = '0.0.3'
|
7
|
+
end
|
8
|
+
|
9
|
+
require File.dirname(__FILE__) + '/pluralizer/pluralizer'
|
10
|
+
require File.dirname(__FILE__) + '/gilenson/gilenson_port' #Предельно идентичный порт Тыпографицы
|
11
|
+
#require File.dirname(__FILE__) + '/gilenson/gilenson' #Гиленсон, рефакторенный нами - todo
|
12
|
+
require File.dirname(__FILE__) + '/datetime/datetime' # Дата и время без локалей
|
13
|
+
require File.dirname(__FILE__) + '/transliteration/transliteration' # Транслит
|
14
|
+
require File.dirname(__FILE__) + '/integration/integration' # Интеграция с rails, textile и тд
|