rutils 0.2.3 → 0.2.4
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 → 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
|