cube-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +62 -0
- data/Rakefile +12 -0
- data/cube-ruby.gemspec +19 -0
- data/lib/cube.rb +89 -0
- data/lib/cube/version.rb +3 -0
- data/spec/cube_spec.rb +111 -0
- data/spec/helper.rb +37 -0
- metadata +73 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Cody Krieger
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# cube-ruby
|
2
|
+
|
3
|
+
A Cube client for Ruby (http://square.github.com/cube). Heavily based on
|
4
|
+
[this statsd ruby client](https://github.com/github/statsd-ruby).
|
5
|
+
|
6
|
+
MIT licensed. See https://github.com/codykrieger/cube-ruby/blob/master/LICENSE
|
7
|
+
for more details.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'cube-ruby', require: "cube"
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install cube-ruby
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Set up a global Cube client
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
# use default hostname and port of localhost:1180
|
29
|
+
$cube = Cube::Client.new
|
30
|
+
|
31
|
+
# use custom hostname and port
|
32
|
+
$cube = Cube::Client.new 'cube.example.org', 2280
|
33
|
+
```
|
34
|
+
|
35
|
+
### Send Cube some metrics!
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# send a new event to cube that looks like this:
|
39
|
+
# { type: "request", time: [now], data: { value: "somevalue" } }
|
40
|
+
$cube.send "request", value: "somevalue"
|
41
|
+
|
42
|
+
# optionally specify a specific date/time (two days ago)
|
43
|
+
$cube.send "request", DateTime.now - 2, value: "othervalue"
|
44
|
+
|
45
|
+
# specify an event id (https://github.com/square/cube/wiki/Events)
|
46
|
+
event_id = 42
|
47
|
+
$cube.send "request", DateTime.now, event_id, duration_ms: 234
|
48
|
+
```
|
49
|
+
|
50
|
+
## Testing
|
51
|
+
|
52
|
+
Run the specs with `rake`.
|
53
|
+
|
54
|
+
To include real UDP socket testing in the specs, run `LIVE=true rake`.
|
55
|
+
|
56
|
+
## Contributing
|
57
|
+
|
58
|
+
1. Fork it
|
59
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
60
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
61
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
62
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/cube-ruby.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/cube/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Cody Krieger"]
|
6
|
+
gem.email = ["cody@codykrieger.com"]
|
7
|
+
gem.description = %q{A Cube client for Ruby (https://square.github.com/cube)}
|
8
|
+
gem.summary = %q{A Cube client for Ruby (https://square.github.com/cube)}
|
9
|
+
gem.homepage = "https://github.com/codykrieger/cube-ruby"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "cube-ruby"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Cube::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "minitest"
|
19
|
+
end
|
data/lib/cube.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'cube/version'
|
2
|
+
|
3
|
+
module Cube
|
4
|
+
require 'date'
|
5
|
+
# We'll be sending data to Cube over a UDP socket.
|
6
|
+
require 'socket'
|
7
|
+
# Cube requires data to be in JSON format.
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
class Client
|
11
|
+
# A namespace to prepend to all Cube calls.
|
12
|
+
attr_accessor :namespace
|
13
|
+
|
14
|
+
# We'll use this to eliminate any unwanted/disallowed characters from our
|
15
|
+
# event type later on.
|
16
|
+
RESERVED_CHARS_REGEX = /[^\w\d]/
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# Set to any logger instance that responds to #debug and #error (like the
|
20
|
+
# Rails or stdlib logger) to enable metric logging.
|
21
|
+
attr_accessor :logger
|
22
|
+
end
|
23
|
+
|
24
|
+
# Set 'er up with a host and port, defaults to `localhost:1180`.
|
25
|
+
def initialize(host="localhost", port=1180)
|
26
|
+
@host, @port = host, port
|
27
|
+
end
|
28
|
+
|
29
|
+
# The primary endpoint for sending metrics to Cube - call like this:
|
30
|
+
#
|
31
|
+
# ```ruby
|
32
|
+
# cube = Cube::Client.new
|
33
|
+
# cube.send "request", value: "somevalue"
|
34
|
+
# cube.send "request", DateTime.now, duration_ms: 234
|
35
|
+
# ```
|
36
|
+
def send(type, *args)
|
37
|
+
default_time = DateTime.now
|
38
|
+
time = nil
|
39
|
+
id = nil
|
40
|
+
data = nil
|
41
|
+
|
42
|
+
until args.empty?
|
43
|
+
arg = args.shift
|
44
|
+
|
45
|
+
if arg.is_a? DateTime
|
46
|
+
time ||= arg
|
47
|
+
elsif arg.is_a? Hash
|
48
|
+
data ||= arg
|
49
|
+
else
|
50
|
+
id ||= arg
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Send off our parsed arguments to be further massaged and socketized.
|
55
|
+
actual_send type, (time || default_time), id, data
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Actually send the given data to a socket, and potentially log it along
|
61
|
+
# the way.
|
62
|
+
def actual_send(type, time, id, data)
|
63
|
+
# Namespace support!
|
64
|
+
prefix = "#{@namespace}_" unless @namespace.nil?
|
65
|
+
# Get rid of any unwanted characters, and replace each of them with an _.
|
66
|
+
type = type.to_s.gsub RESERVED_CHARS_REGEX, '_'
|
67
|
+
|
68
|
+
# Start constructing the message to be sent to Cube over UDP.
|
69
|
+
message = {
|
70
|
+
type: "#{prefix}#{type}",
|
71
|
+
time: time.iso8601
|
72
|
+
}
|
73
|
+
message[:id] = id unless id.nil?
|
74
|
+
message[:data] = data unless data.nil?
|
75
|
+
|
76
|
+
# JSONify it, log it, and send it off.
|
77
|
+
message_str = message.to_json
|
78
|
+
self.class.logger.debug { "Cube: #{message_str}" } if self.class.logger
|
79
|
+
|
80
|
+
socket.send message_str, 0, @host, @port
|
81
|
+
rescue => err
|
82
|
+
self.class.logger.error { "Cube: #{err.class} #{err}" } if self.class.logger
|
83
|
+
end
|
84
|
+
|
85
|
+
# Helper for getting the socket. `@socket` can be set to a mock object to
|
86
|
+
# test without needing an actual UDP socket.
|
87
|
+
def socket ; @socket ||= UDPSocket.new ; end
|
88
|
+
end
|
89
|
+
end
|
data/lib/cube/version.rb
ADDED
data/spec/cube_spec.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'date'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
# a few things yanked verbatim from here: https://github.com/github/statsd-ruby/blob/master/spec/statsd_spec.rb
|
6
|
+
# license: https://github.com/github/statsd-ruby/blob/master/LICENSE.txt
|
7
|
+
|
8
|
+
describe Cube::Client do
|
9
|
+
before do
|
10
|
+
@cube = Cube::Client.new "cube.example.org", 1185
|
11
|
+
class << @cube
|
12
|
+
attr_reader :host, :port
|
13
|
+
def socket ; @socket ||= FakeUDPSocket.new ; end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
after { @cube.socket.clear }
|
18
|
+
|
19
|
+
describe "#initialize" do
|
20
|
+
it "should set the host and port" do
|
21
|
+
@cube.host.must_equal "cube.example.org"
|
22
|
+
@cube.port.must_equal 1185
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should default the host and port to localhost:1180" do
|
26
|
+
cube = Cube::Client.new
|
27
|
+
cube.instance_variable_get('@host').must_equal 'localhost'
|
28
|
+
cube.instance_variable_get('@port').must_equal 1180
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#send" do
|
33
|
+
it "should format the message according to the Cube spec" do
|
34
|
+
type = "request"
|
35
|
+
time = DateTime.now
|
36
|
+
data = { duration_ms: 234 }
|
37
|
+
@cube.send type, time, data
|
38
|
+
recv = JSON.parse @cube.socket.recv.first
|
39
|
+
recv["type"].must_equal "request"
|
40
|
+
recv["time"].must_equal time.iso8601
|
41
|
+
recv["data"].must_equal({ "duration_ms" => 234 })
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "with an id" do
|
45
|
+
it "should format the message according to the Cube spec" do
|
46
|
+
type = "request"
|
47
|
+
time = DateTime.now
|
48
|
+
id = 42
|
49
|
+
data = { duration_ms: 234 }
|
50
|
+
@cube.send type, time, id, data
|
51
|
+
recv = JSON.parse @cube.socket.recv.first
|
52
|
+
recv["type"].must_equal "request"
|
53
|
+
recv["time"].must_equal time.iso8601
|
54
|
+
recv["id"].must_equal 42
|
55
|
+
recv["data"].must_equal({ "duration_ms" => 234 })
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "with namespace" do
|
61
|
+
before { @cube.namespace = 'someservice' }
|
62
|
+
|
63
|
+
it "should add namespace to send" do
|
64
|
+
@cube.send "request"
|
65
|
+
recv = JSON.parse @cube.socket.recv.first
|
66
|
+
recv["type"].must_equal 'someservice_request'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "with logging" do
|
71
|
+
require 'stringio'
|
72
|
+
before { Cube::Client.logger = Logger.new(@log = StringIO.new) }
|
73
|
+
|
74
|
+
it "should write to the log in debug" do
|
75
|
+
Cube::Client.logger.level = Logger::DEBUG
|
76
|
+
|
77
|
+
@cube.send "request"
|
78
|
+
|
79
|
+
recv = @cube.socket.recv.first
|
80
|
+
@log.string.must_match "Cube: #{recv}"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should not write to the log unless debug" do
|
84
|
+
Cube::Client.logger.level = Logger::INFO
|
85
|
+
|
86
|
+
@cube.send "request"
|
87
|
+
|
88
|
+
recv = @cube.socket.recv.first
|
89
|
+
@log.string.must_be_empty
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe Cube::Client do
|
95
|
+
describe "with a real UDP socket" do
|
96
|
+
it "should actually send stuff over the socket" do
|
97
|
+
socket = UDPSocket.new
|
98
|
+
host, port = 'localhost', 12345
|
99
|
+
socket.bind host, port
|
100
|
+
|
101
|
+
time = DateTime.now
|
102
|
+
cube = Cube::Client.new host, port
|
103
|
+
cube.send "request", time
|
104
|
+
|
105
|
+
recv = JSON.parse socket.recvfrom(64).first
|
106
|
+
recv["type"].must_equal "request"
|
107
|
+
recv["time"].must_equal time.iso8601
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end if ENV['LIVE']
|
111
|
+
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
# yanked almost verbatim from here: https://github.com/github/statsd-ruby/blob/master/spec/helper.rb
|
4
|
+
# license: https://github.com/github/statsd-ruby/blob/master/LICENSE.txt
|
5
|
+
|
6
|
+
$:.unshift File.dirname(__FILE__)
|
7
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
8
|
+
|
9
|
+
require 'cube'
|
10
|
+
require 'logger'
|
11
|
+
|
12
|
+
class FakeUDPSocket
|
13
|
+
def initialize
|
14
|
+
@buffer = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def send(message, *args)
|
18
|
+
@buffer.push [message]
|
19
|
+
end
|
20
|
+
|
21
|
+
def recv
|
22
|
+
res = @buffer.shift
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear
|
26
|
+
@buffer = []
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
inspect
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"<FakeUDPSocket: #{@buffer.inspect}>"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cube-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Cody Krieger
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: minitest
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: A Cube client for Ruby (https://square.github.com/cube)
|
31
|
+
email:
|
32
|
+
- cody@codykrieger.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- cube-ruby.gemspec
|
43
|
+
- lib/cube.rb
|
44
|
+
- lib/cube/version.rb
|
45
|
+
- spec/cube_spec.rb
|
46
|
+
- spec/helper.rb
|
47
|
+
homepage: https://github.com/codykrieger/cube-ruby
|
48
|
+
licenses: []
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.8.23
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: A Cube client for Ruby (https://square.github.com/cube)
|
71
|
+
test_files:
|
72
|
+
- spec/cube_spec.rb
|
73
|
+
- spec/helper.rb
|