openopus-core-people 1.1.1 → 1.1.14

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
  SHA256:
3
- metadata.gz: 44368a166f11f7362f786285cb70c6ec83c75018b3ea1b81c3c81dc2e17d1a17
4
- data.tar.gz: 427ca23fabbcf709d3a16cff2a65af7ad68edbc099370320329f824eb69b54b9
3
+ metadata.gz: 2a1210e0ee30df4b15de2054b11faaa01c73aab0c2756acafbe6541bf2dc16b0
4
+ data.tar.gz: 39678b84725ad8c7b320690492a1d5e97e6902ebb05e1ab9160faf5ee90348ad
5
5
  SHA512:
6
- metadata.gz: 9c39bc6ee24bb7f41f9c9366f537d3fd809d981dffe43f831b0a6df0c67889dbd8fe5fc201ba55a5aa2e8e690099a076ce20719297f045cf3183af3e3b35eb36
7
- data.tar.gz: fdeafcfaa6b820b4b37969c512c7661ed409131dbc07b811c2e77da53ab075d6939a59ef2a778b33915a8456ff30d6e626d5c53fa7746a61b0f1cb1ab2615008
6
+ metadata.gz: 61d3d9c7542d3b535ddee482e36f76a88c6e6c763c1504893f083281053146fd6b6c3f2fb87cf8a404ccfca35fef5c305e3bfc2bdacb876af360c21d52d1a5da
7
+ data.tar.gz: e936e3bde4f9a3a742c11cec779ceb3f9aa4bc6c4405e8764a49b9046eaf5681d866f80d57be25fc69650ae0e471f2652e3c1dbe108e42489f3ca5c18c7fb4bc
data/README.md CHANGED
@@ -17,13 +17,14 @@ Install the gem, run migrations. This will create human structures, from Organi
17
17
  Add this line to your application's Gemfile:
18
18
 
19
19
  ```ruby
20
- gem 'openopus-core-people', git: "https://github.com/opuslogica/openopus-core-people"
20
+ gem 'openopus-core-people', git: "https://github.com/openopus/openopus-core-people"
21
21
  ```
22
22
 
23
23
  And then execute:
24
24
  ```bash
25
25
  $ bundle install
26
- $ bundle exec rake db:migrate
26
+ $ ./bin/rake openopus_core_people_engine:install:migrations
27
+ $ ./bin/rake db:migrate
27
28
  ```
28
29
 
29
30
  Or install it yourself as:
data/Rakefile CHANGED
@@ -15,12 +15,11 @@ RDoc::Task.new(:rdoc) do |rdoc|
15
15
  end
16
16
 
17
17
  APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
- load 'rails/tasks/engine.rake'
19
-
18
+ # load 'rails/tasks/engine.rake'
20
19
  load 'rails/tasks/statistics.rake'
20
+ load 'lib/tasks/openopus/core/people/tasks.rake'
21
21
 
22
22
  require 'bundler/gem_tasks'
23
-
24
23
  require 'rake/testtask'
25
24
 
26
25
  Rake::TestTask.new(:test) do |t|
@@ -1,6 +1,6 @@
1
1
  class Address < ApplicationRecord
2
- belongs_to :label, optional: true
3
- belongs_to :addressable, polymorphic: true
2
+ include Labelize
3
+ belongs_to :addressable, polymorphic: true, optional: true
4
4
 
5
5
  accepts_nested_attributes_for :label
6
6
 
@@ -0,0 +1,26 @@
1
+ # labelize.rb: -*- Ruby -*- DESCRIPTIVE TEXT.
2
+ #
3
+ # Copyright (c) 2019 Brian J. Fox Opus Logica, Inc.
4
+ # Author: Brian J. Fox (bfox@opuslogica.com)
5
+ # Birthdate: Wed Sep 11 09:37:52 2019.
6
+ module Labelize
7
+ extend ActiveSupport::Concern
8
+ included do
9
+ belongs_to :label, optional: true
10
+
11
+ def label
12
+ db_label = super
13
+ if not db_label
14
+ self.label = "Work"
15
+ db_label = super
16
+ end
17
+
18
+ db_label.value
19
+ end
20
+
21
+ def label=(name)
22
+ super(Label.get(name))
23
+ self.label
24
+ end
25
+ end
26
+ end
data/app/models/email.rb CHANGED
@@ -1,17 +1,27 @@
1
1
  class Email < ApplicationRecord
2
- belongs_to :label, optional: true
3
- belongs_to :emailable, polymorphic: true
2
+ include Labelize
3
+ belongs_to :emailable, polymorphic: true, optional: true
4
4
  accepts_nested_attributes_for :label
5
5
  before_create :default_label
6
+ before_save :canonicalize
7
+
8
+ VALID_EMAIL_FORMAT_REGEX = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
9
+
10
+ validates_format_of :address, with: VALID_EMAIL_FORMAT_REGEX, :on => :create
6
11
 
7
12
  def self.canonicalize(addr)
8
- addr.strip.downcase if not addr.blank?
13
+ candidate = addr.strip.downcase if not addr.blank?
14
+ candidate ||= addr
9
15
  end
10
16
 
11
17
  def canonicalize
12
18
  self.address = self.class.canonicalize(self.address)
13
19
  end
14
20
 
21
+ def self.valid_format?(addr)
22
+ return (addr =~ VALID_EMAIL_FORMAT_REGEX) != nil
23
+ end
24
+
15
25
  def default_label
16
26
  self.label = Label.get("Work")
17
27
  end
data/app/models/person.rb CHANGED
@@ -11,12 +11,25 @@ class Person < ApplicationRecord
11
11
  accepts_nested_attributes_for :emails
12
12
 
13
13
  def self.lookup(name)
14
+ return nil if not name
14
15
  person = self.find_by(self.name_components(name))
15
16
  person ||= self.includes(:nicknames).joins(:nicknames).find_by("nicknames.nickname" => name)
17
+
18
+ # Maybe we're looking up by phone number?
16
19
  if not person
17
- people = self.all.collect do |p|
18
- [p.id, [(p.fname[0] || ""), (p.minitial[0] || ""), (p.lname[0] || "")].join("").upcase]
19
- end
20
+ phone = Phone.where(number: Phone.canonicalize(name)).first rescue nil
21
+ person = phone.phoneable if phone
22
+ end
23
+
24
+ if not person
25
+ email = Email.where(emailable_type: self.name.to_s, address: Email.canonicalize(name.downcase)).first
26
+ person = email.emailable if email
27
+ end
28
+
29
+ if not person
30
+ # Try hard to find a person by their initials, even if there wasn't a nickname for them.
31
+ people = self.all.collect {|p| [p.id, p.initials]}
32
+
20
33
  people.each do |parry|
21
34
  if parry[1] == name.upcase
22
35
  person = self.find(parry[0])
@@ -25,11 +38,6 @@ class Person < ApplicationRecord
25
38
  end
26
39
  end
27
40
 
28
- if not person
29
- addr = Email.where(emailable_type: self.name.to_s, address: Email.canonicalize(name.downcase)).first
30
- person = addr.person.first if addr
31
- end
32
-
33
41
  person
34
42
  end
35
43
 
@@ -82,7 +90,7 @@ class Person < ApplicationRecord
82
90
  result = possibles.include?(text.gsub(/[.]*/, "").downcase) if text.present?
83
91
  end
84
92
 
85
- def self.name_components(name)
93
+ def self.name_components(name, transformer=nil)
86
94
  res = {}
87
95
  component = ""
88
96
  components = name.gsub(/,/, " ").gsub(/ /, " ").split(" ") rescue [name]
@@ -92,37 +100,56 @@ class Person < ApplicationRecord
92
100
  # What kind of thing is this?
93
101
  if is_name_prefix?(component)
94
102
  res[:prefix] = component
103
+ res[:prefix] = res[:prefix].send(transformer) if res[:prefix] && transformer
95
104
  res[:fname] = components.shift
96
105
  else
97
106
  res[:fname] = component
98
107
  end
99
108
 
109
+ res[:fname] = res[:fname].send(transformer) if res[:fname] && transformer
110
+
100
111
  # Next up, middle initial or last name.
101
112
  # If only one word remains, that's the last name
102
113
  if components.length == 1
103
114
  res[:lname] = components.shift
115
+ res[:lname] = res[:lname].send(transformer) if res[:lname] && transformer
104
116
  elsif components.length > 0
105
117
  # At least 2 words remain. We might have middle names, prefixes, suffixes, etc.
106
118
  components.reverse!
107
119
  component = components.shift
120
+
108
121
  if is_name_suffix?(component)
109
122
  res[:suffix] = component
123
+ res[:suffix] = res[:suffix].send(transformer) if res[:suffix] && transformer
110
124
  res[:lname] = components.shift
111
125
  else
112
126
  res[:lname] = component
113
127
  end
128
+
129
+ res[:lname] = res[:lname].send(transformer) if res[:lname] && transformer
130
+
114
131
  res[:minitial] = components.shift
132
+ res[:minitial] = res[:minitial].send(transformer) if res[:minitial] && transformer
133
+
115
134
  end
116
135
 
117
136
  res[:minitial] = res[:minitial].gsub(/[.]/, "") if res[:minitial].present?
118
137
  res
119
138
  end
120
-
139
+
140
+ def initials
141
+ res = ""
142
+ res += fname[0] if fname
143
+ res += minitial[0] if minitial
144
+ res += lname[0] if lname
145
+ res.upcase
146
+ end
147
+
121
148
  def name
122
149
  components = []
123
150
 
124
151
  if prefix.present?
125
- if not prefix.include?(".")
152
+ if not prefix.include?(".") and not prefix =~ /^miss$/i
126
153
  components << "#{prefix}."
127
154
  else
128
155
  components << prefix
@@ -231,8 +258,8 @@ class Person < ApplicationRecord
231
258
 
232
259
  def add_address(address_line, label="Home")
233
260
  if self != self.class.find_by_address(address_line)
234
- a = Address.parse(address_line);
235
- a.addressable_type = self.class.name;
261
+ a = Address.parse(address_line)
262
+ a.addressable_type = self.class.name
236
263
  a.label = Label.get(label)
237
264
  a.save
238
265
  addresses << a
@@ -240,5 +267,10 @@ class Person < ApplicationRecord
240
267
  a = Address.find_by_address(address_line)
241
268
  end
242
269
 
243
-
270
+ def as_api_json(options={})
271
+ candidate = self.as_json(options)
272
+ candidate[:emails] = self.emails.collect {|e| { label: e.label, address: e.address }}
273
+ candidate[:addresses] = self.addresses.collect {|a| { label: a.label, address: a.address }}
274
+ candidate
275
+ end
244
276
  end
data/app/models/phone.rb CHANGED
@@ -1,18 +1,23 @@
1
+ require "phony_rails"
2
+
1
3
  class Phone < ApplicationRecord
2
- belongs_to :label, optional: true
3
- belongs_to :phoneable, polymorphic: true
4
+ include Labelize
5
+ belongs_to :phoneable, polymorphic: true, optional: true
4
6
  before_validation :canonicalize
5
7
 
6
8
  def self.canonicalize(digits_and_stuff)
7
- canonical = digits_and_stuff
8
- canonical.gsub!(" ", "") #remove extra spaces
9
+ canonical = digits_and_stuff.gsub(" ", "")
9
10
  if canonical
10
11
  canonical = canonical[2..100].strip if canonical.starts_with?("+1")
11
- if canonical[0] != "+"
12
- digits = digits_and_stuff.gsub(/[^0-9]/, "")
13
- digits = digits[1..-1] if digits[0] == '1'
14
- digits = "805" + digits if digits.length == 7
15
- canonical = "(#{digits[0..2]}) #{digits[3..5]}-#{digits[6..10]}"
12
+ if self.const_defined?("PhonyRails")
13
+ canonical = PhonyRails.normalize_number(digits_and_stuff, default_country_code: "US").phony_formatted(format: :international)
14
+ else
15
+ if canonical[0] != "+"
16
+ digits = digits_and_stuff.gsub(/[^0-9]/, "")
17
+ digits = digits[1..-1] if digits[0] == '1'
18
+ digits = "805" + digits if digits.length == 7
19
+ canonical = "(#{digits[0..2]}) #{digits[3..5]}-#{digits[6..10]}"
20
+ end
16
21
  end
17
22
  end
18
23
  canonical
@@ -23,4 +28,18 @@ class Phone < ApplicationRecord
23
28
  self.number = self.class.canonicalize(self.number) if self.number
24
29
  end
25
30
 
31
+ def label
32
+ db_label = super
33
+ if not db_label
34
+ self.label = "Work"
35
+ db_label = super
36
+ end
37
+
38
+ db_label.value
39
+ end
40
+
41
+ def label=(name)
42
+ super(Label.get(name))
43
+ self.label
44
+ end
26
45
  end
data/app/models/user.rb CHANGED
@@ -3,21 +3,16 @@ class User < ApplicationRecord
3
3
  accepts_nested_attributes_for :person
4
4
  has_and_belongs_to_many :organizations
5
5
 
6
- delegate :name, :name=, to: :person
7
- delegate :fname, :fname=, to: :person
8
- delegate :lname, :lname, to: :person
9
- delegate :minitial, :minitial, to: :person
10
- delegate :prefix, :prefix, to: :person
11
- delegate :suffix, :suffix, to: :person
12
- delegate :emails, :emails=, :email, :email=, to: :person
13
- delegate :phones, :phones=, :phone, :phone=, to: :person
14
- delegate :addresses, :addresses=, :address, :address=, to: :person
15
- delegate :birthdate, :birthdate=, to: :person
16
- delegate :age, :age=, to: :person
6
+ PERSON_ATTR = %w(name fname lname minitial prefix suffix email phone emails phones addresses birthdate age nationality language)
7
+ PERSON_SYMS = PERSON_ATTR.collect {|x| [x.to_sym, (x + "=").to_sym] }.flatten
8
+ PERSON_SET = PERSON_SYMS + PERSON_ATTR
9
+
10
+ delegate(*PERSON_SYMS, to: :person)
17
11
 
18
12
  def self.lookup(item)
19
13
  person = Person.lookup(item)
20
- (person.send self.name.downcase.to_sym) if person
14
+ this_class = self.name.downcase.to_sym
15
+ (person.send this_class) if person and person.respond_to?(this_class)
21
16
  end
22
17
 
23
18
  def organization=(org)
@@ -27,4 +22,11 @@ class User < ApplicationRecord
27
22
  def organization
28
23
  self.organizations.order(created_at: :desc).first
29
24
  end
25
+
26
+ def assign_attributes(attr)
27
+ if not attr.keys.to_set.intersection(PERSON_SET).empty?
28
+ self.person = Person.new
29
+ end
30
+ super(attr)
31
+ end
30
32
  end
@@ -8,6 +8,7 @@ class CreatePeople < ActiveRecord::Migration[5.2]
8
8
  t.string :suffix, limit: 10
9
9
  t.string :birthdate
10
10
  t.string :nationality
11
+ t.string :language, limit: 10
11
12
 
12
13
  t.timestamps
13
14
  end
@@ -2,11 +2,10 @@ module Openopus
2
2
  module Core
3
3
  module People
4
4
  class Engine < ::Rails::Engine
5
- isolate_namespace People
6
5
  initializer :append_migrations do |app|
7
6
  unless app.root.to_s.match(root.to_s)
8
7
  config.paths["db/migrate"].expanded.each do |expanded_path|
9
- app.config.paths["db/migrate"] << expanded_path
8
+ # app.config.paths["db/migrate"] << expanded_path
10
9
  end
11
10
  end
12
11
  end
@@ -1,7 +1,7 @@
1
1
  module Openopus
2
2
  module Core
3
3
  module People
4
- VERSION = '1.1.1'
4
+ VERSION = '1.1.14'
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,44 @@
1
+ namespace :openopus do
2
+ namespace :core do
3
+ namespace :people do
4
+ desc "Generate one or many random people in your database. Requires 'gem rest-client'. Only for testing!"
5
+ task :generate_people, [:count] => :environment do |task, args|
6
+ def generate_person
7
+ json = ActiveSupport::HashWithIndifferentAccess.new(JSON(RestClient::Request.execute(method: :get, url: 'https://randomuser.me/api?format=json&nat=US').body))
8
+ jp = json[:results][0]
9
+ l = jp[:location]
10
+ a = Address.new
11
+ a.line1 = l[:street].titleize; a.city = l[:city].titleize; a.state = l[:state].titleize;
12
+ a.postal = l[:postcode].to_s.upcase; a.country = "US"
13
+ name = jp[:name]
14
+ p = Person.create(name: "#{name[:title]} #{name[:first]} #{name[:last]}".titleize,
15
+ phone: jp[:phone], address: a, email: jp[:email],
16
+ birthdate: jp[:dob][:date], nationality: jp[:nat])
17
+ p.picture = jp[:picture][:medium] if p.respond_to?("picture=".to_sym)
18
+ p.photo = jp[:picture][:medium] if p.respond_to?("photo=".to_sym)
19
+ p
20
+ end
21
+
22
+ count = args[:count]
23
+ count ||= 1
24
+ count.to_i.times do
25
+ person = generate_person
26
+ puts("openopus made: #{person.name} of #{person.address.oneline}")
27
+ end
28
+ end
29
+
30
+ desc "Generate users from the people in your database that aren't associated with any user. Only for testing!"
31
+ task :generate_users, [:count] => :environment do |task, args|
32
+ def generate_user(person)
33
+ user = User.create(person_id: person.id, status: "generated") if not person.user and not person.email.blank?
34
+ if user and user.respond_to?(:credentials) and user.credentials.blank?
35
+ user.credentials.create(password: "password")
36
+ end
37
+ end
38
+
39
+ Rake::Task["openopus:core:people:generate_people"].invoke(args[:count]) if not args[:count].blank?
40
+ Person.all.each { |person| generate_user(person) }
41
+ end
42
+ end
43
+ end
44
+ end
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openopus-core-people
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian J. Fox
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-21 00:00:00.000000000 Z
11
+ date: 2021-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">"
18
18
  - !ruby/object:Gem::Version
19
19
  version: 5.2.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.2.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: phony_rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.14'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.14'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bcrypt
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,6 +67,7 @@ files:
53
67
  - Rakefile
54
68
  - app/assets/config/openopus_core_people_manifest.js
55
69
  - app/models/address.rb
70
+ - app/models/concerns/labelize.rb
56
71
  - app/models/email.rb
57
72
  - app/models/label.rb
58
73
  - app/models/nickname.rb
@@ -72,11 +87,10 @@ files:
72
87
  - db/migrate/00000000000009_create_join_table_organizations_users.rb
73
88
  - db/seeds.rb
74
89
  - lib/openopus/core/people.rb
75
- - lib/openopus/core/people/#engine.rb#
76
90
  - lib/openopus/core/people/engine.rb
77
91
  - lib/openopus/core/people/version.rb
78
- - lib/tasks/openopus/core/people_tasks.rake
79
- homepage: https://github.com/opuslogica/openopus-core-people
92
+ - lib/tasks/openopus/core/people/tasks.rake
93
+ homepage: https://github.com/openopus/openopus-core-people
80
94
  licenses:
81
95
  - MIT
82
96
  metadata:
@@ -96,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
110
  - !ruby/object:Gem::Version
97
111
  version: '0'
98
112
  requirements: []
99
- rubygems_version: 3.0.1
113
+ rubygems_version: 3.1.4
100
114
  signing_key:
101
115
  specification_version: 4
102
116
  summary: Model the real world of people in your application, making user interaction
@@ -1,16 +0,0 @@
1
- module Openopus
2
- module Core
3
- module People
4
- class Engine < ::Rails::Engine
5
- isolate_namespace People
6
- initializer :append_migrations do |app|
7
- unless app.root.to_s.match(root.to_s)
8
- config.paths["db/migrate"].expanded.each do |expanded_path|
9
- app.config.paths["db/migrate"] << expanded_path
10
- end
11
- end
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :openopus_core_people do
3
- # # Task goes here
4
- # end