petrovich 0.1.4 → 0.1.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e254345aa99e9b41420872a0a26772a591770d11
4
- data.tar.gz: 06d8364dc873d2d8240b47214820ab226d8027e6
3
+ metadata.gz: 172b74ffbc89257e10d65ff26748ec1cd60e8d7a
4
+ data.tar.gz: fddc2e767c1e1a0870c2ed56062aee987f88bcf5
5
5
  SHA512:
6
- metadata.gz: cd90dab2d64215eaa63e405577cf24a78193057f978f719d46f016e58a23f19b4b8ff1ab1408abf78c1fb332f6e0ecd2714c14f81dbd1c39d0308c970dfc2aa5
7
- data.tar.gz: 50c85d2aaf853a2d709ab1672641cd47cef24d72eeb947370ad74d3ceffa6e78a8f1b5b0188796453e3cc4dae49cd43287e4949f693c54fb5579203e7fb5f235
6
+ metadata.gz: 618bd1860e0809bc81700dc45fc14736bf0045ea6f5681574248e5965a84353753fb9ae2cd6dea2dd398529dfc1f58b32b8d4309f8c5578fdcc45415c571d033
7
+ data.tar.gz: 34caf140f11fb16c4a351be843ccabbb70520e1385db435c096f73c733adfba4baaa705f4c183ae354157cd8ddaf59fc3ebbf249a0a2c38dd884f3ebc0f7911e
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Petrovich Developers
1
+ Copyright (c) 2013-2014 Andrew Kozloff & Dmitry Ustalov
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -27,12 +27,12 @@ gem 'petrovich'
27
27
 
28
28
  Для работы гема требуется Ruby не младше версии 1.9.1. Petrovich не
29
29
  привязан к Ruby on Rails и может свободно использоваться практически
30
- в любых приложениях и библиотеках на Ruby.
30
+ в любых приложениях и библиотеках на языке Ruby.
31
31
 
32
32
  ## Использование
33
33
 
34
34
  Вы задаёте начальные значения (фамилию, имя и отчество) в именительном
35
- падеже.
35
+ падеже, а библиотека делает всё остальное.
36
36
 
37
37
  ```ruby
38
38
  p = Petrovich.new(:male)
@@ -155,6 +155,33 @@ person.my_lastname_dative # => Сафронову
155
155
  | instrumental | творительный | Кем? Чем? |
156
156
  | prepositional | предложный | О ком? О чём? |
157
157
 
158
+ ## Лемматизация
159
+
160
+ Petrovich умеет выполнять преобразовывание фамилии, имени и отчества
161
+ в произвольном падеже к нормальной форме, а именно в именительный падеж.
162
+ Такой процесс называется *лемматизацией*.
163
+
164
+ Для выполнения лемматизации нужно передать в методы склонения третий
165
+ аргумент, означающий текущий падеж слова, а во втором аргументе указать
166
+ желаемый падеж — именительный.
167
+
168
+ ```ruby
169
+ p = Petrovich.new(:male )
170
+ p.firstname('Савве', :nominative, :dative) # => Савва
171
+ p.middlename('Палладиевичу', :nominative, :dative) # => Палладиевич
172
+ p.lastname('Солунскому', :nominative, :dative) # => Солунский
173
+ ```
174
+
175
+ Также Petrovich позволяет преобразовывать слово сразу в любой падеж,
176
+ отличный от именительного:
177
+
178
+ ```ruby
179
+ p = Petrovich.new(:female )
180
+ p.firstname('Марфе', :accusative, :dative) # => Марфу
181
+ p.middlename('Золовне', :accusative, :dative) # => Золовну
182
+ p.lastname('Соловецкой', :accusative, :dative) # => Соловецкую
183
+ ```
184
+
158
185
  ## Оценка точности
159
186
 
160
187
  Тестирование гема при склонении коллекции фамилий из морфологического
@@ -168,11 +195,11 @@ person.my_lastname_dative # => Сафронову
168
195
 
169
196
  ```
170
197
  Pr(nominative|male) = 100.0000%
171
- Pr(genitive|male) = 99.7261%
172
- Pr(dative|male) = 99.7510%
173
- Pr(accusative|male) = 99.7635%
174
- Pr(instrumental|male) = 99.2664%
175
- Pr(prepositional|male) = 99.7386%
198
+ Pr(genitive|male) = 99.7635%
199
+ Pr(dative|male) = 99.7884%
200
+ Pr(accusative|male) = 99.8008%
201
+ Pr(instrumental|male) = 99.3037%
202
+ Pr(prepositional|male) = 99.7759%
176
203
  ```
177
204
  ```
178
205
  Pr(nominative|female) = 100.0000%
@@ -183,7 +210,7 @@ Pr(instrumental|female) = 99.4636%
183
210
  Pr(prepositional|female) = 99.9401%
184
211
  ```
185
212
 
186
- В настоящий момент наблюдается точность в 99.7815% на основе обработки
213
+ В настоящий момент наблюдается точность в 99.7984% на основе обработки
187
214
  88314 примеров.
188
215
 
189
216
  [АОТ]: http://seman.svn.sourceforge.net/viewvc/seman/trunk/Docs/Morph_UNIX.txt?revision=HEAD&view=markup
@@ -195,7 +222,12 @@ Pr(prepositional|female) = 99.9401%
195
222
  ## Разработчики
196
223
 
197
224
  * [Андрей Козлов](https://github.com/tanraya)
198
- * [Дмитрий Усталов](http://eveel.ru)
225
+ * [Дмитрий Усталов](http://ustalov.name)
226
+
227
+ ## Благодарности
228
+
229
+ Эта библиотека не была бы столь замечательна без содействия Павла Скрылёва,
230
+ Никиты Помящего, Игоря Бочарикова, и других хороших людей.
199
231
 
200
232
  ## Портирование
201
233
 
data/Rakefile CHANGED
@@ -2,18 +2,14 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  require 'bundler/gem_tasks'
5
+ require 'rake/testtask'
5
6
 
6
- begin
7
- require 'rdoc/task'
8
- rescue LoadError
9
- require 'rdoc/rdoc'
10
- require 'rake/rdoctask'
11
- RDoc::Task = Rake::RDocTask
7
+ Rake::TestTask.new(:spec) do |test|
8
+ test.libs << 'spec'
9
+ test.test_files = Dir['spec/**/*_spec.rb']
10
+ test.verbose = true
12
11
  end
13
12
 
14
- require 'rspec/core/rake_task'
15
- RSpec::Core::RakeTask.new(:spec)
16
-
17
13
  task :petrovich do
18
14
  require 'petrovich'
19
15
  end
@@ -106,12 +106,20 @@ class Petrovich
106
106
  attribute = match[1]
107
107
  gcase = match[2]
108
108
 
109
- petrovich_create_getter(method_name, attribute, gcase).call
109
+ petrovich_create_getter(method_name, attribute, gcase)
110
+
111
+ if respond_to_without_petrovich?(method_name)
112
+ send method_name
113
+ else
114
+ super
115
+ end
110
116
  else
111
117
  super
112
118
  end
113
119
  end
114
120
 
121
+ alias :respond_to_without_petrovich? :respond_to?
122
+
115
123
  def respond_to?(method_name, include_private = false)
116
124
  if match = method_name.to_s.match(petrovich_method_regex)
117
125
  true
@@ -6,9 +6,20 @@ class Petrovich
6
6
 
7
7
  class UnknownCaseException < Exception;;end
8
8
  class UnknownRuleException < Exception;;end
9
+ class CantApplyRuleException < Exception;;end
9
10
 
10
11
  # Набор методов для нахождения и применения правил к имени, фамилии и отчеству.
11
12
  class Rules
13
+ attr_reader :gender
14
+
15
+ Matchers = [
16
+ proc {| x, y, i | y[ 0 ].size <=> x[ 0 ].size },
17
+ proc {| x, y, i | x[ 1 ][ 'gender' ] != i.gender && 1 ||
18
+ y[ 1 ][ 'gender' ] != i.gender && -1 || 0 },
19
+ proc {| x, y, i | y[ 1 ][ 'test' ][ 0 ].size <=>
20
+ x[ 1 ][ 'test' ][ 0 ].size },
21
+ proc {| x, y, i | x[ 1 ][ 'test' ][ 0 ] <=> y[ 1 ][ 'test' ][ 0 ] } ]
22
+
12
23
  def initialize(gender = nil)
13
24
  @gender = gender
14
25
  end
@@ -25,8 +36,8 @@ class Petrovich
25
36
  # lastname_accusative('Комаров') # => Комарова
26
37
  #
27
38
  [:lastname, :firstname, :middlename].each do |method_name|
28
- define_method(method_name) do |name, gcase|
29
- inflect(name, gcase, Petrovich::RULES[method_name.to_s])
39
+ define_method(method_name) do |name, gcase, scase|
40
+ inflect(name, gcase, scase, Petrovich::RULES[method_name.to_s])
30
41
  end
31
42
  end
32
43
 
@@ -36,14 +47,20 @@ class Petrovich
36
47
  # Признак +first_word+ указывает, что данное слово является первым словом
37
48
  # в составном слове. Например, в двойной русской фамилии Иванов-Сидоров.
38
49
  #
39
- def match?(name, rule, match_whole_word, tags)
50
+ def match?(name, gcase, scase, rule, match_whole_word, tags)
40
51
  return false unless tags_allow? tags, rule['tags']
41
- return false if rule['gender'] == 'male' && female? || rule['gender'] == 'female' && !female?
52
+ return false if rule['gender'] == 'male' && female? ||
53
+ rule['gender'] == 'female' && !female?
42
54
 
43
- name = UnicodeUtils.downcase(name)
44
55
  rule['test'].each do |chars|
45
- test = match_whole_word ? name : name.slice([name.size - chars.size, 0].max .. -1)
46
- return true if test == chars
56
+ begin
57
+ chars = apply(chars, rule, scase, gcase) if scase != NOMINATIVE
58
+ rescue CantApplyRuleException
59
+ next
60
+ end
61
+
62
+ test = match_whole_word ? name : name.slice([name.size - chars.size, 0].max .. -1)
63
+ return chars if test == chars
47
64
  end
48
65
 
49
66
  false
@@ -57,25 +74,28 @@ class Petrovich
57
74
  @gender == 'female'
58
75
  end
59
76
 
60
- def inflect(name, gcase, rules)
77
+ def inflect(name, gcase, scase, rules)
61
78
  i = 0
62
79
 
63
80
  parts = name.split('-')
64
81
 
65
82
  parts.map! do |part|
66
83
  first_word = (i += 1) == 1 && parts.size > 1
67
- find_and_apply(part, gcase, rules, first_word: first_word)
84
+ find_and_apply(part, gcase, scase, rules, first_word: first_word)
68
85
  end
69
86
 
70
87
  parts.join('-')
71
88
  end
72
89
 
73
90
  # Применить правило
74
- def apply(name, gcase, rule)
75
- modificator_for(gcase, rule).each_char do |char|
91
+ def apply(name, rule, gcase, scase)
92
+ mod = modificator_from(scase, rule) + modificator_for(gcase, rule)
93
+ skip = 0
94
+ mod.each_char do |char|
76
95
  case char
77
96
  when '.'
78
97
  when '-'
98
+ raise CantApplyRuleException if name.empty?
79
99
  name = name.slice(0, name.size - 1)
80
100
  else
81
101
  name += char
@@ -86,32 +106,46 @@ class Petrovich
86
106
  end
87
107
 
88
108
  # Найти правило и применить к имени с учетом склонения
89
- def find_and_apply(name, gcase, rules, features = {})
90
- rule = find_for(name, rules, features)
91
- apply(name, gcase, rule)
92
- rescue UnknownRuleException
93
- # Если не найдено правило для имени, возвращаем неизмененное имя.
109
+ def find_and_apply(name, gcase, scase, rules, features = {})
110
+ rule = find_for(name, gcase, scase, rules, features)
111
+ apply(name, rule, gcase, scase)
112
+ rescue UnknownRuleException, CantApplyRuleException
113
+ # Если не найдено правило для имени, или случилась ошибка применения
114
+ # правила, возвращаем неизмененное имя.
94
115
  name
95
116
  end
96
117
 
97
118
  # Найти подходящее правило в исключениях или суффиксах
98
- def find_for(name, rules, features = {})
119
+ def find_for(name, gcase, scase, rules, features = {})
99
120
  tags = extract_tags(features)
100
121
 
101
122
  # Сначала пытаемся найти исключения
102
123
  if rules.has_key?('exceptions')
103
- p = find(name, rules['exceptions'], true, tags)
124
+ p = find(name, gcase, scase, rules['exceptions'], true, tags)
104
125
  return p if p
105
126
  end
106
127
 
107
128
  # Не получилось, ищем в суффиксах. Если не получилось найти и в них,
108
129
  # возвращаем неизмененное имя.
109
- find(name, rules['suffixes'], false, tags) or raise UnknownRuleException, "Cannot find rule for #{name}"
130
+ find(name, gcase, scase, rules['suffixes'], false, tags) ||
131
+ raise( UnknownRuleException, "Cannot find rule for #{name}" )
110
132
  end
111
133
 
112
134
  # Найти подходящее правило в конкретном списке правил
113
- def find(name, rules, match_whole_word, tags)
114
- rules.find { |rule| match?(name, rule, match_whole_word, tags) }
135
+ def find(name, gcase, scase, rules, match_whole_word, tags)
136
+ name = UnicodeUtils.downcase(name)
137
+ first =
138
+ rules.map do| rule |
139
+ score = match?(name, gcase, scase, rule, match_whole_word, tags)
140
+ score && [ score, rule ] || nil
141
+ end.compact.sort do| x, y |
142
+ Matchers.reduce( 0 ) do| c, m |
143
+ c = m.call( x, y, self )
144
+ break c if c != 0
145
+ end
146
+ end.first
147
+
148
+ first && first[ 1 ]
115
149
  end
116
150
 
117
151
  # Получить модификатор из указанного правиля для указанного склонения
@@ -134,6 +168,29 @@ class Petrovich
134
168
  end
135
169
  end
136
170
 
171
+ # Получить модификатор из указанного правила для преобразования
172
+ # из указанного склонения
173
+ def modificator_from(scase, rule)
174
+ return '.' if scase.to_sym == NOMINATIVE
175
+
176
+ # TODO т.к. именительный падеж не может быть восстановлен
177
+ # в некоторых случаях верно, используется первый попавшийся вариант
178
+ # видимо нужно менять формат таблицы, или развязывать варианты,
179
+ # находящиеся в поле test
180
+ base = rule['test'][0].unpack('U*')
181
+ mod = modificator_for(scase, rule).unpack('U*')
182
+ mod.map do | char |
183
+ case char
184
+ when 46 # '.'
185
+ 46
186
+ when 45 # '-'
187
+ base.pop
188
+ else
189
+ 45
190
+ end
191
+ end.reverse.pack('U*')
192
+ end
193
+
137
194
  # Преобразование +{a: true, b: false, c: true}+ в +%w(a c)+.
138
195
  def extract_tags(features = {})
139
196
  features.keys.select { |k| features[k] == true }.map(&:to_s)
data/lib/petrovich.rb CHANGED
@@ -48,16 +48,16 @@ class Petrovich
48
48
  @gender = gender.to_s
49
49
  end
50
50
 
51
- def lastname(name, gcase)
52
- Rules.new(@gender).lastname(name, gcase)
51
+ def lastname(name, gcase, scase = :nominative)
52
+ Rules.new(@gender).lastname(name, gcase, scase)
53
53
  end
54
54
 
55
- def firstname(name, gcase)
56
- Rules.new(@gender).firstname(name, gcase)
55
+ def firstname(name, gcase, scase = :nominative)
56
+ Rules.new(@gender).firstname(name, gcase, scase)
57
57
  end
58
58
 
59
- def middlename(name, gcase)
60
- Rules.new(@gender).middlename(name, gcase)
59
+ def middlename(name, gcase, scase = :nominative)
60
+ Rules.new(@gender).middlename(name, gcase, scase)
61
61
  end
62
62
 
63
63
  alias :patronymic :middlename
data/rules/rules.yml CHANGED
@@ -81,12 +81,12 @@ lastname:
81
81
  mods: [-ой, -ой, -ую, -ой, -ой]
82
82
 
83
83
  - gender: female
84
- test: [ая]
84
+ test: [цкая, ская, ная, ая]
85
85
  mods: [--ой, --ой, --ую, --ой, --ой]
86
86
 
87
87
  - gender: female
88
- test: [ская]
89
- mods: [--ой, --ой, --ую, --ой, --ой]
88
+ test: [яя]
89
+ mods: [--ей, --ей, --юю, --ей, --ей]
90
90
 
91
91
  - gender: female
92
92
  test: [на]
@@ -132,10 +132,6 @@ lastname:
132
132
  test: [га, ка, ха, ча, ща, жа]
133
133
  mods: [-и, -е, -у, -ой, -е]
134
134
 
135
- - gender: androgynous
136
- test: [ца]
137
- mods: [-и, -е, -у, -ей, -е]
138
-
139
135
  - gender: androgynous
140
136
  test: [а]
141
137
  mods: [-ы, -е, -у, -ой, -е]
@@ -173,7 +169,11 @@ lastname:
173
169
  mods: [-я, -ю, -я, -ем, -е]
174
170
 
175
171
  - gender: male
176
- test: [гой, кой]
172
+ test: [кой]
173
+ mods: [-го, -му, -го, --им, -м]
174
+
175
+ - gender: male
176
+ test: [гой]
177
177
  mods: [-го, -му, -го, --им, -м]
178
178
 
179
179
  - gender: male
@@ -189,7 +189,11 @@ lastname:
189
189
  mods: [--его, --ему, --его, -м, --ем]
190
190
 
191
191
  - gender: male
192
- test: [кий, ый]
192
+ test: [ый]
193
+ mods: [--ого, --ому, --ого, -м, --ом]
194
+
195
+ - gender: male
196
+ test: [кий]
193
197
  mods: [--ого, --ому, --ого, -м, --ом]
194
198
 
195
199
  - gender: male
@@ -279,6 +283,10 @@ firstname:
279
283
  test: [ия]
280
284
  mods: [-и, -и, -ю, -ей, -и]
281
285
 
286
+ - gender: female
287
+ test: [а]
288
+ mods: [-ы, -е, -у, -ой, -е]
289
+
282
290
  - gender: female
283
291
  test: [я]
284
292
  mods: [-и, -е, -ю, -ей, -е]
@@ -299,6 +307,10 @@ firstname:
299
307
  test: [б, в, г, д, ж, з, к, л, м, н, п, р, с, т, ф, х, ц, ч]
300
308
  mods: [а, у, а, ом, е]
301
309
 
310
+ - gender: androgynous
311
+ test: [ния, рия, вия]
312
+ mods: [-и, -и, -ю, -ем, -ем]
313
+
302
314
  middlename:
303
315
  suffixes:
304
316
  - gender: male
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: petrovich
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kozloff
@@ -9,34 +9,34 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-23 00:00:00.000000000 Z
12
+ date: 2014-02-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: unicode_utils
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ~>
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
20
  version: '1.4'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ~>
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.4'
28
28
  - !ruby/object:Gem::Dependency
29
- name: rspec
29
+ name: minitest
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - '>='
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
34
  version: '0'
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - '>='
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  description: A library to inflect Russian anthroponyms such as first names, last names,
@@ -48,14 +48,14 @@ executables: []
48
48
  extensions: []
49
49
  extra_rdoc_files: []
50
50
  files:
51
+ - MIT-LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - lib/petrovich.rb
51
55
  - lib/petrovich/extension.rb
52
56
  - lib/petrovich/rules.rb
53
57
  - lib/tasks/evaluate.rake
54
- - lib/petrovich.rb
55
58
  - rules/rules.yml
56
- - MIT-LICENSE
57
- - Rakefile
58
- - README.md
59
59
  homepage: https://github.com/petrovich/petrovich-ruby
60
60
  licenses:
61
61
  - MIT
@@ -66,17 +66,17 @@ require_paths:
66
66
  - lib
67
67
  required_ruby_version: !ruby/object:Gem::Requirement
68
68
  requirements:
69
- - - '>='
69
+ - - ">="
70
70
  - !ruby/object:Gem::Version
71
71
  version: 1.9.1
72
72
  required_rubygems_version: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - '>='
74
+ - - ">="
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0'
77
77
  requirements: []
78
78
  rubyforge_project:
79
- rubygems_version: 2.1.11
79
+ rubygems_version: 2.2.2
80
80
  signing_key:
81
81
  specification_version: 4
82
82
  summary: Automatic inflection of Russian anthroponyms