rutils 1.0.3 → 1.1.1

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