name-tamer 0.2.14 → 0.2.15

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: 5db87ceb78cbdd1c4f46a674c0514430799dac05
4
- data.tar.gz: 9269bd1897a577ec14ba3b8f792060ef27b5ccc0
3
+ metadata.gz: 8dece3dacb2fbd41e1c31931973b534a36868789
4
+ data.tar.gz: bc65a4149e6d28126cab01d3093bbfdb9670e46e
5
5
  SHA512:
6
- metadata.gz: 76db6dcdfb49fcd3cc449df4bcad7cfecef6a2bae6753b9799794866938e7ac571c9f3b7a424e3c119828814fca3a759296f9af4a51c2c12482801066920a440
7
- data.tar.gz: 2523f85e57a9fb721cb91f099a4a844fddd3be306b911ae7799f96bd42cd5bad99b582de0967a74d04feaa1fe66b014e3137a98b504103231eac3d33d1e44ff0
6
+ metadata.gz: 951fce5bc9a6493145c24ebb8cc08791b5941334f5c38ea649d4e12d5f5e0ba3ec849486a40ef2916d7eb3f7d3da77215df38008f490b9627620e3f8a6408caa
7
+ data.tar.gz: 7297d3ac2c61e7907d117d0ed6d0548c26f283cb08029cf4bd5bcd9c6cee274c4e7b71cb6ba4994dc1a8b3d776fc2df4c27f44161f10a0ec3c161a3ed318f34d
data/.hound.yml CHANGED
@@ -1,27 +1,6 @@
1
- LineLength:
2
- Description: 'Limit lines to 120 characters.'
3
- Max: 120
4
- Enabled: true
5
-
6
- MethodLength:
7
- Description: 'Avoid methods longer than 10 lines of code.'
8
- Max: 23
9
- Enabled: true
10
-
11
- Documentation:
12
- Description: 'Document classes and non-namespace modules.'
13
- Enabled: false
14
-
15
- FileName:
16
- Description: 'Use snake_case for source file names.'
17
- Enabled: false
18
-
19
- DotPosition:
20
- Description: 'Checks the position of the dot in multi-line method calls.'
21
- EnforcedStyle: leading
22
- # EnforcedStyle: trailing
23
- Enabled: true
24
-
25
- StringLiterals:
26
- EnforcedStyle: single_quotes
27
- Enabled: true
1
+ ---
2
+ ruby:
3
+ enabled: true
4
+ config_file: .rubocop.yml
5
+ coffee_script:
6
+ enabled: true
@@ -1,4 +1,30 @@
1
- inherit_from: .hound.yml
1
+ LineLength:
2
+ Description: 'Limit lines to 120 characters.'
3
+ Max: 120
4
+ Enabled: true
5
+
6
+ MethodLength:
7
+ Description: 'Avoid methods longer than 10 lines of code.'
8
+ Max: 23
9
+ Enabled: true
10
+
11
+ Documentation:
12
+ Description: 'Document classes and non-namespace modules.'
13
+ Enabled: false
14
+
15
+ FileName:
16
+ Description: 'Use snake_case for source file names.'
17
+ Enabled: false
18
+
19
+ DotPosition:
20
+ Description: 'Checks the position of the dot in multi-line method calls.'
21
+ EnforcedStyle: leading
22
+ # EnforcedStyle: trailing
23
+ Enabled: true
24
+
25
+ StringLiterals:
26
+ EnforcedStyle: single_quotes
27
+ Enabled: true
2
28
 
3
29
  CyclomaticComplexity:
4
30
  Description: 'Avoid complex methods.'
@@ -7,4 +33,4 @@ CyclomaticComplexity:
7
33
  ClassLength:
8
34
  Description: 'Avoid classes longer than 100 lines of code.'
9
35
  CountComments: false # count full line comments?
10
- Max: 321
36
+ Max: 331
@@ -1 +1 @@
1
- 2.1.2
1
+ 2.2.0
@@ -1,25 +1,34 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- name-tamer (0.2.13)
4
+ name-tamer (0.2.14)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- coveralls (0.7.2)
10
- multi_json (~> 1.3)
11
- rest-client (= 1.6.7)
12
- simplecov (>= 0.7)
13
- term-ansicolor (= 1.2.2)
14
- thor (= 0.18.1)
9
+ ast (2.0.0)
10
+ astrolabe (1.3.0)
11
+ parser (>= 2.2.0.pre.3, < 3.0)
12
+ coveralls (0.7.11)
13
+ multi_json (~> 1.10)
14
+ rest-client (>= 1.6.8, < 2)
15
+ simplecov (~> 0.9.1)
16
+ term-ansicolor (~> 1.3)
17
+ thor (~> 0.19.1)
15
18
  diff-lcs (1.2.5)
16
19
  docile (1.1.5)
17
20
  gem-release (0.7.3)
18
21
  mime-types (2.4.3)
19
22
  multi_json (1.10.1)
23
+ netrc (0.10.3)
24
+ parser (2.2.0.3)
25
+ ast (>= 1.1, < 3.0)
26
+ powerpack (0.1.0)
27
+ rainbow (2.0.0)
20
28
  rake (10.4.2)
21
- rest-client (1.6.7)
22
- mime-types (>= 1.16)
29
+ rest-client (1.7.3)
30
+ mime-types (>= 1.16, < 3.0)
31
+ netrc (~> 0.7)
23
32
  rspec (2.99.0)
24
33
  rspec-core (~> 2.99.0)
25
34
  rspec-expectations (~> 2.99.0)
@@ -27,16 +36,23 @@ GEM
27
36
  rspec-core (2.99.2)
28
37
  rspec-expectations (2.99.2)
29
38
  diff-lcs (>= 1.1.3, < 2.0)
30
- rspec-mocks (2.99.2)
31
- simplecov (0.9.1)
39
+ rspec-mocks (2.99.3)
40
+ rubocop (0.29.1)
41
+ astrolabe (~> 1.3)
42
+ parser (>= 2.2.0.1, < 3.0)
43
+ powerpack (~> 0.1)
44
+ rainbow (>= 1.99.1, < 3.0)
45
+ ruby-progressbar (~> 1.4)
46
+ ruby-progressbar (1.7.1)
47
+ simplecov (0.9.2)
32
48
  docile (~> 1.1.0)
33
49
  multi_json (~> 1.0)
34
- simplecov-html (~> 0.8.0)
35
- simplecov-html (0.8.0)
36
- term-ansicolor (1.2.2)
37
- tins (~> 0.8)
38
- thor (0.18.1)
39
- tins (0.13.2)
50
+ simplecov-html (~> 0.9.0)
51
+ simplecov-html (0.9.0)
52
+ term-ansicolor (1.3.0)
53
+ tins (~> 1.0)
54
+ thor (0.19.1)
55
+ tins (1.3.4)
40
56
 
41
57
  PLATFORMS
42
58
  ruby
@@ -48,4 +64,5 @@ DEPENDENCIES
48
64
  name-tamer!
49
65
  rake (~> 10)
50
66
  rspec (~> 2)
67
+ rubocop (~> 0)
51
68
  simplecov (~> 0.7, >= 0.7.1)
@@ -1,31 +1,31 @@
1
- # encoding: utf-8
2
- require 'csv'
3
-
4
- desc 'Build prefixes and suffixes'
5
- task :adfixes do
6
- pp = []
7
- po = []
8
- sp = []
9
- so = []
10
-
11
- CSV.foreach("#{File.dirname(__FILE__)}/prefixes.csv", headers:true) do |row|
12
- if row[2] == 'person'
13
- pp << row[0]
14
- else
15
- po << row[0]
16
- end
17
- end
18
-
19
- CSV.foreach("#{File.dirname(__FILE__)}/suffixes.csv", headers:true) do |row|
20
- if row[2] == 'person'
21
- sp << row[0]
22
- else
23
- so << row[0]
24
- end
25
- end
26
-
27
- puts "'" + pp.join("', '") + "'"
28
- puts "'" + po.join("', '") + "'"
29
- puts "'" + sp.join("', '") + "'"
30
- puts "'" + so.join("', '") + "'"
31
- end
1
+ # encoding: utf-8
2
+ require 'csv'
3
+
4
+ desc 'Build prefixes and suffixes'
5
+ task :adfixes do
6
+ pp = []
7
+ po = []
8
+ sp = []
9
+ so = []
10
+
11
+ CSV.foreach("#{File.dirname(__FILE__)}/prefixes.csv", headers: true) do |row|
12
+ if row[2] == 'person'
13
+ pp << row[0]
14
+ else
15
+ po << row[0]
16
+ end
17
+ end
18
+
19
+ CSV.foreach("#{File.dirname(__FILE__)}/suffixes.csv", headers: true) do |row|
20
+ if row[2] == 'person'
21
+ sp << row[0]
22
+ else
23
+ so << row[0]
24
+ end
25
+ end
26
+
27
+ puts "'" + pp.join("', '") + "'"
28
+ puts "'" + po.join("', '") + "'"
29
+ puts "'" + sp.join("', '") + "'"
30
+ puts "'" + so.join("', '") + "'"
31
+ end
@@ -211,24 +211,32 @@ class NameTamer
211
211
  def name_wrangle
212
212
  # Fix case if all caps or all lowercase
213
213
  if @last_name.nil?
214
- lowercase = @nice_name.downcase
215
- uppercase = @nice_name.upcase
216
- fix_case = false
217
-
218
- if @contact_type == :organization
219
- fix_case = true if @nice_name == uppercase && @nice_name.length > 4
220
- else
221
- fix_case = true if [uppercase, lowercase].include?(@nice_name)
222
- end
214
+ name_wrangle_single_name
215
+ else
216
+ name_wrangle_split_name
217
+ end
218
+ end
219
+
220
+ def name_wrangle_single_name
221
+ lowercase = @nice_name.downcase
222
+ uppercase = @nice_name.upcase
223
+ fix_case = false
223
224
 
224
- @nice_name = name_case(lowercase) if fix_case
225
+ if @contact_type == :organization
226
+ fix_case = true if @nice_name == uppercase && @nice_name.length > 4
225
227
  else
226
- # It's a person if we've split the name, so no organization logic here
227
- lowercase = @last_name.downcase
228
- uppercase = @last_name.upcase
229
- @last_name = name_case(lowercase) if [uppercase, lowercase].include?(@last_name)
230
- @nice_name = "#{@remainder} #{@last_name}"
228
+ fix_case = true if [uppercase, lowercase].include?(@nice_name)
231
229
  end
230
+
231
+ @nice_name = name_case(lowercase) if fix_case
232
+ end
233
+
234
+ def name_wrangle_split_name
235
+ # It's a person if we've split the name, so no organization logic here
236
+ lowercase = @last_name.downcase
237
+ uppercase = @last_name.upcase
238
+ @last_name = name_case(lowercase) if [uppercase, lowercase].include?(@last_name)
239
+ @nice_name = "#{@remainder} #{@last_name}"
232
240
  end
233
241
 
234
242
  # Conjoin compound names with non-breaking spaces
@@ -256,30 +264,38 @@ class NameTamer
256
264
  def remove_middle_names
257
265
  return unless @contact_type == :person
258
266
 
259
- parts = @simple_name.split
260
- first_name = nil
261
- last_name = nil
267
+ first_name, parts = find_first_usable_name(@simple_name.split)
268
+ last_name, _ = find_last_usable_name(parts)
269
+
270
+ return unless first_name || last_name
271
+
272
+ separator = first_name && last_name ? ' ' : ''
273
+ @simple_name = "#{first_name}#{separator}#{last_name}"
274
+ end
275
+
276
+ def find_first_usable_name(parts)
277
+ part = nil
262
278
 
263
- # Find first usable name
264
279
  parts.each_index do |i|
265
280
  part = parts[i]
266
281
  next if part.gsub(FILTER_COMPAT, '').empty?
267
- first_name = part
268
- parts = parts.slice(i + 1, parts.length) # don't use "slice!"
282
+ parts = parts.slice(i + 1, parts.length) # don't use "slice!"
269
283
  break
270
284
  end
271
285
 
272
- # Find last usable name
273
- parts.reverse_each do |part|
274
- next if part.gsub(FILTER_COMPAT, '').empty?
275
- last_name = part
286
+ [part, parts]
287
+ end
288
+
289
+ def find_last_usable_name(parts)
290
+ part = nil
291
+
292
+ parts.reverse_each do |p|
293
+ next if p.gsub(FILTER_COMPAT, '').empty?
294
+ part = p
276
295
  break
277
296
  end
278
297
 
279
- return unless first_name || last_name
280
-
281
- separator = first_name && last_name ? ' ' : ''
282
- @simple_name = "#{first_name}#{separator}#{last_name}"
298
+ part
283
299
  end
284
300
 
285
301
  def remove_periods_from_initials
@@ -299,17 +315,8 @@ class NameTamer
299
315
  #--------------------------------------------------------
300
316
 
301
317
  def initialize(new_name, args = {})
302
- @name = new_name || ''
303
- args_ct = args[:contact_type]
304
-
305
- if args_ct
306
- ct = args_ct.is_a?(Symbol) ? args_ct : args_ct.dup
307
- ct = ct.to_s unless [String, Symbol].include? ct.class
308
- ct.downcase! if ct.class == String
309
- ct = ct.to_sym
310
- ct = nil unless [:person, :organization].include? ct
311
- @contact_type = ct
312
- end
318
+ @name = new_name || ''
319
+ @contact_type = contact_type_from args
313
320
 
314
321
  @tidy_name = nil
315
322
  @nice_name = nil
@@ -322,6 +329,19 @@ class NameTamer
322
329
  @adfix_found = false
323
330
  end
324
331
 
332
+ def contact_type_from(args)
333
+ args_ct = args[:contact_type]
334
+ return unless args_ct
335
+
336
+ ct = args_ct.is_a?(Symbol) ? args_ct : args_ct.dup
337
+ ct = ct.to_s unless [String, Symbol].include? ct.class
338
+ ct.downcase! if ct.class == String
339
+ ct = ct.to_sym
340
+ ct = nil unless [:person, :organization].include? ct
341
+
342
+ ct
343
+ end
344
+
325
345
  # If we don't know the contact type, what's our best guess?
326
346
  def contact_type_best_effort
327
347
  if @contact_type
@@ -335,34 +355,33 @@ class NameTamer
335
355
 
336
356
  # We pass to this routine either prefixes or suffixes
337
357
  def remove_outermost_adfix(adfix_type, name_part)
338
- adfixes = ADFIX_PATTERNS[adfix_type]
358
+ ct, parts = find_contact_type_and_parts(ADFIX_PATTERNS[adfix_type], name_part)
359
+
360
+ return name_part unless @adfix_found
361
+
362
+ # If we've found a diagnostic adfix then set the contact type
363
+ self.contact_type = ct
364
+
365
+ # The remainder of the name will be in parts[0] or parts[2] depending
366
+ # on whether this is a prefix or a suffix.
367
+ # We'll also remove any trailing commas we've exposed.
368
+ (parts[0] + parts[2]).gsub(/\s*,\s*$/, '')
369
+ end
370
+
371
+ def find_contact_type_and_parts(adfixes, name_part)
339
372
  ct = contact_type_best_effort
340
373
  parts = name_part.partition adfixes[ct]
341
374
  @adfix_found = !parts[1].empty?
342
375
 
376
+ return [ct, parts] if @contact_type || @adfix_found
377
+
343
378
  # If the contact type is indeterminate and we didn't find a diagnostic adfix
344
379
  # for a person then try again for an organization
345
- if @contact_type.nil?
346
- unless @adfix_found
347
- ct = :organization
348
- parts = name_part.partition adfixes[ct]
349
- @adfix_found = !parts[1].empty?
350
- end
351
- end
352
-
353
- if @adfix_found
354
- # If we've found a diagnostic adfix then set the contact type
355
- self.contact_type = ct
356
-
357
- # The remainder of the name will be in parts[0] or parts[2] depending
358
- # on whether this is a prefix or a suffix.
359
- # We'll also remove any trailing commas we've exposed.
360
- result = (parts[0] + parts[2]).gsub(/\s*,\s*$/, '')
361
- else
362
- result = name_part
363
- end
380
+ ct = :organization
381
+ parts = name_part.partition adfixes[ct]
382
+ @adfix_found = !parts[1].empty?
364
383
 
365
- result
384
+ [ct, parts]
366
385
  end
367
386
 
368
387
  # Original Version of NameCase:
@@ -1,3 +1,3 @@
1
1
  class NameTamer
2
- VERSION = '0.2.14'
2
+ VERSION = '0.2.15'
3
3
  end
@@ -107,6 +107,11 @@ class String
107
107
  end
108
108
  end
109
109
 
110
+ fix_apostrophe_modifiers!
111
+ self # Allows chaining
112
+ end
113
+
114
+ def fix_apostrophe_modifiers!
110
115
  %w(Dell D).each do |modifier|
111
116
  gsub!(/(.#{modifier}')(\w)/) { |_| "#{Regexp.last_match[1].rstrip.downcase}#{Regexp.last_match[2]}" }
112
117
  end
@@ -229,10 +234,10 @@ class String
229
234
  '¼' => '¼', '½' => '½', '¾' => '¾', '¿' => '¿', 'À' => 'À',
230
235
  'Ã�' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Ã…' => 'Å',
231
236
  'Æ' => 'Æ', 'Ç' => 'Ç', 'È' => 'È', 'É' => 'É', 'Ê' => 'Ê',
232
- 'Ë' => 'Ë', 'ÃŒ' => 'Ì', 'Ã�' => 'Í', 'ÃŽ' => 'Î', 'Ã�' => 'Ï',
233
- 'Ã�' => 'Ð', 'Ñ' => 'Ñ', 'Ã’' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô',
237
+ 'Ë' => 'Ë', 'ÃŒ' => 'Ì', "\xC3\x8D" => 'Í', 'ÃŽ' => 'Î', "\xC3\x8F" => 'Ï',
238
+ "\xC3\x90" => 'Ð', 'Ñ' => 'Ñ', 'Ã’' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô',
234
239
  'Õ' => 'Õ', 'Ö' => 'Ö', '×' => '×', 'Ø' => 'Ø', 'Ù' => 'Ù',
235
- 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ã�' => 'Ý', 'Þ' => 'Þ',
240
+ 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', "\xC3\x9D" => 'Ý', 'Þ' => 'Þ',
236
241
  'ß' => 'ß', 'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã',
237
242
  'ä' => 'ä', 'Ã¥' => 'å', 'æ' => 'æ', 'ç' => 'ç', 'è' => 'è',
238
243
  'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í',
@@ -7,8 +7,8 @@ Gem::Specification.new do |spec|
7
7
  spec.version = NameTamer::VERSION
8
8
  spec.authors = ['Xenapto']
9
9
  spec.email = ['developers@xenapto.com']
10
- spec.description = %q(Useful methods for taming names)
11
- spec.summary = %q(Example: NameTamer['Mr. John Q. Smith III, MD'].simple_name # => John Smith)
10
+ spec.description = 'Useful methods for taming names'
11
+ spec.summary = "Example: NameTamer['Mr. John Q. Smith III, MD'].simple_name # => John Smith"
12
12
  spec.homepage = 'https://github.com/Xenapto/name-tamer'
13
13
  spec.license = 'MIT'
14
14
 
@@ -23,4 +23,5 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency 'gem-release', '~> 0'
24
24
  spec.add_development_dependency 'simplecov', '~> 0.7', '>= 0.7.1' # https://github.com/colszowka/simplecov/issues/281
25
25
  spec.add_development_dependency 'coveralls', '~> 0'
26
+ spec.add_development_dependency 'rubocop', '~> 0'
26
27
  end
@@ -161,7 +161,7 @@ describe NameTamer do
161
161
  { n: 'Hermann Müller', t: :person, nn: 'Hermann Müller', sn: 'Hermann Müller', s: 'hermann-muller' },
162
162
  { n: 'b-to-v Partners AG', t: :organization, nn: 'b-to-v Partners', sn: 'b-to-v Partners', s: 'b-to-v-partners' },
163
163
  { n: '*', t: :person, nn: '*', sn: '*', s: '_' },
164
- { n: '* *', t: :person, nn: '* *', sn: '* *', s: '_' },
164
+ { n: '* *', t: :person, nn: '* *', sn: '*', s: '_' },
165
165
  { n: '* Olga *', t: :person, nn: '* Olga *', sn: 'Olga', s: 'olga' },
166
166
  { n: '* Olga Bedia García *', t: :person, nn: '* Olga Bedia García *', sn: 'Olga García', s: 'olga-garcia' },
167
167
  { n: 'Jose “Pepe” García', t: :organization, nn: 'Jose “Pepe” García', sn: 'Jose Pepe García',
@@ -190,9 +190,9 @@ describe NameTamer do
190
190
  { n: '’%80', t: :person, nn: '’%80', sn: '’%80', s: '’80' }, # Encoding::CompatibilityError
191
191
  { n: "John Smith\u{FEFF}\u{200B}\u{200C}\u{200D}\u{2063}", t: :person,
192
192
  nn: 'John Smith', sn: 'John Smith', s: 'john-smith' }, # Zero-width characters
193
- { n: 'Herman Melville ,CLP', t: :person, nn:'Herman Melville', sn:'Herman Melville', s:'herman-melville'},
194
- { n: 'Melville ,Herman', t: :person, nn:'Herman Melville', sn:'Herman Melville', s:'herman-melville'},
195
- { n: "John\x00 Smith", t: :person, nn: 'John Smith', sn: 'John Smith', s: 'john-smith'}
193
+ { n: 'Herman Melville ,CLP', t: :person, nn: 'Herman Melville', sn: 'Herman Melville', s: 'herman-melville' },
194
+ { n: 'Melville ,Herman', t: :person, nn: 'Herman Melville', sn: 'Herman Melville', s: 'herman-melville' },
195
+ { n: "John\x00 Smith", t: :person, nn: 'John Smith', sn: 'John Smith', s: 'john-smith' }
196
196
  ]
197
197
  end
198
198
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: name-tamer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.14
4
+ version: 0.2.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xenapto
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-10 00:00:00.000000000 Z
11
+ date: 2015-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
103
117
  description: Useful methods for taming names
104
118
  email:
105
119
  - developers@xenapto.com
@@ -146,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
160
  version: '0'
147
161
  requirements: []
148
162
  rubyforge_project:
149
- rubygems_version: 2.2.2
163
+ rubygems_version: 2.4.5
150
164
  signing_key:
151
165
  specification_version: 4
152
166
  summary: 'Example: NameTamer[''Mr. John Q. Smith III, MD''].simple_name # => John