dboard 1.0.5 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 066c3ae2d4574bad0b7b56e0c5a784fe36b396d5
4
- data.tar.gz: 49aa1e783781c16fabee8416c2bdc97e9eceb0d1
2
+ SHA256:
3
+ metadata.gz: 4e3f07d3f503da759e56a7e315c0a59c3a1e7fa9a4bd3cdc2ac6533294c4d6df
4
+ data.tar.gz: 6f5d28ba5299fa8d219c4f1581635c78db27052ddc9fc06c33e06bd9f4507613
5
5
  SHA512:
6
- metadata.gz: 779322a76e7fd4521fc8c30cd6e394444e2ee25ee16849a7958a2fc04cb883f5d7c7b7e2dd32376f4edc51e4ac0a1ab6d146dc17e642defd532ae2e74bbbd319
7
- data.tar.gz: 14c30edbffe96773c1d783865e7e97581c286d06eff18c87c078d63c33eeca4c49dfbd1cc69591435fa0ca1e652d99021bd89b9dd1c73d724ceb208f8bb16074
6
+ metadata.gz: 3a7d9df2e8c2271a83fc757f9564dd41c5662a3780a01a5c4d5024421035ed3b2eca88f5379b96c7c39a151821be2152cb3d6e11269a5ec3f8db0e5ddf78af07
7
+ data.tar.gz: 004c14e13bc762c8fe3aae9618696a568048c139a0e0ab30c7b49e603f680d0f79d5251d55ad49e058edce6b97315a09981c627bc1dec68d44eea84ed6db4089
@@ -0,0 +1,26 @@
1
+ name: Ruby CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ test:
11
+
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ matrix:
16
+ ruby-version: ["3.0", "2.7", "2.6"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - name: Set up Ruby ${{ matrix.ruby-version }}
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby-version }}
24
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
25
+ - name: Run tests
26
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  todo.txt
6
+ /tmp
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ AllCops:
2
+ Exclude:
3
+ - "tmp/**/*"
4
+ inherit_gem:
5
+ barsoom_utils: shared_rubocop.yml
data/Gemfile CHANGED
@@ -1,2 +1,11 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
  gemspec
3
+
4
+ gem "net_http_timeout_errors"
5
+
6
+ group :development, :test do
7
+ gem "barsoom_utils"
8
+ gem "rspec"
9
+ gem "rubocop"
10
+ gem "webmock"
11
+ end
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Ruby CI](https://github.com/barsoom/dboard/actions/workflows/ci.yml/badge.svg)](https://github.com/barsoom/dboard/actions/workflows/ci.yml)
2
+
1
3
  A dashboard framework.
2
4
 
3
5
  It handles collecting data from user defined sources (simple ruby classes) and provides a simple API to poll for updates. See the [example app](https://github.com/joakimk/dboard_example) for information on how to use it.
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
- require 'bundler'
2
- require 'rspec/core/rake_task'
1
+ require "bundler"
2
+ require "rspec/core/rake_task"
3
3
 
4
4
  Bundler::GemHelper.install_tasks
5
5
 
@@ -7,4 +7,4 @@ desc "Run specs"
7
7
  RSpec::Core::RakeTask.new do |t|
8
8
  end
9
9
 
10
- task :default => :spec
10
+ task default: :spec
data/dboard.gemspec CHANGED
@@ -6,22 +6,20 @@ Gem::Specification.new do |s|
6
6
  s.name = "dboard"
7
7
  s.version = Dboard::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Joakim Kolsjö"]
10
- s.email = ["joakim.kolsjo@gmail.com"]
9
+ s.authors = [ "Joakim Kolsjö" ]
10
+ s.email = [ "joakim.kolsjo@gmail.com" ]
11
11
  s.homepage = ""
12
12
  s.summary = %q{Dashboard framework}
13
13
  s.description = %q{Dashboard framework}
14
14
  s.license = "MIT"
15
+ s.metadata = { "rubygems_mfa_required" => "true" }
15
16
 
16
17
  s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {spec}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
- s.require_paths = ["lib"]
18
+ s.require_paths = [ "lib" ]
20
19
 
20
+ s.add_dependency "dalli"
21
21
  s.add_dependency "httparty"
22
- s.add_dependency "rake"
23
22
  s.add_dependency "json"
24
- s.add_dependency "dalli"
23
+ s.add_dependency "rake"
25
24
  s.add_dependency "sinatra"
26
- s.add_development_dependency "rspec", "2.6.0"
27
25
  end
data/lib/api.rb CHANGED
@@ -1,8 +1,9 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'cache'))
2
- require File.expand_path(File.join(File.dirname(__FILE__), 'collector.rb'))
3
- require 'digest/md5'
4
- require 'json'
5
- require 'httparty'
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "cache"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "collector.rb"))
3
+ require "digest/md5"
4
+ require "json"
5
+ require "httparty"
6
+ require "net_http_timeout_errors"
6
7
 
7
8
  module Dboard
8
9
  class Api
@@ -11,16 +12,37 @@ module Dboard
11
12
 
12
13
  class Client
13
14
  include HTTParty
15
+
16
+ def self.post(*args, **kwargs)
17
+ with_retries { super }
18
+ end
19
+
20
+ private
21
+
22
+ private_class_method \
23
+ def self.with_retries
24
+ remaining_attempts = 3
25
+
26
+ begin
27
+ remaining_attempts -= 1
28
+ yield
29
+ rescue *NetHttpTimeoutErrors.all
30
+ raise if remaining_attempts == 0
31
+
32
+ sleep((ENV["RACK_ENV"] == "test") ? 0 : 5)
33
+ retry
34
+ end
35
+ end
14
36
  end
15
37
 
16
38
  def self.get(params)
17
39
  types = {}
18
- params[:types].split(',').each do |type|
40
+ params[:types].split(",").each do |type|
19
41
  raw_data = CACHE.get("dashboard::source::#{type}")
20
42
  data = raw_data ? JSON.parse(raw_data) : {}
21
- types.merge!(type => { :data => data, :checksum => Digest::MD5.hexdigest(data.inspect) })
43
+ types.merge!(type => { data: data, checksum: Digest::MD5.hexdigest(data.inspect) })
22
44
  end
23
- { :version => (@@version || ENV["COMMIT_HASH"] || "unversioned"), :sources => types }.to_json
45
+ { version: (@@version || ENV["COMMIT_HASH"] || "unversioned"), sources: types }.to_json
24
46
  end
25
47
 
26
48
  def self.version=(version)
data/lib/cache.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'dalli'
1
+ require "dalli"
2
2
 
3
3
  module Dboard
4
4
  if Config.config[:memcache]
data/lib/collector.rb CHANGED
@@ -1,5 +1,5 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'publisher'))
2
- require 'singleton'
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "publisher"))
2
+ require "singleton"
3
3
 
4
4
  module Dboard
5
5
  class Collector
@@ -15,6 +15,10 @@ module Dboard
15
15
  Collector.instance.register_after_update_callback(callback)
16
16
  end
17
17
 
18
+ def self.register_error_callback(callback)
19
+ Collector.instance.register_error_callback(callback)
20
+ end
21
+
18
22
  def self.start
19
23
  instance.start
20
24
  end
@@ -22,6 +26,7 @@ module Dboard
22
26
  def initialize
23
27
  @sources = {}
24
28
  @after_update_callback = lambda {}
29
+ @error_callback = lambda { |exception| }
25
30
  end
26
31
 
27
32
  def start
@@ -45,6 +50,10 @@ module Dboard
45
50
  @after_update_callback = callback
46
51
  end
47
52
 
53
+ def register_error_callback(callback)
54
+ @error_callback = callback
55
+ end
56
+
48
57
  # Public because the old tests depend on it
49
58
  def update_source(source, instance)
50
59
  begin
@@ -56,6 +65,7 @@ module Dboard
56
65
  rescue Exception => ex
57
66
  puts "Failed to update #{source}: #{ex.message}"
58
67
  puts ex.backtrace
68
+ @error_callback.call(ex)
59
69
  end
60
70
 
61
71
  private
data/lib/dboard.rb CHANGED
@@ -1,3 +1,3 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'config.rb'))
2
- require File.expand_path(File.join(File.dirname(__FILE__), 'api.rb'))
3
- require File.expand_path(File.join(File.dirname(__FILE__), 'collector.rb'))
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "config.rb"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "api.rb"))
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "collector.rb"))
data/lib/publisher.rb CHANGED
@@ -1,9 +1,9 @@
1
- require 'json'
1
+ require "json"
2
2
 
3
3
  module Dboard
4
4
  class Publisher
5
5
  def self.publish(source, data)
6
- Api::Client.post("/sources/#{source}", :body => { :data => data.to_json }, :timeout => 10000)
6
+ Api::Client.post("/sources/#{source}", body: { data: data.to_json }, timeout: 10000)
7
7
  rescue SocketError => ex
8
8
  puts "SocketError: #{ex.message}"
9
9
  end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dboard
2
- VERSION = "1.0.5"
2
+ VERSION = "2.0.1"
3
3
  end
@@ -1,30 +1,46 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
2
 
3
- describe Dboard::Collector, "register_source" do
3
+ describe Dboard::Collector, ".register_source" do
4
4
  before do
5
5
  Dboard::Collector.instance.sources.clear
6
6
  end
7
7
 
8
8
  it "can register a source" do
9
- new_relic = mock
10
- new_relic.stub!(:update_interval).and_return(5)
9
+ new_relic = double
10
+ allow(new_relic).to receive(:update_interval).and_return(5)
11
11
  Dboard::Collector.register_source :new_relic, new_relic
12
- Dboard::Collector.instance.sources.should == { :new_relic => new_relic }
12
+ expect(Dboard::Collector.instance.sources).to eq({ new_relic: new_relic })
13
13
  end
14
14
 
15
15
  it "can register an after update callback" do
16
- new_relic = mock
17
- new_relic.stub!(:fetch).and_return({ :db => "100%" })
18
- callback = mock
16
+ new_relic = double
17
+ allow(new_relic).to receive(:fetch).and_return({ db: "100%" })
18
+ callback = double
19
19
  Dboard::Collector.register_after_update_callback callback
20
20
 
21
- callback.should_receive(:call)
22
- Dboard::Publisher.stub!(:publish)
21
+ expect(callback).to receive(:call)
22
+ allow(Dboard::Publisher).to receive(:publish)
23
23
  Dboard::Collector.instance.update_source(:new_relic, new_relic)
24
24
 
25
25
  # since it is a singleton, and this callbacks leaks into the other tests
26
26
  Dboard::Collector.register_after_update_callback(lambda {})
27
27
  end
28
+
29
+ it "can register an error callback" do
30
+ new_relic = double
31
+ error = RuntimeError.new("error")
32
+ allow(new_relic).to receive(:fetch).and_raise(error)
33
+ callback = double
34
+ Dboard::Collector.register_error_callback callback
35
+
36
+ expect(callback).to receive(:call).with(error)
37
+ allow(Dboard::Publisher).to receive(:publish)
38
+ allow_any_instance_of(Dboard::Collector).to receive(:puts)
39
+ Dboard::Collector.instance.update_source(:new_relic, new_relic)
40
+
41
+ # since it is a singleton, and this callbacks leaks into the other tests
42
+ Dboard::Collector.register_error_callback(lambda { |_| })
43
+ end
28
44
  end
29
45
 
30
46
  describe Dboard::Collector, "update_source" do
@@ -32,17 +48,17 @@ describe Dboard::Collector, "update_source" do
32
48
  Dboard::Collector.instance.sources.clear
33
49
  end
34
50
 
35
- it "should collect and publish data from sources" do
36
- new_relic = mock
37
- new_relic.stub!(:fetch).and_return({ :db => "100%" })
38
- Dboard::Publisher.should_receive(:publish).with(:new_relic, { :db => "100%" })
51
+ it "collects and publishes data from sources" do
52
+ new_relic = double
53
+ allow(new_relic).to receive(:fetch).and_return({ db: "100%" })
54
+ expect(Dboard::Publisher).to receive(:publish).with(:new_relic, { db: "100%" })
39
55
  Dboard::Collector.instance.update_source(:new_relic, new_relic)
40
56
  end
41
57
 
42
- it "should print out debugging info" do
43
- new_relic = mock
44
- new_relic.stub!(:fetch).and_raise(Exception.new("some error"))
45
- Dboard::Collector.instance.should_receive(:puts).twice
58
+ it "prints out debugging info" do
59
+ new_relic = double
60
+ allow(new_relic).to receive(:fetch).and_raise(Exception.new("some error"))
61
+ expect(Dboard::Collector.instance).to receive(:puts).twice
46
62
  Dboard::Collector.instance.update_source(:new_relic, new_relic)
47
63
  end
48
64
  end
@@ -1,16 +1,33 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
2
2
 
3
- describe "Publisher", "publish" do
4
-
5
- it "should send data to the dashboard server" do
6
- Dboard::Api::Client.should_receive(:post).with("/sources/new_relic", :body => { :data => { :db => "80%" }.to_json }, :timeout => 10000)
7
- Dboard::Publisher.publish(:new_relic, { :db => "80%" })
3
+ describe "Publisher", ".publish" do
4
+ it "sends data to the dashboard server" do
5
+ expect(Dboard::Api::Client).to receive(:post).with("/sources/new_relic", body: { data: { db: "80%" }.to_json }, timeout: 10000)
6
+ Dboard::Publisher.publish(:new_relic, { db: "80%" })
8
7
  end
9
8
 
10
- it "should handle and log socket errors" do
11
- Dboard::Api::Client.should_receive(:post).and_raise(SocketError.new("failed to connect"))
12
- Dboard::Publisher.should_receive(:puts).with("SocketError: failed to connect")
9
+ it "retries network errors" do
10
+ stub_request(:post, "http://api.example/sources/new_relic").
11
+ to_timeout.times(2).
12
+ to_return({ body: "OK!" })
13
+
13
14
  Dboard::Publisher.publish(:new_relic, {})
14
15
  end
15
16
 
17
+ it "raises network errors if we run out of retries" do
18
+ stub_request(:post, "http://api.example/sources/new_relic").
19
+ to_timeout.times(3).
20
+ to_return({ body: "OK!" })
21
+
22
+ expect {
23
+ Dboard::Publisher.publish(:new_relic, {})
24
+ }.to raise_error(Net::OpenTimeout)
25
+ end
26
+
27
+ # 2021-12-07: No idea why we've treated this one specially, but keeping it for now.
28
+ it "logs socket errors if we run out of retries" do
29
+ expect(Dboard::Api::Client).to receive(:post).and_raise(SocketError.new("failed to connect"))
30
+ expect(Dboard::Publisher).to receive(:puts).with("SocketError: failed to connect")
31
+ Dboard::Publisher.publish(:new_relic, {})
32
+ end
16
33
  end
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,12 @@
1
1
  require "rspec"
2
+ require 'webmock/rspec'
2
3
 
3
- ENV["RACK_ENV"] ||= 'test'
4
- ENV['API_URL'] = "http://localhost:20843"
5
- ENV['API_USER'] = 'test'
6
- ENV['API_PASSWORD'] = 'test'
4
+ ENV["RACK_ENV"] ||= "test"
5
+ ENV["API_URL"] = "http://api.example"
6
+ ENV["API_USER"] = "test"
7
+ ENV["API_PASSWORD"] = "test"
7
8
 
8
- require File.expand_path(File.join(File.dirname(__FILE__), '../lib/dboard'))
9
+ require File.expand_path(File.join(File.dirname(__FILE__), "../lib/dboard"))
9
10
 
10
- Dboard::Api::Client.basic_auth(ENV['API_USER'], ENV['API_PASSWORD'])
11
- Dboard::Api::Client.base_uri(ENV['API_URL'])
11
+ Dboard::Api::Client.basic_auth(ENV["API_USER"], ENV["API_PASSWORD"])
12
+ Dboard::Api::Client.base_uri(ENV["API_URL"])
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joakim Kolsjö
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-08 00:00:00.000000000 Z
11
+ date: 2021-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: httparty
14
+ name: dalli
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: httparty
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: dalli
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rspec
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - '='
88
- - !ruby/object:Gem::Version
89
- version: 2.6.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - '='
95
- - !ruby/object:Gem::Version
96
- version: 2.6.0
97
83
  description: Dashboard framework
98
84
  email:
99
85
  - joakim.kolsjo@gmail.com
@@ -101,8 +87,9 @@ executables: []
101
87
  extensions: []
102
88
  extra_rdoc_files: []
103
89
  files:
90
+ - ".github/workflows/ci.yml"
104
91
  - ".gitignore"
105
- - ".rvmrc"
92
+ - ".rubocop.yml"
106
93
  - Gemfile
107
94
  - LICENCE
108
95
  - README.md
@@ -116,13 +103,13 @@ files:
116
103
  - lib/publisher.rb
117
104
  - lib/version.rb
118
105
  - spec/collector_spec.rb
119
- - spec/integration_spec.rb
120
106
  - spec/publisher_spec.rb
121
107
  - spec/spec_helper.rb
122
108
  homepage: ''
123
109
  licenses:
124
110
  - MIT
125
- metadata: {}
111
+ metadata:
112
+ rubygems_mfa_required: 'true'
126
113
  post_install_message:
127
114
  rdoc_options: []
128
115
  require_paths:
@@ -138,8 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
125
  - !ruby/object:Gem::Version
139
126
  version: '0'
140
127
  requirements: []
141
- rubyforge_project:
142
- rubygems_version: 2.2.1
128
+ rubygems_version: 3.2.32
143
129
  signing_key:
144
130
  specification_version: 4
145
131
  summary: Dashboard framework
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm ruby-2.1.0@dboard --create
@@ -1,54 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
-
3
- # Test app
4
- require 'sinatra'
5
-
6
- get "/sources" do
7
- Dboard::Api.get(params)
8
- end
9
-
10
- post "/sources/:type" do
11
- Dboard::Api.update(params)
12
- end
13
-
14
- describe "Dashboard" do
15
-
16
- def app
17
- Sinatra::Application
18
- end
19
-
20
- def start_app
21
- app.port = 20843
22
- app.environment = 'test'
23
- @app_thread = Thread.new { app.run! }
24
- sleep 1
25
- end
26
-
27
- def stop_app
28
- @app_thread && @app_thread.kill
29
- end
30
-
31
- before do
32
- ENV['API_URL'] = "http://localhost:20843"
33
- ENV['API_USER'] = 'test'
34
- ENV['API_PASSWORD'] = 'test'
35
- @new_relic = mock
36
- @new_relic.stub!(:fetch).and_return({ :db => "33.3%", :memory => "33333 MB" })
37
- Dboard::CACHE.delete "dashboard::source::new_relic"
38
- end
39
-
40
- it "should collect stats and post them to the server" do
41
- start_app
42
- body = Dboard::Api::Client.get("/sources?types=new_relic")
43
- JSON.parse(body)["sources"]["new_relic"]["data"].should == {}
44
- Dboard::Collector.instance.update_source(:new_relic, @new_relic)
45
- body = Dboard::Api::Client.get("/sources?types=new_relic")
46
- JSON.parse(body)["sources"]["new_relic"]["data"].should == { "db" => "33.3%", "memory" => "33333 MB" }
47
- end
48
-
49
- after do
50
- stop_app
51
- end
52
-
53
- end
54
-