gecko-ess 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +91 -0
- data/Rakefile +2 -0
- data/gecko.gemspec +29 -0
- data/lib/gecko.rb +22 -0
- data/lib/gecko/configurator.rb +11 -0
- data/lib/gecko/graph/funnel.rb +67 -0
- data/lib/gecko/graph/geckometer.rb +39 -0
- data/lib/gecko/graph/line.rb +64 -0
- data/lib/gecko/graph/pie.rb +49 -0
- data/lib/gecko/http.rb +79 -0
- data/lib/gecko/version.rb +3 -0
- data/lib/gecko/widget.rb +75 -0
- data/lib/gecko/widget/number_secondary_stat.rb +23 -0
- data/lib/gecko/widget/rag.rb +21 -0
- data/lib/gecko/widget/rag_columns.rb +21 -0
- data/lib/gecko/widget/text.rb +67 -0
- data/spec/gecko/funnel_spec.rb +40 -0
- data/spec/gecko/geckometer_spec.rb +38 -0
- data/spec/gecko/line_spec.rb +42 -0
- data/spec/gecko/number_and_secondary_stat_spec.rb +32 -0
- data/spec/gecko/pie_spec.rb +36 -0
- data/spec/gecko/rag_spec.rb +48 -0
- data/spec/gecko/text_spec.rb +49 -0
- data/spec/gecko_spec.rb +31 -0
- data/spec/helper.rb +25 -0
- data/spec/http_spec.rb +141 -0
- data/spec/support/shared_examples_for_widget.rb +64 -0
- data/spec/widget_spec.rb +172 -0
- metadata +215 -0
data/lib/gecko/http.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'faraday_middleware/multi_json'
|
2
|
+
|
3
|
+
module Gecko
|
4
|
+
class Http
|
5
|
+
def initialize(*args)
|
6
|
+
@connection = Faraday.new(*args) do |connection|
|
7
|
+
connection.response :multi_json, :symbolize_keys => true
|
8
|
+
Gecko.config.connection_builder.call(connection) if Gecko.config.connection_builder.respond_to?(:call)
|
9
|
+
yield connection if block_given?
|
10
|
+
connection.adapter Faraday.default_adapter if connection.builder.handlers.none? { |handler| handler.klass < Faraday::Adapter }
|
11
|
+
end
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def headers
|
16
|
+
{
|
17
|
+
:accept => 'application/json',
|
18
|
+
:user_agent => Gecko.config.http_user_agent
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def encode_body(body)
|
23
|
+
MultiJson.encode(body)
|
24
|
+
end
|
25
|
+
|
26
|
+
def post(url, body, &request_block)
|
27
|
+
Result.new(@connection.post(url, self.encode_body(body), self.headers, &request_block))
|
28
|
+
end
|
29
|
+
|
30
|
+
class Result
|
31
|
+
Error = Struct.new(:text, :status) do
|
32
|
+
def to_s
|
33
|
+
self.text
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :response
|
38
|
+
attr_accessor :response_env
|
39
|
+
|
40
|
+
def initialize(response)
|
41
|
+
@response = response
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_complete(&block)
|
45
|
+
self.response.on_complete do |env|
|
46
|
+
self.response_env = env
|
47
|
+
block.call(self.success?, self)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def response_body
|
52
|
+
self.response.body
|
53
|
+
end
|
54
|
+
|
55
|
+
def success?
|
56
|
+
self.http_200? && !self.error?
|
57
|
+
end
|
58
|
+
|
59
|
+
def http_200?
|
60
|
+
self.response.status == 200
|
61
|
+
end
|
62
|
+
|
63
|
+
def error?
|
64
|
+
self.fetch(:success) != true
|
65
|
+
end
|
66
|
+
|
67
|
+
def error
|
68
|
+
Error.new(self.fetch(:error), self.response.status)
|
69
|
+
end
|
70
|
+
|
71
|
+
def fetch(*keys)
|
72
|
+
keys.inject(self.response_body) do |body_hash, key|
|
73
|
+
break unless body_hash
|
74
|
+
body_hash.fetch(key, nil)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/gecko/widget.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
module Gecko
|
2
|
+
class Widget
|
3
|
+
attr_reader :data, :keys
|
4
|
+
|
5
|
+
def initialize(*keys, &block)
|
6
|
+
self.keys = *keys
|
7
|
+
@on_update = nil
|
8
|
+
block.call(self) if block
|
9
|
+
raise ArgumentError, "1 or more widget keys are required" if self.keys.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
def keys=(*keys)
|
13
|
+
@keys = keys.flatten.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_update(&block)
|
17
|
+
@permanent_on_update = block
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def push_url(key)
|
22
|
+
Gecko.config.api_push_url.gsub(/:widget_key/, key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update(&on_update)
|
26
|
+
self.push_requests do |push_result, key|
|
27
|
+
push_result.on_complete do |*args|
|
28
|
+
args << key
|
29
|
+
@permanent_on_update.call(*args) if @permanent_on_update.respond_to?(:call)
|
30
|
+
on_update.call(*args) if on_update
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def push_requests(&each_result)
|
36
|
+
self.keys.map do |key|
|
37
|
+
http = Gecko::Http.new
|
38
|
+
push_result = http.post(self.push_url(key), self.payload)
|
39
|
+
each_result.call(push_result, key) if each_result
|
40
|
+
push_result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def config(&block)
|
45
|
+
block.call(self)
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def config!(&block)
|
50
|
+
self.config(&block).update
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def data_payload
|
55
|
+
{}
|
56
|
+
end
|
57
|
+
|
58
|
+
def payload
|
59
|
+
{
|
60
|
+
:api_key => Gecko.config.api_key,
|
61
|
+
:data => self.data_payload
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
require 'gecko/widget/number_secondary_stat'
|
68
|
+
require 'gecko/widget/rag'
|
69
|
+
require 'gecko/widget/rag_columns'
|
70
|
+
require 'gecko/widget/text'
|
71
|
+
|
72
|
+
require 'gecko/graph/pie'
|
73
|
+
require 'gecko/graph/geckometer'
|
74
|
+
require 'gecko/graph/funnel'
|
75
|
+
require 'gecko/graph/line'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Gecko
|
2
|
+
class Widget
|
3
|
+
class NumberSecondaryStat < Widget
|
4
|
+
attr_accessor :primary_text, :primary_value, :primary_prefix, :secondary_text, :secondary_value, :secondary_prefix, :absolute, :type
|
5
|
+
|
6
|
+
def data_payload
|
7
|
+
{:item =>
|
8
|
+
[{
|
9
|
+
:text => self.primary_text,
|
10
|
+
:value => self.primary_value,
|
11
|
+
:prefix => self.primary_prefix
|
12
|
+
}, {
|
13
|
+
:text => self.secondary_text,
|
14
|
+
:value => self.secondary_value,
|
15
|
+
:prefix => self.secondary_prefix
|
16
|
+
}],
|
17
|
+
:absolute => self.absolute,
|
18
|
+
:type => self.type
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Gecko
|
2
|
+
class Widget
|
3
|
+
class Rag < Widget
|
4
|
+
attr_accessor :green_text, :green_value, :amber_text, :amber_value, :red_text, :red_value
|
5
|
+
|
6
|
+
def data_payload
|
7
|
+
{:item =>
|
8
|
+
[{
|
9
|
+
:text => self.red_text,
|
10
|
+
:value => self.red_value
|
11
|
+
}, {
|
12
|
+
:text => self.amber_text,
|
13
|
+
:value => self.amber_value
|
14
|
+
}, {
|
15
|
+
:text => self.green_text,
|
16
|
+
:value => self.green_value
|
17
|
+
}]}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Gecko
|
2
|
+
class Widget
|
3
|
+
class RagColumns < Widget
|
4
|
+
attr_accessor :green_text, :green_value, :amber_text, :amber_value, :red_text, :red_value
|
5
|
+
|
6
|
+
def data_payload
|
7
|
+
{:item =>
|
8
|
+
[{
|
9
|
+
:text => self.red_text,
|
10
|
+
:value => self.red_value
|
11
|
+
}, {
|
12
|
+
:text => self.amber_text,
|
13
|
+
:value => self.amber_value
|
14
|
+
}, {
|
15
|
+
:text => self.green_text,
|
16
|
+
:value => self.green_value
|
17
|
+
}]}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Gecko
|
2
|
+
class Widget
|
3
|
+
class Text < Widget
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
class Item
|
7
|
+
TYPES = {
|
8
|
+
:normal => 0,
|
9
|
+
:alert => 1,
|
10
|
+
:info => 2
|
11
|
+
}
|
12
|
+
attr_accessor :text
|
13
|
+
attr_reader :type
|
14
|
+
|
15
|
+
def initialize(text = nil, type = :normal)
|
16
|
+
self.text = text
|
17
|
+
self.type = type
|
18
|
+
end
|
19
|
+
|
20
|
+
def type=(type)
|
21
|
+
@type = if type.kind_of?(Numeric)
|
22
|
+
TYPES.values.include?(type) ? type : nil
|
23
|
+
else
|
24
|
+
TYPES.fetch(type, nil)
|
25
|
+
end
|
26
|
+
@type or raise ArgumentError, "#{type} is not a valid text type"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(*args, &block)
|
31
|
+
super
|
32
|
+
@items = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset
|
36
|
+
@items.clear
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def each(&block)
|
41
|
+
@items.each(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def add(*args)
|
45
|
+
@items.push(Item.new(*args))
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](index)
|
49
|
+
@items[index]
|
50
|
+
end
|
51
|
+
|
52
|
+
def []=(index, *args)
|
53
|
+
@items[index] = Item.new(*args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete(index)
|
57
|
+
@items.delete_at(index)
|
58
|
+
end
|
59
|
+
|
60
|
+
def data_payload
|
61
|
+
{
|
62
|
+
:item => self.map{ |item| {:text => item.text, :type => item.type} }
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Gecko::Widget::Funnel do
|
4
|
+
it_behaves_like "a Gecko::Widget"
|
5
|
+
|
6
|
+
describe '#payload' do
|
7
|
+
before(:each) do
|
8
|
+
@widget = described_class.new('widget_key')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be empty by default' do
|
12
|
+
expect(@widget.payload).to be_a_valid_payload(
|
13
|
+
TEST_API_KEY,
|
14
|
+
{
|
15
|
+
:type => :standard,
|
16
|
+
:percentage => :show,
|
17
|
+
:item => []
|
18
|
+
}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be correct hash when values assigned' do
|
23
|
+
@widget.hide_percentage
|
24
|
+
@widget.reverse
|
25
|
+
@widget.add(100, 'benjamin')
|
26
|
+
@widget.add(1, 'washington')
|
27
|
+
expect(@widget.payload).to be_a_valid_payload(
|
28
|
+
TEST_API_KEY,
|
29
|
+
{
|
30
|
+
:type => :reverse,
|
31
|
+
:percentage => :hide,
|
32
|
+
:item => [
|
33
|
+
{:value => 100, :label => 'benjamin'},
|
34
|
+
{:value => 1, :label => 'washington'}
|
35
|
+
]
|
36
|
+
}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Gecko::Widget::Geckometer do
|
4
|
+
it_behaves_like "a Gecko::Widget"
|
5
|
+
|
6
|
+
describe '#payload' do
|
7
|
+
before(:each) do
|
8
|
+
@widget = described_class.new('widget_key')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be empty by default' do
|
12
|
+
expect(@widget.payload).to be_a_valid_payload(
|
13
|
+
TEST_API_KEY,
|
14
|
+
{
|
15
|
+
:item => nil,
|
16
|
+
:min => {:value => nil, :text => nil},
|
17
|
+
:max => {:value => nil, :text => nil}
|
18
|
+
}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be correct hash when values assigned' do
|
23
|
+
@widget.min_value = 0
|
24
|
+
@widget.max_value = 100
|
25
|
+
@widget.min_text = 'min'
|
26
|
+
@widget.max_text = 'max'
|
27
|
+
@widget.value = 50
|
28
|
+
expect(@widget.payload).to be_a_valid_payload(
|
29
|
+
TEST_API_KEY,
|
30
|
+
{
|
31
|
+
:item => 50,
|
32
|
+
:min => {:value => 0, :text => 'min'},
|
33
|
+
:max => {:value => 100, :text => 'max'}
|
34
|
+
}
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Gecko::Widget::Line do
|
4
|
+
it_behaves_like "a Gecko::Widget"
|
5
|
+
|
6
|
+
describe '#payload' do
|
7
|
+
before(:each) do
|
8
|
+
@widget = described_class.new('widget_key')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be empty by default' do
|
12
|
+
expect(@widget.payload).to be_a_valid_payload(
|
13
|
+
TEST_API_KEY,
|
14
|
+
{
|
15
|
+
:item => [],
|
16
|
+
:settings => {
|
17
|
+
:axisx => [],
|
18
|
+
:axisy => [],
|
19
|
+
:colour => nil
|
20
|
+
}
|
21
|
+
}
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should be correct hash when values assigned' do
|
26
|
+
@widget.x_axis = [1, 2, 3]
|
27
|
+
@widget.y_axis = %w(a b c)
|
28
|
+
@widget.add(5,6,7)
|
29
|
+
expect(@widget.payload).to be_a_valid_payload(
|
30
|
+
TEST_API_KEY,
|
31
|
+
{
|
32
|
+
:item => [5, 6, 7],
|
33
|
+
:settings => {
|
34
|
+
:axisx => [1, 2, 3],
|
35
|
+
:axisy => ['a', 'b', 'c'],
|
36
|
+
:colour => nil
|
37
|
+
}
|
38
|
+
}
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Gecko::Widget::NumberSecondaryStat do
|
4
|
+
it_behaves_like "a Gecko::Widget"
|
5
|
+
|
6
|
+
describe '#payload' do
|
7
|
+
before(:each) do
|
8
|
+
@widget = described_class.new('widget_key')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be empty by default' do
|
12
|
+
expect(@widget.payload).to be_a_valid_payload(
|
13
|
+
TEST_API_KEY,
|
14
|
+
{:item => [{:value => nil, :text => nil, :prefix => nil}, {:value => nil, :text => nil, :prefix => nil}], :absolute => nil, :type => nil}
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should be correct hash when values assigned' do
|
19
|
+
@widget.primary_text = 'text A'
|
20
|
+
@widget.primary_value = 1
|
21
|
+
@widget.primary_prefix = "$"
|
22
|
+
@widget.secondary_text = 'text B'
|
23
|
+
@widget.secondary_value = 2
|
24
|
+
@widget.absolute = "true"
|
25
|
+
@widget.type = "reverse"
|
26
|
+
expect(@widget.payload).to be_a_valid_payload(
|
27
|
+
TEST_API_KEY,
|
28
|
+
{:item => [{:value => 1, :text => 'text A', :prefix => "$"}, {:value => 2, :text => 'text B', :prefix => nil}], :absolute => "true", :type => "reverse"}
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|