rutils 0.0.3

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.
@@ -0,0 +1,332 @@
1
+ module RuTils
2
+
3
+ module GilensonMixin
4
+ # Форматирует строку с помощью Gilensize
5
+ def gilensize(*args)
6
+ RuTils::Gilenson.new(self, *args).to_html
7
+ end
8
+ end
9
+
10
+ # Это - прямой порт Тыпографицы от pixelapes.
11
+ # Настройки можно регулировать через методы, т.е.
12
+ #
13
+ # typ = Typografica.new('Эти "так называемые" великие деятели')
14
+ # typ.html = false => "false"
15
+ # typ.dash = true => "true"
16
+ # typ.to_html => 'Эти «так называемые» великие деятели'
17
+ class Gilenson
18
+ def initialize(text, *args)
19
+ @_text = text
20
+ @skip_tags = true;
21
+ @p_prefix = "<p class=typo>";
22
+ @p_postfix = "</p>";
23
+ @a_soft = true;
24
+ @indent_a = "images/z.gif width=25 height=1 border=0 alt=\'\' align=top />" # <->
25
+ @indent_b = "images/z.gif width=50 height=1 border=0 alt=\'\' align=top />" # <-->
26
+ @fixed_size = 80 # максимальная ширина
27
+ @ignore = /notypo/ # regex, который игнорируется. Этим надо воспользоваться для обработки pre и code
28
+
29
+ @de_nobr = true;
30
+
31
+ @phonemasks = [[ /([0-9]{4})\-([0-9]{2})\-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/,
32
+ /([0-9]{4})\-([0-9]{2})\-([0-9]{2})/,
33
+ /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
34
+ /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
35
+ /(\([0-9\+\-]+\)) ?([0-9]{3})\-([0-9]{2})/,
36
+ /(\([0-9\+\-]+\)) ?([0-9]{2})\-([0-9]{3})/,
37
+ /([0-9]{3})\-([0-9]{2})\-([0-9]{2})/,
38
+ /([0-9]{2})\-([0-9]{2})\-([0-9]{2})/,
39
+ /([0-9]{1})\-([0-9]{2})\-([0-9]{2})/,
40
+ /([0-9]{2})\-([0-9]{3})/,
41
+ /([0-9]+)\-([0-9]+)/,
42
+ ],[
43
+ '<nobr>\1&ndash;\2&ndash;\3&nbsp;\4:\5:\6</nobr>',
44
+ '<nobr>\1&ndash;\2&ndash;\3</nobr>',
45
+ '<nobr>\1&nbsp;\2&ndash;\3&ndash;\4</nobr>',
46
+ '<nobr>\1&nbsp;\2&ndash;\3&ndash;\4</nobr>',
47
+ '<nobr>\1&nbsp;\2&ndash;\3</nobr>',
48
+ '<nobr>\1&nbsp;\2&ndash;\3</nobr>',
49
+ '<nobr>\1&ndash;\2&ndash;\3</nobr>',
50
+ '<nobr>\1&ndash;\2&ndash;\3</nobr>',
51
+ '<nobr>\1&ndash;\2&ndash;\3</nobr>',
52
+ '<nobr>\1&ndash;\2</nobr>',
53
+ '<nobr>\1&ndash;\2</nobr>'
54
+ ]]
55
+
56
+ @glueleft = ['рис.', 'табл.', 'см.', 'им.', 'ул.', 'пер.', 'кв.', 'офис', 'оф.', 'г.']
57
+ @glueright = ['руб.', 'коп.', 'у.е.', 'мин.']
58
+
59
+ @settings = {
60
+ "inches" => true, # преобразовывать дюймы в &quot;
61
+ "laquo" => true, # кавычки-ёлочки
62
+ "farlaquo" => false, # кавычки-ёлочки для фара (знаки "больше-меньше")
63
+ "quotes" => true, # кавычки-английские лапки
64
+ "dash" => true, # короткое тире (150)
65
+ "emdash" => true, # длинное тире двумя минусами (151)
66
+ "(c)" => true,
67
+ "(r)" => true,
68
+ "(tm)" => true,
69
+ "(p)" => true,
70
+ "+-" => true, # спецсимволы, какие - понятно
71
+ "degrees" => true, # знак градуса
72
+ "<-->" => true, # отступы $Indent*
73
+ "dashglue" => true, "wordglue" => true, # приклеивание предлогов и дефисов
74
+ "spacing" => true, # запятые и пробелы, перестановка
75
+ "phones" => true, # обработка телефонов
76
+ "fixed" => false, # подгон под фиксированную ширину
77
+ "html" => false # запрет тагов html
78
+ }
79
+ # irrelevant - indentation with images
80
+ @indent_a = "<!--indent-->"
81
+ @indent_b = "<!--indent-->"
82
+
83
+ @mark_tag = "\xF0\xF0\xF0\xF0" # Подстановочные маркеры тегов
84
+ @mark_ignored = "\201" # Подстановочные маркеры неизменяемых групп
85
+ end
86
+
87
+
88
+ # Proxy unknown method calls as setting switches. Methods with = will set settings, methods without - fetch them
89
+ def method_missing(meth, *args) #:nodoc:
90
+ setting = meth.to_s.gsub(/=$/, '')
91
+ super(meth, *args) unless @settings.has_key?(setting) #this will pop the exception if we have no such setting
92
+
93
+ return @settings[meth.to_s] if setting == meth.to_s
94
+ return (@settings[meth.to_s] = args[0])
95
+ end
96
+
97
+
98
+ def to_html(no_paragraph = false)
99
+
100
+ text = @_text
101
+
102
+ # -2. игнорируем ещё регексп
103
+ ignored = []
104
+
105
+
106
+ text.scan(@ignore) do |result|
107
+ ignored << result
108
+ end
109
+
110
+ text.gsub!(@ignore, @mark_ignored) # маркер игнора
111
+
112
+ # -1. запрет тагов html
113
+ text.gsub!(/&/, '&amp;') if @settings["html"]
114
+
115
+
116
+ # 0. Вырезаем таги
117
+ # проблема на самом деле в том, на что похожи таги.
118
+ # вариант 1, простой (закрывающий таг) </abcz>
119
+ # вариант 2, простой (просто таг) <abcz>
120
+ # вариант 3, посложней <abcz href="abcz">
121
+ # вариант 4, простой (просто таг) <abcz />
122
+ # вариант 5, вакка \xA2\xA2...== нафиг нафиг
123
+ # самый сложный вариант - это когда в параметре тага встречается вдруг символ ">"
124
+ # вот он: <abcz href="abcz>">
125
+ # как работает вырезание? введём спецсимвол. Да, да, спецсимвол.
126
+ # нам он ещё вопьётся =)
127
+ # заменим все таги на спец.символ, запоминая одновременно их в массив.
128
+ # и будем верить, что спец.символы в дикой природе не встречаются.
129
+
130
+ tags = []
131
+ if (@skip_tags)
132
+ # re = /<\/?[a-z0-9]+("+ # имя тага
133
+ # "\s+("+ # повторяющая конструкция: хотя бы один разделитель и тельце
134
+ # "[a-z]+("+ # атрибут из букв, за которым может стоять знак равенства и потом
135
+ # "=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+))"+ #
136
+ # ")?"+
137
+ # ")?"+
138
+ # ")*\/?>|\xA2\xA2[^\n]*?==/i;
139
+
140
+ # re = /<\/?[a-z0-9]+(\s+([a-z]+(=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+)))?)?)*\/?>|\xA2\xA2[^\n]*?==/ui
141
+
142
+ re = /(<\/?[a-z0-9]+(\s+([a-z]+(=((\'[^\']*\')|(\"[^\"]*\")|([0-9@\-_a-z:\/?&=\.]+)))?)?)*\/?>)/ui
143
+
144
+ # по-хорошему атрибуты тоже нужно типографить. Или не нужно? бугага...
145
+
146
+ tags = text.scan(re).map{|tag| tag[0] }
147
+ # match = "&lt;" + match if @settings["html"]
148
+ text.gsub!(re, @mark_tag) #маркер тега, мы используем Invalid UTF-sequence для него
149
+
150
+ # puts "matched #{tags.size} tags"
151
+ end
152
+
153
+ # 1. Запятые и пробелы
154
+ if @settings["spacing"]
155
+ text.gsub!( /(\s*)([,]*)/sui, '\2\1');
156
+ text.gsub!( /(\s*)([\.?!]*)(\s*[ЁА-ЯA-Z])/su, '\2\1\3');
157
+ end
158
+
159
+ # 2. Разбиение на строки длиной не более ХХ символов
160
+ # --- для ваки не портировано ---
161
+ # --- для ваки не портировано ---
162
+
163
+ # 3. Спецсимволы
164
+ # 0. дюймы с цифрами
165
+ text.gsub!(/\s([0-9]{1,2}([\.,][0-9]{1,2})?)\"/ui, ' \1&quot;') if @settings["inches"]
166
+
167
+ # 1. лапки
168
+ if (@settings["quotes"])
169
+ text.gsub!( /\"\"/ui, "&quot;&quot;")
170
+ text.gsub!( /\"\.\"/ui, "&quot;.&quot;")
171
+ _text = '""';
172
+ while _text != text do
173
+ _text = text
174
+ text.gsub!( /(^|\s|\201|\xF0\xF0\xF0\xF0|>)\"([0-9A-Za-z\'\!\s\.\?\,\-\&\;\:\_\xF0\xF0\xF0\xF0\201]+(\"|&#148;))/ui, '\1&#147;\2')
175
+ #this doesnt work in-place. somehow.
176
+ text = text.gsub( /(\&\#147\;([A-Za-z0-9\'\!\s\.\?\,\-\&\;\:\xF0\xF0\xF0\xF0\201\_]*).*[A-Za-z0-9][\xF0\xF0\xF0\xF0\201\?\.\!\,]*)\"/ui, '\1&#148;')
177
+ end
178
+ end
179
+
180
+ # 2. ёлочки
181
+ if @settings["laquo"]
182
+ text.gsub!( /\"\"/ui, "&quot;&quot;");
183
+ text.gsub!( /(^|\s|\201|\xF0\xF0\xF0\xF0|>|\()\"((\201|\xF0\xF0\xF0\xF0)*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, "\\1&laquo;\\2");
184
+ # nb: wacko only regexp follows:
185
+ text.gsub!( /(^|\s|\201|\xF0\xF0\xF0\xF0|>|\()\"((\201|\xF0\xF0\xF0\xF0|\/&nbsp;|\/|\!)*[~0-9ёЁA-Za-zА-Яа-я\-:\/\.])/ui, "\\1&laquo;\\2")
186
+ _text = "\"\"";
187
+ while (_text != text) do
188
+ _text = text;
189
+ text.gsub!( /(\&laquo\;([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/](\201|\xF0\xF0\xF0\xF0)*)\"/sui, "\\1&raquo;")
190
+ # nb: wacko only regexps follows:
191
+ text.gsub!( /(\&laquo\;([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/](\201|\xF0\xF0\xF0\xF0)*\?(\201|\xF0\xF0\xF0\xF0)*)\"/sui, "\\1&raquo;")
192
+ text.gsub!( /(\&laquo\;([^\"]*)[ёЁA-Za-zА-Яа-я0-9\.\-:\/](\201|\xF0\xF0\xF0\xF0|\/|\!)*)\"/sui, "\\1&raquo;")
193
+ end
194
+ end
195
+
196
+
197
+ # 2b. одновременно ёлочки и лапки
198
+ if (@settings["quotes"] && (@settings["laquo"] or @settings["farlaquo"]))
199
+ text.gsub!(/(\&\#147\;(([A-Za-z0-9'!\.?,\-&;:]|\s|\xF0\xF0\xF0\xF0|\201)*)&laquo;(.*)&raquo;)&raquo;/ui,"\\1&#148;");
200
+ end
201
+
202
+
203
+ # 3. тире
204
+ if (@settings["dash"])
205
+ text.gsub!( /(\s|;)\-(\s)/ui, "\\1&ndash;\\2")
206
+ end
207
+
208
+
209
+ # 3a. тире длинное
210
+ if (@settings["emdash"])
211
+ text.gsub!( /(\s|;)\-\-(\s)/ui, "\\1&mdash;\\2")
212
+ # 4. (с)
213
+ text.gsub!(/\([сСcC]\)((?=\w)|(?=\s[0-9]+))/u, "&copy;") if @settings["(c)"]
214
+ # 4a. (r)
215
+ text.gsub!( /\(r\)/ui, "<sup>&#174;</sup>") if @settings["(r)"]
216
+
217
+ # 4b. (tm)
218
+ text.gsub!( /\(tm\)|\(тм\)/ui, "&#153;") if @settings["(tm)"]
219
+ # 4c. (p)
220
+ text.gsub!( /\(p\)/ui, "&#167;") if @settings["(p)"]
221
+ end
222
+
223
+
224
+ # 5. +/-
225
+ text.gsub!(/[^+]\+\-/ui, "&#177;") if @settings["+-"]
226
+
227
+
228
+ # 5a. 12^C
229
+ if @settings["degrees"]
230
+ text.gsub!( /-([0-9])+\^([FCС])/, "&ndash;\\1&#176\\2")
231
+ text.gsub!( /\+([0-9])+\^([FCС])/, "+\\1&#176\\2")
232
+ text.gsub!( /\^([FCС])/, "&#176\\1")
233
+ end
234
+
235
+
236
+ # 6. телефоны
237
+ if @settings["phones"]
238
+ @phonemasks[0].each_with_index do |v, i|
239
+ text.gsub!(v, @phonemasks[1][i])
240
+ end
241
+ end
242
+
243
+
244
+ # 7. Короткие слова и &nbsp;
245
+ if (@settings["wordglue"])
246
+
247
+ text = " " + text + " ";
248
+ _text = " " + text + " ";
249
+ until _text == text
250
+ _text = text
251
+ text.gsub!( /(\s+)([a-zа-яА-Я]{1,2})(\s+)([^\\s$])/ui, '\1\2&nbsp;\4')
252
+ text.gsub!( /(\s+)([a-zа-яА-Я]{3})(\s+)([^\\s$])/ui, '\1\2&nbsp;\4')
253
+ end
254
+
255
+ for i in @glueleft
256
+ text.gsub!( /(\s)(#{i})(\s+)/sui, '\1\2&nbsp;')
257
+ end
258
+
259
+ for i in @glueright
260
+ text.gsub!( /(\s)(#{i})(\s+)/sui, '&nbsp;\2\3')
261
+ end
262
+ end
263
+
264
+
265
+
266
+ # 8. Склейка ласт. Тьфу! дефисов.
267
+ text.gsub!( /([a-zа-яА-Я0-9]+(\-[a-zа-яА-Я0-9]+)+)/ui, '<nobr>\1</nobr>') if @settings["dashglue"]
268
+
269
+
270
+ # 9. Макросы
271
+
272
+
273
+
274
+ # 10. Переводы строк
275
+ # --- для ваки не портировано ---
276
+ # --- для ваки не портировано ---
277
+
278
+
279
+ # БЕСКОНЕЧНОСТЬ. Вставляем таги обратно.
280
+ # if (@skip_tags)
281
+ # text = text.split("\xF0\xF0\xF0\xF0").join
282
+ #
283
+
284
+ tags.each do |tag|
285
+ text.sub!(@mark_tag, tag)
286
+ end
287
+
288
+ # i = 0
289
+ # text.gsub!(@mark_tag) {
290
+ # i + 1
291
+ # tags[i-1]
292
+ # }
293
+
294
+ # text = text.split("\xF0\xF0\xF0\xF0")
295
+ #puts "reinserted #{i} tags"
296
+ #
297
+ # end
298
+
299
+
300
+ #ext.gsub!("a", '')
301
+ # raise "Text still has tag markers!" if text.include?("a")
302
+
303
+ # БЕСКОНЕЧНОСТЬ-2. вставляем ещё сигнорированный регексп
304
+ #
305
+ # if @ignore
306
+ # ignored.each { | tag | text.sub!(@mark_ignored, tag) }
307
+ # end
308
+
309
+ # raise "Text still has ignored markers!" if text.include?("\201")
310
+
311
+ # БОНУС: прокручивание ссылок через A(...)
312
+ # --- для ваки не портировано ---
313
+ # --- для ваки не портировано ---
314
+
315
+ # фуф, закончили.
316
+ # text.gsub!(/<nobr>/, "<span class=\"nobr\">").gsub(/<\/nobr>/, "</span>") if (@de_nobr)
317
+
318
+ # text.gsub!(/<nobr>/, "<span class=\"nobr\">").gsub(/<\/nobr>/, "</span>") if (@de_nobr)
319
+
320
+ text.gsub(/(\s)+$/, "").gsub(/^(\s)+/, "")
321
+
322
+ end
323
+
324
+ private
325
+
326
+ end
327
+
328
+ end #end RuTils
329
+
330
+ class String
331
+ include RuTils::GilensonMixin
332
+ end
@@ -0,0 +1,8 @@
1
+ if defined?(BlueCloth)
2
+ class BlueCloth < String #:nodoc:
3
+ alias_method :old_to_html, :to_html
4
+ def to_html(*opts)
5
+ RuTils::overrides_enabled? ? RuTils::Gilenson.new(old_to_html(*opts)).to_html : old_to_html(*opts)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ load File.dirname(__FILE__) + '/blue_cloth_override.rb'
2
+ load File.dirname(__FILE__) + '/red_cloth_override.rb'
3
+ load File.dirname(__FILE__) + '/rails_date_helper_override.rb'
4
+
5
+ module RuTils
6
+ @@overrides = true
7
+
8
+ # Метод позволяет проверить, включена ли перегрузка функций других модулей.
9
+ # Попутно он спрашивает модуль Locale (если таковой имеется) является ли русский
10
+ # текущим языком, и если является, включает перегрузку функций имплицитно.
11
+ # Модуль Locale можно скачать и скомпилировать а можно получить как часть Multilingual Rails.
12
+ def self.overrides_enabled?
13
+ if defined?(Locale) and Locale.respond_to?(:current)
14
+ return true if Locale.current.split('_').first == 'ru'
15
+ end
16
+ @@overrides ? true : false
17
+ end
18
+
19
+ # Включает или выключает перегрузки других модулей. Полезно, например, в случае когда нужно рендерить страницу
20
+ # сайта на нескольких языках и нужно отключить русское оформление текста для других языков.
21
+ def self.overrides= (new_override_flag)
22
+ @@overrides = (new_override_flag ? true : false)
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ if defined?(ActionView``)
2
+ module ActionView #:nodoc:
3
+ module Helpers #:nodoc:
4
+ module DateHelper #:nodoc:
5
+
6
+ # Reports the approximate distance in time between two Time objects or integers.
7
+ # For example, if the distance is 47 minutes, it'll return
8
+ # "about 1 hour". See the source for the complete wording list.
9
+ #
10
+ # Integers are interpreted as seconds. So,
11
+ # <tt>distance_of_time_in_words(50)</tt> returns "less than a minute".
12
+ #
13
+ # Set <tt>include_seconds</tt> to true if you want more detailed approximations if distance < 1 minute
14
+
15
+ alias :distance_of_time_in_words :stock_distance_of_time_in_words
16
+ def distance_of_time_in_words(*args)
17
+ RuTils::overrides_enabled? ? RuTils::DateTime::distance_of_time_in_words(*args) : stock_distance_of_time_in_words
18
+ end
19
+
20
+ # Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
21
+ def time_ago_in_words(from_time, include_seconds = false)
22
+ distance_of_time_in_words(from_time, Time.now, include_seconds)
23
+ end
24
+
25
+ # Returns a select tag with options for each of the months January through December with the current month selected.
26
+ # The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are used as values
27
+ # (what's submitted to the server). It's also possible to use month numbers for the presentation instead of names --
28
+ # set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you want both numbers and names,
29
+ # set the <tt>:add_month_numbers</tt> key in +options+ to true. Examples:
30
+ #
31
+ # select_month(Date.today) # Will use keys like "January", "March"
32
+ # select_month(Date.today, :use_month_numbers => true) # Will use keys like "1", "3"
33
+ # select_month(Date.today, :add_month_numbers => true) # Will use keys like "1 - January", "3 - March"
34
+ #
35
+ # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
36
+ #
37
+ # If you would prefer to show month names as abbreviations, set the
38
+ # <tt>:use_short_month</tt> key in +options+ to true.
39
+ def select_month(date, options = {})
40
+ month_options = []
41
+ if RuTils::overrides_enabled?
42
+ month_names = options[:use_short_month] ? RuTils::DateTime::ABBR_MONTHNAMES : RuTils::DateTime::MONTHNAMES
43
+ else
44
+ month_names = options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES
45
+ end
46
+ 1.upto(12) do |month_number|
47
+ month_name = if options[:use_month_numbers]
48
+ month_number
49
+ elsif options[:add_month_numbers]
50
+ month_number.to_s + ' - ' + month_names[month_number]
51
+ else
52
+ month_names[month_number]
53
+ end
54
+
55
+ month_options << ((date && (date.kind_of?(Fixnum) ? date : date.month) == month_number) ?
56
+ %(<option value="#{month_number}" selected="selected">#{month_name}</option>\n) :
57
+ %(<option value="#{month_number}">#{month_name}</option>\n)
58
+ )
59
+ end
60
+
61
+ select_html(options[:field_name] || 'month', month_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end #endif
@@ -0,0 +1,17 @@
1
+ if defined?(RedCloth)
2
+ # RuTils выполняет перегрузку Textile Glyphs в RedCloth, перенося форматирование спецсимволов на Gilenson.
3
+ class RedCloth < String #:nodoc:
4
+ # Этот метод в RedCloth эскейпит слишком много HTML, нам ничего не оставляет :-)
5
+ def htmlesc(text, mode=0) #:nodoc:
6
+ text
7
+ end
8
+
9
+ # А этот метод обрабатывает Textile Glyphs - ту самую типографицу.
10
+ # Вместо того чтобы влезать в таблицы мы просто заменим Textile Glyphs - и все будут рады.
11
+ alias_method :stock_pgl, :pgl
12
+ def pgl(text) #:nodoc:
13
+ # RuTils::overrides_enabled? ? text.replace(RuTils::Gilenson.new(text).to_html) : stock_pgl(text)
14
+ text.replace(RuTils::Gilenson.new(text).to_html)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,208 @@
1
+ module RuTils
2
+ module Pluralization
3
+ # Выбирает нужный падеж существительного в зависимости от числа
4
+ def self.choose_plural(amount, *variants)
5
+ variant = (amount%10==1 && amount%100!=11 ? 1 : amount%10>=2 && amount%10<=4 && (amount%100<10 || amount%100>=20) ? 2 : 3)
6
+ variants[variant-1]
7
+ end
8
+
9
+ # Выполняет преобразование числа из цифрого вида в символьное
10
+ # amount - числительное
11
+ # gender = 1 - мужской, = 2 - женский, = 3 - средний
12
+ # one_item - именительный падеж единственного числа (= 1)
13
+ # two_items - родительный падеж единственного числа (= 2-4)
14
+ # five_items - родительный падеж множественного числа ( = 5-10)
15
+ def self.sum_string(amount, gender, one_item='', two_items='', five_items='')
16
+ into = ''
17
+ tmp_val ||= 0
18
+
19
+ return "ноль " + five_items if amount == 0
20
+
21
+ tmp_val = amount
22
+
23
+ # единицы
24
+ into, tmp_val = sum_string_fn(into, tmp_val, gender, one_item, two_items, five_items)
25
+
26
+ return into if tmp_val == 0
27
+
28
+ # тысячи
29
+ into, tmp_val = sum_string_fn(into, tmp_val, 2, "тысяча", "тысячи", "тысяч")
30
+
31
+ return into if tmp_val == 0
32
+
33
+ # миллионы
34
+ into, tmp_val = sum_string_fn(into, tmp_val, 1, "миллион", "миллиона", "миллионов")
35
+
36
+ return into if tmp_val == 0
37
+
38
+ # миллиардов
39
+ into, tmp_val = sum_string_fn(into, tmp_val, 1, "миллиард", "миллиарда", "миллиардов")
40
+ return into
41
+ end
42
+
43
+ private
44
+ def self.sum_string_fn(into, tmp_val, gender, one_item='', two_items='', five_items='')
45
+ rest, rest1, end_word, ones, tens, hundreds = [nil]*6
46
+ #
47
+ rest = tmp_val % 1000
48
+ tmp_val = tmp_val / 1000
49
+ if rest == 0
50
+ # последние три знака нулевые
51
+ into = five_items + " " if into == ""
52
+ return [into, tmp_val]
53
+ end
54
+ #
55
+ # начинаем подсчет с Rest
56
+ end_word = five_items
57
+ # сотни
58
+ case rest / 100
59
+ when 0 then hundreds = ""
60
+ when 1 then hundreds = "сто "
61
+ when 2 then hundreds = "двести "
62
+ when 3 then hundreds = "триста "
63
+ when 4 then hundreds = "четыреста "
64
+ when 5 then hundreds = "пятьсот "
65
+ when 6 then hundreds = "шестьсот "
66
+ when 7 then hundreds = "семьсот "
67
+ when 8 then hundreds = "восемьсот "
68
+ when 9 then hundreds = "девятьсот "
69
+ end
70
+
71
+ # десятки
72
+ rest = rest % 100
73
+ rest1 = rest / 10
74
+ ones = ""
75
+ case rest1
76
+ when 0 then tens = ""
77
+ when 1 # особый случай
78
+ case rest
79
+ when 10 then tens = "десять "
80
+ when 11 then tens = "одиннадцать "
81
+ when 12 then tens = "двенадцать "
82
+ when 13 then tens = "тринадцать "
83
+ when 14 then tens = "четырнадцать "
84
+ when 15 then tens = "пятнадцать "
85
+ when 16 then tens = "шестнадцать "
86
+ when 17 then tens = "семнадцать "
87
+ when 18 then tens = "восемнадцать "
88
+ when 19 then tens = "девятнадцать "
89
+ end
90
+ when 2: tens = "двадцать "
91
+ when 3: tens = "тридцать "
92
+ when 4: tens = "сорок "
93
+ when 5: tens = "пятьдесят "
94
+ when 6: tens = "шестьдесят "
95
+ when 7: tens = "семьдесят "
96
+ when 8: tens = "восемьдесят "
97
+ when 9: tens = "девяносто "
98
+ end
99
+ #
100
+ if rest1 < 1 or rest1 > 1 # единицы
101
+ case rest % 10
102
+ when 1
103
+ case gender
104
+ when 1
105
+ ones = "один "
106
+ when 2
107
+ ones = "одна "
108
+ when 3
109
+ ones = "одно "
110
+ end
111
+ end_word = one_item
112
+ when 2
113
+ if gender == 2
114
+ ones = "две "
115
+ else
116
+ ones = "два "
117
+ end
118
+ end_word = two_items
119
+ when 3
120
+ ones = "три " if end_word = two_items
121
+ when 4
122
+ ones = "четыре " if end_word = two_items
123
+ when 5
124
+ ones = "пять "
125
+ when 6
126
+ ones = "шесть "
127
+ when 7
128
+ ones = "семь "
129
+ when 8
130
+ ones = "восемь "
131
+ when 9
132
+ ones = "девять "
133
+ end
134
+ end
135
+
136
+ # сборка строки
137
+ return [(hundreds + tens + ones + end_word + " " + into).strip, tmp_val]
138
+ end
139
+
140
+ # Реализует вывод прописью любого объекта, реализующего Float
141
+ module FloatFormatting
142
+
143
+ # Выдает сумму прописью с учетом дробной доли. Дробная доля округляется до миллионной, или (если
144
+ # дробная доля оканчивается на нули) до ближайшей доли ( 500 тысячных округляется до 5 десятых).
145
+ # Дополнительный аргумент - род существительного (1 - мужской, 2- женский, 3-средний)
146
+ def propisju(gender = 2)
147
+ raise "NaN propisju eto ne propis!" if self.nan?
148
+
149
+ st = RuTils::Pluralization::sum_string(self.to_i, gender, "целая", "целых", "целых")
150
+ it = []
151
+
152
+ rmdr = self.to_s.match(/\.(\d+)/)[1]
153
+
154
+ signs = rmdr.to_s.size- 1
155
+
156
+ it << ["десятая", "десятых"]
157
+ it << ["сотая", "сотых"]
158
+ it << ["тысячная", "тысячных"]
159
+ it << ["десятитысячная", "десятитысячных"]
160
+ it << ["стотысячная", "стотысячных"]
161
+ it << ["миллионная", "милллионных"]
162
+ # it << ["десятимиллионная", "десятимилллионных", "десятимиллионных"]
163
+ # it << ["стомиллионная", "стомилллионных", "стомиллионных"]
164
+ # it << ["миллиардная", "миллиардных", "миллиардных"]
165
+ # it << ["десятимиллиардная", "десятимиллиардных", "десятимиллиардных"]
166
+ # it << ["стомиллиардная", "стомиллиардных", "стомиллиардных"]
167
+ # it << ["триллионная", "триллионных", "триллионных"]
168
+
169
+ while it[signs].nil?
170
+ rmdr = (rmdr/10).round
171
+ signs = rmdr.to_s.size- 1
172
+ end
173
+
174
+ suf1, suf2, suf3 = it[signs][0], it[signs][1], it[signs][2]
175
+ st + " " + RuTils::Pluralization::sum_string(rmdr.to_i, 2, suf1, suf2, suf2)
176
+ end
177
+ end
178
+
179
+ # Реализует вывод прописью любого объекта, реализующего Numeric
180
+ module NumericFormatting
181
+ # Выбирает корректный вариант числительного в зависимости от рода и числа и оформляет сумму прописью
182
+ # 234.propisju => "двести сорок три"
183
+ # 221.propisju(2) => "двести двадцать одна"
184
+ def propisju(gender = 1)
185
+ RuTils::Pluralization::sum_string(self, gender, "")
186
+ end
187
+
188
+ def propisju_items(gender=1, *forms)
189
+ RuTils::Pluralization::sum_string(self, gender, "") + " " + RuTils::Pluralization::choose_plural(self, *forms)
190
+ end
191
+
192
+ # Выбирает корректный вариант числительного в зависимости от рода и числа. Например:
193
+ # * 4.items("колесо", "колеса", "колес") => "колеса"
194
+ def items(one_item, two_items, five_items)
195
+ RuTils::Pluralization::choose_plural(self, one_item, two_items, five_items)
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ class Numeric
202
+ include RuTils::Pluralization::NumericFormatting
203
+ end
204
+
205
+
206
+ class Float
207
+ include RuTils::Pluralization::FloatFormatting
208
+ end
data/lib/rutils.rb ADDED
@@ -0,0 +1,14 @@
1
+ $KCODE = 'u'
2
+ require 'jcode'
3
+
4
+ # Главный контейнер модуля
5
+ module RuTils
6
+ VERSION = '0.0.3'
7
+ end
8
+
9
+ require File.dirname(__FILE__) + '/pluralizer/pluralizer'
10
+ require File.dirname(__FILE__) + '/gilenson/gilenson_port' #Предельно идентичный порт Тыпографицы
11
+ #require File.dirname(__FILE__) + '/gilenson/gilenson' #Гиленсон, рефакторенный нами - todo
12
+ require File.dirname(__FILE__) + '/datetime/datetime' # Дата и время без локалей
13
+ require File.dirname(__FILE__) + '/transliteration/transliteration' # Транслит
14
+ require File.dirname(__FILE__) + '/integration/integration' # Интеграция с rails, textile и тд