lib/blurrily/client.rb
# encoding: utf-8
require 'socket'
require 'ipaddr'
require 'blurrily/defaults'
module Blurrily
class Client
Error = Class.new(RuntimeError)
# Initialize a new {Blurrily::Client} connection to {Blurrily::Server}.
#
# @param host IP Address or FQDN of the Blurrily::Server.
# Defaults to Blurrily::DEFAULT_HOST.
# @param port Port Blurrily::Server is listening on.
# Defaults to Blurrily::DEFAULT_PORT.
# @param db_name Name of the data store being targeted.
# Defaults to Blurrily::DEFAULT_DATABASE.
#
# Examples
#
# ```
# Blurrily::Client.new('127.0.0.1', 12021, 'location_en')
# # => #<Blurrily::Client:0x007fcd0d33e708 @host="127.0.0.1", @port=12021, @db_name="location_en">
# ```
#
# @returns the instance of {Blurrily::Client}
def initialize(options = {})
@host = options.fetch(:host, DEFAULT_HOST)
@port = options.fetch(:port, DEFAULT_PORT)
@db_name = options.fetch(:db_name, DEFAULT_DATABASE)
end
# Find record references based on a given string (needle)
#
# @param needle The string you're searching for matches on.
# Must not contain tabs.
# Required
# @param limit Limit the number of results retruned (default: 10).
# Must be numeric.
# Optional
#
# Examples
#
# ```
# @client.find('London')
# # => [[123,6,3],[124,5,3]...]
# ```
#
# @returns an Array of matching [`ref`,`score`,`weight`] ordered by score. `ref` is the identifying value of the original record.
# Note that unless modified, `weight` is simply the string length.
def find(needle, limit = nil)
limit ||= LIMIT_DEFAULT
check_valid_needle(needle)
raise(ArgumentError, "LIMIT value must be in #{LIMIT_RANGE}") unless LIMIT_RANGE.include?(limit)
cmd = ["FIND", @db_name, needle, limit]
send_cmd_and_get_results(cmd).map(&:to_i).each_slice(3).to_a
end
# Index a given record.
#
# @param db_name The name of the data store being targeted. Required
# @param needle The string you wish to index. Must not contain tabs. Required
# @param ref The indentifying value of the record being indexed. Must be numeric. Required
# @param weight Weight of this particular reference. Default 0. Don't change unless you know what you're doing. Optional.
#
# Examples
#
# ```
# @client.put('location_en', 'London', 123, 0)
# # => OK
# ```
#
# @returns something to let you know that all is well.
def put(needle, ref, weight = 0)
check_valid_needle(needle)
check_valid_ref(ref)
raise(ArgumentError, "WEIGHT value must be in #{WEIGHT_RANGE}") unless WEIGHT_RANGE.include?(weight)
cmd = ["PUT", @db_name, needle, ref, weight]
send_cmd_and_get_results(cmd)
return
end
def delete(ref)
check_valid_ref(ref)
cmd = ['DELETE', @db_name, ref]
send_cmd_and_get_results(cmd)
return
end
def clear()
send_cmd_and_get_results(['CLEAR', @db_name])
return
end
private
PORT_RANGE = 1025..32768
def check_valid_needle(needle)
raise(ArgumentError, "bad needle") if !needle.kind_of?(String) || needle.empty? || needle.include?("\t")
end
def check_valid_ref(ref)
raise(ArgumentError, "REF value must be in #{REF_RANGE}") unless REF_RANGE.include?(ref)
end
def connection
@connection ||= TCPSocket.new(@host, @port)
end
def send_cmd_and_get_results(argv)
output = argv.join("\t")
connection.puts output
input = connection.gets
case input
when "OK\n"
return []
when /^OK\t(.*)\n/
return $1.split("\t")
when /^ERROR\t(.*)\n/
raise Error, $1
when nil
raise Error, 'Server disconnected'
else
raise Error, 'Server did not respect protocol'
end
end
end
end