suricate 0.0.2
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.
- checksums.yaml +7 -0
- data/lib/suricate/application.rb +90 -0
- data/lib/suricate/charts/chart.rb +15 -0
- data/lib/suricate/charts/chart_builder.rb +22 -0
- data/lib/suricate/charts/chart_serie.rb +19 -0
- data/lib/suricate/charts/chart_serie_builder.rb +29 -0
- data/lib/suricate/configuration/configuration.rb +13 -0
- data/lib/suricate/configuration/configuration_builder.rb +29 -0
- data/lib/suricate/configuration/widget_configuration.rb +24 -0
- data/lib/suricate/configuration/widget_configurations_builder.rb +64 -0
- data/lib/suricate/delegation_callback.rb +22 -0
- data/lib/suricate/generator/assets/javascript/api.js +53 -0
- data/lib/suricate/generator/assets/javascript/application.js +97 -0
- data/lib/suricate/generator/assets/javascript/chart-js-chart-factory.js +54 -0
- data/lib/suricate/generator/assets/javascript/suricate.js +4 -0
- data/lib/suricate/generator/assets/javascript/vendor/Chart.min.js +11 -0
- data/lib/suricate/generator/assets/javascript/vendor/bootstrap.min.js +7 -0
- data/lib/suricate/generator/assets/javascript/vendor/jquery.min.js +4 -0
- data/lib/suricate/generator/assets/javascript/widget-factory.js +43 -0
- data/lib/suricate/generator/assets/javascript/widget-updater.js +48 -0
- data/lib/suricate/generator/assets/javascript/widget-view.js +46 -0
- data/lib/suricate/generator/assets/javascript/widgets/counter-widget.js +23 -0
- data/lib/suricate/generator/assets/javascript/widgets/line-chart-widget.js +26 -0
- data/lib/suricate/generator/assets/javascript/widgets/widget.js +46 -0
- data/lib/suricate/generator/assets/javascript/widgets-container.js +22 -0
- data/lib/suricate/generator/assets/stylesheets/suricate.css +85 -0
- data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap-theme.css.map +1 -0
- data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap-theme.min.css +5 -0
- data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap.css.map +1 -0
- data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap.min.css +5 -0
- data/lib/suricate/output_driver.rb +28 -0
- data/lib/suricate/refinements/string.rb +27 -0
- data/lib/suricate/request_context.rb +14 -0
- data/lib/suricate/sinatra_output_driver.rb +14 -0
- data/lib/suricate/template/template.rb +11 -0
- data/lib/suricate/template/template_repository.rb +41 -0
- data/lib/suricate/version.rb +3 -0
- data/lib/suricate/widget_repository.rb +23 -0
- data/lib/suricate/widgets/counter_widget.rb +10 -0
- data/lib/suricate/widgets/line_chart_widget.rb +9 -0
- data/lib/suricate/widgets/responses/chart_widget_response.rb +17 -0
- data/lib/suricate/widgets/responses/counter_widget_response.rb +18 -0
- data/lib/suricate/widgets/widget.rb +31 -0
- data/lib/suricate.rb +50 -0
- metadata +200 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fafd76c3fca39a449326894d87061f3fb75d3bd3
|
4
|
+
data.tar.gz: a0e8c87b6667fdda4c24ec5b7a5a8499ad0e7110
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f7bc99b933c0fd737d6f930b4340ca66679ca367f4b924c1b65f4bc0e9ae5e4c40d103dfe74a80bbc7c09c550af9225d3e46d58a5435a67c707df860251befb6
|
7
|
+
data.tar.gz: a81dac32f5283ff2e91ec188c250592db16a734f86f2eb2bf50b0c3ce639cdd7045fb6400a6044d7c36d312eda2b4a1a4b6c97d45ca443999b8463dcd155316b
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Suricate
|
5
|
+
class Application < Sinatra::Base
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@configuration, :widget_repository, :template_repository
|
8
|
+
|
9
|
+
def initialize(configuration)
|
10
|
+
super(nil)
|
11
|
+
# TODO : switch to Rack directly
|
12
|
+
# Good : hide Sinatra complexity from outside,
|
13
|
+
# the interface will be the same when switchting to Rack
|
14
|
+
# Bad : set class var from instance
|
15
|
+
self.class.set(:configuration, configuration)
|
16
|
+
@configuration = configuration
|
17
|
+
end
|
18
|
+
|
19
|
+
configure do
|
20
|
+
set :public_folder, Proc.new { configuration.public_directory }
|
21
|
+
set :views, Proc.new { configuration.templates_directory }
|
22
|
+
set :show_exceptions, :after_handler
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
#
|
29
|
+
# Errors
|
30
|
+
#
|
31
|
+
|
32
|
+
error WidgetRepository::WidgetNotFound do
|
33
|
+
output.api_error(404, env['sinatra.error'].message)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# API
|
41
|
+
#
|
42
|
+
|
43
|
+
# Get widgets' configuration
|
44
|
+
get('/api/widgets') do
|
45
|
+
configurations = widget_repository.configurations.map(&:to_h)
|
46
|
+
output.api_success(widgets: configurations)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get widget's data
|
50
|
+
get('/api/widgets/:id') do
|
51
|
+
widget = widget_repository.instantiate(params['id'], context)
|
52
|
+
widget.execute
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
#
|
59
|
+
# Pages
|
60
|
+
#
|
61
|
+
|
62
|
+
# Pages
|
63
|
+
get('/:page') do
|
64
|
+
render_page(params['page'])
|
65
|
+
end
|
66
|
+
|
67
|
+
# Default page
|
68
|
+
get('/') do
|
69
|
+
render_page(@configuration.default_page)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
def context
|
77
|
+
RequestContext.new(request: request, session: session, output: output)
|
78
|
+
end
|
79
|
+
|
80
|
+
def output
|
81
|
+
@output ||= SinatraOutputDriver.new(self)
|
82
|
+
end
|
83
|
+
|
84
|
+
def render_page(page)
|
85
|
+
template = template_repository.find_page(page)
|
86
|
+
output.render(template.render)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Suricate
|
2
|
+
class ChartBuilder
|
3
|
+
def initialize
|
4
|
+
@series = []
|
5
|
+
@labels = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def serie
|
9
|
+
builder = ChartSerieBuilder.new
|
10
|
+
yield builder
|
11
|
+
@series << builder.serie
|
12
|
+
end
|
13
|
+
|
14
|
+
def labels(labels)
|
15
|
+
@labels = labels
|
16
|
+
end
|
17
|
+
|
18
|
+
def chart
|
19
|
+
Chart.new(@labels, @series)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Suricate
|
2
|
+
class ChartSerie
|
3
|
+
attr_reader :name, :values, :color
|
4
|
+
|
5
|
+
def initialize(name, values, color)
|
6
|
+
@name = name
|
7
|
+
@values = values
|
8
|
+
@color = color
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
{
|
13
|
+
name: @name,
|
14
|
+
values: @values,
|
15
|
+
color: @color
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Suricate
|
2
|
+
class ChartSerieBuilder
|
3
|
+
def initialize
|
4
|
+
@values = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def name(name)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def color(color)
|
12
|
+
@color = color
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add multiple values
|
16
|
+
def values(values)
|
17
|
+
@values.concat(values)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add one value
|
21
|
+
def value(value)
|
22
|
+
@values << value
|
23
|
+
end
|
24
|
+
|
25
|
+
def serie
|
26
|
+
ChartSerie.new(@name, @values, @color)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Suricate
|
2
|
+
class Configuration
|
3
|
+
attr_reader :template_repository, :public_directory, :widget_repository,
|
4
|
+
:default_page
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@template_repository = options[:template_repository]
|
8
|
+
@public_directory = options[:public_directory]
|
9
|
+
@widget_repository = options[:widget_repository]
|
10
|
+
@default_page = options[:default_page]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Suricate
|
2
|
+
class ConfigurationBuilder
|
3
|
+
attr_accessor :templates_directory, :public_directory, :default_page
|
4
|
+
|
5
|
+
def configuration
|
6
|
+
Configuration.new(template_repository: template_repository,
|
7
|
+
widget_repository: widget_repository,
|
8
|
+
default_page: @default_page,
|
9
|
+
public_directory: @public_directory)
|
10
|
+
end
|
11
|
+
|
12
|
+
def widgets
|
13
|
+
yield widget_configurations_builder
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def template_repository
|
18
|
+
TemplateRepository.new(@templates_directory)
|
19
|
+
end
|
20
|
+
|
21
|
+
def widget_repository
|
22
|
+
WidgetRepository.new(widget_configurations_builder.configurations)
|
23
|
+
end
|
24
|
+
|
25
|
+
def widget_configurations_builder
|
26
|
+
@widgets_builder ||= WidgetConfigurationsBuilder.new(template_repository)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Suricate
|
2
|
+
class WidgetConfiguration
|
3
|
+
attr_reader :id, :klass, :collector, :options
|
4
|
+
|
5
|
+
def initialize(id, klass, collector, options = {})
|
6
|
+
@id = id
|
7
|
+
@klass = klass
|
8
|
+
@collector = collector
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def instantiate(context)
|
13
|
+
@klass.new(id: id, context: context, collector: collector, options: options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_h
|
17
|
+
{
|
18
|
+
id: @id,
|
19
|
+
type: @klass.type,
|
20
|
+
configuration: @options
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Suricate
|
2
|
+
class WidgetConfigurationsBuilder
|
3
|
+
using Refinements::String
|
4
|
+
class IDAlreadyUsedError < StandardError; end
|
5
|
+
attr_reader :configurations
|
6
|
+
|
7
|
+
def initialize(template_repository)
|
8
|
+
@template_repository = template_repository
|
9
|
+
@configurations = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def counter(id, collector, options = {})
|
13
|
+
register(id, CounterWidget, collector, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def line_chart(id, collector, options = {})
|
17
|
+
register(id, LineChartWidget, collector, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def register(id, klass, collector, options = {})
|
21
|
+
id = id.to_sym
|
22
|
+
if find_with_id(id)
|
23
|
+
raise IDAlreadyUsedError.new("id \"#{id}\" already taken")
|
24
|
+
else
|
25
|
+
configuration = build_configuration(id, klass, collector, options)
|
26
|
+
@configurations << configuration
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def build_configuration(id, klass, collector, options)
|
32
|
+
build_options(id, klass.type, options)
|
33
|
+
WidgetConfiguration.new(id.to_sym, klass, collector, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_options(id, type, options)
|
37
|
+
options[:template] = find_widget_template(id, type, options[:template])
|
38
|
+
if options[:templates]
|
39
|
+
options[:templates].map! do |name|
|
40
|
+
@template_repository.find_widget(name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_widget_template(id, type, wanted_template)
|
46
|
+
template = nil
|
47
|
+
default_template = type.underscore.sub(/_widget$/, '')
|
48
|
+
template_names = [wanted_template || id.to_s, default_template]
|
49
|
+
|
50
|
+
template_names.each do |name|
|
51
|
+
begin
|
52
|
+
template = @template_repository.find_widget(name) and break
|
53
|
+
rescue TemplateRepository::TemplateNotFound
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
template.render if template
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_with_id(id)
|
61
|
+
@configurations.find { |conf| conf.id == id }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Suricate
|
2
|
+
class DelegationCallback
|
3
|
+
def initialize(*callbacks)
|
4
|
+
callbacks.each do |callback|
|
5
|
+
define_callback(callback)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(callback_name, *args)
|
10
|
+
callback = instance_variable_get(:"@#{callback_name}_callback")
|
11
|
+
callback.call(*args) if callback
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
private
|
16
|
+
def define_callback(callback)
|
17
|
+
define_singleton_method callback.to_sym do |&block|
|
18
|
+
instance_variable_set(:"@#{callback}_callback", block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
(function () {
|
2
|
+
"use strict";
|
3
|
+
window.Suricate = window.Suricate || {};
|
4
|
+
|
5
|
+
/*
|
6
|
+
* Public
|
7
|
+
*/
|
8
|
+
|
9
|
+
Suricate.API = function(baseURL) {
|
10
|
+
this.baseURL = baseURL;
|
11
|
+
};
|
12
|
+
|
13
|
+
// Get widget configurations
|
14
|
+
Suricate.API.prototype.getWidgets = function(callback) {
|
15
|
+
this.sendRequest("GET", "/widgets", {}, function(data) {
|
16
|
+
callback(data.widgets);
|
17
|
+
});
|
18
|
+
};
|
19
|
+
|
20
|
+
// Get updated data for widget
|
21
|
+
Suricate.API.prototype.getWidgetData = function(widgetID, callback) {
|
22
|
+
var path = "/widgets/" + widgetID;
|
23
|
+
this.sendRequest("GET", path, {}, callback);
|
24
|
+
};
|
25
|
+
|
26
|
+
|
27
|
+
/*
|
28
|
+
* Private
|
29
|
+
*/
|
30
|
+
|
31
|
+
// Send request to API
|
32
|
+
Suricate.API.prototype.sendRequest = function(method, path, params, callback) {
|
33
|
+
var self = this;
|
34
|
+
var url = this.baseURL + path;
|
35
|
+
$.ajax({
|
36
|
+
url: url,
|
37
|
+
method: method,
|
38
|
+
data: params,
|
39
|
+
success: function(data) {
|
40
|
+
self.handleResponse(url, data, callback);
|
41
|
+
}
|
42
|
+
});
|
43
|
+
};
|
44
|
+
|
45
|
+
Suricate.API.prototype.handleResponse = function(url, json, callback) {
|
46
|
+
if(json.status == 200) {
|
47
|
+
callback(json.data);
|
48
|
+
}
|
49
|
+
else {
|
50
|
+
console.error("Failed to load request", "url", url, "response", json);
|
51
|
+
}
|
52
|
+
};
|
53
|
+
}());
|
@@ -0,0 +1,97 @@
|
|
1
|
+
(function() {
|
2
|
+
"use strict";
|
3
|
+
window.Suricate = window.Suricate || {};
|
4
|
+
|
5
|
+
Suricate.Application = function(widgetsContainer, configuration) {
|
6
|
+
this.widgetsContainer = new Suricate.WidgetsContainer(widgetsContainer);
|
7
|
+
this.configuration = configuration;
|
8
|
+
this.chartFactory = configuration.chartFactory || new Suricate.ChartJSChartFactory();
|
9
|
+
this.initialized = false;
|
10
|
+
this.widgets = [];
|
11
|
+
this.updateInterval = undefined;
|
12
|
+
};
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
// Starts the application
|
17
|
+
Suricate.Application.prototype.start = function() {
|
18
|
+
var self = this;
|
19
|
+
this.init(function() {
|
20
|
+
self.startUpdates();
|
21
|
+
});
|
22
|
+
};
|
23
|
+
|
24
|
+
Suricate.Application.prototype.stop = function() {
|
25
|
+
this.stopUpdates();
|
26
|
+
};
|
27
|
+
|
28
|
+
|
29
|
+
// Creates and return an object
|
30
|
+
// to communicate with the API
|
31
|
+
Suricate.Application.prototype.createAPI = function() {
|
32
|
+
var baseURL = location.protocol + "//" + location.host + "/api";
|
33
|
+
return new Suricate.API(baseURL);
|
34
|
+
};
|
35
|
+
|
36
|
+
Suricate.Application.prototype.getWidgetsContainer = function() {
|
37
|
+
return this.widgetsContainer;
|
38
|
+
};
|
39
|
+
|
40
|
+
Suricate.Application.prototype.getChartFactory = function() {
|
41
|
+
return this.chartFactory;
|
42
|
+
};
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
/*
|
47
|
+
* Private
|
48
|
+
*/
|
49
|
+
|
50
|
+
Suricate.Application.prototype.init = function(callback) {
|
51
|
+
if(!this.initialized) {
|
52
|
+
var self = this;
|
53
|
+
this.initWidgets(function() {
|
54
|
+
self.initialized = true;
|
55
|
+
callback();
|
56
|
+
});
|
57
|
+
}
|
58
|
+
else {
|
59
|
+
callback();
|
60
|
+
}
|
61
|
+
};
|
62
|
+
|
63
|
+
Suricate.Application.prototype.initWidgets = function(callback) {
|
64
|
+
var api = this.createAPI();
|
65
|
+
var self = this;
|
66
|
+
api.getWidgets(function(configurations) {
|
67
|
+
var factory = new Suricate.WidgetFactory(self, configurations);
|
68
|
+
self.widgets = factory.buildWidgets();
|
69
|
+
callback();
|
70
|
+
});
|
71
|
+
};
|
72
|
+
|
73
|
+
Suricate.Application.prototype.startUpdates = function() {
|
74
|
+
if(this.updateInterval === undefined) {
|
75
|
+
var self = this;
|
76
|
+
this.update();
|
77
|
+
this.updateInterval = window.setInterval(function() {
|
78
|
+
self.update();
|
79
|
+
}, 20000);
|
80
|
+
}
|
81
|
+
};
|
82
|
+
|
83
|
+
Suricate.Application.prototype.stopUpdates = function() {
|
84
|
+
if(this.updateInterval !== undefined) {
|
85
|
+
window.clearInterval(this.updateInterval);
|
86
|
+
this.updateInterval = undefined;
|
87
|
+
}
|
88
|
+
};
|
89
|
+
|
90
|
+
Suricate.Application.prototype.update = function() {
|
91
|
+
var now = new Date();
|
92
|
+
for (var widgetIndex = 0, l = this.widgets.length; widgetIndex < l; widgetIndex ++) {
|
93
|
+
var widget = this.widgets[widgetIndex];
|
94
|
+
widget.update(now);
|
95
|
+
}
|
96
|
+
};
|
97
|
+
}());
|
@@ -0,0 +1,54 @@
|
|
1
|
+
(function() {
|
2
|
+
"use strict";
|
3
|
+
window.Suricate = window.Suricate || {};
|
4
|
+
|
5
|
+
Suricate.ChartJSChartFactory = function() {
|
6
|
+
|
7
|
+
};
|
8
|
+
|
9
|
+
Suricate.ChartJSChartFactory.prototype.createLineChart = function(container, options) {
|
10
|
+
var chart = {};
|
11
|
+
var libraryObject;
|
12
|
+
|
13
|
+
var convertData = function(data) {
|
14
|
+
var datasets = [];
|
15
|
+
for (var index = 0, seriesCount = data.series.length; index < seriesCount; index++) {
|
16
|
+
var serie = data.series[index];
|
17
|
+
datasets.push({
|
18
|
+
label: serie.name,
|
19
|
+
strokeColor: serie.color,
|
20
|
+
fillColor: "rgba(0, 0, 0, 0)",
|
21
|
+
pointStrokeColor: serie.color,
|
22
|
+
pointColor: serie.color,
|
23
|
+
data: serie.values
|
24
|
+
});
|
25
|
+
}
|
26
|
+
|
27
|
+
return {
|
28
|
+
labels: data.labels,
|
29
|
+
datasets: datasets
|
30
|
+
};
|
31
|
+
};
|
32
|
+
|
33
|
+
var init = function(data, options) {
|
34
|
+
var ctx = container.find(".chart")[0].getContext("2d");
|
35
|
+
libraryObject = new Chart(ctx).Line(data, options);
|
36
|
+
var legend = libraryObject.generateLegend();
|
37
|
+
container.find(".legend").html(legend);
|
38
|
+
};
|
39
|
+
|
40
|
+
var data = {
|
41
|
+
labels: [],
|
42
|
+
datasets: [ { } ]
|
43
|
+
};
|
44
|
+
init(data, options);
|
45
|
+
|
46
|
+
chart.update = function(data) {
|
47
|
+
var chartJSData = convertData(data);
|
48
|
+
libraryObject.destroy();
|
49
|
+
init(chartJSData, options);
|
50
|
+
};
|
51
|
+
|
52
|
+
return chart;
|
53
|
+
};
|
54
|
+
}());
|