Skip to content

Commit

Permalink
Add encode for Line Protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
jnbt committed Nov 24, 2015
1 parent 704bfe7 commit 43628d8
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/linr.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "linr/version"
require "linr/data"
require "linr/encoder"
require "linr/connection"

# A simple UDP client for InfluxDB
Expand Down
8 changes: 7 additions & 1 deletion lib/linr/connection/udp.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
module Linr
module Connection
# Connects to a InfluxDB UDP port
# @see https://influxdb.com/docs/v0.9/write_protocols/udp.html
# @example
# UDP.new("localhost", 8089).send("series a=0.51")
class UDP
# Use non-custom flags
SEND_FLAGS = 0
Expand All @@ -12,7 +16,9 @@ def initialize(host, port)
@socket.connect(host, port)
end

# Send arbitrary data ober the UDP connection
# Send data over the UDP connection. Should be encoded using the Line
# Protocol.
# @see Encoder::Line
# @param data [String]
def send(data)
@socket.send(data, SEND_FLAGS)
Expand Down
1 change: 1 addition & 0 deletions lib/linr/encoder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require "linr/encoder/line"
81 changes: 81 additions & 0 deletions lib/linr/encoder/line.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
module Linr
module Encoder
# Encodes data entry into the InfluxDB line format.
# @see https://influxdb.com/docs/v0.9/write_protocols/write_syntax.html
# @example Encode a data series
# Line.new.dump(Data.new(measurement: "series", fields: { a: 0.5 }))
class Line
# Encodes data into the line syntax
# @param data [Data]
# @return [String]
def dump(data)
[
dump_measurement(data.measurement),
dump_tags(data.tags),
dump_fields(data.fields),
dump_timestamp(data.timestamp)
].compact.join
end

private

def dump_measurement(measurement)
escape_key(measurement)
end

def dump_tags(tags)
dump_collection(tags, ",") do |key, value|
"#{escape_key(key)}=#{escape_value(value)}"
end
end

def dump_fields(fields)
dump_collection(fields) do |key, value|
"#{escape_key(key)}=#{encode_field_value(value)}"
end
end

def dump_timestamp(timestamp)
" #{timestamp}" if timestamp
end

def dump_collection(data, prefix = " ", &block)
return unless data && !data.empty?
prefix + data.map(&block).join(",")
end

def encode_field_value(value)
case value
when Numeric
encode_numberic(value)
when TrueClass, FalseClass
value.to_s
else
escape_qoute(escape_value(value))
end
end

def encode_numberic(value)
"#{value}i" if value.is_a?(Fixnum)
value
end

def escape_value(input)
input.to_s
.gsub(/\s/, '\ ')
.gsub(",", '\,')
.gsub('"', '\"')
end

def escape_qoute(input)
%("#{input}")
end

def escape_key(input)
input.to_s
.gsub(/\s/, '\ ')
.gsub(",", '\,')
end
end
end
end
71 changes: 71 additions & 0 deletions spec/encoder/line_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require "spec_helper"

describe ::Linr::Encoder::Line do
subject { ::Linr::Encoder::Line.new }
let(:data) { ::Linr::Data.new(source) }
let(:output) { subject.dump(data) }

describe "whitespace" do
let(:source) do
{
measurement: "A test measurement",
fields: { "some long field" => 5 },
tags: { "some tag" => "with whitespace" },
timestamp: 123_456_789
}
end

it "escapes" do
expected = 'A\ test\ measurement'
expected << ',some\ tag=with\ whitespace'
expected << ' some\ long\ field=5'
expected << " 123456789"
output.must_equal expected
end
end

describe "commas" do
let(:source) do
{
measurement: "A,test,measurement",
fields: { "some,long,field" => 5 },
tags: { "some,tag" => "with,comma" },
timestamp: 123_456_789
}
end

it "escapes" do
expected = 'A\,test\,measurement'
expected << ',some\,tag=with\,comma'
expected << ' some\,long\,field=5'
expected << " 123456789"
output.must_equal expected
end
end

describe "minimum data" do
let(:source) do
{
measurement: "series",
fields: { "field" => 0.51 }
}
end

it "dumps" do
output.must_equal "series field=0.51"
end
end

describe "complex data types" do
let(:source) do
{
measurement: "series",
fields: { A: true, B: false, C: 'A"B' }
}
end

it "dumps" do
output.must_equal 'series A=true,B=false,C="A\"B"'
end
end
end

0 comments on commit 43628d8

Please sign in to comment.