Module: ActiveSanction::Sources

Extended by:
T::Sig
Defined in:
lib/active_sanction/sources.rb,
lib/active_sanction/sources/base.rb,
lib/active_sanction/sources/ofac.rb,
lib/active_sanction/sources/eu_fsf.rb,
lib/active_sanction/sources/remarks.rb,
lib/active_sanction/sources/ofac_sdn.rb,
lib/active_sanction/sources/definition.rb,
lib/active_sanction/sources/canada_sema.rb,
lib/active_sanction/sources/ofac/record.rb,
lib/active_sanction/sources/eu_fsf/record.rb,
lib/active_sanction/sources/australia_dfat.rb,
lib/active_sanction/sources/un_consolidated.rb,
lib/active_sanction/sources/ofac_consolidated.rb,
lib/active_sanction/sources/uk_sanctions_list.rb,
lib/active_sanction/sources/canada_sema/record.rb,
lib/active_sanction/sources/ofac/remarks_parser.rb,
lib/active_sanction/sources/australia_dfat/record.rb,
lib/active_sanction/sources/canada_sema/source_ref.rb,
lib/active_sanction/sources/un_consolidated/record.rb,
lib/active_sanction/sources/ofac_consolidated/record.rb,
lib/active_sanction/sources/uk_sanctions_list/record.rb,
lib/active_sanction/sources/ofac/remarks_parser/coverage.rb,
lib/active_sanction/sources/australia_dfat/published_date.rb,
lib/active_sanction/sources/ofac/remarks_parser/vocabulary.rb,
lib/active_sanction/sources/uk_sanctions_list/published_date.rb

Overview

Every sanctions list this library knows how to read, filed under the key its adapter declares.

ActiveSanction::Sources[:un_consolidated]   # => the adapter class
ActiveSanction::Sources.keys                # => [:ofac_sdn, :un_consolidated, ...]

The registry is open, and that is the point of this milestone. A bank with an internal watchlist, or a gem adding a jurisdiction this one has not got to, registers an adapter of its own without forking:

ActiveSanction::Sources.register(MyCompany::InternalWatchlist)
ActiveSanction.configure { |c| c.sources = %i[ofac_sdn my_internal_watchlist] }

Nothing here requires Sources::Base. Registration is duck-typed on .key and .new, so a source backed by a database table rather than a published file -- which has no URL to declare and no payload to fetch -- is a first-class citizen rather than something that has to pretend to be a file download. Base is the convenient way to write an adapter, not the price of admission.

Built-in adapters register themselves at the bottom of their own file, one explicit Sources.register(self) line each, rather than being enrolled by inherited. Auto-registering every subclass would also enrol the abstract intermediates that adapters sharing a publisher will want (an OFAC base holding the authority and the format for SDN and Consolidated both) and every throwaway subclass a test defines.

Defined Under Namespace

Modules: Definition, Remarks Classes: AustraliaDfat, Base, CanadaSema, DeclarationError, DuplicateKey, EuFsf, MissingPayload, OfacConsolidated, OfacSdn, UkSanctionsList, UnConsolidated, UnknownSource

Class Method Summary collapse

Class Method Details

.[](key) ⇒ T.untyped

The adapter registered under a key.

Raises rather than returning nil, because every caller of this method is resolving a name a human typed -- into config.sources, into activesanction sync ofac_sdb -- and a nil surfaces three layers later as a NoMethodError that says nothing about the misspelling. The message lists what is registered, which is also the answer to "why is my adapter not being picked up" (its file was never required).

Parameters:

  • key (T.untyped)

Returns:

  • (T.untyped)


117
118
119
120
121
122
# File 'lib/active_sanction/sources.rb', line 117

def [](key)
  name = key.to_sym
  registry.fetch(name) do
    raise UnknownSource, "no source registered as #{name.inspect}. Registered: #{list}"
  end
end

.all ⇒ Array<T.untyped>

Every registered adapter, ordered by key so a CLI listing and a sync summary do not reshuffle themselves between runs.

Returns:

  • (Array<T.untyped>)


130
# File 'lib/active_sanction/sources.rb', line 130

def all = registry.keys.sort.map { |key| registry[key] }

.empty? ⇒ Boolean

Returns:

  • (Boolean)


139
# File 'lib/active_sanction/sources.rb', line 139

def empty? = registry.empty?

.enabled(configured = ActiveSanction.config.sources) ⇒ Array<T.untyped>

The adapters a sync should run: what config.sources names, or every registered source when it names nothing. An unknown key raises here, at the start of the run, rather than after the other lists have been downloaded.

Parameters:

  • configured (Array<Symbol>, nil) (defaults to: ActiveSanction.config.sources)

Returns:

  • (Array<T.untyped>)


146
147
148
149
150
# File 'lib/active_sanction/sources.rb', line 146

def enabled(configured = ActiveSanction.config.sources)
  return all if configured.nil?

  configured.map { |key| self[key] }
end

.keys ⇒ Array<Symbol>

Returns:

  • (Array<Symbol>)


133
# File 'lib/active_sanction/sources.rb', line 133

def keys = registry.keys.sort

.register(source) ⇒ T.untyped

Adds a source to the registry and returns it. Registering the same class twice is a no-op, so a file that manages to get loaded under two paths does not take the whole process down with it.

Parameters:

  • source (T.untyped)

Returns:

  • (T.untyped)


97
98
99
100
101
102
103
104
105
106
# File 'lib/active_sanction/sources.rb', line 97

def register(source)
  key = registrable!(source)
  MUTEX.synchronize do
    claimed = registry[key]
    raise DuplicateKey, duplicate_message(key, source, claimed) if claimed && claimed != source

    registry[key] = source
  end
  source
end

.registered?(key) ⇒ Boolean

Parameters:

  • key (T.untyped)

Returns:

  • (Boolean)


125
# File 'lib/active_sanction/sources.rb', line 125

def registered?(key) = registry.key?(key.to_sym)

.size ⇒ Integer

Returns:

  • (Integer)


136
# File 'lib/active_sanction/sources.rb', line 136

def size = registry.size

.unregister(key) ⇒ T.untyped

Drops a key and returns what was registered there, or nil. The supported way to replace a built-in adapter with a patched one:

ActiveSanction::Sources.unregister(:ofac_sdn)
ActiveSanction::Sources.register(MyCompany::PatchedOfacSdn)

There is deliberately no clear!. Built-in adapters register on require, and require runs once per process: a suite that emptied the registry between examples would leave every later example running against a library that has forgotten its own sources.

Parameters:

  • key (T.untyped)

Returns:

  • (T.untyped)


163
164
165
# File 'lib/active_sanction/sources.rb', line 163

def unregister(key)
  MUTEX.synchronize { registry.delete(key.to_sym) }
end