petrovich 0.1.4 → 0.1.5

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