Class: ActiveSanction::Address
- Inherits:
-
Object
- Object
- ActiveSanction::Address
- Extended by:
- T::Sig
- Defined in:
- lib/active_sanction/address.rb
Overview
A place a sanctions list attached to an entity. OFAC ships 25,078 of them in ADD.CSV; the UN ships far fewer and far thinner ones.
ActiveSanction::Address.new(
street: "Ave. Luis Maria Drago 1136",
city: "Buenos Aires",
state_province: nil,
postal_code: "C1414",
country: "Argentina",
note: "as of early 2016"
)
Every field is optional because the publishers populate wildly different subsets: the UN routinely supplies only COUNTRY plus a free-text NOTE, and an address type that insisted on a street would drop those rows entirely. What it will not accept is an address that says nothing at all -- a record with every field blank is a parsing accident, not a location.
A pure data holder: it stores what the publisher said and nothing more. No geocoding, no country-code lookup, no case folding. Instances are frozen on construction and compare by value.
Instance Attribute Summary collapse
- #city ⇒ String? readonly
- #country ⇒ String? readonly
- #note ⇒ String? readonly
- #postal_code ⇒ String? readonly
- #state_province ⇒ String? readonly
-
#street ⇒ String?
readonly
Every field is nilable because the publishers populate wildly different subsets of them; what #initialize refuses is all six being empty at once.
Class Method Summary collapse
-
.from_h(hash) ⇒ T.attached_class
Rebuilds an address from #to_h output.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Class is part of the comparison to keep #== and #hash agreeing, which is what Hash and Set rely on.
- #hash ⇒ Integer
-
#initialize(street: nil, city: nil, state_province: nil, postal_code: nil, country: nil, note: nil) ⇒ void
constructor
Untyped on purpose, and the same choice Entity makes: all six are the publisher's own text arriving as whatever the parser made of it, and #string_or_nil says below what happens to it.
- #inspect ⇒ String
-
#note_only? ⇒ Boolean
True when nothing but a note survived parsing -- the UN's "as of early 2016" with no place attached.
-
#parts ⇒ Array<String>
The address parts the publisher actually filled in, in canonical order.
- #to_h ⇒ Hash{Symbol => T.untyped}
-
#to_s ⇒ String
A single line, which is how an address is displayed in a hit list and how the normalizer (#26) will want it before folding.
Constructor Details
#initialize(street: nil, city: nil, state_province: nil, postal_code: nil, country: nil, note: nil) ⇒ void
Untyped on purpose, and the same choice Entity makes: all six are the publisher's own text arriving as whatever the parser made of it, and #string_or_nil says below what happens to it.
82 83 84 85 86 87 88 89 90 91 |
# File 'lib/active_sanction/address.rb', line 82 def initialize(street: nil, city: nil, state_province: nil, postal_code: nil, country: nil, note: nil) @street = T.let(string_or_nil(street), T.nilable(String)) @city = T.let(string_or_nil(city), T.nilable(String)) @state_province = T.let(string_or_nil(state_province), T.nilable(String)) @postal_code = T.let(string_or_nil(postal_code), T.nilable(String)) @country = T.let(string_or_nil(country), T.nilable(String)) @note = T.let(string_or_nil(note), T.nilable(String)) reject_empty! freeze end |
Instance Attribute Details
#city ⇒ String? (readonly)
50 51 52 |
# File 'lib/active_sanction/address.rb', line 50 def city @city end |
#country ⇒ String? (readonly)
59 60 61 |
# File 'lib/active_sanction/address.rb', line 59 def country @country end |
#note ⇒ String? (readonly)
62 63 64 |
# File 'lib/active_sanction/address.rb', line 62 def note @note end |
#postal_code ⇒ String? (readonly)
56 57 58 |
# File 'lib/active_sanction/address.rb', line 56 def postal_code @postal_code end |
#state_province ⇒ String? (readonly)
53 54 55 |
# File 'lib/active_sanction/address.rb', line 53 def state_province @state_province end |
#street ⇒ String? (readonly)
Every field is nilable because the publishers populate wildly different subsets of them; what #initialize refuses is all six being empty at once.
47 48 49 |
# File 'lib/active_sanction/address.rb', line 47 def street @street end |
Class Method Details
.from_h(hash) ⇒ T.attached_class
Rebuilds an address from #to_h output. Accepts string keys, so a record that has been through JSON round-trips without a separate coercion step.
67 68 69 70 71 72 73 |
# File 'lib/active_sanction/address.rb', line 67 def self.from_h(hash) attributes = hash.to_h.transform_keys(&:to_sym) unknown = attributes.keys - MEMBERS raise InvalidArgument, "unknown Address attribute(s): #{unknown.join(", ")}" if unknown.any? new(**attributes) end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Class is part of the comparison to keep #== and #hash agreeing, which is what Hash and Set rely on.
131 132 133 134 135 |
# File 'lib/active_sanction/address.rb', line 131 def ==(other) return false unless other.instance_of?(self.class) to_h == other.to_h end |
#hash ⇒ Integer
139 140 141 |
# File 'lib/active_sanction/address.rb', line 139 def hash [self.class, to_h].hash end |
#inspect ⇒ String
144 145 146 |
# File 'lib/active_sanction/address.rb', line 144 def inspect "#<#{self.class} #{to_s.inspect}>" end |
#note_only? ⇒ Boolean
True when nothing but a note survived parsing -- the UN's "as of early 2016" with no place attached. Such an address is worth keeping (it is evidence the publisher had something) but is not worth matching on.
103 |
# File 'lib/active_sanction/address.rb', line 103 def note_only? = parts.empty? |
#parts ⇒ Array<String>
The address parts the publisher actually filled in, in canonical order.
95 96 97 |
# File 'lib/active_sanction/address.rb', line 95 def parts PARTS.filter_map { |member| public_send(member) } end |
#to_h ⇒ Hash{Symbol => T.untyped}
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/active_sanction/address.rb', line 106 def to_h { street: street, city: city, state_province: state_province, postal_code: postal_code, country: country, note: note } end |
#to_s ⇒ String
A single line, which is how an address is displayed in a hit list and how the normalizer (#26) will want it before folding.
120 121 122 123 124 125 126 |
# File 'lib/active_sanction/address.rb', line 120 def to_s line = parts.join(", ") note = self.note return line if note.nil? line.empty? ? note : "#{line} (#{note})" end |