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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/lib/suricate/application.rb +90 -0
  3. data/lib/suricate/charts/chart.rb +15 -0
  4. data/lib/suricate/charts/chart_builder.rb +22 -0
  5. data/lib/suricate/charts/chart_serie.rb +19 -0
  6. data/lib/suricate/charts/chart_serie_builder.rb +29 -0
  7. data/lib/suricate/configuration/configuration.rb +13 -0
  8. data/lib/suricate/configuration/configuration_builder.rb +29 -0
  9. data/lib/suricate/configuration/widget_configuration.rb +24 -0
  10. data/lib/suricate/configuration/widget_configurations_builder.rb +64 -0
  11. data/lib/suricate/delegation_callback.rb +22 -0
  12. data/lib/suricate/generator/assets/javascript/api.js +53 -0
  13. data/lib/suricate/generator/assets/javascript/application.js +97 -0
  14. data/lib/suricate/generator/assets/javascript/chart-js-chart-factory.js +54 -0
  15. data/lib/suricate/generator/assets/javascript/suricate.js +4 -0
  16. data/lib/suricate/generator/assets/javascript/vendor/Chart.min.js +11 -0
  17. data/lib/suricate/generator/assets/javascript/vendor/bootstrap.min.js +7 -0
  18. data/lib/suricate/generator/assets/javascript/vendor/jquery.min.js +4 -0
  19. data/lib/suricate/generator/assets/javascript/widget-factory.js +43 -0
  20. data/lib/suricate/generator/assets/javascript/widget-updater.js +48 -0
  21. data/lib/suricate/generator/assets/javascript/widget-view.js +46 -0
  22. data/lib/suricate/generator/assets/javascript/widgets/counter-widget.js +23 -0
  23. data/lib/suricate/generator/assets/javascript/widgets/line-chart-widget.js +26 -0
  24. data/lib/suricate/generator/assets/javascript/widgets/widget.js +46 -0
  25. data/lib/suricate/generator/assets/javascript/widgets-container.js +22 -0
  26. data/lib/suricate/generator/assets/stylesheets/suricate.css +85 -0
  27. data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap-theme.css.map +1 -0
  28. data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap-theme.min.css +5 -0
  29. data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap.css.map +1 -0
  30. data/lib/suricate/generator/assets/stylesheets/vendor/bootstrap.min.css +5 -0
  31. data/lib/suricate/output_driver.rb +28 -0
  32. data/lib/suricate/refinements/string.rb +27 -0
  33. data/lib/suricate/request_context.rb +14 -0
  34. data/lib/suricate/sinatra_output_driver.rb +14 -0
  35. data/lib/suricate/template/template.rb +11 -0
  36. data/lib/suricate/template/template_repository.rb +41 -0
  37. data/lib/suricate/version.rb +3 -0
  38. data/lib/suricate/widget_repository.rb +23 -0
  39. data/lib/suricate/widgets/counter_widget.rb +10 -0
  40. data/lib/suricate/widgets/line_chart_widget.rb +9 -0
  41. data/lib/suricate/widgets/responses/chart_widget_response.rb +17 -0
  42. data/lib/suricate/widgets/responses/counter_widget_response.rb +18 -0
  43. data/lib/suricate/widgets/widget.rb +31 -0
  44. data/lib/suricate.rb +50 -0
  45. 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,15 @@
1
+ module Suricate
2
+ class Chart
3
+ def initialize(labels, series)
4
+ @labels = labels
5
+ @series = series
6
+ end
7
+
8
+ def to_h
9
+ {
10
+ labels: @labels,
11
+ series: @series.map(&:to_h)
12
+ }
13
+ end
14
+ end
15
+ 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
+ }());
@@ -0,0 +1,4 @@
1
+ (function() {
2
+ "use strict";
3
+ window.Suricate = {};
4
+ }());