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.
@@ -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) - текст выводимый GIlenson можно верстать на бумаге
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 => "&#8722;", # minus sign
155
150
  :inch => "&#8243;", # inch/second sign (u0x2033) (не путать с кавычками!)
156
151
  :thinsp => "&#8201;", # полукруглая шпация (тонкий пробел)
157
- :nob_open => '<nobr>', # открывающий блок без переноса слов
158
- :nob_close => '</nobr>', # открывающий блок без переноса слов
159
- } #:nodoc:
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 { self.glyph[glyph_to_lookup] }
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);)/, self.glyph[: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, glyph[:nob_open]+'\1'+glyph[:nob_close])
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!(/&/, glyph[:amp])
498
- text.gsub!(/</, glyph[:lt])
499
- text.gsub!(/>/, glyph[:gt])
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'+self.glyph[:ndash]+'\2')
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'+self.glyph[:mdash]+'\2')
520
+ text.gsub!( /(\s|;)\-\-(\s)/ui, '\1'+@mdash+'\2')
513
521
  # 4. (с)
514
- text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, self.glyph[:copy]) if @settings["(c)"]
522
+ text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, @copy) if @settings["(c)"]
515
523
  # 4a. (r)
516
- text.gsub!( /\(r\)/ui, '<sup>'+self.glyph[:reg]+'</sup>') if @settings["(r)"]
524
+ text.gsub!( /\(r\)/ui, '<sup>'+@reg+'</sup>') if @settings["(r)"]
517
525
 
518
526
  # 4b. (tm)
519
- text.gsub!( /\(tm\)|\(тм\)/ui, self.glyph[:trade]) if @settings["(tm)"]
527
+ text.gsub!( /\(tm\)|\(тм\)/ui, @trade) if @settings["(tm)"]
520
528
  # 4c. (p)
521
- text.gsub!( /\(p\)/ui, self.glyph[:sect]) if @settings["(p)"]
529
+ text.gsub!( /\(p\)/ui, @sect) if @settings["(p)"]
522
530
  end
523
531
 
524
532
  def process_ellipsises(text)
525
- text.gsub!( '...', self.glyph[:hellip])
533
+ text.gsub!( '...', @hellip)
526
534
  end
527
535
 
528
536
  def process_laquo(text)
529
- text.gsub!( /\"\"/ui, self.glyph[:quot]*2);
530
- text.gsub!( /(^|\s|#{@mark_tag}|>|\()\"((#{@mark_tag})*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, '\1'+self.glyph[:laquo]+'\2');
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!( /(#{self.glyph[:laquo]}([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/\?\!](#{@mark_tag})*)\"/sui,
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, self.glyph[:quot]*2)
541
- text.gsub!( /\"\.\"/ui, self.glyph[:quot]+"."+self.glyph[:quot])
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}|>)\"([0-9A-Za-z\'\!\s\.\?\,\-\&\;\:\_\#{@mark_tag}]+(\"|#{self.glyph[:rdquo]}))/ui, '\1'+self.glyph[:ldquo]+'\2')
546
- #this doesnt work in-place. somehow.
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!(/(#{self.glyph[:ldquo]}(([A-Za-z0-9'!\.?,\-&;:]|\s|#{@mark_tag})*)#{self.glyph[:laquo]}(.*)#{self.glyph[:raquo]})#{self.glyph[:raquo]}/ui,'\1'+self.glyph[:rdquo]);
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С])/, self.glyph[:ndash]+'\1'+self.glyph[:deg]+'\2') #deg
557
- text.gsub!( /\+([0-9])+\^([FCС])/, '+\1'+self.glyph[:deg]+'\2')
558
- text.gsub!( /\^([FCС])/, self.glyph[:deg]+'\1')
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' + glyph[:nbsp]+'\4')
568
- text.gsub!( /(\s+)([a-zа-яА-Я]{3})(\s+)([^\\s$])/ui, '\1\2' + glyph[:nbsp]+'\4')
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
- text.gsub!(/(\s+)([a-zа-яА-Я]{1,2}[\)\]\!\?,\.;]{0,3}\s$)/ui, glyph[:nbsp]+'\2')
580
+ # Пунктуация это либо один из наших глифов, либо мемберы класса. В данном случае
581
+ # мы цепляемся за кончик строки поэтому можум прихватить и глиф тоже
582
+ # Пунктуация включает наши собственные глифы!
583
+ punct = glyph.values.map{|v| Regexp.escape(v)}.join('|')
584
+ vpunct = /(#{punct}|[\)\]\!\?,\.;])/
572
585
 
573
- @glueleft.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, '\1\2' + glyph[:nbsp]) }
586
+ text.gsub!(/(\s+)([a-zа-яА-Я0-9]{1,2}#{vpunct}{0,3}\s$)/ui, @nbsp+'\2')
574
587
 
575
- @glueright.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, glyph[:nbsp]+'\2\3') }
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)' % glyph[:thinsp])
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'+self.glyph[:inch])
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, self.glyph[:plusmn])
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
- opts = args.last.is_a?(Hash) ? args.last : {}
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 :old_to_html, :to_html
3
+ alias_method :to_html_without_rutils, :to_html
4
4
  def to_html(*opts)
5
- RuTils::overrides_enabled? ? RuTils::Gilenson::Formatter.new(old_to_html(*opts)).to_html : old_to_html(*opts)
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 можно получить как часть Multilingual Rails, как часть Ruby-Gettext или как отдельный
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
- # Заменяет ActionView::Helpers::DateHelper::distance_of_time_in_words на русское сообщение.
5
- #
6
- # Целые числа интерпретируются как секунды
7
- # <tt>distance_of_time_in_words(50)</tt> возвращает "меньше минуты".
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
- alias :stock_distance_of_time_in_words :distance_of_time_in_words
10
- def distance_of_time_in_words(*args)
11
- RuTils::overrides_enabled? ? RuTils::DateTime::distance_of_time_in_words(*args) : stock_distance_of_time_in_words
12
- end
13
-
14
- # Заменяет ActionView::Helpers::DateHelper::select_month меню выбора русских месяцев.
15
- #
16
- # select_month(Date.today) # Использует ключи "Январь", "Март"
17
- # select_month(Date.today, :use_month_numbers => true) # Использует ключи "1", "3"
18
- # select_month(Date.today, :add_month_numbers => true) # Использует ключи "1 - Январь", "3 - Март"
19
- def select_month(date, options = {})
20
- month_options = []
21
- if RuTils::overrides_enabled?
22
- month_names = case true
23
- when options[:use_short_month]
24
- Date::RU_ABBR_MONTHNAMES
25
- when options[:order] && options[:order].include?(:day) # использование в контексте date_select с днями требует родительный падеж
26
- Date::RU_INFLECTED_MONTHNAMES
27
- else
28
- Date::RU_MONTHNAMES
29
- end
30
- else
31
- month_names = options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES
32
- end
33
- 1.upto(12) do |month_number|
34
- month_name = if options[:use_month_numbers]
35
- month_number
36
- elsif options[:add_month_numbers]
37
- month_number.to_s + ' - ' + month_names[month_number]
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
- month_names[month_number]
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
- select_html(options[:field_name] || 'month', month_options, options)
47
+ else
48
+ month_names = options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES
49
49
  end
50
-
51
- # Заменяет ActionView::Helpers::DateHelper::select_date меню выбора русской даты.
52
- def select_date(date = Date.today, options = {})
53
- options[:order] ||= []
54
- [:day, :month, :year].each { |o| options[:order].push(o) unless options[:order].include?(o) }
55
-
56
- select_date = ''
57
- options[:order].each do |o|
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
- select_date
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
- # Заменяет ActionView::Helpers::DateHelper::select_datetime меню выбора русской даты.
64
- def select_datetime(datetime = Time.now, options = {})
65
- select_day(datetime, options) + select_month(datetime, options) + select_year(datetime, options) +
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) and (!RedCloth.instance_methods.include?(:stock_pgl))
1
+ if defined?(Object::RedCloth) && !RedCloth.instance_methods.include?(:htmlesc_without_rutils)
2
2
  # RuTils выполняет перегрузку Textile Glyphs в RedCloth, перенося форматирование спецсимволов на Gilenson.
3
- class ::Object::RedCloth < String #:nodoc:
4
-
3
+ class RedCloth < String #:nodoc:
4
+
5
5
  # Этот метод в RedCloth при наличии Гиленсона надо отключать
6
- alias_method :stock_htmlesc, :htmlesc
6
+ alias_method :htmlesc_without_rutils, :htmlesc
7
7
  def htmlesc(text, mode=0) #:nodoc:
8
- RuTils::overrides_enabled? ? text : stock_htmlesc(text, mode)
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 :stock_pgl, :pgl
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
- stock_pgl(text)
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 = 3
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
@@ -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 test_strftime_ru
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