class ZipCode::FR
TODO: factor index system out rubocop:disable Metrics/ClassLength
Public Class Methods
new()
click to toggle source
# File lib/zipcode-fr.rb, line 8 def initialize @indexes = {} end
Public Instance Methods
complete(name, str, key = nil)
click to toggle source
# File lib/zipcode-fr.rb, line 180 def complete(name, str, key = nil) key ||= name search(name, str).map { |e| e[key] } end
index!(name, data, modes = nil, key: nil)
click to toggle source
# File lib/zipcode-fr.rb, line 70 def index!(name, data, modes = nil, key: nil) key ||= name index = Hash.new { |h, k| h[k] = [] unless h.frozen? } modes = [modes] unless modes.is_a?(Enumerable) modes.each do |mode| data.each(&appender(index, key, mode)) end index.each_value(&:uniq!) index.freeze @indexes[name] = index end
load()
click to toggle source
# File lib/zipcode-fr.rb, line 12 def load # TODO: non-optimal, but not overly long either index!(:name, reader, %i[word_prefix match]) index!(:zip, reader, :prefix) @loaded = true end
memsize_of_index(name)
click to toggle source
# File lib/zipcode-fr.rb, line 157 def memsize_of_index(name) require 'objspace' ObjectSpace.memsize_of(@indexes[name]) + @indexes[name].reduce(0) { |a, (_, v)| a + ObjectSpace.memsize_of(v) } end
ready?()
click to toggle source
# File lib/zipcode-fr.rb, line 19 def ready? @loaded end
search(name, str, case_insensitive: true)
click to toggle source
# File lib/zipcode-fr.rb, line 175 def search(name, str, case_insensitive: true) str = str.upcase if case_insensitive read_at(*index(name)[str.hash]) end
Private Instance Methods
append_infixes(idx, pos, val, min_size: 1)
click to toggle source
# File lib/zipcode-fr.rb, line 141 def append_infixes(idx, pos, val, min_size: 1) each_prefix(val, min_size: min_size) do |prefix| each_suffix(prefix, min_size: min_size) do |infix| idx[infix.hash] << pos end end end
append_match(idx, pos, val)
click to toggle source
# File lib/zipcode-fr.rb, line 104 def append_match(idx, pos, val) idx[val.hash] << pos end
append_prefixes(idx, pos, val, min_size: 1)
click to toggle source
# File lib/zipcode-fr.rb, line 121 def append_prefixes(idx, pos, val, min_size: 1) each_prefix(val, min_size: min_size) { |prefix| idx[prefix.hash] << pos } end
append_word_prefixes(idx, pos, val)
click to toggle source
# File lib/zipcode-fr.rb, line 114 def append_word_prefixes(idx, pos, val) each_word(val) do |word| each_prefix(word) { |prefix| idx[prefix.hash] << pos } end end
append_words(idx, pos, val)
click to toggle source
# File lib/zipcode-fr.rb, line 109 def append_words(idx, pos, val) each_word(val) { |w| idx[w.hash] << pos } end
appender(idx, key, mode)
click to toggle source
TODO: create an appender registry rubocop:disable Metrics/AbcSize rubocop:disable Metrics/MethodLength
# File lib/zipcode-fr.rb, line 88 def appender(idx, key, mode) case mode when :prefix ->(pos, record) { append_prefixes(idx, pos, record[key]) } when :infix ->(pos, record) { append_infixes(idx, pos, record[key]) } when :word ->(pos, record) { append_words(idx, pos, record[key]) } when :word_prefix ->(pos, record) { append_word_prefixes(idx, pos, record[key]) } else ->(pos, record) { append_match(idx, pos, record[key]) } end end
clean(row)
click to toggle source
# File lib/zipcode-fr.rb, line 55 def clean(row) row_to_h(row_clean(row)) end
data_source()
click to toggle source
# File lib/zipcode-fr.rb, line 23 def data_source path = 'vendor/data/code_postaux_v201703.csv' File.expand_path(File.join(File.dirname(__FILE__), '..', path)) end
each_prefix(val, min_size: 1) { |val| ... }
click to toggle source
# File lib/zipcode-fr.rb, line 131 def each_prefix(val, min_size: 1) min_size.upto(val.length) { |i| yield val[0...i] } end
each_suffix(val, min_size: 1) { |val| ... }
click to toggle source
# File lib/zipcode-fr.rb, line 136 def each_suffix(val, min_size: 1) min_size.upto(val.length) { |i| yield val[-i..-1] } end
each_word(val, &block)
click to toggle source
# File lib/zipcode-fr.rb, line 126 def each_word(val, &block) val.split.each(&block) end
index(name)
click to toggle source
# File lib/zipcode-fr.rb, line 150 def index(name) raise "no index named #{name.inspect}" unless @indexes.key?(name) @indexes[name] end
open() { |csv| ... }
click to toggle source
# File lib/zipcode-fr.rb, line 37 def open CSV.open(data_source, 'rb', reader_options) do |csv| csv.take(1) # skip header manually to preserve tell() yield csv end end
read_at(*positions, count: 1) { |clean(row)| ... }
click to toggle source
# File lib/zipcode-fr.rb, line 163 def read_at(*positions, count: 1) return enum_for(:read_at, *positions, count: count) unless block_given? open do |io| positions.each do |pos| io.seek(pos) io.take(count).each { |row| yield clean(row) } end end end
reader() { |pos, clean(row)| ... }
click to toggle source
# File lib/zipcode-fr.rb, line 45 def reader return enum_for(:reader) unless block_given? open do |io| pos = io.tell io.each { |row| yield(pos, clean(row)); pos = io.tell } end end
reader_options()
click to toggle source
# File lib/zipcode-fr.rb, line 29 def reader_options { col_sep: ';', encoding: 'ISO-8859-1', } end
row_clean(row)
click to toggle source
# File lib/zipcode-fr.rb, line 60 def row_clean(row) row.map { |e| e.strip.encode('UTF-8') } end
row_to_h(row)
click to toggle source
# File lib/zipcode-fr.rb, line 65 def row_to_h(row) %i[insee name zip alt_name].zip(row).to_h end