rutils 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGELOG → History.txt} +49 -18
- data/Manifest.txt +27 -0
- data/README.txt +214 -0
- data/Rakefile.rb +48 -120
- data/{TODO → TODO.txt} +1 -4
- data/bin/gilensize +8 -4
- data/bin/rutilize +0 -0
- data/lib/datetime/datetime.rb +15 -17
- data/lib/gilenson/gilenson.rb +74 -60
- data/lib/integration/blue_cloth_override.rb +7 -3
- data/lib/integration/integration.rb +2 -2
- data/lib/integration/rails_date_helper_override.rb +91 -57
- data/lib/integration/red_cloth_override.rb +8 -8
- data/lib/pluralizer/pluralizer.rb +4 -0
- data/lib/rutils.rb +11 -6
- data/lib/transliteration/bidi.rb +8 -1
- data/test/t_datetime.rb +16 -1
- data/test/t_gilenson.rb +45 -23
- data/test/t_integration.rb +73 -29
- data/test/t_pluralize.rb +2 -0
- metadata +78 -62
- data/README +0 -192
- data/lib/gilenson/gilenson_port.rb +0 -321
- data/test/t_typografica.rb +0 -57
data/lib/gilenson/gilenson.rb
CHANGED
@@ -5,20 +5,13 @@ module RuTils
|
|
5
5
|
def self.new(*args) #:nodoc:
|
6
6
|
RuTils::Gilenson::Formatter.new(*args)
|
7
7
|
end
|
8
|
-
|
9
|
-
# Загружаем "старый" Гиленсон если он будет нужен
|
10
|
-
def self.const_missing(const) #:nodoc:
|
11
|
-
super(const) unless const == :Obsolete
|
12
|
-
require File.dirname(__FILE__) + '/gilenson_port'
|
13
|
-
return RuTils::Gilenson::Obsolete
|
14
|
-
end
|
15
8
|
end
|
16
9
|
end
|
17
10
|
|
18
11
|
# ==Что такое Gilenson
|
19
12
|
# Обработчик типографских символов в HTML согласно общепринятым правилам.
|
20
13
|
# Посвящается П.Г.Гиленсону[http://www.rudtp.ru/lib.php?book=172], благодаря которому русские правила тех.
|
21
|
-
# редактуры еще как минимум 20 лет останутся
|
14
|
+
# редактуры еще как минимум 20 лет таки останутся бессмысленно старомодными.
|
22
15
|
#
|
23
16
|
# Gilenson расставит в тексте "умные" правильные кавычки (русские - для кириллицы, английские - для латиницы),
|
24
17
|
# заменит "хитрые" пунктуационные символы на entities и отформатирует знаки типа (c), (tm), телефоны и адреса.
|
@@ -26,7 +19,7 @@ end
|
|
26
19
|
# Gilenson базируется на коде Typografica[http://pixel-apes.com/typografica] от PixelApes,
|
27
20
|
# который был приведен к положенному в Ruby стандарту. Основные отличия Gilenson от Typografica на PHP:
|
28
21
|
# * работа только и полностью в UTF-8 (включая entities, применимые в XML)
|
29
|
-
# * поддержка "raw"-вывода (символов вместо entities) - текст выводимый
|
22
|
+
# * поддержка "raw"-вывода (символов вместо entities) - текст выводимый Gilenson можно верстать на бумаге
|
30
23
|
#
|
31
24
|
# Если вам нужно получать идентичный Typografica вывод, пользуйтесь RuTils::Gilenson::Obsolete
|
32
25
|
# вместо RuTils::Gilenson::Formatter.
|
@@ -119,6 +112,8 @@ class RuTils::Gilenson::Formatter
|
|
119
112
|
"raw_output" => false, # выводить UTF-8 вместо entities
|
120
113
|
"skip_attr" => false, # при true не отрабатывать типографику в атрибутах тегов
|
121
114
|
"skip_code" => true, # при true не отрабатывать типографику внутри <code/>, <tt/>, CDATA
|
115
|
+
"enforce_en_quotes" => false, # только латинские кавычки
|
116
|
+
"enforce_ru_quotes" => false, # только русские кавычки (enforce_en_quotes при этом игнорируется)
|
122
117
|
} #:nodoc:
|
123
118
|
|
124
119
|
SETTINGS.freeze
|
@@ -154,9 +149,9 @@ class RuTils::Gilenson::Formatter
|
|
154
149
|
:minus => "−", # minus sign
|
155
150
|
:inch => "″", # inch/second sign (u0x2033) (не путать с кавычками!)
|
156
151
|
:thinsp => " ", # полукруглая шпация (тонкий пробел)
|
157
|
-
:nob_open => '<nobr>', # открывающий блок без переноса слов
|
158
|
-
:nob_close => '</
|
159
|
-
}
|
152
|
+
:nob_open => '<span class="nobr">', # открывающий блок без переноса слов
|
153
|
+
:nob_close => '</span>', # закрывающий блок без переноса слов
|
154
|
+
}
|
160
155
|
|
161
156
|
GLYPHS.freeze
|
162
157
|
# Нормальные "типографские" символы в UTF-виде. Браузерами обрабатываются плохонько, поэтому
|
@@ -211,7 +206,7 @@ class RuTils::Gilenson::Formatter
|
|
211
206
|
FORBIDDEN_NUMERIC_ENTITIES.freeze #:nodoc:
|
212
207
|
|
213
208
|
PROTECTED_SETTINGS = [ :raw_output ] #:nodoc:
|
214
|
-
|
209
|
+
|
215
210
|
def initialize(*args)
|
216
211
|
@_text = args[0].is_a?(String) ? args[0] : ''
|
217
212
|
setup_default_settings!
|
@@ -238,6 +233,7 @@ class RuTils::Gilenson::Formatter
|
|
238
233
|
# Дополнительные аргументы передаются как параметры форматтера и не сохраняются после прогона.
|
239
234
|
def process(text_to_process, *args)
|
240
235
|
@_text = text_to_process
|
236
|
+
|
241
237
|
if args.last.is_a?(Hash)
|
242
238
|
with_configuration(args.last) { self.to_html }
|
243
239
|
else
|
@@ -250,13 +246,27 @@ class RuTils::Gilenson::Formatter
|
|
250
246
|
return '' unless @_text
|
251
247
|
|
252
248
|
text = @_text.strip
|
249
|
+
|
250
|
+
# -6. Подмухляем таблицу глифов, если нам ее передали
|
251
|
+
glyph_table = glyph.dup
|
252
|
+
|
253
|
+
if @settings["enforce_ru_quotes"]
|
254
|
+
glyph_table[:ldquo], glyph_table[:rdquo] = glyph_table[:laquo], glyph_table[:raquo]
|
255
|
+
elsif @settings["enforce_en_quotes"]
|
256
|
+
glyph_table[:laquo], glyph_table[:raquo] = glyph_table[:ldquo], glyph_table[:rdquo]
|
257
|
+
end
|
258
|
+
|
259
|
+
# -5. Копируем глифы в ивары, к ним доступ быстр и в коде они глаза тоже не мозолят
|
260
|
+
glyph_table.each_pair do | ki, wi |
|
261
|
+
instance_variable_set("@#{ki}", wi)
|
262
|
+
end
|
253
263
|
|
254
264
|
# -4. запрет тагов html
|
255
265
|
process_escape_html(text) unless @settings["html"]
|
256
266
|
|
257
267
|
# -3. Никогда (вы слышите?!) не пущать лабуду &#not_correct_number;
|
258
268
|
FORBIDDEN_NUMERIC_ENTITIES.dup.each_pair do | key, rep |
|
259
|
-
text.gsub!(/&##{key};/, glyph[rep])
|
269
|
+
text.gsub!(/&##{key};/, self.glyph[rep])
|
260
270
|
end
|
261
271
|
|
262
272
|
# -2. Чистим copy&paste
|
@@ -271,16 +281,14 @@ class RuTils::Gilenson::Formatter
|
|
271
281
|
# 1. Запятые и пробелы
|
272
282
|
process_spacing(text) if @settings["spacing"]
|
273
283
|
|
274
|
-
# 3. Спецсимволы
|
275
|
-
# 0. дюймы с цифрами
|
276
|
-
# заменено на инчи
|
277
|
-
process_inches(text) if @settings["inches"]
|
278
|
-
|
279
284
|
# 1. лапки
|
280
285
|
process_quotes(text) if @settings["quotes"]
|
281
286
|
|
282
287
|
# 2. ёлочки
|
283
288
|
process_laquo(text) if @settings["laquo"]
|
289
|
+
|
290
|
+
# 3. Инчи
|
291
|
+
process_inches(text) if @settings["inches"]
|
284
292
|
|
285
293
|
# 2b. одновременно ёлочки и лапки
|
286
294
|
process_compound_quotes(text) if (@settings["quotes"] && @settings["laquo"])
|
@@ -386,9 +394,9 @@ class RuTils::Gilenson::Formatter
|
|
386
394
|
end
|
387
395
|
|
388
396
|
# Позволяет получить процедуру, при вызове возвращающую значение глифа
|
389
|
-
def lookup(glyph_to_lookup)
|
390
|
-
return Proc.new {
|
391
|
-
end
|
397
|
+
# def lookup(glyph_to_lookup)
|
398
|
+
# return Proc.new { g[glyph_to_lookup] }
|
399
|
+
# end
|
392
400
|
|
393
401
|
# Подставляет "символы" (двоеточие + имя глифа) на нужное значение глифа заданное в данном форматтере
|
394
402
|
def substitute_glyphs_in_string(str)
|
@@ -457,7 +465,7 @@ class RuTils::Gilenson::Formatter
|
|
457
465
|
def reinsert_fragments(text, fragments)
|
458
466
|
fragments.each do |fragment|
|
459
467
|
fragment.gsub!(/ (href|src|data)=((?:(\')([^\']*)(\'))|(?:(\")([^\"]*)(\")))/uim) do
|
460
|
-
" #{$1}=" + $2.gsub(/&(?!(#0*38)|(amp);)/,
|
468
|
+
" #{$1}=" + $2.gsub(/&(?!(#0*38)|(amp);)/, @amp)
|
461
469
|
end # unless @settings['raw_output'] -- делать это надо всегда (mash)
|
462
470
|
|
463
471
|
unless @settings['skip_attr']
|
@@ -490,13 +498,13 @@ class RuTils::Gilenson::Formatter
|
|
490
498
|
end
|
491
499
|
|
492
500
|
def process_dashglue(text)
|
493
|
-
text.gsub!( /([a-zа-яА-Я0-9]+(\-[a-zа-яА-Я0-9]+)+)/ui,
|
501
|
+
text.gsub!( /([a-zа-яА-Я0-9]+(\-[a-zа-яА-Я0-9]+)+)/ui, @nob_open+'\1'+ @nob_close)
|
494
502
|
end
|
495
503
|
|
496
504
|
def process_escape_html(text)
|
497
|
-
text.gsub!(/&/,
|
498
|
-
text.gsub!(/</,
|
499
|
-
text.gsub!(/>/,
|
505
|
+
text.gsub!(/&/, @amp)
|
506
|
+
text.gsub!(/</, @lt)
|
507
|
+
text.gsub!(/>/, @gt)
|
500
508
|
end
|
501
509
|
|
502
510
|
def process_span_instead_of_nobr(text)
|
@@ -505,76 +513,82 @@ class RuTils::Gilenson::Formatter
|
|
505
513
|
end
|
506
514
|
|
507
515
|
def process_dash(text)
|
508
|
-
text.gsub!( /(\s|;)\-(\s)/ui, '\1'
|
516
|
+
text.gsub!( /(\s|;)\-(\s)/ui, '\1'+@ndash+'\2')
|
509
517
|
end
|
510
518
|
|
511
519
|
def process_emdash(text)
|
512
|
-
text.gsub!( /(\s|;)\-\-(\s)/ui, '\1'
|
520
|
+
text.gsub!( /(\s|;)\-\-(\s)/ui, '\1'+@mdash+'\2')
|
513
521
|
# 4. (с)
|
514
|
-
text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u,
|
522
|
+
text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, @copy) if @settings["(c)"]
|
515
523
|
# 4a. (r)
|
516
|
-
text.gsub!( /\(r\)/ui, '<sup>'
|
524
|
+
text.gsub!( /\(r\)/ui, '<sup>'+@reg+'</sup>') if @settings["(r)"]
|
517
525
|
|
518
526
|
# 4b. (tm)
|
519
|
-
text.gsub!( /\(tm\)|\(тм\)/ui,
|
527
|
+
text.gsub!( /\(tm\)|\(тм\)/ui, @trade) if @settings["(tm)"]
|
520
528
|
# 4c. (p)
|
521
|
-
text.gsub!( /\(p\)/ui,
|
529
|
+
text.gsub!( /\(p\)/ui, @sect) if @settings["(p)"]
|
522
530
|
end
|
523
531
|
|
524
532
|
def process_ellipsises(text)
|
525
|
-
text.gsub!( '...',
|
533
|
+
text.gsub!( '...', @hellip)
|
526
534
|
end
|
527
535
|
|
528
536
|
def process_laquo(text)
|
529
|
-
text.gsub!( /\"\"/ui,
|
530
|
-
text.gsub!( /(^|\s|#{@mark_tag}|>|\()\"((#{@mark_tag})*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, '\1'+
|
537
|
+
text.gsub!( /\"\"/ui, @quot * 2);
|
538
|
+
text.gsub!( /(^|\s|#{@mark_tag}|>|\()\"((#{@mark_tag})*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, '\1' + @laquo + '\2');
|
531
539
|
_text = '""';
|
532
540
|
until _text == text do
|
533
541
|
_text = text;
|
534
|
-
text.gsub!( /(#{
|
535
|
-
'\1'+self.glyph[:raquo])
|
542
|
+
text.gsub!( /(#{@laquo}([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/\?\!](#{@mark_tag})*)\"/sui, '\1' + @raquo)
|
536
543
|
end
|
537
544
|
end
|
538
|
-
|
545
|
+
|
539
546
|
def process_quotes(text)
|
540
|
-
text.gsub!( /\"\"/ui,
|
541
|
-
text.gsub!( /\"\.\"/ui,
|
547
|
+
text.gsub!( /\"\"/ui, @quot*2)
|
548
|
+
text.gsub!( /\"\.\"/ui, @quot+"."+@quot)
|
542
549
|
_text = '""';
|
550
|
+
lat_c = '0-9A-Za-z'
|
551
|
+
punct = /\'\!\s\.\?\,\-\&\;\:\\/
|
552
|
+
|
543
553
|
until _text == text do
|
544
554
|
_text = text.dup
|
545
|
-
text.gsub!( /(^|\s|#{@mark_tag}|>)\"([
|
546
|
-
#
|
547
|
-
text.gsub!( /(#{self.glyph[:ldquo]}([A-Za-z0-9\'\!\s\.\?\,\-\&\;\:\#{@mark_tag}\_]*).*[A-Za-z0-9][\#{@mark_tag}\?\.\!\,]*)\"/ui, '\1'+self.glyph[:rdquo])
|
555
|
+
text.gsub!( /(^|\s|#{@mark_tag}|>)\"([#{lat_c}#{punct}\_\#{@mark_tag}]+(\"|#{@rdquo}))/ui, '\1'+ @ldquo +'\2')
|
556
|
+
text.gsub!( /(#{@ldquo}([#{lat_c}#{punct}#{@mark_tag}\_]*).*[#{lat_c}][\#{@mark_tag}\?\.\!\,\\]*)\"/ui, '\1'+ @rdquo)
|
548
557
|
end
|
549
558
|
end
|
550
559
|
|
551
560
|
def process_compound_quotes(text)
|
552
|
-
text.gsub!(/(#{
|
561
|
+
text.gsub!(/(#{@ldquo}(([A-Za-z0-9'!\.?,\-&;:]|\s|#{@mark_tag})*)#{@laquo}(.*)#{@raquo})#{@raquo}/ui, '\1' + @rdquo);
|
553
562
|
end
|
554
563
|
|
555
564
|
def process_degrees(text)
|
556
|
-
text.gsub!( /-([0-9])+\^([FCС])/,
|
557
|
-
text.gsub!( /\+([0-9])+\^([FCС])/, '+\1'+
|
558
|
-
text.gsub!( /\^([FCС])/,
|
565
|
+
text.gsub!( /-([0-9])+\^([FCС])/, @ndash+'\1'+ @deg +'\2') #deg
|
566
|
+
text.gsub!( /\+([0-9])+\^([FCС])/, '+\1'+ @deg +'\2')
|
567
|
+
text.gsub!( /\^([FCС])/, @deg+'\1')
|
559
568
|
end
|
560
569
|
|
561
570
|
def process_wordglue(text)
|
562
571
|
text.replace(" " + text + " ")
|
563
572
|
_text = " " + text + " "
|
564
|
-
|
573
|
+
|
565
574
|
until _text == text
|
566
575
|
_text = text
|
567
|
-
text.gsub!( /(\s+)([a-zа-яА-Я]{1,2})(\s+)([^\\s$])/ui, '\1\2' +
|
568
|
-
text.gsub!( /(\s+)([a-zа-яА-Я]{3})(\s+)([^\\s$])/ui, '\1\2' +
|
576
|
+
text.gsub!( /(\s+)([a-zа-яА-Я0-9]{1,2})(\s+)([^\\s$])/ui, '\1\2' + @nbsp +'\4')
|
577
|
+
text.gsub!( /(\s+)([a-zа-яА-Я0-9]{3})(\s+)([^\\s$])/ui, '\1\2' + @nbsp+'\4')
|
569
578
|
end
|
570
579
|
|
571
|
-
|
580
|
+
# Пунктуация это либо один из наших глифов, либо мемберы класса. В данном случае
|
581
|
+
# мы цепляемся за кончик строки поэтому можум прихватить и глиф тоже
|
582
|
+
# Пунктуация включает наши собственные глифы!
|
583
|
+
punct = glyph.values.map{|v| Regexp.escape(v)}.join('|')
|
584
|
+
vpunct = /(#{punct}|[\)\]\!\?,\.;])/
|
572
585
|
|
573
|
-
|
586
|
+
text.gsub!(/(\s+)([a-zа-яА-Я0-9]{1,2}#{vpunct}{0,3}\s$)/ui, @nbsp+'\2')
|
574
587
|
|
575
|
-
@
|
588
|
+
@glueleft.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, '\1\2' + @nbsp) }
|
589
|
+
|
590
|
+
@glueright.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, @nbsp+'\2\3') }
|
576
591
|
|
577
|
-
text.strip!
|
578
592
|
end
|
579
593
|
|
580
594
|
def process_phones(text)
|
@@ -587,7 +601,7 @@ class RuTils::Gilenson::Formatter
|
|
587
601
|
def process_acronyms(text)
|
588
602
|
acronym = /\b([A-ZА-Я][A-ZА-Я0-9]{2,})\b(?:[(]([^)]*)[)])/u
|
589
603
|
if @settings["raw_output"]
|
590
|
-
text.gsub!(acronym, '\1%s(\2)' %
|
604
|
+
text.gsub!(acronym, '\1%s(\2)' % @thinsp)
|
591
605
|
else
|
592
606
|
text.gsub!(acronym) do
|
593
607
|
expl = $2.to_s; process_escape_html(expl)
|
@@ -596,12 +610,13 @@ class RuTils::Gilenson::Formatter
|
|
596
610
|
end
|
597
611
|
end
|
598
612
|
|
613
|
+
# Обработка знака дюйма, кроме случаев когда он внутри кавычек
|
599
614
|
def process_inches(text)
|
600
|
-
text.gsub!(/\s([0-9]{1,2}([\.,][0-9]{1,2})?)\"/ui, ' \1'+
|
615
|
+
text.gsub!(/\s([0-9]{1,2}([\.,][0-9]{1,2})?)(\"){1,1}/ui, ' \1' + @inch)
|
601
616
|
end
|
602
617
|
|
603
618
|
def process_plusmin(text)
|
604
|
-
text.gsub!(/[^+]\+\-/ui,
|
619
|
+
text.gsub!(/[^+]\+\-/ui, @plusmn)
|
605
620
|
end
|
606
621
|
|
607
622
|
# Подменяет все юникодные entities в тексте на истинные UTF-8-символы
|
@@ -628,8 +643,7 @@ end
|
|
628
643
|
module RuTils::Gilenson::StringFormatting
|
629
644
|
# Форматирует строку с помощью Gilenson::Formatter. Все дополнительные опции передаются форматтеру.
|
630
645
|
def gilensize(*args)
|
631
|
-
|
632
|
-
RuTils::Gilenson::Formatter.new(self, *opts).to_html
|
646
|
+
RuTils::Gilenson::Formatter.new(self, args.shift || {}).to_html
|
633
647
|
end
|
634
648
|
|
635
649
|
# Форматирует строку с помощью Gilenson::Obsolete. Всe дополнительные опции передаются форматтеру.
|
@@ -1,8 +1,12 @@
|
|
1
|
-
if defined?(BlueCloth)
|
1
|
+
if defined?(BlueCloth) && !BlueCloth.instance_methods.include?("to_html_without_rutils")
|
2
2
|
class Object::BlueCloth < String #:nodoc:
|
3
|
-
alias_method :
|
3
|
+
alias_method :to_html_without_rutils, :to_html
|
4
4
|
def to_html(*opts)
|
5
|
-
RuTils::overrides_enabled?
|
5
|
+
if RuTils::overrides_enabled?
|
6
|
+
RuTils::Gilenson::Formatter.new(to_html_without_rutils(*opts)).to_html
|
7
|
+
else
|
8
|
+
to_html_without_rutils(*opts)
|
9
|
+
end
|
6
10
|
end
|
7
11
|
end
|
8
12
|
end
|
@@ -5,8 +5,8 @@ module RuTils
|
|
5
5
|
# Попутно он спрашивает модуль Locale (если таковой имеется) является ли русский
|
6
6
|
# текущим языком, и если является, включает перегрузку функций имплицитно.
|
7
7
|
# Это позволяет подчинить настройку перегруженных функций настроенной локали.
|
8
|
-
# Модуль Locale можно получить как часть
|
9
|
-
# модуль ruby-locale. Мы поддерживаем
|
8
|
+
# Модуль Locale можно получить как часть Ruby-Gettext или как отдельный
|
9
|
+
# модуль ruby-locale. Мы поддерживаем оба.
|
10
10
|
def self.overrides_enabled?
|
11
11
|
if defined?(Locale) and Locale.respond_to?(:current)
|
12
12
|
return true if Locale.current.to_s.split('_').first == 'ru'
|
@@ -1,69 +1,103 @@
|
|
1
1
|
if defined?(Object::ActionView)
|
2
2
|
module Object::ActionView::Helpers::DateHelper
|
3
|
+
# Несколько хаков для корректной работы модуля с Rails 1.2--2.0 одновременно с Rails 2.1 и выше.
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# Хелперы DateHelper принимают параметр <tt>html_options</tt> (идет последним) начиная с Rails 2.1.
|
6
|
+
# Нужно понять, имеем ли мы дело с Rails 2.1+, для этого проверяем наличие классметода helper_modules у
|
7
|
+
# ActionView::Base, который появился как раз в версии 2.1.
|
8
|
+
DATE_HELPERS_RECEIVE_HTML_OPTIONS = ActionView::Base.respond_to?(:helper_modules) #:nodoc:
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
10
|
+
# В Rails Edge (2.1+) определяется <tt>Time.current</tt> для работы с временными зонами.
|
11
|
+
unless Time.respond_to? :current
|
12
|
+
class << ::Time # :nodoc:
|
13
|
+
def current; now; end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Заменяет <tt>ActionView::Helpers::DateHelper::distance_of_time_in_words</tt> на русское сообщение.
|
18
|
+
#
|
19
|
+
# Целые числа интерпретируются как секунды.
|
20
|
+
# <tt>distance_of_time_in_words(50)</tt> возвращает "меньше минуты".
|
21
|
+
alias :stock_distance_of_time_in_words :distance_of_time_in_words
|
22
|
+
def distance_of_time_in_words(*args)
|
23
|
+
RuTils::overrides_enabled? ? RuTils::DateTime::distance_of_time_in_words(*args) : stock_distance_of_time_in_words
|
24
|
+
end
|
25
|
+
|
26
|
+
# Заменяет ActionView::Helpers::DateHelper::select_month меню выбора русских месяцев.
|
27
|
+
#
|
28
|
+
# select_month(Date.today) # Использует ключи "Январь", "Март"
|
29
|
+
# select_month(Date.today, :use_month_numbers => true) # Использует ключи "1", "3"
|
30
|
+
# select_month(Date.today, :add_month_numbers => true) # Использует ключи "1 - Январь", "3 - Март"
|
31
|
+
def select_month(date, options = {}, html_options = {})
|
32
|
+
val = date ? (date.kind_of?(Fixnum) ? date : date.month) : ''
|
33
|
+
if options[:use_hidden]
|
34
|
+
hidden_html(options[:field_name] || 'month', val, options)
|
35
|
+
else
|
36
|
+
month_options = []
|
37
|
+
if RuTils::overrides_enabled?
|
38
|
+
month_names = case true
|
39
|
+
when options[:use_short_month]
|
40
|
+
Date::RU_ABBR_MONTHNAMES
|
41
|
+
# использование в контексте date_select с днями требует родительный падеж
|
42
|
+
when options[:order] && options[:order].include?(:day)
|
43
|
+
Date::RU_INFLECTED_MONTHNAMES
|
38
44
|
else
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
month_options << ((date && (date.kind_of?(Fixnum) ? date : date.month) == month_number) ?
|
43
|
-
%(<option value="#{month_number}" selected="selected">#{month_name}</option>\n) :
|
44
|
-
%(<option value="#{month_number}">#{month_name}</option>\n)
|
45
|
-
)
|
45
|
+
Date::RU_MONTHNAMES
|
46
46
|
end
|
47
|
-
|
48
|
-
|
47
|
+
else
|
48
|
+
month_names = options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES
|
49
49
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
select_date << self.send("select_#{o}", date, options)
|
50
|
+
month_names.unshift(nil) if month_names.size < 13
|
51
|
+
1.upto(12) do |month_number|
|
52
|
+
month_name = if options[:use_month_numbers]
|
53
|
+
month_number
|
54
|
+
elsif options[:add_month_numbers]
|
55
|
+
month_number.to_s + ' - ' + month_names[month_number]
|
56
|
+
else
|
57
|
+
month_names[month_number]
|
59
58
|
end
|
60
|
-
|
59
|
+
|
60
|
+
month_options << ((val == month_number) ?
|
61
|
+
content_tag(:option, month_name, :value => month_number, :selected => "selected") :
|
62
|
+
content_tag(:option, month_name, :value => month_number)
|
63
|
+
)
|
64
|
+
month_options << "\n"
|
61
65
|
end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
select_hour(datetime, options) + select_minute(datetime, options)
|
66
|
+
if DATE_HELPERS_RECEIVE_HTML_OPTIONS
|
67
|
+
select_html(options[:field_name] || 'month', month_options.join, options, html_options)
|
68
|
+
else
|
69
|
+
select_html(options[:field_name] || 'month', month_options.join, options)
|
67
70
|
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
alias :stock_select_date :select_date
|
75
|
+
# Заменяет ActionView::Helpers::DateHelper::select_date меню выбора русской даты.
|
76
|
+
def select_date(date = Date.today, options = {}, html_options = {})
|
77
|
+
options[:order] ||= [:day, :month, :year]
|
78
|
+
if DATE_HELPERS_RECEIVE_HTML_OPTIONS
|
79
|
+
stock_select_date(date, options, html_options)
|
80
|
+
else
|
81
|
+
stock_select_date(date, options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module Object::ActionView::Helpers
|
87
|
+
if defined?(InstanceTag) && InstanceTag.private_method_defined?(:date_or_time_select)
|
88
|
+
class InstanceTag #:nodoc:
|
89
|
+
private
|
90
|
+
alias :stock_date_or_time_select :date_or_time_select
|
91
|
+
def date_or_time_select(options, html_options = {})
|
92
|
+
options[:order] ||= [:day, :month, :year]
|
93
|
+
if DATE_HELPERS_RECEIVE_HTML_OPTIONS
|
94
|
+
stock_date_or_time_select(options, html_options)
|
95
|
+
else
|
96
|
+
stock_date_or_time_select(options)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
68
101
|
end
|
102
|
+
|
69
103
|
end #endif
|
@@ -1,16 +1,16 @@
|
|
1
|
-
if defined?(Object::RedCloth)
|
1
|
+
if defined?(Object::RedCloth) && !RedCloth.instance_methods.include?(:htmlesc_without_rutils)
|
2
2
|
# RuTils выполняет перегрузку Textile Glyphs в RedCloth, перенося форматирование спецсимволов на Gilenson.
|
3
|
-
class
|
4
|
-
|
3
|
+
class RedCloth < String #:nodoc:
|
4
|
+
|
5
5
|
# Этот метод в RedCloth при наличии Гиленсона надо отключать
|
6
|
-
alias_method :
|
6
|
+
alias_method :htmlesc_without_rutils, :htmlesc
|
7
7
|
def htmlesc(text, mode=0) #:nodoc:
|
8
|
-
RuTils::overrides_enabled? ? text :
|
8
|
+
RuTils::overrides_enabled? ? text : htmlesc_without_rutils(text, mode)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
# А этот метод обрабатывает Textile Glyphs - ту самую типографицу.
|
12
12
|
# Вместо того чтобы влезать в чужие таблицы мы просто заменим Textile Glyphs на Gilenson - и все будут рады.
|
13
|
-
alias_method :
|
13
|
+
alias_method :pgl_without_rutils, :pgl
|
14
14
|
def pgl(text) #:nodoc:
|
15
15
|
if RuTils::overrides_enabled?
|
16
16
|
# Suspend the spaces in the start and end of the block because Gilenson chews them off, and RedCloth feeds them to us in packs
|
@@ -20,7 +20,7 @@ if defined?(Object::RedCloth) and (!RedCloth.instance_methods.include?(:stock_pg
|
|
20
20
|
text.gsub!(/(\s+)\Z/) { spaces[1] = $1; '' }
|
21
21
|
text.replace(spaces[0].to_s + RuTils::Gilenson::Formatter.new(text).to_html + spaces[1].to_s)
|
22
22
|
else
|
23
|
-
|
23
|
+
pgl_without_rutils(text)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -219,9 +219,13 @@ module RuTils
|
|
219
219
|
|
220
220
|
# Выводит сумму в рублях прописью. Например:
|
221
221
|
# * (15.4).rublej => "пятнадцать рублей сорок копеек"
|
222
|
+
# * 1.rubl => "один рубль"
|
223
|
+
# * (3.14).rublja => "три рубля четырнадцать копеек"
|
222
224
|
def rublej
|
223
225
|
RuTils::Pluralization::rublej(self)
|
224
226
|
end
|
227
|
+
alias :rubl :rublej
|
228
|
+
alias :rublja :rublej
|
225
229
|
end
|
226
230
|
end
|
227
231
|
end
|
data/lib/rutils.rb
CHANGED
@@ -2,18 +2,23 @@ $KCODE = 'u'
|
|
2
2
|
|
3
3
|
# Главный контейнер модуля
|
4
4
|
module RuTils
|
5
|
+
#:stopdoc:
|
6
|
+
|
5
7
|
# Папка, куда установлен модуль RuTils. Нужно чтобы автоматически копировать RuTils в другие приложения.
|
6
8
|
INSTALLATION_DIRECTORY = File.expand_path(File.dirname(__FILE__) + '/../') #:nodoc:
|
7
|
-
MAJOR = 0
|
8
|
-
MINOR = 2
|
9
|
-
TINY
|
9
|
+
MAJOR = 0 #:nodoc:
|
10
|
+
MINOR = 2 #:nodoc:
|
11
|
+
TINY = 4 #:nodoc:
|
10
12
|
|
11
|
-
# Версия RuTils
|
12
|
-
VERSION = [MAJOR, MINOR ,TINY].join('.') #:nodoc:
|
13
|
-
|
14
13
|
# Стандартный маркер для подстановок - invalid UTF sequence
|
15
14
|
SUBSTITUTION_MARKER = "\xF0\xF0\xF0\xF0" #:nodoc:
|
16
15
|
|
16
|
+
# :startdoc:
|
17
|
+
|
18
|
+
# Версия RuTils
|
19
|
+
VERSION = [MAJOR, MINOR ,TINY].join('.')
|
20
|
+
|
21
|
+
|
17
22
|
def self.load_component(name) #:nodoc:
|
18
23
|
require File.join(RuTils::INSTALLATION_DIRECTORY, "lib", name.to_s, name.to_s)
|
19
24
|
end
|
data/lib/transliteration/bidi.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
# Реализует транслитерацию "в обе стороны", дающую возможность автоматически использовать URL как ключ
|
1
|
+
# Реализует транслитерацию "в обе стороны", дающую возможность автоматически использовать URL как ключ записи.
|
2
|
+
# Перед применением убедитесь что среди ваших пользователей не осталось ни одного человека, пользующегося
|
3
|
+
# любым другим алфавитом на земле кроме русского и латинского. К примеру можете таких пользователей лично истребить.
|
4
|
+
#
|
5
|
+
# Перед тем как использовать этот модуль подумайте - скорее всего вы строите приложение в котором Юникод использован
|
6
|
+
# неправильно или не полностью. Честно.
|
2
7
|
module RuTils::Transliteration::BiDi
|
3
8
|
TABLE_TO = {
|
4
9
|
"А"=>"A","Б"=>"B","В"=>"V","Г"=>"G","Д"=>"D",
|
@@ -24,6 +29,8 @@ module RuTils::Transliteration::BiDi
|
|
24
29
|
TABLE_FROM = TABLE_TO.unshift([" ","__"]).clone
|
25
30
|
TABLE_TO.unshift(["_","__"])
|
26
31
|
|
32
|
+
#:startdoc:
|
33
|
+
|
27
34
|
def self.translify(str, allow_slashes = true)
|
28
35
|
slash = allow_slashes ? '/' : '';
|
29
36
|
|
data/test/t_datetime.rb
CHANGED
@@ -13,7 +13,7 @@ class DistanceOfTimeTest < Test::Unit::TestCase
|
|
13
13
|
end
|
14
14
|
|
15
15
|
class StrftimeRuTest < Test::Unit::TestCase
|
16
|
-
def
|
16
|
+
def test_should_format_in_russian
|
17
17
|
@@old_overrides = RuTils::overrides_enabled?
|
18
18
|
|
19
19
|
RuTils::overrides = true
|
@@ -22,6 +22,8 @@ class StrftimeRuTest < Test::Unit::TestCase
|
|
22
22
|
assert_equal "%сб, %суббота, %дек, %декабрь", Time.local(2005,"dec",31).strftime("%%%a, %%%A, %%%b, %%%B")
|
23
23
|
assert_equal "Сегодня: 31 декабря, суббота, 2005 года", Time.local(2005,"dec",31).strftime("Сегодня: %d %B, %A, %Y года")
|
24
24
|
assert_equal "Сегодня: ноябрь, 30 число, дождик в четверг, а год у нас - 2006", Time.local(2006,11,30).strftime("Сегодня: %B, %d число, дождик в %A, а год у нас - %Y")
|
25
|
+
assert_equal "01 декабря", Time.local(1985, "dec", 01).strftime("%d %B")
|
26
|
+
assert_equal "11 декабря", Time.local(1985, "dec", 11).strftime("%d %B")
|
25
27
|
|
26
28
|
date = Date.new(2005, 12, 31)
|
27
29
|
assert_equal "дек декабрь сб суббота", "#{Date::RU_ABBR_MONTHNAMES[date.mon]} #{Date::RU_MONTHNAMES[date.mon]} #{Date::RU_ABBR_DAYNAMES[date.wday]} #{Date::RU_DAYNAMES[date.wday]}"
|
@@ -33,10 +35,23 @@ class StrftimeRuTest < Test::Unit::TestCase
|
|
33
35
|
assert_equal "%a, %A, %b, %B", Time.local(2005,"dec",31).strftime("%%a, %%A, %%b, %%B")
|
34
36
|
assert_equal "%Sat, %Saturday, %Dec, %December", Time.local(2005,"dec",31).strftime("%%%a, %%%A, %%%b, %%%B")
|
35
37
|
assert_equal "Сегодня: 31 December, Saturday, 2005 год", Time.local(2005,"dec",31).strftime("Сегодня: %d %B, %A, %Y год")
|
38
|
+
assert_equal "01 December", Time.local(1985, "dec", 01).strftime("%d %B")
|
39
|
+
assert_equal "11 December", Time.local(1985, "dec", 11).strftime("%d %B")
|
36
40
|
|
37
41
|
date = Date.new(2005, 11, 9)
|
38
42
|
assert_equal "Nov November Wed Wednesday", "#{Date::ABBR_MONTHNAMES[date.mon]} #{Date::MONTHNAMES[date.mon]} #{Date::ABBR_DAYNAMES[date.wday]} #{Date::DAYNAMES[date.wday]}"
|
39
43
|
|
40
44
|
RuTils::overrides = @@old_overrides
|
41
45
|
end
|
46
|
+
|
47
|
+
def test_formatter_should_actually_return
|
48
|
+
the_fmt = "Это случилось %d %B"
|
49
|
+
assert_equal "Это случилось 01 декабря", RuTils::DateTime::ru_strftime(Time.local(1985, "dec", 01), the_fmt)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_formatter_should_not_mutate_passed_format_strings
|
53
|
+
the_fmt, backup = "Это случилось %d %B", "Это случилось %d %B"
|
54
|
+
assert_equal "Это случилось 01 декабря", RuTils::DateTime::ru_strftime(Time.local(1985, "dec", 01), the_fmt)
|
55
|
+
assert_equal the_fmt, backup
|
56
|
+
end
|
42
57
|
end
|