rutils 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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