name_of_person 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b03a84a4207dc1c212ede9e467ece80d14b4d73b889ad93c9665e177c1a744c0
4
+ data.tar.gz: 8e8420b6460848be39581db10a64b8b2a7dfa4504ad5b355cfae98cfda5366d8
5
+ SHA512:
6
+ metadata.gz: 3f0be6604bb1a94775c009cbba1bb34d6369747cdd9466b35ae24385da15bbbcf4f00605913456b9360464dc26ffd8779183c1506567dc8a9eb3ee7becf28060
7
+ data.tar.gz: e31dc4fb63ae0dc3387d343842b6e5aae1348863fc6b6c82ce8770247157ef95ff47bed6dfca50c6d6d32d7f80666f7014108560fc687c09d063451002de36e6
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'byebug'
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ name_of_person (0.1.0)
5
+ activesupport (>= 5.2.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (5.2.1)
11
+ activesupport (= 5.2.1)
12
+ activesupport (5.2.1)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ byebug (10.0.2)
18
+ concurrent-ruby (1.0.5)
19
+ i18n (1.1.0)
20
+ concurrent-ruby (~> 1.0)
21
+ minitest (5.11.3)
22
+ rake (12.3.1)
23
+ thread_safe (0.3.6)
24
+ tzinfo (1.2.5)
25
+ thread_safe (~> 0.1)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ activemodel (>= 5.2.0)
32
+ bundler (~> 1.15)
33
+ byebug
34
+ name_of_person!
35
+ rake
36
+
37
+ BUNDLED WITH
38
+ 1.16.3
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2018 Basecamp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Name of Person
2
+
3
+ Presenting names for English-language applications where a basic model of first and last name(s) combined is sufficient. This approach is not meant to cover all possible naming cases, deal with other languages, or even titulations. Just the basics.
4
+
5
+ ## Examples
6
+
7
+ ```ruby
8
+ # Relies on Person having a schema with first_name and last_name columns.
9
+ class Person < ApplicationRecord
10
+ has_person_name
11
+ end
12
+
13
+ # Saves a new record using { first_name: "David", last_name: "Heinemeier Hansson" }
14
+ person = Person.create! name: "David Heinemeier Hansson"
15
+
16
+ person.name.full # => "David Heinemeier Hansson"
17
+ person.name.first # => "David"
18
+ person.name.last # => "Heinemeier Hansson"
19
+ person.name.initials # => "DHH"
20
+ person.name.familiar # => "David H."
21
+ person.name.abbreviated # => "D. Heinemeier Hansson"
22
+ person.name.sorted # => "Heinemeier Hansson, David"
23
+ person.name.mentionable # => "davidh"
24
+ person.name.possessive # => "David Heinemeier Hansson's"
25
+
26
+ # Use directly
27
+ name = NameOfPerson::PersonName.full("David Heinemeier Hansson")
28
+ name.first # => "David"
29
+ ```
30
+
31
+ ## Maintenance Expectations
32
+
33
+ This library is an extraction from Basecamp that's been sufficient in almost unaltered form for over 10 years. While contributions are always welcome, do not expect a lot of feature evolution beyond the basics. Feel free to fork this library if you'd like to add large upgrades like titulations or different accomodations for other languages.
34
+
35
+ ## License
36
+
37
+ Name of Person is released under the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,11 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |test|
6
+ test.libs << "test"
7
+ test.test_files = FileList["test/*_test.rb"]
8
+ test.warning = false
9
+ end
10
+
11
+ task default: :test
@@ -0,0 +1,5 @@
1
+ module NameOfPerson
2
+ end
3
+
4
+ require 'name_of_person/loaders/active_model_has_person_name'
5
+ require 'name_of_person/loaders/active_record_has_person_name'
@@ -0,0 +1,16 @@
1
+ require 'name_of_person/person_name'
2
+
3
+ module NameOfPerson
4
+ module AssignableName
5
+ # Assigns first_name and last_name attributes as extracted from a supplied full name.
6
+ def name=(name)
7
+ full_name = NameOfPerson::PersonName.full(name)
8
+ self.first_name, self.last_name = full_name.try(:first), full_name.try(:last)
9
+ end
10
+
11
+ # Returns a PersonName object created from the first_name and last_name attributes.
12
+ def name
13
+ NameOfPerson::PersonName.new(first_name, last_name) if first_name
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require 'name_of_person/assignable_name'
2
+
3
+ module NameOfPerson
4
+ module HasPersonName
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ # Defines the instance methods name/name= from AssignableName.
9
+ def has_person_name
10
+ include NameOfPerson::AssignableName
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ begin
2
+ require 'active_model'
3
+ require 'name_of_person/has_person_name'
4
+
5
+ require 'active_model/model'
6
+ ActiveModel::Model.send :include, NameOfPerson::HasPersonName
7
+ rescue LoadError
8
+ # Active Model won't be auto-configured with has_person_name
9
+ end
@@ -0,0 +1,8 @@
1
+ begin
2
+ require 'active_record'
3
+ require 'name_of_person/has_person_name'
4
+
5
+ ActiveRecord::Base.send :include, NameOfPerson::HasPersonName
6
+ rescue LoadError
7
+ # Active Record won't be auto-configured with has_person_name
8
+ end
@@ -0,0 +1,58 @@
1
+ require 'active_support/core_ext/string'
2
+
3
+ module NameOfPerson
4
+ class PersonName < String
5
+ attr_reader :first, :last
6
+
7
+ def self.full(full_name)
8
+ first, last = full_name.to_s.strip.split(/\s+/, 2)
9
+ new(first, last) if first.present?
10
+ end
11
+
12
+ def initialize(first, last = nil)
13
+ raise ArgumentError, "First name is required" unless first.present?
14
+ @first, @last = first, last
15
+ super full
16
+ end
17
+
18
+ # Returns first + last, such as "Jason Fried".
19
+ def full
20
+ @full ||= last.present? ? "#{first} #{last}" : first
21
+ end
22
+
23
+ # Returns first + last initial, such as "Jason F.".
24
+ def familiar
25
+ @familiar ||= last.present? ? "#{first} #{last.first}." : first
26
+ end
27
+
28
+ # Returns first initial + last, such as "J. Fried".
29
+ def abbreviated
30
+ @abbreviated ||= last.present? ? "#{first.first}. #{last}" : first
31
+ end
32
+
33
+ # Returns last + first for sorting.
34
+ def sorted
35
+ @sorted ||= last.present? ? "#{last}, #{first}" : first
36
+ end
37
+
38
+ # Returns full name with with trailing 's or ' if name ends in s.
39
+ def possessive
40
+ @possessive ||= "#{self}'#{"s" unless end_with?("s")}"
41
+ end
42
+
43
+ # Returns just the initials.
44
+ def initials
45
+ @initials ||= scan(/\b(\S)\S*/).join
46
+ end
47
+
48
+ # Returns a mentionable version of the familiar name
49
+ def mentionable
50
+ @mentionable ||= familiar.chop.delete(' ').downcase
51
+ end
52
+
53
+ # Override to_yaml to serialize as a plain string.
54
+ def encode_with(coder)
55
+ coder.represent_scalar nil, to_s
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'name_of_person'
3
+ s.version = '1.0.0'
4
+ s.authors = 'David Heinemeier Hansson'
5
+ s.email = 'david@basecamp.com'
6
+ s.summary = 'Presenting names of people in full, familiar, abbreviated, and initialized forms (but without titulation etc)'
7
+ s.homepage = 'https://github.com/basecamp/name_of_person'
8
+ s.license = 'MIT'
9
+
10
+ s.required_ruby_version = '>= 2.4.0'
11
+
12
+ s.add_dependency 'activesupport', '>= 5.2.0'
13
+
14
+ s.add_development_dependency 'activemodel', '>= 5.2.0'
15
+ s.add_development_dependency 'bundler', '~> 1.15'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- test/*`.split("\n")
19
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_support'
2
+ require 'active_support/testing/autorun'
3
+
4
+ require 'name_of_person/assignable_name'
5
+
6
+ class StructPerson < Struct.new(:first_name, :last_name)
7
+ include NameOfPerson::AssignableName
8
+ end
9
+
10
+ class AssignableNameTest < ActiveSupport::TestCase
11
+ include NameOfPerson
12
+
13
+ setup do
14
+ @person = StructPerson.new("David", "Heinemeier Hansson")
15
+ end
16
+
17
+ test "reading name" do
18
+ assert_equal PersonName.new("David", "Heinemeier Hansson"), @person.name
19
+ end
20
+
21
+ test "writing name" do
22
+ @person.name = PersonName.new("Jason", "Fried")
23
+ assert_equal PersonName.new("Jason", "Fried"), @person.name
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require 'active_support'
2
+ require 'active_support/testing/autorun'
3
+
4
+ require 'name_of_person/loaders/active_model_has_person_name'
5
+
6
+ class ModelPerson
7
+ include ActiveModel::Model
8
+ attr_accessor :first_name, :last_name
9
+
10
+ has_person_name
11
+ end
12
+
13
+ class HasPersonNameTest < ActiveSupport::TestCase
14
+ include NameOfPerson
15
+
16
+ setup do
17
+ @person = ModelPerson.new(first_name: "David", last_name: "Heinemeier Hansson")
18
+ end
19
+
20
+ test "reading name" do
21
+ assert_equal PersonName.new("David", "Heinemeier Hansson"), @person.name
22
+ end
23
+
24
+ test "writing name" do
25
+ @person.name = PersonName.new("Jason", "Fried")
26
+ assert_equal PersonName.new("Jason", "Fried"), @person.name
27
+ end
28
+ end
@@ -0,0 +1,104 @@
1
+ require 'active_support'
2
+ require 'active_support/testing/autorun'
3
+ require 'yaml'
4
+
5
+ require 'name_of_person/person_name'
6
+
7
+ class PersonNameTest < ActiveSupport::TestCase
8
+ include NameOfPerson
9
+
10
+ setup do
11
+ @name = PersonName.new('Foo', 'Bar')
12
+ @first = PersonName.new('Baz')
13
+ end
14
+
15
+ test "first is required" do
16
+ assert_raise ArgumentError do
17
+ PersonName.new(nil)
18
+ end
19
+ end
20
+
21
+ test "last may be omitted" do
22
+ assert_nothing_raised do
23
+ PersonName.new('Foo', nil)
24
+ end
25
+ end
26
+
27
+ test "first and last" do
28
+ assert_equal 'Foo', @name.first
29
+ assert_equal 'Bar', @name.last
30
+
31
+ assert_equal 'Baz', @first.first
32
+ assert_nil @first.last
33
+ end
34
+
35
+ test "full name" do
36
+ assert_equal 'Foo Bar', @name.full
37
+ assert_equal 'Foo Bar', @name
38
+
39
+ assert_equal @first.first, @first.full
40
+ assert_equal @first.first, @first
41
+ end
42
+
43
+ test "abbreviations" do
44
+ assert_equal 'Foo B.', @name.familiar
45
+ assert_equal 'F. Bar', @name.abbreviated
46
+ assert_equal 'Bar, Foo', @name.sorted
47
+
48
+ assert_equal @first.first, @first.familiar
49
+ assert_equal @first.first, @first.abbreviated
50
+ assert_equal @first.first, @first.sorted
51
+ end
52
+
53
+ test "possessive" do
54
+ assert_equal "#{@name.full}'s", @name.possessive
55
+ assert_equal "#{@first.full}'s", @first.possessive
56
+ assert_equal "Foo Bars'", PersonName.new('Foo', 'Bars').possessive
57
+ end
58
+
59
+ test "from full name" do
60
+ name = PersonName.full('Will St. Clair')
61
+ assert_equal 'Will', name.first
62
+ assert_equal 'St. Clair', name.last
63
+
64
+ first = PersonName.full('Will')
65
+ assert_equal 'Will', first.first
66
+ assert_nil first.last
67
+
68
+ assert_nil PersonName.full(nil)
69
+ assert_nil PersonName.full('')
70
+ end
71
+
72
+ test "blank last name behaves the same as nil" do
73
+ name = PersonName.new('Baz', '')
74
+ assert_equal @first.full, name.full
75
+ assert_equal @first.familiar, name.familiar
76
+ assert_equal @first.abbreviated, name.abbreviated
77
+ assert_equal @first.sorted, name.sorted
78
+ assert_equal @first.possessive, name.possessive
79
+ end
80
+
81
+ test "mentionable" do
82
+ assert_equal 'foob', @name.mentionable
83
+ end
84
+
85
+ test "mentionable with three names" do
86
+ name = PersonName.full('Will St. Clair')
87
+ assert_equal 'wills', name.mentionable
88
+ end
89
+
90
+ test "familiar" do
91
+ assert_equal 'Foo B.', @name.familiar
92
+ end
93
+
94
+ test "familiar with three names" do
95
+ name = PersonName.full('Will St. Clair')
96
+ assert_equal 'Will S.', name.familiar
97
+ end
98
+
99
+ test "serialize yaml" do
100
+ assert_equal @name, YAML.load(YAML.dump(@name))
101
+ assert_equal @first, YAML.load(YAML.dump(@first))
102
+ assert_equal String, YAML.load(YAML.dump(@name)).class
103
+ end
104
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: name_of_person
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - David Heinemeier Hansson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activemodel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 5.2.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 5.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.15'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.15'
55
+ description:
56
+ email: david@basecamp.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - Gemfile
62
+ - Gemfile.lock
63
+ - MIT-LICENSE
64
+ - README.md
65
+ - Rakefile
66
+ - lib/name_of_person.rb
67
+ - lib/name_of_person/assignable_name.rb
68
+ - lib/name_of_person/has_person_name.rb
69
+ - lib/name_of_person/loaders/active_model_has_person_name.rb
70
+ - lib/name_of_person/loaders/active_record_has_person_name.rb
71
+ - lib/name_of_person/person_name.rb
72
+ - name_of_person.gemspec
73
+ - test/assignable_name_test.rb
74
+ - test/has_person_name_test.rb
75
+ - test/person_name_test.rb
76
+ homepage: https://github.com/basecamp/name_of_person
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 2.4.0
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.7.6
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Presenting names of people in full, familiar, abbreviated, and initialized
100
+ forms (but without titulation etc)
101
+ test_files:
102
+ - test/assignable_name_test.rb
103
+ - test/has_person_name_test.rb
104
+ - test/person_name_test.rb