rutils 1.0.3 → 1.1.1

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.
@@ -10,6 +10,5 @@ class RdiscountIntegrationTest < Test::Unit::TestCase
10
10
 
11
11
  assert_equal "<p>И вот&#160;&#171;они пошли туда&#187;, и&#160;шли шли&#160;шли</p>",
12
12
  C.new('И вот "они пошли туда", и шли шли шли').to_html
13
-
14
13
  end
15
14
  end
@@ -43,6 +43,15 @@ class PropisjuTestCase < Test::Unit::TestCase
43
43
  assert_equal "один рубль", 1.rubl
44
44
  assert_equal "три рубля четырнадцать копеек", (3.14).rublja
45
45
  end
46
+
47
+ def test_griven
48
+ assert_equal "сто двадцать три гривны", 123.griven
49
+ assert_equal "триста сорок три гривны двадцать копеек", (343.20).griven
50
+ assert_equal "сорок две копейки", (0.4187).griven
51
+ assert_equal "триста тридцать две гривны", (331.995).griven
52
+ assert_equal "одна гривна", 1.grivna
53
+ assert_equal "три гривны четырнадцать копеек", (3.14).grivny
54
+ end
46
55
 
47
56
  def test_kopeek
48
57
  assert_equal "сто двадцать три рубля", 12300.kopeek
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rutils
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ hash: 17
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 1
10
+ version: 1.1.1
5
11
  platform: ruby
6
12
  authors:
7
13
  - Julian 'Julik' Tarkhanov
@@ -11,25 +17,60 @@ autorequire:
11
17
  bindir: bin
12
18
  cert_chain: []
13
19
 
14
- date: 2009-12-20 00:00:00 +01:00
20
+ date: 2010-08-15 00:00:00 +02:00
15
21
  default_executable:
16
22
  dependencies:
17
23
  - !ruby/object:Gem::Dependency
18
- name: hoe
24
+ name: gilenson
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 3
32
+ segments:
33
+ - 0
34
+ version: "0"
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rubyforge
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 2
48
+ - 0
49
+ - 4
50
+ version: 2.0.4
19
51
  type: :development
20
- version_requirement:
21
- version_requirements: !ruby/object:Gem::Requirement
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: hoe
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
22
58
  requirements:
23
59
  - - ">="
24
60
  - !ruby/object:Gem::Version
25
- version: 2.3.3
26
- version:
61
+ hash: 21
62
+ segments:
63
+ - 2
64
+ - 6
65
+ - 1
66
+ version: 2.6.1
67
+ type: :development
68
+ version_requirements: *id003
27
69
  description: Simple processing of russian strings
28
70
  email:
29
71
  - me@julik.nl
30
72
  - yaroslav@markin.net
31
73
  executables:
32
- - gilensize
33
74
  - rutilize
34
75
  extensions: []
35
76
 
@@ -38,21 +79,18 @@ extra_rdoc_files:
38
79
  - Manifest.txt
39
80
  - README.txt
40
81
  - TODO.txt
41
- - WHAT_HAS_CHANGED.txt
42
82
  files:
43
83
  - History.txt
44
84
  - Manifest.txt
45
85
  - README.txt
46
86
  - Rakefile.rb
47
87
  - TODO.txt
48
- - WHAT_HAS_CHANGED.txt
49
- - bin/gilensize
50
88
  - bin/rutilize
51
89
  - init.rb
52
90
  - lib/countries/countries.rb
53
91
  - lib/datetime/datetime.rb
54
92
  - lib/gilenson/bluecloth_extra.rb
55
- - lib/gilenson/gilenson.rb
93
+ - lib/gilenson/gilenson_stub.rb
56
94
  - lib/gilenson/helper.rb
57
95
  - lib/gilenson/maruku_extra.rb
58
96
  - lib/gilenson/rdiscount_extra.rb
@@ -76,7 +114,6 @@ files:
76
114
  - test/extras/integration_redcloth4.rb
77
115
  - test/run_tests.rb
78
116
  - test/test_datetime.rb
79
- - test/test_gilenson.rb
80
117
  - test/test_integration.rb
81
118
  - test/test_integration_flag.rb
82
119
  - test/test_pluralize.rb
@@ -96,27 +133,32 @@ rdoc_options:
96
133
  require_paths:
97
134
  - lib
98
135
  required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
99
137
  requirements:
100
138
  - - ">="
101
139
  - !ruby/object:Gem::Version
140
+ hash: 3
141
+ segments:
142
+ - 0
102
143
  version: "0"
103
- version:
104
144
  required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
105
146
  requirements:
106
147
  - - ">="
107
148
  - !ruby/object:Gem::Version
149
+ hash: 3
150
+ segments:
151
+ - 0
108
152
  version: "0"
109
- version:
110
153
  requirements: []
111
154
 
112
155
  rubyforge_project: rutils
113
- rubygems_version: 1.3.5
156
+ rubygems_version: 1.3.7
114
157
  signing_key:
115
158
  specification_version: 3
116
159
  summary: Simple processing of russian strings
117
160
  test_files:
118
161
  - test/test_datetime.rb
119
- - test/test_gilenson.rb
120
162
  - test/test_integration.rb
121
163
  - test/test_integration_flag.rb
122
164
  - test/test_pluralize.rb
data/WHAT_HAS_CHANGED.txt DELETED
@@ -1,44 +0,0 @@
1
- == Изменения по отношению к RuTils 0.2.x
2
-
3
- === Никаких оверрайдов
4
-
5
- К сожалению, переделать все существующие во Вселенной библиотеки невозможно. Мы предоставляем
6
- унаследованные классы которые реализуют нужную нам функциональность с минимальной хирургией чужих
7
- модулей. Например, если раньше вы делали так:
8
-
9
- RuTils.overrides = true
10
- BlueCloth.new(text_to_markdown_and_gilensize).to_html
11
-
12
- то теперь нужно делать так
13
-
14
- RuTils::Gilenson::BlueClothExtra.new(text_to_markdown_and_gilensize).to_html
15
-
16
- === Почему удален BiDi-транслит?
17
-
18
- В отличие от DHH авторы RuTils придерживаются стратегии синтактической серной кислоты вместо синтактического уксуса. Основная область
19
- применения bidi-транслитераций - генерация "как-бы" более удобочитаемых URL. К сожалению, у нее есть ряд свойств которые делают ее
20
- бессмысленной (кроме как в качестве упражнения по строковым итераторам Ruby). Приводим список аргументов почему вам не нужны
21
- автоматические транслитерированные URL:
22
-
23
- * Они будут работать только для набора символов "ASCII + русский". Любая попытка засунуть в ваш URL слово stød закончится печально.
24
- * Они будут работать только для русского языка, кириллиц много. Транслитерация по определению языкозависима.
25
- * Вашим пользователям придется непрерывно угадывать, пишется буква "я". "Ja"? "Ya"? "J"? Если вы хоть раз наблюдали иностранца, пытающегося
26
- выговорить "ОВИРскую" транслитерацию вашей фамилии из загранпаспорта, вы знаете о чем мы говорим.
27
- * Приличные браузеры не кодируют русский текст в адресной строке если Ваш вебсервер его принял. Если браузер неприличный то он позволяет этот текст ввести.
28
- Если браузер совсем неприличный, то средство ввода у него - стилус размером с зубочистку, а пользуются им в трамвае. Вводить ваш транслит
29
- в таких условиях все равно никто не будет, успокойтесь.
30
-
31
- Приводить список аргументов "почему такой транслит вам нужен" мы не будем дабы никого не травмировать.
32
-
33
- Если вам таки страшно неймется реализовать транслит в URL, рекомендуем следующий подход:
34
-
35
- * Для адресуемого обьекта сохраняется поле ++slug++
36
- * При сохранении в это поле автоматически пишется "дирифицированное" название, но пользователь может его вручную отредактировать
37
- * Дальнейшая адресация производится без "транслитерации обратно", по полю slug напрямую
38
-
39
- Дополнительно рекомендуется запретить загрузку файлов с русскими именами (с помощью Javascript и свойства value элемента input).
40
-
41
- === Рубинов много
42
-
43
- Это актуально только для тех кто собирается слать патчи. RuTils тестирован и работает под двумя версиями MRI и под JRuby. Это автоматически означает
44
- что патчи должны тестироваться во всех этих средах.
data/bin/gilensize DELETED
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # == Synopsis
3
- #
4
- # Processes text on standard input with Gilenson. Please feed it UTF-8!
5
- #
6
- # == Usage
7
- #
8
- # cat myfile.txt | gilensize > output.txt
9
- #
10
- # == Author
11
- # Julik <me@julik.nl>
12
-
13
- $KCODE = 'u'
14
- require File.dirname(__FILE__) + '/../lib/rutils'
15
- require 'optparse'
16
- OptionParser.new do | p |
17
- p.on(" -h", "--help") { require 'rdoc/usage'; RDoc::usage }
18
- end.parse!
19
-
20
- while st = gets do
21
- puts RuTils::Gilenson.new(st).to_html
22
- end
@@ -1,665 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- module RuTils
3
- module Gilenson
4
- # Позволяет возвращать класс форматтера при вызове
5
- # RuTils::Gilenson.new
6
- def self.new(*args) #:nodoc:
7
- RuTils::Gilenson::Formatter.new(*args)
8
- end
9
-
10
- autoload :BlueClothExtra, File.dirname(__FILE__) + '/bluecloth_extra'
11
- autoload :RedClothExtra, File.dirname(__FILE__) + '/redcloth_extra'
12
- autoload :RDiscountExtra, File.dirname(__FILE__) + '/rdiscount_extra'
13
- autoload :MarukuExtra, File.dirname(__FILE__) + '/maruku_extra'
14
- autoload :Helper, File.dirname(__FILE__) + '/helper'
15
- end
16
- end
17
-
18
- # ==Что такое Gilenson
19
- # Обработчик типографских символов в HTML согласно общепринятым правилам.
20
- # Посвящается П.Г.Гиленсону[http://www.rudtp.ru/lib.php?book=172], благодаря которому русские правила тех.
21
- # редактуры еще как минимум 20 лет таки останутся бессмысленно старомодными.
22
- #
23
- # Gilenson расставит в тексте "умные" правильные кавычки (русские - для кириллицы, английские - для латиницы),
24
- # заменит "хитрые" пунктуационные символы на entities и отформатирует знаки типа (c), (tm), телефоны и адреса.
25
- #
26
- # Gilenson базируется на коде Typografica[http://pixel-apes.com/typografica] от PixelApes,
27
- # который был приведен к положенному в Ruby стандарту. Основные отличия Gilenson от Typografica на PHP:
28
- # * работа только и полностью в UTF-8 (включая entities, применимые в XML)
29
- # * поддержка "raw"-вывода (символов вместо entities) - текст выводимый Gilenson можно верстать на бумаге
30
- #
31
- #
32
- # ==Использование
33
- # Быстрее всего - через метод ++gilensize++ для любой строковой переменной
34
- # %{ И вот они таки "приехали"}.gilensize => 'И&#160;вот они&#160;таки &#171;приехали&#187;'
35
- # Все дополнительные настройки в таком случае передаются форматтеру
36
- # %{ И вот они таки "приехали"}.gilensize(:laquo=>false) => 'И&#160;вот они&#160;таки "приехали"'
37
- #
38
- # Если форматтер надо настроить более тонко, можно использовать его и так:
39
- # typ = RuTils::Gilenson.new('Эти "так называемые" великие деятели')
40
- # typ.to_html => 'Эти &#171;так называемые&#187; великие деятели'
41
- #
42
- # или как фильтр
43
- # formatter = RuTils::Gilenson.new
44
- # formatter.configure(:dash=>true)
45
- # for string in strings
46
- # puts formatter.process(string)
47
- # end
48
- #
49
- # ==Настройки
50
- # Настройки регулируются через методы
51
- # formatter.dashglue = true
52
- # или ассоциированным хешем
53
- # formatter.configure!(:dash=>true, :quotes=>false)
54
- #
55
- # Хеш также можно передавать как последний аргумент методам process и to_html,
56
- # в таком случае настройки будут применены только при этом вызове
57
- #
58
- # beautified = formatter.process(my_text, :dash=>true)
59
- #
60
- # В параметры можно подставить также ключ :all чтобы временно включить или выключить все фильтры
61
- #
62
- # beautified = formatter.process(my_text, :all=>true)
63
- #
64
- # Помимо этого можно пользоваться каждым фильтром по отдельности используя метод +apply+
65
- #
66
- # Можно менять глифы, которые форматтер использует для подстановок. К примеру,
67
- # formatter.glyph[:nbsp] = '&nbsp;'
68
- # заставит форматтер расставлять "традиционные" неразрывные пробелы. Именно это - большая глупость,
69
- # но другие глифы заменить может быть нужно.
70
- #
71
- # ==Настройки форматтера
72
- # "inches" - преобразовывать дюймы в знак дюйма;
73
- # "laquo" - кавычки-ёлочки
74
- # "quotes" - кавычки-английские лапки
75
- # "dash" - проставлять короткое тире (150)
76
- # "emdash" - длинное тире двумя минусами (151)
77
- # "initials" - проставлять тонкие шпации в инициалах
78
- # "copypaste" - замена непечатных и "специальных" юникодных символов на entities
79
- # "(c)" - обрабатывать знак копирайта
80
- # "(r)", "(tm)", "(p)", "+-" - спецсимволы, какие - понятно
81
- # "acronyms" - сворачивание пояснений к аббревиатурам (пояснение - в скобках после аббревиатуры
82
- # без пробела). В текстовой версии пояснение будет "приклеено" к аббревиатуре
83
- # полукруглой шпацией
84
- # "degrees" - знак градуса
85
- # "dashglue", "wordglue" - приклеивание предлогов и дефисов
86
- # "spacing" - запятые и пробелы, перестановка
87
- # "phones" - обработка телефонов
88
- # "html" - при false - запрет использования тагов html
89
- # "de_nobr" - при true все <nobr/> заменяются на <span class="nobr"/>
90
- # "raw_output" - (по умолчанию false) - при true вместо entities выводятся UTF-символы
91
- # "skip_attr" - (по умолчанию false) - при true не отрабатывать типографику в атрибутах тегов (title, alt)
92
- # "skip_code" - (по умолчанию true) - при true не отрабатывать типографику внутри <code/>, <tt/>, CDATA
93
- class RuTils::Gilenson::Formatter
94
- attr_accessor :glyph
95
- attr_accessor :settings
96
-
97
- SETTINGS = {
98
- "inches" => true, # преобразовывать дюймы в знак дюйма;
99
- "laquo" => true, # кавычки-ёлочки
100
- "quotes" => true, # кавычки-английские лапки
101
- "dash" => true, # короткое тире (150)
102
- "emdash" => true, # длинное тире двумя минусами (151)
103
- "initials" => true, # тонкие шпации в инициалах
104
- "copypaste" => false, # замена непечатных и "специальных" юникодных символов на entities
105
- "(c)" => true, # обрабатывать знак копирайта
106
- "(r)" => true,
107
- "(tm)" => true,
108
- "(p)" => true,
109
- "acronyms" => true, # Акронимы с пояснениями - ЖЗЛ(Жизнь Замечатльных Людей)
110
- "+-" => true, # спецсимволы, какие - понятно
111
- "degrees" => true, # знак градуса
112
- "dashglue" => true, "wordglue" => true, # приклеивание предлогов и дефисов
113
- "spacing" => true, # запятые и пробелы, перестановка
114
- "phones" => true, # обработка телефонов
115
- "html" => true, # разрешение использования тагов html
116
- "de_nobr" => false, # при true все <nobr/> заменяются на <span class="nobr"/>
117
- "raw_output" => false, # выводить UTF-8 вместо entities
118
- "skip_attr" => false, # при true не отрабатывать типографику в атрибутах тегов
119
- "skip_code" => true, # при true не отрабатывать типографику внутри <code/>, <tt/>, CDATA
120
- "enforce_en_quotes" => false, # только латинские кавычки
121
- "enforce_ru_quotes" => false, # только русские кавычки (enforce_en_quotes при этом игнорируется)
122
- } #:nodoc:
123
-
124
- SETTINGS.freeze
125
-
126
- # Глифы, использующиеся в подстановках по-умолчанию
127
- GLYPHS = {
128
- :quot => "&#34;", # quotation mark
129
- :amp => "&#38;", # ampersand
130
- :apos => "&#39;", # apos
131
- :gt => "&#62;", # greater-than sign
132
- :lt => "&#60;", # less-than sign
133
- :nbsp => "&#160;", # non-breaking space
134
- :sect => "&#167;", # section sign
135
- :copy => "&#169;", # copyright sign
136
- :laquo => "&#171;", # left-pointing double angle quotation mark = left pointing guillemet
137
- :reg => "&#174;", # registered sign = registered trade mark sign
138
- :deg => "&#176;", # degree sign
139
- :plusmn => "&#177;", # plus-minus sign = plus-or-minus sign
140
- :para => "&#182;", # pilcrow sign = paragraph sign
141
- :middot => "&#183;", # middle dot = Georgian comma = Greek middle dot
142
- :raquo => "&#187;", # right-pointing double angle quotation mark = right pointing guillemet
143
- :ndash => "&#8211;", # en dash
144
- :mdash => "&#8212;", # em dash
145
- :lsquo => "&#8216;", # left single quotation mark
146
- :rsquo => "&#8217;", # right single quotation mark
147
- :ldquo => "&#8220;", # left double quotation mark
148
- :rdquo => "&#8221;", # right double quotation mark
149
- :bdquo => "&#8222;", # double low-9 quotation mark
150
- :bull => "&#8226;", # bullet = black small circle
151
- :hellip => "&#8230;", # horizontal ellipsis = three dot leader
152
- :numero => "&#8470;", # numero
153
- :trade => "&#8482;", # trade mark sign
154
- :minus => "&#8722;", # minus sign
155
- :inch => "&#8243;", # inch/second sign (u0x2033) (не путать с кавычками!)
156
- :thinsp => "&#8201;", # полукруглая шпация (тонкий пробел)
157
- :nob_open => '<span class="nobr">', # открывающий блок без переноса слов
158
- :nob_close => '</span>', # закрывающий блок без переноса слов
159
- }.freeze
160
-
161
- # Нормальные "типографские" символы в UTF-виде. Браузерами обрабатываются плохонько, поэтому
162
- # лучше заменять их на entities.
163
- VERBATIM_GLYPHS = {
164
- ' ' => :nbsp,# alt+0160 (NBSP here)
165
- '«' => :laquo,
166
- '»' => :raquo,
167
- '§' => :sect,
168
- '©' => :copy,
169
- '®' => :reg,
170
- '°' => :deg,
171
- '±' => :plusmn,
172
- '¶' => :para,
173
- '·' => :middot,
174
- '–' => :ndash,
175
- '—' => :mdash,
176
- '‘' => :lsquo,
177
- '’' => :rsquo,
178
- '“' => :ldquo,
179
- '”' => :rdquo,
180
- '„' => :bdquo,
181
- '•' => :bull,
182
- '…' => :hellip,
183
- '№' => :numero,
184
- '™' => :trade,
185
- '−' => :minus,
186
- ' ' => :thinsp,
187
- '″' => :inch,
188
- }.freeze
189
-
190
- # Метка на которую подменяются вынутые теги
191
- REPLACEMENT_MARKER = RuTils::SUBSTITUTION_MARKER #:nodoc:
192
-
193
- # Кто придумал &#147;? Не учите людей плохому...
194
- # Привет А.Лебедеву http://www.artlebedev.ru/kovodstvo/62/
195
- # Используем символы, потом берем по символам из glyphs форматтера.
196
- # Молодец mash!
197
- FORBIDDEN_NUMERIC_ENTITIES = {
198
- '132' => :bdquo,
199
- '133' => :hellip,
200
- '146' => :apos,
201
- '147' => :ldquo,
202
- '148' => :rdquo,
203
- '149' => :bull,
204
- '150' => :ndash,
205
- '151' => :mdash,
206
- '153' => :trade,
207
- }.freeze
208
-
209
- # All the unicode whitespace
210
- UNICODE_WHITESPACE = [
211
- (0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
212
- 0x0020, # White_Space # Zs SPACE
213
- 0x0085, # White_Space # Cc <control-0085>
214
- 0x00A0, # White_Space # Zs NO-BREAK SPACE
215
- 0x1680, # White_Space # Zs OGHAM SPACE MARK
216
- 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
217
- (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
218
- 0x2028, # White_Space # Zl LINE SEPARATOR
219
- 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
220
- 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
221
- 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
222
- 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
223
- ].flatten.pack("U*").freeze
224
-
225
- PROTECTED_SETTINGS = [ :raw_output ] #:nodoc:
226
-
227
- def initialize(*args)
228
- @_text = args[0].is_a?(String) ? args[0] : ''
229
- setup_default_settings!
230
- accept_configuration_arguments!(args.last) if args.last.is_a?(Hash)
231
- end
232
-
233
- # Настраивает форматтер ассоциированным хешем
234
- # formatter.configure!(:dash=>true, :wordglue=>false)
235
- def configure!(*config)
236
- accept_configuration_arguments!(config.last) if config.last.is_a?(Hash)
237
- end
238
-
239
- alias :configure :configure! #Дружественный API
240
-
241
- # Неизвестные методы - настройки. С = - установка ключа, без - получение значения
242
- def method_missing(meth, *args) #:nodoc:
243
- setting = meth.to_s.gsub(/=$/, '')
244
- super(meth, *args) unless @settings.has_key?(setting) #this will pop the exception if we have no such setting
245
-
246
- return (@settings[setting] = args[0])
247
- end
248
-
249
- # Обрабатывает text_to_process с сохранением настроек, присвоенных обьекту-форматтеру
250
- # Дополнительные аргументы передаются как параметры форматтера и не сохраняются после прогона.
251
- def process(text_to_process, *args)
252
- @_text = text_to_process
253
-
254
- args.last.is_a?(Hash) ? with_configuration(args.last) { to_html } : to_html
255
- end
256
-
257
- # Обрабатывает текст, присвоенный форматтеру при создании и возвращает результат обработки
258
- def to_html
259
- return '' unless @_text
260
-
261
- # NOTE: strip is Unicode-space aware on 1.9.1, so here we simulate that
262
- text = @_text.gsub(/[#{UNICODE_WHITESPACE}]\z/, '').gsub(/\A[#{UNICODE_WHITESPACE}]/, '')
263
-
264
- # -6. Подмухляем таблицу глифов, если нам ее передали
265
- glyph_table = glyph.dup
266
-
267
- if @settings["enforce_ru_quotes"]
268
- glyph_table[:ldquo], glyph_table[:rdquo] = glyph_table[:laquo], glyph_table[:raquo]
269
- elsif @settings["enforce_en_quotes"]
270
- glyph_table[:laquo], glyph_table[:raquo] = glyph_table[:ldquo], glyph_table[:rdquo]
271
- end
272
-
273
- # -5. Копируем глифы в ивары, к ним доступ быстр и в коде они глаза тоже не мозолят
274
- glyph_table.each_pair do | ki, wi |
275
- instance_variable_set("@#{ki}", wi)
276
- end
277
-
278
- # -4. запрет тагов html
279
- process_escape_html(text) unless @settings["html"]
280
-
281
- # -3. Никогда (вы слышите?!) не пущать лабуду &#not_correct_number;
282
- FORBIDDEN_NUMERIC_ENTITIES.dup.each_pair do | key, rep |
283
- text.gsub!(/&##{key};/, self.glyph[rep])
284
- end
285
-
286
- # -2. Чистим copy&paste
287
- process_copy_paste_clearing(text) if @settings['copypaste']
288
-
289
- # -1. Замена &entity_name; на входе ('&nbsp;' => '&#160;' и т.д.)
290
- process_html_entities(text)
291
-
292
- # 0. Вырезаем таги
293
- tags = lift_ignored_elements(text) if @skip_tags
294
-
295
- # 1. Запятые и пробелы
296
- process_spacing(text) if @settings["spacing"]
297
-
298
- # 1. лапки
299
- process_quotes(text) if @settings["quotes"]
300
-
301
- # 2. ёлочки
302
- process_laquo(text) if @settings["laquo"]
303
-
304
- # 3. Инчи
305
- process_inches(text) if @settings["inches"]
306
-
307
- # 2b. одновременно ёлочки и лапки
308
- process_compound_quotes(text) if (@settings["quotes"] && @settings["laquo"])
309
-
310
- # 3. тире
311
- process_dash(text) if @settings["dash"]
312
-
313
- # 3a. тире длинное
314
- process_emdash(text) if @settings["emdash"]
315
-
316
- # 5. +/-
317
- process_plusmin(text) if @settings["+-"]
318
-
319
- # 5a. 12^C
320
- process_degrees(text) if @settings["degrees"]
321
-
322
- # 6. телефоны
323
- process_phones(text) if @settings["phones"]
324
-
325
- # 7. Короткие слова и &nbsp;
326
- process_wordglue(text) if @settings["wordglue"]
327
-
328
- # 8. Склейка ласт. Тьфу! дефисов.
329
- process_dashglue(text) if @settings["dashglue"]
330
-
331
- # 8a. Инициалы
332
- process_initials(text) if @settings['initials']
333
-
334
- # 8b. Троеточия
335
- process_ellipsises(text) if @settings["wordglue"]
336
-
337
- # 9. Акронимы от Текстиля
338
- process_acronyms(text) if @settings["acronyms"]
339
-
340
- # БЕСКОНЕЧНОСТЬ. Вставляем таги обратно.
341
- reinsert_fragments(text, tags) if @skip_tags
342
-
343
- # фуф, закончили.
344
- process_span_instead_of_nobr(text) if @settings["de_nobr"]
345
-
346
- # заменяем entities на истинные символы
347
- process_raw_output(text) if @settings["raw_output"]
348
-
349
- text.strip
350
- end
351
-
352
-
353
- # Применяет отдельный фильтр к text и возвращает результат. Например:
354
- # formatter.apply(:wordglue, "Вот так") => "Вот&#160;так"
355
- # Удобно применять когда вам нужно задействовать отдельный фильтр Гиленсона, но не нужна остальная механика
356
- # Последний аргумент определяет, нужно ли при применении фильтра сохранить в неприкосновенности таги и другие
357
- # игнорируемые фрагменты текста (по умолчанию они сохраняются).
358
- def apply(filter, text, lift_ignored_elements = true)
359
- copy = text.dup
360
- unless lift_ignored_elements
361
- self.send("process_#{filter}".to_sym, copy)
362
- else
363
- lifting_fragments(copy) { self.send("process_#{filter}".to_sym, copy) }
364
- end
365
- copy
366
- end
367
-
368
- private
369
-
370
- def setup_default_settings!
371
- @skip_tags = true;
372
- @ignore = /notypo/ # regex, который игнорируется. Этим надо воспользоваться для обработки pre и code
373
-
374
- @glueleft = ['рис.', 'табл.', 'см.', 'им.', 'ул.', 'пер.', 'кв.', 'офис', 'оф.', 'г.']
375
- @glueright = ['руб.', 'коп.', 'у.е.', 'мин.']
376
-
377
- # Установки можно менять в каждом экземпляре
378
- @settings = SETTINGS.dup
379
-
380
- @mark_tag = REPLACEMENT_MARKER
381
- # Глифы можено подменять в экземпляре форматтера поэтому копируем их из константы
382
- @glyph = GLYPHS.dup
383
-
384
- @phonemasks = [[ /([0-9]{4})\-([0-9]{2})\-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/,
385
- /([0-9]{4})\-([0-9]{2})\-([0-9]{2})/,
386
- /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
387
- /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
388
- /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})/,
389
- /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{3})/,
390
- /([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
391
- /([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
392
- /([0-9]{1})\-([0-9]{2})\-([0-9]{2})/,
393
- /([0-9]{2})\-([0-9]{3})/,
394
- /([0-9]+)\-([0-9]+)/,
395
- ],[
396
- ':nob_open\1:ndash\2:ndash\3:nbsp\4:\5:\6:nob_close',
397
- ':nob_open\1:ndash\2:ndash\3:nob_close',
398
- ':nob_open\1:nbsp\2:ndash\3:ndash\4:nob_close',
399
- ':nob_open\1:nbsp\2:ndash\3:ndash\4:nob_close',
400
- ':nob_open\1:nbsp\2:ndash\3:nob_close',
401
- ':nob_open\1:nbsp\2:ndash\3:nob_close',
402
- ':nob_open\1:ndash\2:ndash\3:nob_close',
403
- ':nob_open\1:ndash\2:ndash\3:nob_close',
404
- ':nob_open\1:ndash\2:ndash\3:nob_close',
405
- ':nob_open\1:ndash\2:nob_close',
406
- ':nob_open\1:ndash\2:nob_close'
407
- ]]
408
- end
409
-
410
- # Позволяет получить процедуру, при вызове возвращающую значение глифа
411
- # def lookup(glyph_to_lookup)
412
- # return Proc.new { g[glyph_to_lookup] }
413
- # end
414
-
415
- # Подставляет "символы" (двоеточие + имя глифа) на нужное значение глифа заданное в данном форматтере
416
- def substitute_glyphs_in_string(str)
417
- re = str.dup
418
- @glyph.each_pair do | key, subst |
419
- re.gsub!(":#{key.to_s}", subst)
420
- end
421
- re
422
- end
423
-
424
- # Выполняет блок, временно включая настройки переданные в +hash+
425
- def with_configuration(hash, &block)
426
- old_settings, old_glyphs = @settings.dup, @glyph.dup
427
- accept_configuration_arguments!(hash)
428
- txt = yield
429
- @settings, @glyph = old_settings, old_glyphs
430
-
431
- return txt
432
- end
433
-
434
- def accept_configuration_arguments!(args_hash)
435
-
436
- # Специальный случай - :all=>true|false
437
- if args_hash.has_key?(:all)
438
- if args_hash[:all]
439
- @settings.each_pair {|k, v| @settings[k] = true unless PROTECTED_SETTINGS.include?(k.to_sym)}
440
- else
441
- @settings.each_pair {|k, v| @settings[k] = false unless PROTECTED_SETTINGS.include?(k.to_sym)}
442
- end
443
- else
444
-
445
- # Кинуть ошибку если настройка нам неизвестна
446
- unknown_settings = args_hash.keys.collect{|k|k.to_s} - @settings.keys.collect { |k| k.to_s }
447
- raise RuTils::Gilenson::UnknownSetting, unknown_settings if unknown_settings.any?
448
-
449
- args_hash.each_pair do | key, value |
450
- @settings[key.to_s] = (value ? true : false)
451
- end
452
- end
453
- end
454
-
455
- # Вынимает игнорируемые фрагменты и заменяет их маркером, выполняет переданный блок и вставляет вынутое на место
456
- def lifting_fragments(text, &block)
457
- lifted = lift_ignored_elements(text)
458
- yield
459
- reinsert_fragments(text, lifted)
460
- end
461
-
462
- #Вынимает фрагменты из текста и возвращает массив с фрагментами
463
- def lift_ignored_elements(text)
464
- # re = /<\/?[a-z0-9]+("+ # имя тага
465
- # "\s+("+ # повторяющая конструкция: хотя бы один разделитель и тельце
466
- # "[a-z]+("+ # атрибут из букв, за которым может стоять знак равенства и потом
467
- # "=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+))"+ #
468
- # ")?"+
469
- # ")?"+
470
- # ")*\/?>|\xA2\xA2[^\n]*?==/i;
471
-
472
- re_skipcode = '((<(code|tt)[ >](.*?)<\/(code|tt)>)|(<!\[CDATA\[(.*?)\]\]>))|' if @settings['skip_code']
473
- re = /(#{re_skipcode}<\/?[a-z0-9]+(\s+([a-z]+(=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+)))?)?)*\/?>)/uim
474
- tags = text.scan(re).map{ |tag| tag[0] } # первая группа!
475
- text.gsub!(re, @mark_tag) #маркер тега, мы используем Invalid UTF-sequence для него
476
- return tags
477
- end
478
-
479
- def reinsert_fragments(text, fragments)
480
- fragments.each do |fragment|
481
- fragment.gsub!(/ (href|src|data)=((?:(\')([^\']*)(\'))|(?:(\")([^\"]*)(\")))/uim) do
482
- " #{$1}=" + $2.gsub(/&(?!(#0*38)|(amp);)/, @amp)
483
- end # unless @settings['raw_output'] -- делать это надо всегда (mash)
484
-
485
- unless @settings['skip_attr']
486
- fragment.gsub!(/ (title|alt)=((?:(\')([^\']*)(\'))|(?:(\")([^\"]*)(\")))/uim) do
487
- " #{$1}=#{$3}" + self.process($4.to_s) + "#{$5}#{$6}" + self.process($7.to_s) + "#{$8}"
488
- end
489
- end
490
- text.sub!(@mark_tag, fragment)
491
- end
492
- end
493
-
494
- ### Имплементации фильтров
495
- def process_html_entities(text)
496
- self.glyph.each { |key, value| text.gsub!(/&#{key};/, value)}
497
- end
498
-
499
- def process_initials(text)
500
- initials = /([А-Я])[\.]{1,2}[\s]*?([А-Я])[\.]*[\s]*?([А-Я])([а-я])/u
501
- replacement = substitute_glyphs_in_string('\1.\2.:thinsp\3\4')
502
- text.gsub!(initials, replacement)
503
- end
504
-
505
- def process_copy_paste_clearing(text)
506
- VERBATIM_GLYPHS.each {|key,value| text.gsub!(/#{Regexp.escape(key)}/, glyph[value]) }
507
- end
508
-
509
- def process_spacing(text)
510
- text.gsub!( /(\s*)([,]*)/sui, '\2\1');
511
- text.gsub!( /(\s*)([\.?!]*)(\s*[ЁА-ЯA-Z])/su, '\2\1\3');
512
- end
513
-
514
- def process_dashglue(text)
515
- text.gsub!( /([a-zа-яА-Я0-9]+(\-[a-zа-яА-Я0-9]+)+)/ui, @nob_open+'\1'+ @nob_close)
516
- end
517
-
518
- def process_escape_html(text)
519
- text.gsub!(/&/, @amp)
520
- text.gsub!(/</, @lt)
521
- text.gsub!(/>/, @gt)
522
- end
523
-
524
- def process_span_instead_of_nobr(text)
525
- text.gsub!(/<nobr>/, '<span class="nobr">')
526
- text.gsub!(/<\/nobr>/, '</span>')
527
- end
528
-
529
- def process_dash(text)
530
- text.gsub!( /(\s|;)\-(\s)/ui, '\1'+@ndash+'\2')
531
- end
532
-
533
- def process_emdash(text)
534
- text.gsub!( /(\s|;)\-\-(\s)/ui, '\1'+@mdash+'\2')
535
- # 4. (с)
536
- text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, @copy) if @settings["(c)"]
537
- # 4a. (r)
538
- text.gsub!( /\(r\)/ui, '<sup>'+@reg+'</sup>') if @settings["(r)"]
539
-
540
- # 4b. (tm)
541
- text.gsub!( /\(tm\)|\(тм\)/ui, @trade) if @settings["(tm)"]
542
- # 4c. (p)
543
- text.gsub!( /\(p\)/ui, @sect) if @settings["(p)"]
544
- end
545
-
546
- def process_ellipsises(text)
547
- text.gsub!( '...', @hellip)
548
- end
549
-
550
- def process_laquo(text)
551
- text.gsub!( /\"\"/ui, @quot * 2);
552
- text.gsub!( /(^|\s|#{@mark_tag}|>|\()\"((#{@mark_tag})*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, '\1' + @laquo + '\2');
553
- _text = '""';
554
- until _text == text do
555
- _text = text;
556
- text.gsub!( /(#{@laquo}([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/\?\!](#{@mark_tag})*)\"/sui, '\1' + @raquo)
557
- end
558
- end
559
-
560
- def process_quotes(text)
561
- text.gsub!( /\"\"/ui, @quot*2)
562
- text.gsub!( /\"\.\"/ui, @quot+"."+@quot)
563
- _text = '""';
564
- lat_c = '0-9A-Za-z'
565
- punct = /\'\!\s\.\?\,\-\&\;\:\\/
566
-
567
- until _text == text do
568
- _text = text.dup
569
- text.gsub!( /(^|\s|#{@mark_tag}|>)\"([#{lat_c}#{punct}\_\#{@mark_tag}]+(\"|#{@rdquo}))/ui, '\1'+ @ldquo +'\2')
570
- text.gsub!( /(#{@ldquo}([#{lat_c}#{punct}#{@mark_tag}\_]*).*[#{lat_c}][\#{@mark_tag}\?\.\!\,\\]*)\"/ui, '\1'+ @rdquo)
571
- end
572
- end
573
-
574
- def process_compound_quotes(text)
575
- text.gsub!(/(#{@ldquo}(([A-Za-z0-9'!\.?,\-&;:]|\s|#{@mark_tag})*)#{@laquo}(.*)#{@raquo})#{@raquo}/ui, '\1' + @rdquo);
576
- end
577
-
578
- def process_degrees(text)
579
- text.gsub!( /-([0-9])+\^([FCС])/, @ndash+'\1'+ @deg +'\2') #deg
580
- text.gsub!( /\+([0-9])+\^([FCС])/, '+\1'+ @deg +'\2')
581
- text.gsub!( /\^([FCС])/, @deg+'\1')
582
- end
583
-
584
- def process_wordglue(text)
585
- text.replace(" " + text + " ")
586
- _text = " " + text + " "
587
-
588
- until _text == text
589
- _text = text
590
- text.gsub!( /(\s+)([a-zа-яА-Я0-9]{1,2})(\s+)([^\\s$])/ui, '\1\2' + @nbsp +'\4')
591
- text.gsub!( /(\s+)([a-zа-яА-Я0-9]{3})(\s+)([^\\s$])/ui, '\1\2' + @nbsp+'\4')
592
- end
593
-
594
- # Пунктуация это либо один из наших глифов, либо мемберы класса. В данном случае
595
- # мы цепляемся за кончик строки поэтому можум прихватить и глиф тоже
596
- # Пунктуация включает наши собственные глифы!
597
- punct = glyph.values.map{|v| Regexp.escape(v)}.join('|')
598
- vpunct = /(#{punct}|[\)\]\!\?,\.;])/
599
-
600
- text.gsub!(/(\s+)([a-zа-яА-Я0-9]{1,2}#{vpunct}{0,3}\s$)/ui, @nbsp+'\2')
601
-
602
- @glueleft.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, '\1\2' + @nbsp) }
603
-
604
- @glueright.each { | i | text.gsub!( /(\s)(#{i})(\s+)/sui, @nbsp+'\2\3') }
605
-
606
- end
607
-
608
- def process_phones(text)
609
- @phonemasks[0].each_with_index do |pattern, i|
610
- replacement = substitute_glyphs_in_string(@phonemasks[1][i])
611
- text.gsub!(pattern, replacement)
612
- end
613
- end
614
-
615
- def process_acronyms(text)
616
- acronym = /\b([A-ZА-Я][A-ZА-Я0-9]{2,})\b(?:[(]([^)]*)[)])/u
617
- if @settings["raw_output"]
618
- text.gsub!(acronym, '\1%s(\2)' % @thinsp)
619
- else
620
- text.gsub!(acronym) do
621
- expl = $2.to_s; process_escape_html(expl)
622
- "<acronym title=\"#{expl}\">#{$1}</acronym>"
623
- end
624
- end
625
- end
626
-
627
- # Обработка знака дюйма, кроме случаев когда он внутри кавычек
628
- def process_inches(text)
629
- text.gsub!(/\s([0-9]{1,2}([\.,][0-9]{1,2})?)(\"){1,1}/ui, ' \1' + @inch)
630
- end
631
-
632
- def process_plusmin(text)
633
- text.gsub!(/[^+]\+\-/ui, @plusmn)
634
- end
635
-
636
- # Подменяет все юникодные entities в тексте на истинные UTF-8-символы
637
- def process_raw_output(text)
638
- # Все глифы
639
- @glyph.values.each do | entity |
640
- next unless entity =~ /^&#(\d+);/
641
- text.gsub!(/#{entity}/, entity_to_raw_utf8(entity))
642
- end
643
- end
644
-
645
- # Конвертирует юникодные entities в UTF-8-codepoints
646
- def entity_to_raw_utf8(entity)
647
- entity =~ /^&#(\d+);/
648
- $1 ? [$1.to_i].pack("U") : entity
649
- end
650
-
651
- end #end Gilenson
652
-
653
- # Выбрасывается если форматтеру задается неизвестная настройка
654
- class RuTils::Gilenson::UnknownSetting < RuntimeError
655
- end
656
-
657
- module RuTils::Gilenson::StringFormatting
658
- # Форматирует строку с помощью Gilenson::Formatter. Все дополнительные опции передаются форматтеру.
659
- def gilensize(*args)
660
- RuTils::Gilenson::Formatter.new(self, args.shift || {}).to_html
661
- end
662
-
663
- end
664
-
665
- Object::String.send(:include, RuTils::Gilenson::StringFormatting)