petrovich 0.1.2 → 0.1.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.
data/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
 
6
6
  [![Build Status](https://secure.travis-ci.org/rocsci/petrovich.png)](http://travis-ci.org/rocsci/petrovich)
7
7
 
8
+ #### Посмотреть в действии: http://petrovich.rocketscience.it
9
+
8
10
  ## Установка
9
11
 
10
12
  Добавьте в Gemfile:
@@ -47,7 +49,7 @@ p.middlename('Сергеевич', :dative) # => Сергеевичу
47
49
  такие как Нельсон.
48
50
 
49
51
  Важно понимать, что явное указание пола повышает точность обработки слов.
50
- Если пол не известен, однако известно отчество, то гем постарается
52
+ Если пол неизвестен, однако известно отчество, то гем постарается
51
53
  определить по пол отчеству на основе простой эвристики.
52
54
 
53
55
  ### Продвинутое использование
@@ -166,22 +168,22 @@ person.my_lastname_dative # => Сафронову
166
168
 
167
169
  ```
168
170
  Pr(nominative|male) = 100.0000%
169
- Pr(genitive|male) = 99.6763%
170
- Pr(dative|male) = 99.7012%
171
- Pr(accusative|male) = 99.7261%
172
- Pr(instrumental|male) = 97.9485%
173
- Pr(prepositional|male) = 99.6888%
171
+ Pr(genitive|male) = 99.7137%
172
+ Pr(dative|male) = 99.7386%
173
+ Pr(accusative|male) = 99.7635%
174
+ Pr(instrumental|male) = 97.9858%
175
+ Pr(prepositional|male) = 99.7261%
174
176
  ```
175
177
  ```
176
178
  Pr(nominative|female) = 100.0000%
177
- Pr(genitive|female) = 99.8652%
178
- Pr(dative|female) = 99.8952%
179
- Pr(accusative|female) = 99.9251%
180
- Pr(instrumental|female) = 99.4189%
181
- Pr(prepositional|female) = 99.8952%
179
+ Pr(genitive|female) = 99.9102%
180
+ Pr(dative|female) = 99.9401%
181
+ Pr(accusative|female) = 99.9701%
182
+ Pr(instrumental|female) = 99.4636%
183
+ Pr(prepositional|female) = 99.9401%
182
184
  ```
183
185
 
184
- В настоящий момент наблюдается точность в 99.6275% на основе обработки
186
+ В настоящий момент наблюдается точность в 99.6614% на основе обработки
185
187
  88314 примеров.
186
188
 
187
189
  [АОТ]: http://seman.svn.sourceforge.net/viewvc/seman/trunk/Docs/Morph_UNIX.txt?revision=HEAD&view=markup
@@ -203,7 +205,6 @@ Pr(prepositional|female) = 99.8952%
203
205
  * добавить отладочный режим, чтобы видеть, какое именно правило было
204
206
  использовано при словообразовании;
205
207
  * интерфейс командной строки, чтобы работать с гемом из командной строки;
206
- * Web-интерфейс для обработки ФИО;
207
208
  * проверка совместимости с различными ORM и сторонними библиотеками.
208
209
 
209
210
  Если вы хотите помочь этому проекту, вы можете реализовать любую
@@ -3,7 +3,7 @@
3
3
  class Petrovich
4
4
  # Этот модуль разработан для возможности его подмешивания в класс Ruby.
5
5
  # Его можно подмешать в любой класс, например, в модель ActiveRecord.
6
- #
6
+ #
7
7
  # При помощи вызова метода +petrovich+ вы указываете, какие аттрибуты или методы класса
8
8
  # будут возвращать фамилию, имя и отчество.
9
9
  #
@@ -24,23 +24,23 @@ class Petrovich
24
24
  # на основе файла правил.
25
25
  #
26
26
  # Пример использования
27
- #
27
+ #
28
28
  # class User
29
29
  # include Petrovich::Extension
30
- #
30
+ #
31
31
  # petrovich :firstname => :my_firstname,
32
32
  # :middlename => :my_middlename,
33
33
  # :lastname => :my_lastname,
34
34
  # :gender => :my_gender
35
- #
35
+ #
36
36
  # def my_firstname
37
37
  # 'Пётр'
38
38
  # end
39
- #
39
+ #
40
40
  # def my_middlename
41
41
  # 'Александрович'
42
42
  # end
43
- #
43
+ #
44
44
  # def my_lastname
45
45
  # 'Ларин'
46
46
  # end
@@ -74,7 +74,7 @@ class Petrovich
74
74
  end
75
75
 
76
76
  self.petrovich_configuration = {
77
- :fullname => nil,
77
+ :lastname => nil,
78
78
  :firstname => nil,
79
79
  :middlename => nil,
80
80
  :gender => nil
@@ -84,11 +84,20 @@ class Petrovich
84
84
 
85
85
  def petrovich_create_getter(method_name, attribute, gcase)
86
86
  options = self.class.petrovich_configuration
87
- reflection = options.key(attribute.to_sym) or raise "No reflection for attribute '#{attribute}'!"
87
+ reflection = options.key(attribute.to_sym) or
88
+ raise "No reflection for attribute '#{attribute}'!"
88
89
 
89
90
  self.class.send(:define_method, method_name) do
90
- rn = Petrovich.new(options[:gender])
91
- rn.send(reflection, send(attribute), gcase)
91
+ # detect by gender attr if defined
92
+ gender = options[:gender] && send(options[:gender])
93
+ # detect by middlename attr if defined
94
+ gender ||= begin
95
+ middlename = options[:middlename] && send(options[:middlename])
96
+ middlename && Petrovich.detect_gender(middlename)
97
+ end
98
+
99
+ rn = Petrovich.new gender
100
+ rn.send reflection, send(attribute), gcase
92
101
  end
93
102
  end
94
103
 
@@ -26,13 +26,18 @@ class Petrovich
26
26
  #
27
27
  [:lastname, :firstname, :middlename].each do |method_name|
28
28
  define_method(method_name) do |name, gcase|
29
- find_and_apply(name, gcase, Petrovich::RULES[method_name.to_s])
29
+ inflect(name, gcase, Petrovich::RULES[method_name.to_s])
30
30
  end
31
31
  end
32
32
 
33
33
  protected
34
-
35
- def match?(name, rule, match_whole_word)
34
+ # Известно несколько типов признаков, которые влияют на процесс поиска.
35
+ #
36
+ # Признак +first_word+ указывает, что данное слово является первым словом
37
+ # в составном слове. Например, в двойной русской фамилии Иванов-Сидоров.
38
+ #
39
+ def match?(name, rule, match_whole_word, tags)
40
+ return false unless tags_allow? tags, rule['tags']
36
41
  return false if rule['gender'] == 'male' && female? || rule['gender'] == 'female' && !female?
37
42
 
38
43
  name = UnicodeUtils.downcase(name)
@@ -52,6 +57,19 @@ class Petrovich
52
57
  @gender == 'female'
53
58
  end
54
59
 
60
+ def inflect(name, gcase, rules)
61
+ i = 0
62
+
63
+ parts = name.split('-')
64
+
65
+ parts.map! do |part|
66
+ first_word = (i += 1) == 1 && parts.size > 1
67
+ find_and_apply(part, gcase, rules, first_word: first_word)
68
+ end
69
+
70
+ parts.join('-')
71
+ end
72
+
55
73
  # Применить правило
56
74
  def apply(name, gcase, rule)
57
75
  modificator_for(gcase, rule).each_char do |char|
@@ -68,8 +86,8 @@ class Petrovich
68
86
  end
69
87
 
70
88
  # Найти правило и применить к имени с учетом склонения
71
- def find_and_apply(name, gcase, rules)
72
- rule = find_for(name, rules)
89
+ def find_and_apply(name, gcase, rules, features = {})
90
+ rule = find_for(name, rules, features)
73
91
  apply(name, gcase, rule)
74
92
  rescue UnknownRuleException
75
93
  # Если не найдено правило для имени, возвращаем неизмененное имя.
@@ -77,21 +95,23 @@ class Petrovich
77
95
  end
78
96
 
79
97
  # Найти подходящее правило в исключениях или суффиксах
80
- def find_for(name, rules)
98
+ def find_for(name, rules, features = {})
99
+ tags = extract_tags(features)
100
+
81
101
  # Сначала пытаемся найти исключения
82
102
  if rules.has_key?('exceptions')
83
- p = find(name, rules['exceptions'], true)
103
+ p = find(name, rules['exceptions'], true, tags)
84
104
  return p if p
85
105
  end
86
106
 
87
107
  # Не получилось, ищем в суффиксах. Если не получилось найти и в них,
88
108
  # возвращаем неизмененное имя.
89
- find(name, rules['suffixes'], false) or raise UnknownRuleException, "Cannot find rule for #{name}"
109
+ find(name, rules['suffixes'], false, tags) or raise UnknownRuleException, "Cannot find rule for #{name}"
90
110
  end
91
111
 
92
112
  # Найти подходящее правило в конкретном списке правил
93
- def find(name, rules, match_whole_word)
94
- rules.find { |rule| match?(name, rule, match_whole_word) }
113
+ def find(name, rules, match_whole_word, tags)
114
+ rules.find { |rule| match?(name, rule, match_whole_word, tags) }
95
115
  end
96
116
 
97
117
  # Получить модификатор из указанного правиля для указанного склонения
@@ -114,5 +134,17 @@ class Petrovich
114
134
  end
115
135
  end
116
136
 
137
+ # Преобразование +{a: true, b: false, c: true}+ в +%w(a c)+.
138
+ def extract_tags(features = {})
139
+ features.keys.select { |k| features[k] == true }.map(&:to_s)
140
+ end
141
+
142
+ # Правило не подходит только в том случае, если оно содержит больше
143
+ # тегов, чем требуется для данного слова.
144
+ #
145
+ def tags_allow?(tags, rule_tags)
146
+ rule_tags ||= []
147
+ (rule_tags - tags).empty?
148
+ end
117
149
  end
118
150
  end
@@ -29,8 +29,29 @@
29
29
  #
30
30
  lastname:
31
31
  exceptions:
32
+ # Неизменяемые первые части двойных русских фамилий.
33
+ #
34
+ - gender: androgynous
35
+ test:
36
+ - бонч
37
+ - абдул
38
+ - белиц
39
+ - гасан
40
+ - дюссар
41
+ - дюмон
42
+ - книппер
43
+ - корвин
44
+ - ван
45
+ - шолом
46
+ - тер
47
+ - призван
48
+ - мелик
49
+ - вар
50
+ mods: [., ., ., ., .]
51
+ tags: [first_word]
52
+
32
53
  - gender: androgynous
33
- test: [дюма, тома, дега, люка, ферма, гамарра, петипа, шандра]
54
+ test: [дюма, тома, дега, люка, ферма, гамарра, петипа, шандра, скаля]
34
55
  mods: [., ., ., ., .]
35
56
 
36
57
  - gender: androgynous
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.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-07-08 00:00:00.000000000 Z
13
+ date: 2013-09-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: unicode_utils